From 5fe0576dcbf47954957007296839e43471b088f9 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Tue, 26 Mar 2013 20:33:10 +0000 Subject: [PATCH 001/949] Working on 1.9 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index c0ef34c6..f00937f0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 1.8) +AC_INIT(tmux, 1.9) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) @@ -41,7 +41,7 @@ AC_CHECK_HEADERS( ) # Is this a debug build? -#found_debug=yes +found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, create a debug build), From 982354765bc6f0bfb225d7c1f96e5b73f9880533 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 27 Mar 2013 11:17:12 +0000 Subject: [PATCH 002/949] Remove tmux's (already minimal) 88 colour support. Such terminals are few and unnecessary. --- colour.c | 26 -------------------------- options-table.c | 2 +- server-client.c | 2 -- tmux.h | 6 ++---- tty-term.c | 4 +--- tty.c | 32 ++++---------------------------- 6 files changed, 8 insertions(+), 64 deletions(-) diff --git a/colour.c b/colour.c index ff492687..9e90596f 100644 --- a/colour.c +++ b/colour.c @@ -287,29 +287,3 @@ colour_256to16(u_char c) return (table[c]); } - -/* Convert 256 colour palette to 88. */ -u_char -colour_256to88(u_char c) -{ - static const u_char table[256] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 17, 18, 18, 19, 20, 21, 21, 22, 22, 23, 20, 21, 21, 22, - 22, 23, 24, 25, 25, 26, 26, 27, 24, 25, 25, 26, 26, 27, 28, 29, - 29, 30, 30, 31, 32, 33, 33, 34, 34, 35, 36, 37, 37, 38, 38, 39, - 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, 40, 41, 41, 42, - 42, 43, 44, 45, 45, 46, 46, 47, 32, 33, 33, 34, 34, 35, 36, 37, - 37, 38, 38, 39, 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, - 40, 41, 41, 42, 42, 43, 44, 45, 45, 46, 46, 47, 48, 49, 49, 50, - 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, 54, 55, 56, 57, - 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, 61, 62, 62, 63, - 48, 49, 49, 50, 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, - 54, 55, 56, 57, 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, - 61, 62, 62, 63, 64, 65, 65, 66, 66, 67, 68, 69, 69, 70, 70, 71, - 68, 69, 69, 70, 70, 71, 72, 73, 73, 74, 74, 75, 72, 73, 73, 74, - 74, 75, 76, 77, 77, 78, 78, 79, 0, 0, 80, 80, 80, 81, 81, 81, - 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87 - }; - - return (table[c]); -} diff --git a/options-table.c b/options-table.c index ae711882..c4ada1f2 100644 --- a/options-table.c +++ b/options-table.c @@ -414,7 +414,7 @@ const struct options_table_entry session_options_table[] = { { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, - .default_str = "*88col*:colors=88,*256col*:colors=256" + .default_str = "*256col*:colors=256" ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cc=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Cs=\\E[%p1%d q:Csr=\\E[2 q,screen*:XT" diff --git a/server-client.c b/server-client.c index f61912bc..ac4ecf29 100644 --- a/server-client.c +++ b/server-client.c @@ -981,8 +981,6 @@ server_client_msg_identify( c->tty.flags |= TTY_UTF8; if (data->flags & IDENTIFY_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; - else if (data->flags & IDENTIFY_88COLOURS) - c->tty.term_flags |= TERM_88COLOURS; tty_resize(&c->tty); diff --git a/tmux.h b/tmux.h index 10da2bee..053b574e 100644 --- a/tmux.h +++ b/tmux.h @@ -481,7 +481,7 @@ struct msg_identify_data { #define IDENTIFY_UTF8 0x1 #define IDENTIFY_256COLOURS 0x2 -#define IDENTIFY_88COLOURS 0x4 +/* 0x4 unused */ #define IDENTIFY_CONTROL 0x8 #define IDENTIFY_TERMIOS 0x10 int flags; @@ -1141,8 +1141,7 @@ struct tty_term { struct tty_code codes[NTTYCODE]; #define TERM_256COLOURS 0x1 -#define TERM_88COLOURS 0x2 -#define TERM_EARLYWRAP 0x4 +#define TERM_EARLYWRAP 0x2 int flags; LIST_ENTRY(tty_term) entry; @@ -1986,7 +1985,6 @@ void colour_set_bg(struct grid_cell *, int); const char *colour_tostring(int); int colour_fromstring(const char *); u_char colour_256to16(u_char); -u_char colour_256to88(u_char); /* attributes.c */ const char *attributes_tostring(u_char); diff --git a/tty-term.c b/tty-term.c index c827a444..254569f6 100644 --- a/tty-term.c +++ b/tty-term.c @@ -404,11 +404,9 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) goto error; } - /* Figure out if we have 256 or 88 colours. */ + /* Figure out if we have 256. */ if (tty_term_number(term, TTYC_COLORS) == 256) term->flags |= TERM_256COLOURS; - if (tty_term_number(term, TTYC_COLORS) == 88) - term->flags |= TERM_88COLOURS; /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 diff --git a/tty.c b/tty.c index 75a2f657..bffddac6 100644 --- a/tty.c +++ b/tty.c @@ -35,7 +35,6 @@ void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); int tty_try_256(struct tty *, u_char, const char *); -int tty_try_88(struct tty *, u_char, const char *); void tty_colours(struct tty *, const struct grid_cell *); void tty_check_fg(struct tty *, struct grid_cell *); @@ -1446,9 +1445,7 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { /* And not a 256 colour mode? */ - if (!(tty->term->flags & TERM_88COLOURS) && - !(tty->term_flags & TERM_88COLOURS) && - !(tty->term->flags & TERM_256COLOURS) && + if (!(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->fg = colour_256to16(gc->fg); if (gc->fg & 8) { @@ -1481,9 +1478,7 @@ tty_check_bg(struct tty *tty, struct grid_cell *gc) * palette. Bold background doesn't exist portably, so just * discard the bold bit if set. */ - if (!(tty->term->flags & TERM_88COLOURS) && - !(tty->term_flags & TERM_88COLOURS) && - !(tty->term->flags & TERM_256COLOURS) && + if (!(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->bg = colour_256to16(gc->bg); if (gc->bg & 8) @@ -1511,11 +1506,9 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { - /* Try as 256 colours or translating to 88. */ + /* Try as 256 colours. */ if (tty_try_256(tty, fg, "38") == 0) goto save_fg; - if (tty_try_88(tty, fg, "38") == 0) - goto save_fg; /* Else already handled by tty_check_fg. */ return; } @@ -1546,11 +1539,9 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { - /* Try as 256 colours or translating to 88. */ + /* Try as 256 colours. */ if (tty_try_256(tty, bg, "48") == 0) goto save_bg; - if (tty_try_88(tty, bg, "48") == 0) - goto save_bg; /* Else already handled by tty_check_bg. */ return; } @@ -1591,21 +1582,6 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) return (0); } -int -tty_try_88(struct tty *tty, u_char colour, const char *type) -{ - char s[32]; - - if (!(tty->term->flags & TERM_88COLOURS) && - !(tty->term_flags & TERM_88COLOURS)) - return (-1); - colour = colour_256to88(colour); - - xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); - tty_puts(tty, s); - return (0); -} - void tty_bell(struct tty *tty) { From 5e4d9a3197a229ecb30d51f5b7e6756ef31dc1d2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 27 Mar 2013 11:19:19 +0000 Subject: [PATCH 003/949] Move the cursor back into the last column on CUU/CUD to match xterm behaviour. From George Nachman. --- screen-write.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/screen-write.c b/screen-write.c index 348065c6..c0935c95 100644 --- a/screen-write.c +++ b/screen-write.c @@ -488,6 +488,8 @@ screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) if (ny > s->cy - s->rupper) ny = s->cy - s->rupper; } + if (s->cx == screen_size_x(s)) + s->cx--; if (ny == 0) return; @@ -512,6 +514,8 @@ screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) if (ny > s->rlower - s->cy) ny = s->rlower - s->cy; } + if (s->cx == screen_size_x(s)) + s->cx--; if (ny == 0) return; From 7f636587098593c6c0efd3feaecb970aa7170116 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 27 Mar 2013 11:24:18 +0000 Subject: [PATCH 004/949] Add TMUX_TMPDIR variable to put the socket directory outside TMPDIR. From Ben Boeckel. --- tmux.1 | 13 +++++-------- tmux.c | 15 ++++++--------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/tmux.1 b/tmux.1 index 0dc68bfe..9b1488fc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -98,10 +98,6 @@ The options are as follows: Force .Nm to assume the terminal supports 256 colours. -.It Fl 8 -Like -.Fl 2 , -but indicates that the terminal supports 88 colours. .It Fl C Start in control mode. Given twice @@ -145,11 +141,12 @@ session created, and continues to process the rest of the configuration file. .It Fl L Ar socket-name .Nm stores the server socket in a directory under -.Pa /tmp -(or +.Ev TMUX_TMPDIR , .Ev TMPDIR -if set); -the default socket is named +if it is unset, or +.Pa /tmp +if both are unset. +The default socket is named .Em default . This option allows a different socket name to be specified, allowing several independent diff --git a/tmux.c b/tmux.c index 368562f8..1f4057dc 100644 --- a/tmux.c +++ b/tmux.c @@ -164,10 +164,12 @@ makesocketpath(const char *label) u_int uid; uid = getuid(); - if ((s = getenv("TMPDIR")) == NULL || *s == '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); - else + if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') + xsnprintf(base, sizeof base, "%s/", s); + else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); + else + xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) return (NULL); @@ -244,15 +246,10 @@ main(int argc, char **argv) quiet = flags = 0; label = path = NULL; login_shell = (**argv == '-'); - while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUv")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { switch (opt) { case '2': flags |= IDENTIFY_256COLOURS; - flags &= ~IDENTIFY_88COLOURS; - break; - case '8': - flags |= IDENTIFY_88COLOURS; - flags &= ~IDENTIFY_256COLOURS; break; case 'c': free(shell_cmd); From 629cfec8a3f9da2837a74f42d56143c11bc93b90 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 27 Mar 2013 23:37:05 +0000 Subject: [PATCH 005/949] Trivial typo fixes in changes. --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index dba58be0..b9ad4e69 100644 --- a/CHANGES +++ b/CHANGES @@ -17,7 +17,7 @@ Normal Changes * run-shell learnt '-t' to specify the pane to use when displaying output. * Support for middle-click pasting. * choose-tree learns '-u' to start uncollapsed. -* select-window learnt '-T; to toggle to the last window if it's already +* select-window learnt '-T' to toggle to the last window if it's already current. * New session option 'assume-paste-time' for pasting text versus key-binding actions. @@ -37,9 +37,9 @@ Normal Changes the 'source-file' command. * 'copy-pipe' mode command to copy selection and pipe the selection to a command. -* Changes panes can now emit focus notifications for certain applications +* Panes can now emit focus notifications for certain applications which use those. -* run-shell and if-shell now accept format placeholders. +* run-shell and if-shell now accept formats. * resize-pane learnt '-Z' for zooming a pane temporarily. * new-session learnt '-A' to make it behave like attach-session. * set-option learnt '-o' to prevent setting an option which is already set. From 64ea8829af8719c0dc1eb7c0d2c9933a7b3d6e04 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 Mar 2013 00:00:13 +0000 Subject: [PATCH 006/949] Add define for timersub to compat.h. --- compat.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compat.h b/compat.h index b9ee7ba1..622006e1 100644 --- a/compat.h +++ b/compat.h @@ -152,6 +152,18 @@ typedef uint64_t u_int64_t; } while (0) #endif +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + #ifndef TTY_NAME_MAX #define TTY_NAME_MAX 32 #endif From dc2af8347b5072a827d564623af8d7f10261e7f0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 Mar 2013 08:36:34 +0000 Subject: [PATCH 007/949] New code doesn't build on old versions of OS X so only support 10.7 and later. Reported by Jared Scheel and tested by Chris Johnsen. --- osdep-darwin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osdep-darwin.c b/osdep-darwin.c index 23de9d52..3b5e9d3c 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -33,6 +34,7 @@ struct event_base *osdep_event_init(void); char * osdep_get_name(int fd, unused char *tty) { +#ifdef __MAC_10_7 struct proc_bsdshortinfo bsdinfo; pid_t pgrp; int ret; @@ -44,6 +46,7 @@ osdep_get_name(int fd, unused char *tty) &bsdinfo, sizeof bsdinfo); if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0') return (strdup(bsdinfo.pbsi_comm)); +#endif return (NULL); } From 4b0ed56e32a97cf8f7fc45a72da11c3dcd6792ca Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 Mar 2013 12:42:00 +0000 Subject: [PATCH 008/949] Tidy up and trim down TODO file. --- TODO | 227 +++++++++++++++++++++++++---------------------------------- 1 file changed, 95 insertions(+), 132 deletions(-) diff --git a/TODO b/TODO index a57dc932..7d9641eb 100644 --- a/TODO +++ b/TODO @@ -1,155 +1,118 @@ -NOTES -===== +- command bits and pieces: + * use "--" to mark start of command w/ neww etc to avoid quoting + * why doesn't command-prompt work if made read-only? + * allow multiple targets: fnmatch for -t/-c, for example detach all + clients with -t* + * add -c for new-session like new-window + * attach should take a pane and select it as well as attaching + * ' and " should be parsed the same (eg "\e" vs '\e') in config + and command prompt + * last-pane across sessions -This file describes rough notes regarding ideas for potential future tmux -development. It's not necessarily guaranteed that items in this TODO file -will ever get implemented. +- make command sequences more usable + * don't require space after ; + * options for error handling: && and ||? -It is asked therefore, that anyone thinking of undertaking a task in this -TODO file, email tmux-users@lists.sf.net to discuss the feature. +- options bits and pieces: + * set-remain-on-exit is a complete hack + * way to set socket path from config file -Thie file is split up between tmux user interface (UI) issues, and terminal -compatibility issues. +- format improvements: + * option to quote format (#{session_name:quoted}) + * formats need conditions for >0 (for #P) + * some way to pad # stuff with spaces, #!2T maybe + * status stuff is redundant with formats -TMUX UI ISSUES -============== +- choose mode improvements: + * choose-pane command (augment choose-tree to do this?) + * choose-mode and copy-mode are very similar, make choose-mode a subset? + * flag to choose-* for sort order + * chose mode would be better per client than per window + +- improve monitor-*: + * straighten out rules for multiple clients + * think about what happens across sessions + * monitor changes within a region + * perhaps monitor /all/ panes in the window not just one + +- panning over window (window larger than visible) -- implicitly add exec to the commands for new windows (switch to disable it)? -- bring back detach-session to detach all clients on a session? -- allow fnmatch for -c, so that you can, eg, detach all clients -- garbage collect window history (100 lines at a time?) if it hasn't been used - in $x time -- flags to centre screen in window -- activity/bell should be per-window not per-link? what if it is cur win in - session not being watched? -- should be able to move to a hidden pane and it would be moved into view. pane - number in status line/top-right would be cool for this -- support other mouse modes (highlight etc) and use it in copy mode -- set-remain-on-exit is a bit of a hack, some way to do it generically? -- would be nice to be able to use "--" to mark start of command w/ neww etc - to avoid quoting -- make command sequences more usable: don't require space after ;, handle - errors better -- choice and more mode would be better per client than per window? -- hooks to which commands may be attached, for example: tmux add-hook - "new-session" if-shell "[ -e $HOME/.tmux-session.conf ]" source-file - $HOME/.tmux-session.conf -- way to set socket path from config file -- warts on current naming: - - display-time but message-fg/bg/attr - - list-* vs show-* - - server-info - - up-pane/down-pane/swap-pane -U/swap-pane -D vs next-*/previous-* - - split-window -> split-pane?? -- some way to force a screen to use the entire terminal even if it is forced - to be smaller by other clients. pan smaller terminal? (like screen F) - -- idea of a "view" onto a window, need base x/y offsets for redraw -- commands should be able to succeed or fail and have || or && for command - lists -- some way to keep a command running continually and just use its last line of - output -- UTF-8 to a non-UTF-8 terminal should not be able to balls up - the terminal - www/ruby-addressable; make regress -- support esc-esc to quit in modes -- fix ctrl+F1-F4 output. to what? -- better utf8 support: window names, prompt input, message display -- option to move copy mode indicator into status line -- selection behaviour closer to vi in vi mode -- live update: server started with -U connects to server, requests sessions and - windows, receives fds -- sort out inheriting config from shell on new sessions/windows: - should pick up default-path/termios/etc from client if possible, - else leave empty/default - link panes into multiple windows -- bells should be passed between sessions with visual-bell etc - sequence until its shell exits, to allow them to be used from the config file -- better session sharing: create-socket command to create socket somewhere (-r - flag for readonly) -- multiline status line (no?) -- support title stack, both internally and externally + +- improve mouse support: + * bind commands to mouse in different areas? + * more fine-grained options + * commands executed when clicking on a pattern (URL) + +- hooks! + +- warts on current naming: + * display-time but message-fg/bg/attr + * list-* vs show-* + * server-info + * split-window -> split-pane?? + +- way to keep a job running just read its last line of output for #() + +- better UTF-8 support: + * window names and titles + * message display + * prompt input + * multibyte key input + +- live update: server started with -U connects to server, requests sessions and + windows, receives file descriptors + +- there are inconsistencies in what we get from old shell and what comes from + config for new sessions and windows + +- multiline status line? + +- support title stack, both internally and externally (restore on detach) http://docs.freebsd.org/cgi/getmsg.cgi?fetch=1149299+0+archive/2010/freebsd-questions/20100207.freebsd-questions -- some way to pad # stuff with spaces, #!2T maybe -- a binding to "scroll down and exit at bottom" copy mode -- some way to pass keystrokes in copy mode through to underlying window. why? -- last window update time and # replacement for it for display-message -- find-window across sessions - other ways to make session handling easier? -- ' and " should be parsed the same (eg "\e" vs '\e') in config and command - prompt? -- command to toggle selection not to move it in copy-mode -- audit of escape sequence support vs xterm -- support binding keys to mouse (mouse-select-pane -> mouse-keys or something, - mouse click == select-pane -t %%, mouse scroll up == copy-mode) -- bind commands to key sequences? -- make it so ALL keys go through a table, - first an implicit table in which C-b is the only default binding to a - command that says "next key from $othertable" and so on. means -n can - go away as well -- monitor, bell etc should monitor /all/ panes in the window not just one -- a history of commands that can be reversed (reverse member of each command, - and a buffer) -- info() when changing to same window -- way to add dest for break-pane; maybe some easier way to unbreak-pane -- case insensitive searching -- incremental searching in copy mode. -- configurable borders and empty space filler for when panes < window? -- mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the - flag on w/o checking the others before calling tty_update_mode) -- pass shell commands as argv rather than strings, allow them to be specified - in commands without quotes -- named buffers and allow gaps in the stack -- monitor-activity is broken in several ways with multiple clients -- monitor-activity should be more powerful (eg set a region) -- maybe a way to put pane names instead of window names in status line -- support for borderless panes -- wait-for command 20130222153957.GY6782@yelena.nicm.ath.cx -- last-pane across sessions + +- last window update time and format for it + +- bind commands to key sequences -- make it so ALL keys go through a table, + first an implicit table in which C-b is the only default binding to a command + that says "next key from $othertable" and so on. means -n can go away as well + +- why are -s and -t different for swap- and move-pane? + +- copy/paste improvements: + * case insensitive searching + * incremental searching + * append to buffer + * paste w/o trailing whitespace + * named buffers and allow gaps in the stack + * command to toggle selection not to move it in copy-mode + +- mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the flag + on w/o checking the others before calling tty_update_mode) + - panes should have names like windows -- command-prompt doesn't work if made read-only. why? -- option to quote format eg #{session_name:quoted} -- formats need conditions for >0 (for #P) -- fetch full command line on !Linux, and add option to strip prefixes - such as "sh " "/bin/sh " etc etc -- synchronize-windows option -- append to buffer in copy mode -- way to paste w/o trailing whitespace -- flag to switch-client to switch all clients -- history of layouts and undo/redo flags to selectl + - way to tag a layout as a number/name + - optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx -- support multibyte key strings -- allow commands to be executed when certain patterns in a screen - are clicked on with the mouse -- flag to make next/previous commands skip a window -- way to do tmux command/run-shell from mode keys -- send command to all windows -- choose-pane command (augment choose-tree to do this?) -- choose-mode and copy-mode are very similar. Perhaps make choose-mode a subset - of copy-mode in that it inherits key-bindings and other traits but not all -- add -c for new-session like new-window -- flag to choose-* for sort order (eg sort windows/sessions/clients by last - used time) - perhaps using formats (but what about numeric sort)? + - instead of separate window and session options, just one master options list with each option having a type (window or session), then options on window, on session, and global. for window options we look window->session->global, and for session we look session->global + - maybe keep last layout + size around and if size reverts just put it back + - way to set hints/limits about pane size for resizing + - revamp layouts: they are too complicated, should be more closely integrated, should support hints, layout sets should just be a special case of custom layouts, and we should support panes that are not attached to a cell at all. this could be the time to introduce panelink to replace layout_cell -- run-shell/if-shell should support formats -- attach should take a pane and select it as well as attaching -- attach should have a flag to create session if it doesn't exist. or better - new a flag to attach it - -TERMINAL ISSUES -================ - use a better termcap internally instead of screen, perhaps xterm -- clear window title on exit (see using xterm title stack) -- get it passing all the vttest tests that don't require resizing the terminal -- support for bce + - use screen-256color when started on 256 colour terminal? -* We need a tmux terminfo entry to document the extensions we are using in - upstream terminfo. Must NOT change (only add or remove) anything from - TERM=screen so we can fallback! + +- we need a tmux terminfo entry to document the extensions we are using in + upstream terminfo From 66afcf5be092ed0ab86d1d4059426823e72c63d5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 Mar 2013 15:07:42 +0000 Subject: [PATCH 009/949] Make copy-mode -u still scroll up if already in copy mode, handy for people who bind it with -n. --- cmd-copy-mode.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 59b5a9e5..883a9376 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -54,9 +54,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wp, &window_copy_mode) != 0) - return (CMD_RETURN_NORMAL); - window_copy_init_from_pane(wp); + if (wp->mode != &window_copy_mode) { + if (window_pane_set_mode(wp, &window_copy_mode) != 0) + return (CMD_RETURN_NORMAL); + window_copy_init_from_pane(wp); + } if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp); From 738e789dbd7712ab94073036cf4e903abc68447f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 Mar 2013 15:08:12 +0000 Subject: [PATCH 010/949] If -s to swap-pane is not given, use the current pane. --- cmd-swap-pane.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index e7ea6242..e6b46d67 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -75,8 +75,12 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - } else - return (CMD_RETURN_NORMAL); + } else { + src_wl = cmd_find_pane(cmdq, NULL, NULL, &src_wp); + if (src_wl == NULL) + return (CMD_RETURN_ERROR); + src_w = src_wl->window; + } } else { src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) From 3ea893464f0deb382a41dbab6a4de12c008a4fd9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 12:43:08 +0100 Subject: [PATCH 011/949] TODO changes. --- TODO | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TODO b/TODO index 7d9641eb..d3e3b46b 100644 --- a/TODO +++ b/TODO @@ -27,7 +27,7 @@ * choose-pane command (augment choose-tree to do this?) * choose-mode and copy-mode are very similar, make choose-mode a subset? * flag to choose-* for sort order - * chose mode would be better per client than per window + * choose mode would be better per client than per window - improve monitor-*: * straighten out rules for multiple clients @@ -77,8 +77,6 @@ first an implicit table in which C-b is the only default binding to a command that says "next key from $othertable" and so on. means -n can go away as well -- why are -s and -t different for swap- and move-pane? - - copy/paste improvements: * case insensitive searching * incremental searching From 46b3c1a02564268ab810d0f54190f1e8fc1f4f6d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 12:46:29 +0100 Subject: [PATCH 012/949] Use proc_bsdinfo which works on older OS X versions, from OZAKI Kiichi. --- osdep-darwin.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osdep-darwin.c b/osdep-darwin.c index 3b5e9d3c..dc60b092 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -18,7 +18,6 @@ #include -#include #include #include #include @@ -34,19 +33,17 @@ struct event_base *osdep_event_init(void); char * osdep_get_name(int fd, unused char *tty) { -#ifdef __MAC_10_7 - struct proc_bsdshortinfo bsdinfo; + struct proc_bsdinfo bsdinfo; pid_t pgrp; int ret; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); - ret = proc_pidinfo(pgrp, PROC_PIDT_SHORTBSDINFO, 0, + ret = proc_pidinfo(pgrp, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof bsdinfo); - if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0') - return (strdup(bsdinfo.pbsi_comm)); -#endif + if (ret == sizeof bsdinfo && *bsdinfo.pbi_comm != '\0') + return (strdup(bsdinfo.pbi_comm)); return (NULL); } From 69c86379e39476013205fce627951dd733d647b3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 11:51:16 +0000 Subject: [PATCH 013/949] Remove some code not needed on OpenBSD. --- server-client.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/server-client.c b/server-client.c index ac4ecf29..6cacefa7 100644 --- a/server-client.c +++ b/server-client.c @@ -534,18 +534,8 @@ server_client_check_resize(struct window_pane *wp) ws.ws_col = wp->sx; ws.ws_row = wp->sy; - 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) -#endif + if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) fatal("ioctl failed"); - } wp->flags &= ~PANE_RESIZE; } From 743bd1275fecb7e33c8bc1cbeeeb065384ca3f9d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 12:52:40 +0100 Subject: [PATCH 014/949] Need errno.h, reported by Swaroop M S. --- server-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-client.c b/server-client.c index 77e6de78..0ab50b5d 100644 --- a/server-client.c +++ b/server-client.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include From e312db140868754358d40ec17595327a8fbbf180 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 13:04:19 +0100 Subject: [PATCH 015/949] Add wait-for to tmux.vim from Ben Boeckel. --- examples/tmux.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 076115c1..e85f8ff6 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -56,7 +56,7 @@ syn keyword tmuxCmds \ list-buffers loadb load-buffer pasteb paste-buffer saveb save-buffer \ setb set-buffer showb show-buffer \ clock-mode if[-shell] lock[-server] run[-shell] server-info info - \ choose-list + \ choose-list wait-for syn keyword tmuxOptsSet \ buffer-limit escape-time exit-unattached exit-unattached quiet From 20f0d917beb0f774af2628ed7efe2a33cf59f42a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 12:07:18 +0000 Subject: [PATCH 016/949] Missed -o from set-window-option, from Ben Boeckel. --- tmux.1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 9b1488fc..b38b43bb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2645,7 +2645,7 @@ The default is .Ql \ -_@ . .El .It Xo Ic set-window-option -.Op Fl agqu +.Op Fl agoqu .Op Fl t Ar target-window .Ar option Ar value .Xc @@ -2654,6 +2654,7 @@ Set a window option. The .Fl a , .Fl g , +.Fl o , .Fl q and .Fl u From 7ada64d5f8a0df39229c41b992c0ee8ac9f0a1d7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 12:15:36 +0000 Subject: [PATCH 017/949] Fix bug where end guard in control mode was not printed after session destroyed, from George Nachman. --- cmd-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-queue.c b/cmd-queue.c index b1c0a4eb..a64d332c 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -158,7 +158,7 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard) { struct client *c = cmdq->client; - if (c == NULL || c->session == NULL) + if (c == NULL) return 0; if (!(c->flags & CLIENT_CONTROL)) return 0; From 9fcda95a6f55f017536cdf24366754a2304c1059 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Apr 2013 12:20:35 +0000 Subject: [PATCH 018/949] Set EV_WRITE for jobs or run/if-shell jobs can hang. From Chris Johnsen. --- job.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/job.c b/job.c index b76b3345..291e000e 100644 --- a/job.c +++ b/job.c @@ -109,7 +109,7 @@ job_run(const char *cmd, struct session *s, job->event = bufferevent_new(job->fd, NULL, job_write_callback, job_callback, job); - bufferevent_enable(job->event, EV_READ); + bufferevent_enable(job->event, EV_READ|EV_WRITE); log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); return (job); From cbee283c26968304b473e2191d2bb5f52208b58d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 11 Apr 2013 07:27:27 +0000 Subject: [PATCH 019/949] Send an SGR0 after turning on modifyOtherKeys to fix Terminal.app which treats \033[>4;1m and \033[4;1m (bold+underline). Reported & tested by otto@. --- tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty.c b/tty.c index bffddac6..74988eb3 100644 --- a/tty.c +++ b/tty.c @@ -220,7 +220,7 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) - tty_puts(tty, "\033[c\033[>4;1m\033[?1004h"); + tty_puts(tty, "\033[c\033[>4;1m\033[?1004h\033[m"); tty->cx = UINT_MAX; tty->cy = UINT_MAX; @@ -283,7 +283,7 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) - tty_raw(tty, "\033[>4m\033[?1004l"); + tty_raw(tty, "\033[>4m\033[?1004l\033[m"); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); From 4ccb2e2c218a10a8039ca801231aaf09b6bf3bce Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 11 Apr 2013 22:45:05 +0100 Subject: [PATCH 020/949] TODO tweaks. --- TODO | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index d3e3b46b..92b60a9a 100644 --- a/TODO +++ b/TODO @@ -47,7 +47,7 @@ - hooks! - warts on current naming: - * display-time but message-fg/bg/attr + * display-time but message-fg/bg/attr * list-* vs show-* * server-info * split-window -> split-pane?? @@ -55,6 +55,7 @@ - way to keep a job running just read its last line of output for #() - better UTF-8 support: + * #22T can split in the middle of UTF-8 characters! * window names and titles * message display * prompt input From 7f9b225cc269211b86a4c4d2168146c217d63118 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 11 Apr 2013 21:52:18 +0000 Subject: [PATCH 021/949] Call setlocale(LC_TIME) at startup. --- clock.c | 15 +++++++++++---- tmux.c | 3 +++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/clock.c b/clock.c index 00d818be..283a4a1e 100644 --- a/clock.c +++ b/clock.c @@ -103,13 +103,20 @@ clock_draw(struct screen_write_ctx *ctx, int colour, int style) struct grid_cell gc; char tim[64], *ptr; time_t t; + struct tm *tm; u_int i, j, x, y, idx; t = time(NULL); - if (style == 0) - strftime(tim, sizeof tim, "%l:%M %p", localtime(&t)); - else - strftime(tim, sizeof tim, "%H:%M", localtime(&t)); + tm = localtime(&t); + if (style == 0) { + strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); + if (tm->tm_hour >= 12) + strlcat(tim, "PM", sizeof tim); + else + strlcat(tim, "AM", sizeof tim); + } else + strftime(tim, sizeof tim, "%H:%M", tm); + screen_write_clearscreen(ctx); diff --git a/tmux.c b/tmux.c index 1f4057dc..e9b28d7e 100644 --- a/tmux.c +++ b/tmux.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -243,6 +244,8 @@ main(int argc, char **argv) malloc_options = (char *) "AFGJPX"; #endif + setlocale(LC_TIME, ""); + quiet = flags = 0; label = path = NULL; login_shell = (**argv == '-'); From caa8290510244990b26106e027aa253237ada629 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 12 Apr 2013 12:44:31 +0000 Subject: [PATCH 022/949] Copy the client into the new cmdq in source-file so commands that work on it (such as new-session) can work. Fixes issue reported by oss-adv at users dot sf dot net. --- cmd-source-file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-source-file.c b/cmd-source-file.c index 1bd2bb0a..f50efbe3 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -49,6 +49,7 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) char *cause; cmdq1 = cmdq_new(NULL); + cmdq1->client = cmdq->client; cmdq1->emptyfn = cmd_source_file_done; cmdq1->data = cmdq; From 27dcf470dc4aa5901ac7f01b3a9f971e02f2229e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 12 Apr 2013 12:50:36 +0000 Subject: [PATCH 023/949] Remove some Korean characters from the zero-width list that apparently shouldn't be there, from Jeong Mok Cho. --- utf8.c | 1 - 1 file changed, 1 deletion(-) diff --git a/utf8.c b/utf8.c index b276d872..1c81392b 100644 --- a/utf8.c +++ b/utf8.c @@ -171,7 +171,6 @@ struct utf8_width_entry utf8_width_table[] = { { 0x30000, 0x3fffd, 2, NULL, NULL }, { 0x00711, 0x00711, 0, NULL, NULL }, { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, - { 0x01160, 0x011ff, 0, NULL, NULL }, { 0x0180b, 0x0180d, 0, NULL, NULL }, { 0x10a3f, 0x10a3f, 0, NULL, NULL }, { 0x00981, 0x00981, 0, NULL, NULL }, From 2c4543b9e9bd38bcc45393dad94930bcde872e6c Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 14 Apr 2013 18:07:08 +0100 Subject: [PATCH 024/949] Add back missing -V flag This went walkies from a previous git commit. --- tmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.c b/tmux.c index e8fd98b2..e6de5cfe 100644 --- a/tmux.c +++ b/tmux.c @@ -253,7 +253,7 @@ main(int argc, char **argv) quiet = flags = 0; label = path = NULL; login_shell = (**argv == '-'); - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { switch (opt) { case '2': flags |= IDENTIFY_256COLOURS; From c24b58e2ee8691870736959deb252c225b205b4d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 16 Apr 2013 11:33:53 +0100 Subject: [PATCH 025/949] Generate tmux.1 using mdoc2man.awk on Solaris, issue brought up and changes tested by Dagobert Michelsen. --- .gitignore | 1 + Makefile.am | 14 +- configure.ac | 5 + mdoc2man.awk | 370 ++++++++++++++++++++++++++++++++++++++++++++ tmux.1 => tmux.1.in | 0 5 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 mdoc2man.awk rename tmux.1 => tmux.1.in (100%) diff --git a/.gitignore b/.gitignore index c3906ada..3b400861 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ tmux Makefile Makefile.in configure +tmux.1 diff --git a/Makefile.am b/Makefile.am index 2ce54b1a..b85eae0c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,12 +2,14 @@ # Obvious program stuff. bin_PROGRAMS = tmux -dist_man1_MANS = tmux.1 +nodist_man1_MANS = tmux.1 +CLEANFILES = tmux.1 # Distribution tarball options. EXTRA_DIST = \ CHANGES FAQ README TODO examples compat \ - array.h compat.h tmux.h osdep-*.c + array.h compat.h tmux.h osdep-*.c mdoc2man.awk \ + tmux.1.in dist-hook: grep "^#found_debug=" configure find $(distdir) -name .svn -type d|xargs rm -Rf @@ -232,6 +234,14 @@ if NO_B64_NTOP nodist_tmux_SOURCES += compat/b64_ntop.c endif +# Build tmux.1 in the right format. +tmux.1: tmux.1.in + if test x@MANFORMAT@ = xmdoc; then \ + cp tmux.1.in tmux.1; \ + else \ + $(AWK) -fmdoc2man.awk tmux.1.in >tmux.1; \ + fi + # Update SF web site. upload-index.html: update-index.html scp www/index.html www/main.css www/images/*.png \ diff --git a/configure.ac b/configure.ac index f00937f0..b4f76f5f 100644 --- a/configure.ac +++ b/configure.ac @@ -416,6 +416,10 @@ else AC_MSG_RESULT(no) fi +# Man page defaults to mdoc. +MANFORMAT=mdoc +AC_SUBST(MANFORMAT) + # Figure out the platform for osdep-*.c and forkpty-*.c. AC_MSG_CHECKING(platform) case "$host_os" in @@ -455,6 +459,7 @@ case "$host_os" in *solaris*) AC_MSG_RESULT(sunos) PLATFORM=sunos + MANFORMAT=man ;; *hpux*) AC_MSG_RESULT(hpux) diff --git a/mdoc2man.awk b/mdoc2man.awk new file mode 100644 index 00000000..80e8d5ff --- /dev/null +++ b/mdoc2man.awk @@ -0,0 +1,370 @@ +#!/usr/bin/awk +# +# $Id: mdoc2man.awk,v 1.9 2009/10/24 00:52:42 dtucker Exp $ +# +# Version history: +# v4+ Adapted for OpenSSH Portable (see cvs Id and history) +# v3, I put the program under a proper license +# Dan Nelson added .An, .Aq and fixed a typo +# v2, fixed to work on GNU awk --posix and MacOS X +# v1, first attempt, didn't work on MacOS X +# +# Copyright (c) 2003 Peter Stuge +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +BEGIN { + optlist=0 + oldoptlist=0 + nospace=0 + synopsis=0 + reference=0 + block=0 + ext=0 + extopt=0 + literal=0 + prenl=0 + breakw=0 + line="" +} + +function wtail() { + retval="" + while(w0;i--) { + add(refauthors[i]) + if(i>1) + add(", ") + } + if(nrefauthors>1) + add(" and ") + if(nrefauthors>0) + add(refauthors[0] ", ") + add("\\fI" reftitle "\\fP") + if(length(refissue)) + add(", " refissue) + if(length(refreport)) { + add(", " refreport) + } + if(length(refdate)) + add(", " refdate) + if(length(refopt)) + add(", " refopt) + add(".") + reference=0 + } else if(reference) { + if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() } + if(match(words[w],"^%T$")) { + reftitle=wtail() + sub("^\"","",reftitle) + sub("\"$","",reftitle) + } + if(match(words[w],"^%N$")) { refissue=wtail() } + if(match(words[w],"^%D$")) { refdate=wtail() } + if(match(words[w],"^%O$")) { refopt=wtail() } + if(match(words[w],"^%R$")) { refreport=wtail() } + } else if(match(words[w],"^Nm$")) { + if(synopsis) { + add(".br") + prenl++ + } + n=words[++w] + if(!length(name)) + name=n + if(!length(n)) + n=name + add("\\fB" n "\\fP") + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Nd$")) { + add("\\- " wtail()) + } else if(match(words[w],"^Fl$")) { + add("\\fB\\-" words[++w] "\\fP") + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Ar$")) { + add("\\fI") + if(w==nwords) + add("file ...\\fP") + else { + add(words[++w] "\\fP") + while(match(words[w+1],"^\\|$")) + add(OFS words[++w] " \\fI" words[++w] "\\fP") + } + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Cm$")) { + add("\\fB" words[++w] "\\fP") + while(w") + if(option) + add("]") + if(ext&&!extopt&&!match(line," $")) + add(OFS) + if(!ext&&!extopt&&length(line)) { + print line + prenl=0 + line="" + } +} diff --git a/tmux.1 b/tmux.1.in similarity index 100% rename from tmux.1 rename to tmux.1.in From 88428cff3a0ef2e2013e990679dc746c58a99d12 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 17 Apr 2013 08:41:41 +0000 Subject: [PATCH 026/949] %zu format for size_t. --- job.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/job.c b/job.c index 291e000e..0ae03b31 100644 --- a/job.c +++ b/job.c @@ -144,8 +144,8 @@ job_write_callback(unused struct bufferevent *bufev, void *data) struct job *job = data; size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); - log_debug("job write %p: %s, pid %ld, output left %lu", job, job->cmd, - (long) job->pid, (unsigned long) len); + log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, + (long) job->pid, len); if (len == 0) { shutdown(job->fd, SHUT_WR); From 55640a31b3fbe5fd9b3eb4537779e2ffac58f47d Mon Sep 17 00:00:00 2001 From: Theo Deraadt Date: Wed, 17 Apr 2013 14:52:31 +0000 Subject: [PATCH 027/949] (long long) and %lld for time_t output ok nicm --- format.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/format.c b/format.c index b357876e..9918cfe8 100644 --- a/format.c +++ b/format.c @@ -288,7 +288,7 @@ format_session(struct format_tree *ft, struct session *s) format_add(ft, "session_group", "%u", session_group_index(sg)); t = s->creation_time.tv_sec; - format_add(ft, "session_created", "%ld", (long) t); + format_add(ft, "session_created", "%lld", (long long) t); tim = ctime(&t); *strchr(tim, '\n') = '\0'; format_add(ft, "session_created_string", "%s", tim); @@ -314,13 +314,13 @@ format_client(struct format_tree *ft, struct client *c) format_add(ft, "client_termname", "%s", c->tty.termname); t = c->creation_time.tv_sec; - format_add(ft, "client_created", "%ld", (long) t); + format_add(ft, "client_created", "%lld", (long long) t); tim = ctime(&t); *strchr(tim, '\n') = '\0'; format_add(ft, "client_created_string", "%s", tim); t = c->activity_time.tv_sec; - format_add(ft, "client_activity", "%ld", (long) t); + format_add(ft, "client_activity", "%lld", (long long) t); tim = ctime(&t); *strchr(tim, '\n') = '\0'; format_add(ft, "client_activity_string", "%s", tim); From 5dda1abc32b7b1eb59901a5592569e21d88c6ad2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 21 Apr 2013 21:32:00 +0000 Subject: [PATCH 028/949] Don't let server_client_check_focus use a dead bufferevent, from Romain Francoise. --- server-client.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server-client.c b/server-client.c index 6cacefa7..3b7b988a 100644 --- a/server-client.c +++ b/server-client.c @@ -514,8 +514,10 @@ server_client_loop(void) w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { - server_client_check_focus(wp); - server_client_check_resize(wp); + if (wp->fd != -1) { + server_client_check_focus(wp); + server_client_check_resize(wp); + } wp->flags &= ~PANE_REDRAW; } } @@ -527,7 +529,7 @@ server_client_check_resize(struct window_pane *wp) { struct winsize ws; - if (wp->fd == -1 || !(wp->flags & PANE_RESIZE)) + if (!(wp->flags & PANE_RESIZE)) return; memset(&ws, 0, sizeof ws); From a46ccbd883495d2acb3eab89e5bcdb61c0c7e195 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Apr 2013 09:39:21 +0100 Subject: [PATCH 029/949] -paths.h. Fixes Solaris, from Dagobert Michelsen. --- tmux.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.c b/tmux.c index e6de5cfe..2916bbb5 100644 --- a/tmux.c +++ b/tmux.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include From d89b35e6826ecaccc4d2f421c8013f35ec5854a0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Apr 2013 08:42:19 +0000 Subject: [PATCH 030/949] Use lockf which is more portable than flock, from Dagobert Michelsen. --- client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 70e7f1ec..0c8657eb 100644 --- a/client.c +++ b/client.c @@ -78,8 +78,8 @@ client_get_lock(char *lockfile) if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) fatal("open failed"); - if (flock(lockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) { - while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) + if (lockf(lockfd, F_TLOCK, 0) == -1 && errno == EAGAIN) { + while (lockf(lockfd, F_LOCK, 0) == -1 && errno == EINTR) /* nothing */; close(lockfd); return (-1); From 792e2856c9774cca262929f81b9adbbac0fa1d13 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Apr 2013 09:44:15 +0100 Subject: [PATCH 031/949] Add compat for cfmakeraw, from Dagobert Michelsen. --- Makefile.am | 3 +++ compat.h | 5 +++++ compat/cfmakeraw.c | 32 ++++++++++++++++++++++++++++++++ configure.ac | 8 ++++++++ 4 files changed, 48 insertions(+) create mode 100644 compat/cfmakeraw.c diff --git a/Makefile.am b/Makefile.am index b85eae0c..c5369fd9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -233,6 +233,9 @@ endif if NO_B64_NTOP nodist_tmux_SOURCES += compat/b64_ntop.c endif +if NO_CFMAKERAW +nodist_tmux_SOURCES += compat/cfmakeraw.c +endif # Build tmux.1 in the right format. tmux.1: tmux.1.in diff --git a/compat.h b/compat.h index 622006e1..d3973797 100644 --- a/compat.h +++ b/compat.h @@ -236,6 +236,11 @@ int setenv(const char *, const char *, int); int unsetenv(const char *); #endif +#ifndef HAVE_CFMAKERAW +/* cfmakeraw.c */ +void cfmakeraw(struct termios *tio); +#endif + #ifdef HAVE_GETOPT #include #else diff --git a/compat/cfmakeraw.c b/compat/cfmakeraw.c new file mode 100644 index 00000000..3f8dc7e5 --- /dev/null +++ b/compat/cfmakeraw.c @@ -0,0 +1,32 @@ +/* $Id$ */ + +/* + * Copyright (c) 2013 Dagobert Michelsen + * Copyright (c) 2013 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +void +cfmakeraw(struct termios *tio) +{ + tio->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + tio->c_oflag &= ~OPOST; + tio->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + tio->c_cflag &= ~(CSIZE|PARENB); + tio->c_cflag |= CS8; +} diff --git a/configure.ac b/configure.ac index b4f76f5f..80cf1266 100644 --- a/configure.ac +++ b/configure.ac @@ -313,6 +313,13 @@ if test "x$found_strnvis" = xyes; then fi AM_CONDITIONAL(NO_VIS, [test "x$found_strnvis" = xno]) +# Look for cfmakeraw, compat/cfmakeraw.c used if missing. +AC_CHECK_FUNC(cfmakeraw, found_cfmakeraw=yes, found_cfmakeraw=no) +if test "x$found_cfmakeraw" = xyes; then + AC_DEFINE(HAVE_CFMAKERAW) +fi +AM_CONDITIONAL(NO_CFMAKERAW, [test "x$found_cfmakeraw" = xno]) + # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) @@ -345,6 +352,7 @@ AC_CHECK_FUNCS( dirfd \ setproctitle \ sysconf \ + cfmakeraw \ ] ) From 11b90bc9591b4b101efef1f8a9a0d9656d2953c9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Apr 2013 14:04:40 +0100 Subject: [PATCH 032/949] Pass tmux.1.in to awk on stdin rather than as an argument. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index c5369fd9..c131940a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -242,7 +242,7 @@ tmux.1: tmux.1.in if test x@MANFORMAT@ = xmdoc; then \ cp tmux.1.in tmux.1; \ else \ - $(AWK) -fmdoc2man.awk tmux.1.in >tmux.1; \ + $(AWK) -fmdoc2man.awk tmux.1; \ fi # Update SF web site. From 46c7dbef0f7803dad39db355ee866ecab8db696c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Apr 2013 13:35:18 +0000 Subject: [PATCH 033/949] Call recalculate_sizes() after killing window in case it is in a grouped session, from Daniel Ralston. --- server-fn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-fn.c b/server-fn.c index c0b005e8..d92754e1 100644 --- a/server-fn.c +++ b/server-fn.c @@ -283,6 +283,7 @@ server_kill_window(struct window *w) if (options_get_number(&s->options, "renumber-windows")) session_renumber_windows(s); } + recalculate_sizes(); } int From 04f54ab38f9a5f32e6fb89b57380cc7150c08006 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Apr 2013 16:34:53 +0000 Subject: [PATCH 034/949] Get session of -t window rather than client's window. --- cmd-choose-tree.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index f22f17c2..db0333e7 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -89,10 +89,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if ((s = c->session) == NULL) - return (CMD_RETURN_ERROR); - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) From 3d2b7d5bce9532cedd647ace319afbf95ebe0e20 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Apr 2013 22:17:29 +0000 Subject: [PATCH 035/949] When using choose-tree -u, start with the current window highlighted. From Thomas Adam. --- cmd-choose-tree.c | 5 ++++- tmux.h | 2 ++ window-choose.c | 14 +++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index db0333e7..601d24f1 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -228,9 +228,12 @@ windows_only: free(final_win_template_last); window_choose_ready(wl->window->active, cur_win, NULL); + window_choose_collapse_all(wl->window->active); - if (args_has(args, 'u')) + if (args_has(args, 'u')) { window_choose_expand_all(wl->window->active); + window_choose_set_current(wl->window->active, cur_win); + } return (CMD_RETURN_NORMAL); } diff --git a/tmux.h b/tmux.h index 053b574e..8fb06223 100644 --- a/tmux.h +++ b/tmux.h @@ -2257,6 +2257,8 @@ struct window_choose_data *window_choose_add_item(struct window_pane *, struct client *, struct winlink *, const char *, const char *, u_int); void window_choose_expand_all(struct window_pane *); +void window_choose_collapse_all(struct window_pane *); +void window_choose_set_current(struct window_pane *, u_int); /* names.c */ void queue_window_name(struct window *); diff --git a/window-choose.c b/window-choose.c index 792224c1..38773605 100644 --- a/window-choose.c +++ b/window-choose.c @@ -44,7 +44,6 @@ void window_choose_scroll_down(struct window_pane *); void window_choose_collapse(struct window_pane *, struct session *); void window_choose_expand(struct window_pane *, struct session *, u_int); -void window_choose_collapse_all(struct window_pane *); enum window_choose_input_type { WINDOW_CHOOSE_NORMAL = -1, @@ -102,8 +101,7 @@ window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) } void -window_choose_ready(struct window_pane *wp, u_int cur, - void (*callbackfn)(struct window_choose_data *)) +window_choose_set_current(struct window_pane *wp, u_int cur) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -112,12 +110,22 @@ window_choose_ready(struct window_pane *wp, u_int cur, if (data->selected > screen_size_y(s) - 1) data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); + window_choose_redraw_screen(wp); +} + +void +window_choose_ready(struct window_pane *wp, u_int cur, + void (*callbackfn)(struct window_choose_data *)) +{ + struct window_choose_mode_data *data = wp->modedata; + data->callbackfn = callbackfn; if (data->callbackfn == NULL) data->callbackfn = window_choose_default_callback; ARRAY_CONCAT(&data->old_list, &data->list); + window_choose_set_current(wp, cur); window_choose_collapse_all(wp); } From 4f3c31a6b63f4489bfc672b510036fd8d9491595 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 24 Apr 2013 10:57:03 +0100 Subject: [PATCH 036/949] Use sysconfdir for the location of global tmux.conf (but default it to /etc), based on changes from Dagobert Michelsen. --- Makefile.am | 7 ++++--- configure.ac | 3 +++ server.c | 8 ++++---- tmux.1.in | 6 +++--- tmux.c | 2 +- tmux.h | 4 ---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Makefile.am b/Makefile.am index c131940a..726582a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,7 +15,7 @@ dist-hook: find $(distdir) -name .svn -type d|xargs rm -Rf # Preprocessor flags. -CPPFLAGS += @XOPEN_DEFINES@ +CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" # glibc as usual does things ass-backwards and hides useful things by default, # so everyone has to add this. @@ -240,9 +240,10 @@ endif # Build tmux.1 in the right format. tmux.1: tmux.1.in if test x@MANFORMAT@ = xmdoc; then \ - cp tmux.1.in tmux.1; \ + sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" tmux.1.in >tmux.1; \ else \ - $(AWK) -fmdoc2man.awk tmux.1; \ + sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" tmux.1.in| \ + $(AWK) -fmdoc2man.awk >tmux.1; \ fi # Update SF web site. diff --git a/configure.ac b/configure.ac index 80cf1266..590b9db0 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,9 @@ AC_PROG_CC AM_PROG_CC_C_O AC_PROG_INSTALL +# Default tmux.conf goes in /etc not ${prefix}/etc. +test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc + # Check for various headers. Alternatives included from compat.h. AC_CHECK_HEADERS( [ \ diff --git a/server.c b/server.c index 4bfa9185..bd28d517 100644 --- a/server.c +++ b/server.c @@ -170,13 +170,13 @@ server_start(int lockfd, char *lockfile) cfg_references = 1; ARRAY_INIT(&cfg_causes); - if (access(SYSTEM_CFG, R_OK) == 0) { - if (load_cfg(SYSTEM_CFG, cfg_cmd_q, &cause) == -1) { - xasprintf(&cause, "%s: %s", SYSTEM_CFG, cause); + if (access(TMUX_CONF, R_OK) == 0) { + if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) { + xasprintf(&cause, "%s: %s", TMUX_CONF, cause); ARRAY_ADD(&cfg_causes, cause); } } else if (errno != ENOENT) { - xasprintf(&cause, "%s: %s", SYSTEM_CFG, strerror(errno)); + xasprintf(&cause, "%s: %s", TMUX_CONF, strerror(errno)); ARRAY_ADD(&cfg_causes, cause); } if (cfg_file != NULL) { diff --git a/tmux.1.in b/tmux.1.in index 98bf9574..7f783b86 100644 --- a/tmux.1.in +++ b/tmux.1.in @@ -122,7 +122,7 @@ Specify an alternative configuration file. By default, .Nm loads the system configuration file from -.Pa /etc/tmux.conf , +.Pa @SYSCONFDIR@/tmux.conf , if present, then looks for a user configuration file at .Pa ~/.tmux.conf . .Pp @@ -3705,12 +3705,12 @@ was renamed to .Ar name . .El .Sh FILES -.Bl -tag -width "/etc/tmux.confXXX" -compact +.Bl -tag -width "@SYSCONFDIR@/tmux.confXXX" -compact .It Pa ~/.tmux.conf Default .Nm configuration file. -.It Pa /etc/tmux.conf +.It Pa @SYSCONFDIR@/tmux.conf System-wide configuration file. .El .Sh EXAMPLES diff --git a/tmux.c b/tmux.c index 2916bbb5..606c574f 100644 --- a/tmux.c +++ b/tmux.c @@ -363,7 +363,7 @@ main(int argc, char **argv) if (pw != NULL) home = pw->pw_dir; } - xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); + xasprintf(&cfg_file, "%s/.tmux.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { free(cfg_file); cfg_file = NULL; diff --git a/tmux.h b/tmux.h index f0b9edf0..fc5561b5 100644 --- a/tmux.h +++ b/tmux.h @@ -39,10 +39,6 @@ extern char *__progname; extern char **environ; -/* Default configuration files. */ -#define DEFAULT_CFG ".tmux.conf" -#define SYSTEM_CFG "/etc/tmux.conf" - /* Default prompt history length. */ #define PROMPT_HISTORY 100 From e323101edede281b65e3c7141d79afdb2501a8ea Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 24 Apr 2013 10:01:32 +0000 Subject: [PATCH 037/949] Rename global configuration define. --- server.c | 8 ++++---- tmux.c | 2 +- tmux.h | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/server.c b/server.c index a07fa1fd..ffc25db0 100644 --- a/server.c +++ b/server.c @@ -169,13 +169,13 @@ server_start(int lockfd, char *lockfile) cfg_references = 1; ARRAY_INIT(&cfg_causes); - if (access(SYSTEM_CFG, R_OK) == 0) { - if (load_cfg(SYSTEM_CFG, cfg_cmd_q, &cause) == -1) { - xasprintf(&cause, "%s: %s", SYSTEM_CFG, cause); + if (access(TMUX_CONF, R_OK) == 0) { + if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) { + xasprintf(&cause, "%s: %s", TMUX_CONF, cause); ARRAY_ADD(&cfg_causes, cause); } } else if (errno != ENOENT) { - xasprintf(&cause, "%s: %s", SYSTEM_CFG, strerror(errno)); + xasprintf(&cause, "%s: %s", TMUX_CONF, strerror(errno)); ARRAY_ADD(&cfg_causes, cause); } if (cfg_file != NULL) { diff --git a/tmux.c b/tmux.c index e9b28d7e..b229967d 100644 --- a/tmux.c +++ b/tmux.c @@ -357,7 +357,7 @@ main(int argc, char **argv) if (pw != NULL) home = pw->pw_dir; } - xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); + xasprintf(&cfg_file, "%s/.tmux.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { free(cfg_file); cfg_file = NULL; diff --git a/tmux.h b/tmux.h index 8fb06223..7404b27f 100644 --- a/tmux.h +++ b/tmux.h @@ -43,9 +43,8 @@ extern char *__progname; extern char **environ; -/* Default configuration files. */ -#define DEFAULT_CFG ".tmux.conf" -#define SYSTEM_CFG "/etc/tmux.conf" +/* Default global configuration file. */ +#define TMUX_CONF "/etc/tmux.conf" /* Default prompt history length. */ #define PROMPT_HISTORY 100 From 2555ac58ccc9b5e9c188dcb3a3a4cce6aa4821d1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 24 Apr 2013 10:15:47 +0000 Subject: [PATCH 038/949] .Op Fl b not .Fl b for run-shell synopsis, from Ben Boeckel. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index b38b43bb..9b874f7b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3536,7 +3536,7 @@ Lock each client individually by running the command specified by the .Ic lock-command option. .It Xo Ic run-shell -.Fl b +.Op Fl b .Op Fl t Ar target-pane .Ar shell-command .Xc From fce095665c62eb38826f42ae55a0fbe998f18be0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 28 Apr 2013 15:37:02 +0100 Subject: [PATCH 039/949] Use $(srcdir) for generating tmux.1, reported by fasta_ on IRC. --- Makefile.am | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 726582a5..3eb3190f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -240,10 +240,11 @@ endif # Build tmux.1 in the right format. tmux.1: tmux.1.in if test x@MANFORMAT@ = xmdoc; then \ - sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" tmux.1.in >tmux.1; \ + sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1.in \ + >$(srcdir)/tmux.1; \ else \ - sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" tmux.1.in| \ - $(AWK) -fmdoc2man.awk >tmux.1; \ + sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1.in| \ + $(AWK) -fmdoc2man.awk >$(srcdir)/tmux.1; \ fi # Update SF web site. From 66f4c60a8477e0cf9eb059e7687ca524f135e442 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 7 May 2013 11:00:16 +0000 Subject: [PATCH 040/949] Don't limit width and height to 222 in standard mouse mode. --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index d7e8513d..2de48e97 100644 --- a/input-keys.c +++ b/input-keys.c @@ -227,7 +227,7 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) len += utf8_split2(m->x + 33, &buf[len]); len += utf8_split2(m->y + 33, &buf[len]); } else { - if (m->xb > 223 || m->x >= 222 || m->y > 222) + if (m->xb > 223) return; len = xsnprintf(buf, sizeof buf, "\033[M"); buf[len++] = m->xb + 32; From 5b1cf02f2ea224922661495af32d46b024e28eef Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 May 2013 16:27:30 +0100 Subject: [PATCH 041/949] Rename tmux.1.in back to tmux.1 and generate tmux.1.{mdoc,man} instead. --- Makefile.am | 16 ++++++++-------- tmux.1.in => tmux.1 | 0 2 files changed, 8 insertions(+), 8 deletions(-) rename tmux.1.in => tmux.1 (100%) diff --git a/Makefile.am b/Makefile.am index c131940a..c746b3d9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,14 +2,12 @@ # Obvious program stuff. bin_PROGRAMS = tmux -nodist_man1_MANS = tmux.1 -CLEANFILES = tmux.1 +CLEANFILES = tmux.1.mdoc tmux.1.man # Distribution tarball options. EXTRA_DIST = \ CHANGES FAQ README TODO examples compat \ - array.h compat.h tmux.h osdep-*.c mdoc2man.awk \ - tmux.1.in + array.h compat.h tmux.h osdep-*.c mdoc2man.awk tmux.1 dist-hook: grep "^#found_debug=" configure find $(distdir) -name .svn -type d|xargs rm -Rf @@ -237,13 +235,15 @@ if NO_CFMAKERAW nodist_tmux_SOURCES += compat/cfmakeraw.c endif -# Build tmux.1 in the right format. -tmux.1: tmux.1.in +# Install tmux.1 in the right format. +install-exec-hook: if test x@MANFORMAT@ = xmdoc; then \ - cp tmux.1.in tmux.1; \ + cp tmux.1 tmux.1.mdoc; \ else \ - $(AWK) -fmdoc2man.awk tmux.1; \ + $(AWK) -fmdoc2man.awk tmux.1.man; \ fi + $(MKDIR_P) $(DESTDIR)$(mandir)/man1 + $(INSTALL_DATA) tmux.1.@MANFORMAT@ $(DESTDIR)$(mandir)/man1/tmux.1 # Update SF web site. upload-index.html: update-index.html diff --git a/tmux.1.in b/tmux.1 similarity index 100% rename from tmux.1.in rename to tmux.1 From 772d61f3ed762a50ea4436b7fb70e7024674e6c6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 May 2013 15:32:14 +0000 Subject: [PATCH 042/949] RIS should reset focus reporting, from Hayaki Saito. --- screen-write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index c0935c95..7fcfc5ee 100644 --- a/screen-write.c +++ b/screen-write.c @@ -56,7 +56,7 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_reset_tabs(s); screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); - s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD); + s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); From 25c430b1cd25d64c52d1c14834957abfaaeb69b6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 May 2013 15:34:09 +0000 Subject: [PATCH 043/949] Reserve space for \0 in cmd_print, from George Nachman. --- cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd.c b/cmd.c index d0348704..e7290f26 100644 --- a/cmd.c +++ b/cmd.c @@ -295,8 +295,8 @@ cmd_print(struct cmd *cmd, char *buf, size_t len) size_t off, used; off = xsnprintf(buf, len, "%s ", cmd->entry->name); - if (off < len) { - used = args_print(cmd->args, buf + off, len - off); + if (off + 1 < len) { + used = args_print(cmd->args, buf + off, len - off - 1); if (used == 0) off--; else From 88a4da97478ec6b4b2f361315a5a183333d0aa3f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 May 2013 15:39:51 +0000 Subject: [PATCH 044/949] Don't let cursor position overflow when reflowing, from Christopher Collins. --- screen.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/screen.c b/screen.c index 754effc2..76aa91c6 100644 --- a/screen.c +++ b/screen.c @@ -365,7 +365,13 @@ void screen_reflow(struct screen *s, u_int new_x) { struct grid *old = s->grid; + u_int change; s->grid = grid_create(old->sx, old->sy, old->hlimit); - s->cy -= grid_reflow(s->grid, old, new_x); + + change = grid_reflow(s->grid, old, new_x); + if (change < s->cy) + s->cy -= change; + else + s->cy = 0; } From e6c77e7afbcee07ed6237f2a0f30e70f45ecf0ba Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 31 May 2013 12:59:17 +0100 Subject: [PATCH 045/949] Add a COPYING file, suggested by Dagobert Michelsen. --- COPYING | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..9d96b3e6 --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE. + +The README, CHANGES, FAQ and TODO files are licensed under the ISC +license. Files under examples/ remain copyright their authors unless otherwise +stated in the file but permission has been received to distribute them with +tmux. All other files have a license and copyright notice at their start, +typically: + +Copyright (c) + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. From a0cf65db77343cf60a72c59596ccfcaebe91c663 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 31 May 2013 12:19:34 +0000 Subject: [PATCH 046/949] Instead of eating 1024 bytes or so for the arguments of each command, save memory by using an RB tree. From Tiago Cunha. --- arguments.c | 120 ++++++++++++++++++++++++++++++++++------------------ tmux.h | 19 ++++++--- 2 files changed, 91 insertions(+), 48 deletions(-) diff --git a/arguments.c b/arguments.c index c15832a4..ca828f00 100644 --- a/arguments.c +++ b/arguments.c @@ -18,12 +18,26 @@ #include -#include #include #include #include "tmux.h" +/* + * Manipulate command arguments. + */ + +struct args_entry *args_find(struct args *, u_char); + +RB_GENERATE(args_tree, args_entry, entry, args_cmp); + +/* Arguments tree comparison function. */ +int +args_cmp(struct args_entry *a1, struct args_entry *a2) +{ + return (a1->flag - a2->flag); +} + /* Create an arguments set with no flags. */ struct args * args_create(int argc, ...) @@ -33,8 +47,6 @@ args_create(int argc, ...) int i; args = xcalloc(1, sizeof *args); - if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) - fatal("bit_alloc failed"); args->argc = argc; if (argc == 0) @@ -50,6 +62,16 @@ args_create(int argc, ...) return (args); } +/* Find a flag in the arguments tree. */ +struct args_entry * +args_find(struct args *args, u_char ch) +{ + struct args_entry entry; + + entry.flag = ch; + return (RB_FIND(args_tree, &args->tree, &entry)); +} + /* Parse an argv and argc into a new argument set. */ struct args * args_parse(const char *template, int argc, char **argv) @@ -59,26 +81,18 @@ args_parse(const char *template, int argc, char **argv) int opt; args = xcalloc(1, sizeof *args); - if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) - fatal("bit_alloc failed"); optreset = 1; optind = 1; while ((opt = getopt(argc, argv, template)) != -1) { - if (opt < 0 || opt >= SCHAR_MAX) + if (opt < 0) continue; if (opt == '?' || (ptr = strchr(template, opt)) == NULL) { - free(args->flags); - free(args); + args_free(args); return (NULL); } - - bit_set(args->flags, opt); - if (ptr[1] == ':') { - free(args->values[opt]); - args->values[opt] = xstrdup(optarg); - } + args_set(args, opt, optarg); } argc -= optind; argv += optind; @@ -93,14 +107,17 @@ args_parse(const char *template, int argc, char **argv) void args_free(struct args *args) { - u_int i; + struct args_entry *entry; + struct args_entry *entry1; cmd_free_argv(args->argc, args->argv); - for (i = 0; i < SCHAR_MAX; i++) - free(args->values[i]); + RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { + RB_REMOVE(args_tree, &args->tree, entry); + free(entry->value); + free(entry); + } - free(args->flags); free(args); } @@ -108,9 +125,10 @@ args_free(struct args *args) size_t args_print(struct args *args, char *buf, size_t len) { - size_t off; - int i; - const char *quotes; + size_t off; + int i; + const char *quotes; + struct args_entry *entry; /* There must be at least one byte at the start. */ if (len == 0) @@ -119,23 +137,23 @@ args_print(struct args *args, char *buf, size_t len) /* Process the flags first. */ buf[off++] = '-'; - for (i = 0; i < SCHAR_MAX; i++) { - if (!bit_test(args->flags, i) || args->values[i] != NULL) + RB_FOREACH(entry, args_tree, &args->tree) { + if (entry->value != NULL) continue; if (off == len - 1) { buf[off] = '\0'; return (len); } - buf[off++] = i; + buf[off++] = entry->flag; buf[off] = '\0'; } if (off == 1) buf[--off] = '\0'; /* Then the flags with arguments. */ - for (i = 0; i < SCHAR_MAX; i++) { - if (!bit_test(args->flags, i) || args->values[i] == NULL) + RB_FOREACH(entry, args_tree, &args->tree) { + if (entry->value == NULL) continue; if (off >= len) { @@ -143,12 +161,13 @@ args_print(struct args *args, char *buf, size_t len) return (len); } - if (strchr(args->values[i], ' ') != NULL) + if (strchr(entry->value, ' ') != NULL) quotes = "\""; else quotes = ""; off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s", - off != 0 ? " " : "", i, quotes, args->values[i], quotes); + off != 0 ? " " : "", entry->flag, quotes, entry->value, + quotes); } /* And finally the argument vector. */ @@ -173,42 +192,59 @@ args_print(struct args *args, char *buf, size_t len) int args_has(struct args *args, u_char ch) { - return (bit_test(args->flags, ch)); + return (args_find(args, ch) == NULL ? 0 : 1); } -/* Set argument value. */ +/* Set argument value in the arguments tree. */ void args_set(struct args *args, u_char ch, const char *value) { - free(args->values[ch]); + struct args_entry *entry; + + /* Replace existing argument. */ + if ((entry = args_find(args, ch)) != NULL) { + free(entry->value); + if (value != NULL) + entry->value = xstrdup(value); + else + entry->value = NULL; + return; + } + + entry = xcalloc(1, sizeof *entry); + entry->flag = ch; if (value != NULL) - args->values[ch] = xstrdup(value); - else - args->values[ch] = NULL; - bit_set(args->flags, ch); + entry->value = xstrdup(value); + + RB_INSERT(args_tree, &args->tree, entry); } /* Get argument value. Will be NULL if it isn't present. */ const char * args_get(struct args *args, u_char ch) { - return (args->values[ch]); + struct args_entry *entry; + + if ((entry = args_find(args, ch)) == NULL) + return (NULL); + return (entry->value); } /* Convert an argument value to a number. */ long long -args_strtonum(struct args *args, - u_char ch, long long minval, long long maxval, char **cause) +args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, + char **cause) { - const char *errstr; - long long ll; + const char *errstr; + long long ll; + struct args_entry *entry; - if (!args_has(args, ch)) { + if ((entry = args_find(args, ch)) == NULL) { *cause = xstrdup("missing"); return (0); } - ll = strtonum(args->values[ch], minval, maxval, &errstr); + ll = strtonum(entry->value, minval, maxval, &errstr); if (errstr != NULL) { *cause = xstrdup(errstr); return (0); diff --git a/tmux.h b/tmux.h index 7404b27f..7c577b4d 100644 --- a/tmux.h +++ b/tmux.h @@ -1361,13 +1361,18 @@ struct client { }; ARRAY_DECL(clients, struct client *); -/* Parsed arguments. */ -struct args { - bitstr_t *flags; - char *values[SCHAR_MAX]; /* XXX This is awfully big. */ +/* Parsed arguments structures. */ +struct args_entry { + u_char flag; + char *value; + RB_ENTRY(args_entry) entry; +}; +RB_HEAD(args_tree, args_entry); - int argc; - char **argv; +struct args { + struct args_tree tree; + int argc; + char **argv; }; /* Command and list of commands. */ @@ -1724,6 +1729,8 @@ extern const char clock_table[14][5][5]; void clock_draw(struct screen_write_ctx *, int, int); /* arguments.c */ +int args_cmp(struct args_entry *, struct args_entry *); +RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); struct args *args_create(int, ...); struct args *args_parse(const char *, int, char **); void args_free(struct args *); From 9fb9f78e43337833825ae464a1c39ed6d78a2fdb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 31 May 2013 12:50:05 +0000 Subject: [PATCH 047/949] Use u_char for the send-keys string to avoid mangling top-bit-set characters when they are promoted to int and passed to window_pane_key. Reported by Jacob Bang. --- cmd-send-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 3ee3dbe6..dcd5f288 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -56,7 +56,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct input_ctx *ictx; - const char *str; + const u_char *str; int i, key; if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) From c30d60f7ae7631a4c2edc241739b5c6eff110a24 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 31 May 2013 19:46:42 +0000 Subject: [PATCH 048/949] Add host_short format, from Tiago Cunha. --- format.c | 74 +++++++++++++++++++++++++++++++++++++++++--------------- tmux.1 | 1 + 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/format.c b/format.c index 9918cfe8..8ed1c1be 100644 --- a/format.c +++ b/format.c @@ -46,8 +46,8 @@ format_cmp(struct format_entry *fe1, struct format_entry *fe2) return (strcmp(fe1->key, fe2->key)); } -/* Single-character aliases. */ -const char *format_aliases[26] = { +/* Single-character uppercase aliases. */ +const char *format_upper[] = { NULL, /* A */ NULL, /* B */ NULL, /* C */ @@ -76,18 +76,52 @@ const char *format_aliases[26] = { NULL /* Z */ }; +/* Single-character lowercase aliases. */ +const char *format_lower[] = { + NULL, /* a */ + NULL, /* b */ + NULL, /* c */ + NULL, /* d */ + NULL, /* e */ + NULL, /* f */ + NULL, /* g */ + "host_short", /* h */ + NULL, /* i */ + NULL, /* j */ + NULL, /* k */ + NULL, /* l */ + NULL, /* m */ + NULL, /* n */ + NULL, /* o */ + NULL, /* p */ + NULL, /* q */ + NULL, /* r */ + NULL, /* s */ + NULL, /* t */ + NULL, /* u */ + NULL, /* v */ + NULL, /* w */ + NULL, /* x */ + NULL, /* y */ + NULL /* z */ +}; + /* Create a new tree. */ struct format_tree * format_create(void) { struct format_tree *ft; - char host[MAXHOSTNAMELEN]; + char host[MAXHOSTNAMELEN], *ptr; ft = xmalloc(sizeof *ft); RB_INIT(ft); - if (gethostname(host, sizeof host) == 0) + if (gethostname(host, sizeof host) == 0) { format_add(ft, "host", "%s", host); + if ((ptr = strrchr(host, '.')) != NULL) + *ptr = '\0'; + format_add(ft, "host_short", "%s", host); + } return (ft); } @@ -109,7 +143,7 @@ format_free(struct format_tree *ft) free(fe); } - free (ft); + free(ft); } /* Add a key-value pair. */ @@ -230,6 +264,7 @@ format_expand(struct format_tree *ft, const char *fmt) fmt++; ch = (u_char) *fmt++; + switch (ch) { case '{': ptr = strchr(fmt, '}'); @@ -242,22 +277,23 @@ format_expand(struct format_tree *ft, const char *fmt) fmt += n + 1; continue; default: - if (ch >= 'A' && ch <= 'Z') { - s = format_aliases[ch - 'A']; - if (s != NULL) { - n = strlen(s); - if (format_replace ( - ft, s, n, &buf, &len, &off) != 0) - break; - continue; + s = NULL; + if (ch >= 'A' && ch <= 'Z') + s = format_upper[ch - 'A']; + else if (ch >= 'a' && ch <= 'z') + s = format_lower[ch - 'a']; + if (s == NULL) { + while (len - off < 3) { + buf = xrealloc(buf, 2, len); + len *= 2; } + buf[off++] = '#'; + buf[off++] = ch; + continue; } - while (len - off < 3) { - buf = xrealloc(buf, 2, len); - len *= 2; - } - buf[off++] = '#'; - buf[off++] = ch; + n = strlen(s); + if (format_replace(ft, s, n, &buf, &len, &off) != 0) + break; continue; } diff --git a/tmux.1 b/tmux.1 index 9b874f7b..1ce94a11 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3056,6 +3056,7 @@ The following variables are available, where appropriate: .It Li "history_limit" Ta "Maximum window history lines" .It Li "history_size" Ta "Size of history in bytes" .It Li "host" Ta "Hostname of local host" +.It Li "host_short" Ta "Hostname of local host (no domain name)" .It Li "insert_flag" Ta "Pane insert flag" .It Li "keypad_cursor_flag" Ta "Pane keypad cursor flag" .It Li "keypad_flag" Ta "Pane keypad flag" From c231381aa3a22340e787baaf78781d9b8ecd6a2c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 31 May 2013 19:56:05 +0000 Subject: [PATCH 049/949] Demote the old single-character replacement variables (#S and friends) to aliases of formats. From Tiago Cunha. --- status.c | 61 +++---------------- tmux.1 | 180 ++++++++++++++++++++++++++----------------------------- 2 files changed, 93 insertions(+), 148 deletions(-) diff --git a/status.c b/status.c index 6ce0b871..ecfd7e58 100644 --- a/status.c +++ b/status.c @@ -38,8 +38,7 @@ void status_job_free(void *); void status_job_callback(struct job *); char *status_print( struct client *, struct winlink *, time_t, struct grid_cell *); -void status_replace1(struct client *, struct session *, struct winlink *, - struct window_pane *, char **, char **, char *, size_t, int); +void status_replace1(struct client *, char **, char **, char *, size_t, int); void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); @@ -384,14 +383,12 @@ out: /* Replace a single special sequence (prefixed by #). */ void -status_replace1(struct client *c, struct session *s, struct winlink *wl, - struct window_pane *wp, char **iptr, char **optr, char *out, +status_replace1(struct client *c, char **iptr, char **optr, char *out, size_t outsize, int jobsflag) { - char ch, tmp[256], *ptr, *endptr, *freeptr; + char ch, tmp[256], *ptr, *endptr; size_t ptrlen; long limit; - u_int idx; errno = 0; limit = strtol(*iptr, &endptr, 10); @@ -403,8 +400,6 @@ status_replace1(struct client *c, struct session *s, struct winlink *wl, if (limit <= 0) limit = LONG_MAX; - freeptr = NULL; - switch (*(*iptr)++) { case '(': if (!jobsflag) { @@ -414,45 +409,6 @@ status_replace1(struct client *c, struct session *s, struct winlink *wl, if ((ptr = status_find_job(c, iptr)) == NULL) return; goto do_replace; - case 'D': - xsnprintf(tmp, sizeof tmp, "%%%u", wp->id); - ptr = tmp; - goto do_replace; - case 'H': - if (gethostname(tmp, sizeof tmp) != 0) - fatal("gethostname failed"); - ptr = tmp; - goto do_replace; - case 'h': - if (gethostname(tmp, sizeof tmp) != 0) - fatal("gethostname failed"); - if ((ptr = strchr(tmp, '.')) != NULL) - *ptr = '\0'; - ptr = tmp; - goto do_replace; - case 'I': - xsnprintf(tmp, sizeof tmp, "%d", wl->idx); - ptr = tmp; - goto do_replace; - case 'P': - if (window_pane_index(wp, &idx) != 0) - fatalx("index not found"); - xsnprintf(tmp, sizeof tmp, "%u", idx); - ptr = tmp; - goto do_replace; - case 'S': - ptr = s->name; - goto do_replace; - case 'T': - ptr = wp->base.title; - goto do_replace; - case 'W': - ptr = wl->window->name; - goto do_replace; - case 'F': - ptr = window_printable_flags(s, wl); - freeptr = ptr; - goto do_replace; case '[': /* * Embedded style, handled at display time. Leave present and @@ -466,6 +422,10 @@ status_replace1(struct client *c, struct session *s, struct winlink *wl, case '#': *(*optr)++ = '#'; break; + default: + xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1)); + ptr = tmp; + goto do_replace; } return; @@ -476,14 +436,12 @@ do_replace: ptrlen = limit; if (*optr + ptrlen >= out + outsize - 1) - goto out; + return; while (ptrlen > 0 && *ptr != '\0') { *(*optr)++ = *ptr++; ptrlen--; } -out: - free(freeptr); return; skip_to: @@ -532,8 +490,7 @@ status_replace(struct client *c, struct session *s, struct winlink *wl, *optr++ = ch; continue; } - status_replace1( - c, s, wl, wp, &iptr, &optr, out, sizeof out, jobsflag); + status_replace1(c, &iptr, &optr, out, sizeof out, jobsflag); } *optr = '\0'; diff --git a/tmux.1 b/tmux.1 index 1ce94a11..40b0ef78 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2438,27 +2438,18 @@ environment variables are set and contain the string .It Ic status-left Ar string Display .Ar string -to the left of the status bar. +(by default the session name) to the left of the status bar. .Ar string will be passed through .Xr strftime 3 -before being used. -By default, the session name is shown. -.Ar string -may contain any of the following special character sequences: +and formats (see +.Sx FORMATS Ns ) +will be expanded. +It may also contain any of the following special character sequences: .Bl -column "Character pair" "Replaced with" -offset indent .It Sy "Character pair" Ta Sy "Replaced with" .It Li "#(shell-command)" Ta "First line of the command's output" .It Li "#[attributes]" Ta "Colour or attribute change" -.It Li "#H" Ta "Hostname of local host" -.It Li "#h" Ta "Hostname of local host without the domain name" -.It Li "#F" Ta "Current window flag" -.It Li "#I" Ta "Current window index" -.It Li "#D" Ta "Current pane unique identifier" -.It Li "#P" Ta "Current pane index" -.It Li "#S" Ta "Session name" -.It Li "#T" Ta "Current pane title" -.It Li "#W" Ta "Current window name" .It Li "##" Ta "A literal" Ql # .El .Pp @@ -3003,16 +2994,13 @@ flag with a .Ar format argument. This is a string which controls the output format of the command. -Special character sequences are replaced as documented under the -.Ic status-left -option and an additional long form is accepted. Replacement variables are enclosed in .Ql #{ and .Ql } , for example -.Ql #{session_name} -is equivalent to +.Ql #{session_name} . +Some variables also have an shorter alias such as .Ql #S . Conditionals are also accepted by prefixing with .Ql \&? @@ -3028,83 +3016,83 @@ if the session is attached and the string if it is unattached. .Pp The following variables are available, where appropriate: -.Bl -column "session_created_string" "Replaced with" -offset indent -.It Sy "Variable name" Ta Sy "Replaced with" -.It Li "alternate_on" Ta "If pane is in alternate screen" -.It Li "alternate_saved_x" Ta "Saved cursor X in alternate screen" -.It Li "alternate_saved_y" Ta "Saved cursor Y in alternate screen" -.It Li "buffer_sample" Ta "First 50 characters from the specified buffer" -.It Li "buffer_size" Ta "Size of the specified buffer in bytes" -.It Li "client_activity" Ta "Integer time client last had activity" -.It Li "client_activity_string" Ta "String time client last had activity" -.It Li "client_created" Ta "Integer time client created" -.It Li "client_created_string" Ta "String time client created" -.It Li "client_cwd" Ta "Working directory of client" -.It Li "client_height" Ta "Height of client" -.It Li "client_last_session" Ta "Name of the client's last session" -.It Li "client_prefix" Ta "1 if prefix key has been pressed" -.It Li "client_readonly" Ta "1 if client is readonly" -.It Li "client_session" Ta "Name of the client's session" -.It Li "client_termname" Ta "Terminal name of client" -.It Li "client_tty" Ta "Pseudo terminal of client" -.It Li "client_utf8" Ta "1 if client supports utf8" -.It Li "client_width" Ta "Width of client" -.It Li "cursor_flag" Ta "Pane cursor flag" -.It Li "cursor_x" Ta "Cursor X position in pane" -.It Li "cursor_y" Ta "Cursor Y position in pane" -.It Li "history_bytes" Ta "Number of bytes in window history" -.It Li "history_limit" Ta "Maximum window history lines" -.It Li "history_size" Ta "Size of history in bytes" -.It Li "host" Ta "Hostname of local host" -.It Li "host_short" Ta "Hostname of local host (no domain name)" -.It Li "insert_flag" Ta "Pane insert flag" -.It Li "keypad_cursor_flag" Ta "Pane keypad cursor flag" -.It Li "keypad_flag" Ta "Pane keypad flag" -.It Li "line" Ta "Line number in the list" -.It Li "mouse_any_flag" Ta "Pane mouse any flag" -.It Li "mouse_button_flag" Ta "Pane mouse button flag" -.It Li "mouse_standard_flag" Ta "Pane mouse standard flag" -.It Li "mouse_utf8_flag" Ta "Pane mouse UTF-8 flag" -.It Li "pane_active" Ta "1 if active pane" -.It Li "pane_current_command" Ta "Current command if available" -.It Li "pane_current_path" Ta "Current path if available" -.It Li "pane_dead" Ta "1 if pane is dead" -.It Li "pane_height" Ta "Height of pane" -.It Li "pane_id" Ta "Unique pane ID" -.It Li "pane_in_mode" Ta "If pane is in a mode" -.It Li "pane_index" Ta "Index of pane" -.It Li "pane_pid" Ta "PID of first process in pane" -.It Li "pane_start_command" Ta "Command pane started with" -.It Li "pane_start_path" Ta "Path pane started with" -.It Li "pane_tabs" Ta "Pane tab positions" -.It Li "pane_title" Ta "Title of pane" -.It Li "pane_tty" Ta "Pseudo terminal of pane" -.It Li "pane_width" Ta "Width of pane" -.It Li "saved_cursor_x" Ta "Saved cursor X in pane" -.It Li "saved_cursor_y" Ta "Saved cursor Y in pane" -.It Li "scroll_region_lower" Ta "Bottom of scroll region in pane" -.It Li "scroll_region_upper" Ta "Top of scroll region in pane" -.It Li "session_attached" Ta "1 if session attached" -.It Li "session_created" Ta "Integer time session created" -.It Li "session_created_string" Ta "String time session created" -.It Li "session_group" Ta "Number of session group" -.It Li "session_grouped" Ta "1 if session in a group" -.It Li "session_height" Ta "Height of session" -.It Li "session_id" Ta "Unique session ID" -.It Li "session_name" Ta "Name of session" -.It Li "session_width" Ta "Width of session" -.It Li "session_windows" Ta "Number of windows in session" -.It Li "window_active" Ta "1 if window active" -.It Li "window_find_matches" Ta "Matched data from the find-window command if available" -.It Li "window_flags" Ta "Window flags" -.It Li "window_height" Ta "Height of window" -.It Li "window_id" Ta "Unique window ID" -.It Li "window_index" Ta "Index of window" -.It Li "window_layout" Ta "Window layout description" -.It Li "window_name" Ta "Name of window" -.It Li "window_panes" Ta "Number of panes in window" -.It Li "window_width" Ta "Width of window" -.It Li "wrap_flag" Ta "Pane wrap flag" +.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" +.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" +.It Li "alternate_on" Ta "" Ta "If pane is in alternate screen" +.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" +.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" +.It Li "buffer_sample" Ta "" Ta "First 50 characters from buffer" +.It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" +.It Li "client_activity" Ta "" Ta "Integer time client last had activity" +.It Li "client_activity_string" Ta "" Ta "String time client last had activity" +.It Li "client_created" Ta "" Ta "Integer time client created" +.It Li "client_created_string" Ta "" Ta "String time client created" +.It Li "client_cwd" Ta "" Ta "Working directory of client" +.It Li "client_height" Ta "" Ta "Height of client" +.It Li "client_last_session" Ta "" Ta "Name of the client's last session" +.It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" +.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_tty" Ta "" Ta "Pseudo terminal of client" +.It Li "client_utf8" Ta "" Ta "1 if client supports utf8" +.It Li "client_width" Ta "" Ta "Width of client" +.It Li "cursor_flag" Ta "" Ta "Pane cursor flag" +.It Li "cursor_x" Ta "" Ta "Cursor X position in pane" +.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" +.It Li "history_bytes" Ta "" Ta "Number of bytes in window history" +.It Li "history_limit" Ta "" Ta "Maximum window history lines" +.It Li "history_size" Ta "" Ta "Size of history in bytes" +.It Li "host" Ta "#H" Ta "Hostname of local host" +.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" +.It Li "insert_flag" Ta "" Ta "Pane insert flag" +.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" +.It Li "keypad_flag" Ta "" Ta "Pane keypad flag" +.It Li "line" Ta "" Ta "Line number in the list" +.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" +.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" +.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" +.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" +.It Li "pane_active" Ta "" Ta "1 if active pane" +.It Li "pane_current_command" Ta "" Ta "Current command if available" +.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_height" Ta "" Ta "Height of pane" +.It Li "pane_id" Ta "#D" Ta "Unique pane ID" +.It Li "pane_in_mode" Ta "" Ta "If pane is in a mode" +.It Li "pane_index" Ta "#P" Ta "Index of pane" +.It Li "pane_pid" Ta "" Ta "PID of first process in pane" +.It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_start_path" Ta "" Ta "Path pane started with" +.It Li "pane_tabs" Ta "" Ta "Pane tab positions" +.It Li "pane_title" Ta "#T" Ta "Title of pane" +.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" +.It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "saved_cursor_x" Ta "" Ta "Saved cursor X in pane" +.It Li "saved_cursor_y" Ta "" Ta "Saved cursor Y in pane" +.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 "session_attached" Ta "" Ta "1 if session attached" +.It Li "session_created" Ta "" Ta "Integer time session created" +.It Li "session_created_string" Ta "" Ta "String time session created" +.It Li "session_group" Ta "" Ta "Number of session group" +.It Li "session_grouped" Ta "" Ta "1 if session in a group" +.It Li "session_height" Ta "" Ta "Height of session" +.It Li "session_id" Ta "" Ta "Unique session ID" +.It Li "session_name" Ta "#S" Ta "Name of session" +.It Li "session_width" Ta "" Ta "Width of session" +.It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" +.It Li "window_flags" Ta "#F" Ta "Window flags" +.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_layout" Ta "" Ta "Window layout description" +.It Li "window_name" Ta "#W" Ta "Name of window" +.It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_width" Ta "" Ta "Width of window" +.It Li "wrap_flag" Ta "" Ta "Pane wrap flag" .El .Sh NAMES AND TITLES .Nm From a97d5b8e60b25e2d38603d472f1b0b70e4ef5fa4 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 1 Jun 2013 10:58:39 +0100 Subject: [PATCH 050/949] Amend tmux.1 handling in .gitignore Now that tmux.1 is used as the canonical source for man page documentation, ensure that we ignore tmux.1.{mdoc,man} instead. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3b400861..20a3bbf4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ tmux Makefile Makefile.in configure -tmux.1 +tmux.1.* From 13441e8cb8b0ce68db3204a44bbdc004bee42a0f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 2 Jun 2013 07:52:15 +0000 Subject: [PATCH 051/949] The actual terminfo entries we ended up with for cursor changes are Cs, Ce, Ss and Se (not Cc, Ce, Cs, Csr). So use and document these instead of the ones we were using earlier. --- options-table.c | 4 ++-- tmux.1 | 4 ++-- tmux.h | 6 +++--- tty-term.c | 6 +++--- tty.c | 18 +++++++++--------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/options-table.c b/options-table.c index c4ada1f2..c73842f7 100644 --- a/options-table.c +++ b/options-table.c @@ -416,8 +416,8 @@ const struct options_table_entry session_options_table[] = { .type = OPTIONS_TABLE_STRING, .default_str = "*256col*:colors=256" ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" - ":Cc=\\E]12;%p1%s\\007:Cr=\\E]112\\007" - ":Cs=\\E[%p1%d q:Csr=\\E[2 q,screen*:XT" + ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" + ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" }, { .name = "update-environment", diff --git a/tmux.1 b/tmux.1 index 40b0ef78..eb5ea47d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3571,7 +3571,7 @@ This command only works from outside understands some extensions to .Xr terminfo 5 : .Bl -tag -width Ds -.It Em Cc , Cr +.It Em Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; the second takes no arguments and restores the default cursor colour. @@ -3581,7 +3581,7 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed -.It Em Cs , Csr +.It Em Ss , Se Change the cursor style. If set, a sequence such as this may be used to change the cursor to an underline: diff --git a/tmux.h b/tmux.h index 7c577b4d..b1624b6a 100644 --- a/tmux.h +++ b/tmux.h @@ -253,15 +253,13 @@ enum tty_code_code { TTYC_BEL, /* bell, bl */ TTYC_BLINK, /* enter_blink_mode, mb */ TTYC_BOLD, /* enter_bold_mode, md */ - TTYC_CC, /* set colour cursor, Cc */ TTYC_CIVIS, /* cursor_invisible, vi */ TTYC_CLEAR, /* clear_screen, cl */ TTYC_CNORM, /* cursor_normal, ve */ TTYC_COLORS, /* max_colors, Co */ TTYC_CR, /* restore cursor colour, Cr */ - TTYC_CS1, /* set cursor style, Cs */ + TTYC_CS, /* set cursor colour, Cs */ TTYC_CSR, /* change_scroll_region, cs */ - TTYC_CSR1, /* reset cursor style, Csr */ TTYC_CUB, /* parm_left_cursor, LE */ TTYC_CUB1, /* cursor_left, le */ TTYC_CUD, /* parm_down_cursor, DO */ @@ -391,6 +389,7 @@ enum tty_code_code { TTYC_RMACS, /* exit_alt_charset_mode */ TTYC_RMCUP, /* exit_ca_mode, te */ TTYC_RMKX, /* keypad_local, ke */ + TTYC_SE, /* reset cursor style, Se */ TTYC_SETAB, /* set_a_background, AB */ TTYC_SETAF, /* set_a_foreground, AF */ TTYC_SGR0, /* exit_attribute_mode, me */ @@ -400,6 +399,7 @@ enum tty_code_code { TTYC_SMKX, /* keypad_xmit, ks */ TTYC_SMSO, /* enter_standout_mode, so */ TTYC_SMUL, /* enter_underline_mode, us */ + TTYC_SS, /* set cursor style, Ss */ TTYC_TSL, /* to_status_line, tsl */ TTYC_VPA, /* row_address, cv */ TTYC_XENL, /* eat_newline_glitch, xn */ diff --git a/tty-term.c b/tty-term.c index 254569f6..8644a845 100644 --- a/tty-term.c +++ b/tty-term.c @@ -38,15 +38,13 @@ const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { { TTYC_BEL, TTYCODE_STRING, "bel" }, { TTYC_BLINK, TTYCODE_STRING, "blink" }, { TTYC_BOLD, TTYCODE_STRING, "bold" }, - { TTYC_CC, TTYCODE_STRING, "Cc" }, { TTYC_CIVIS, TTYCODE_STRING, "civis" }, { TTYC_CLEAR, TTYCODE_STRING, "clear" }, { TTYC_CNORM, TTYCODE_STRING, "cnorm" }, { TTYC_COLORS, TTYCODE_NUMBER, "colors" }, { TTYC_CR, TTYCODE_STRING, "Cr" }, - { TTYC_CS1, TTYCODE_STRING, "Cs" }, + { TTYC_CS, TTYCODE_STRING, "Cs" }, { TTYC_CSR, TTYCODE_STRING, "csr" }, - { TTYC_CSR1, TTYCODE_STRING, "Csr" }, { TTYC_CUB, TTYCODE_STRING, "cub" }, { TTYC_CUB1, TTYCODE_STRING, "cub1" }, { TTYC_CUD, TTYCODE_STRING, "cud" }, @@ -176,6 +174,7 @@ const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { { TTYC_RMACS, TTYCODE_STRING, "rmacs" }, { TTYC_RMCUP, TTYCODE_STRING, "rmcup" }, { TTYC_RMKX, TTYCODE_STRING, "rmkx" }, + { TTYC_SE, TTYCODE_STRING, "Se" }, { TTYC_SETAB, TTYCODE_STRING, "setab" }, { TTYC_SETAF, TTYCODE_STRING, "setaf" }, { TTYC_SGR0, TTYCODE_STRING, "sgr0" }, @@ -185,6 +184,7 @@ const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { { TTYC_SMKX, TTYCODE_STRING, "smkx" }, { TTYC_SMSO, TTYCODE_STRING, "smso" }, { TTYC_SMUL, TTYCODE_STRING, "smul" }, + { TTYC_SS, TTYCODE_STRING, "Ss" }, { TTYC_TSL, TTYCODE_STRING, "tsl" }, { TTYC_VPA, TTYCODE_STRING, "vpa" }, { TTYC_XENL, TTYCODE_FLAG, "xenl" }, diff --git a/tty.c b/tty.c index 74988eb3..652d2b77 100644 --- a/tty.c +++ b/tty.c @@ -270,11 +270,11 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); - if (tty_term_has(tty->term, TTYC_CS1) && tty->cstyle != 0) { - if (tty_term_has(tty->term, TTYC_CSR1)) - tty_raw(tty, tty_term_string(tty->term, TTYC_CSR1)); + if (tty_term_has(tty->term, TTYC_SS) && tty->cstyle != 0) { + if (tty_term_has(tty->term, TTYC_SE)) + tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); else - tty_raw(tty, tty_term_string1(tty->term, TTYC_CS1, 0)); + tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); @@ -455,7 +455,7 @@ tty_force_cursor_colour(struct tty *tty, const char *ccolour) if (*ccolour == '\0') tty_putcode(tty, TTYC_CR); else - tty_putcode_ptr1(tty, TTYC_CC, ccolour); + tty_putcode_ptr1(tty, TTYC_CS, ccolour); free(tty->ccolour); tty->ccolour = xstrdup(ccolour); } @@ -479,12 +479,12 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_putcode(tty, TTYC_CIVIS); } if (tty->cstyle != s->cstyle) { - if (tty_term_has(tty->term, TTYC_CS1)) { + if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && - tty_term_has(tty->term, TTYC_CSR1)) - tty_putcode(tty, TTYC_CSR1); + tty_term_has(tty->term, TTYC_SE)) + tty_putcode(tty, TTYC_SE); else - tty_putcode1(tty, TTYC_CS1, s->cstyle); + tty_putcode1(tty, TTYC_SS, s->cstyle); } tty->cstyle = s->cstyle; } From ddb52a2b157c1970081a2fb30f1413d57ec35b1e Mon Sep 17 00:00:00 2001 From: Jason McIntyre Date: Sun, 2 Jun 2013 14:40:17 +0000 Subject: [PATCH 052/949] escape "Ss", becuase groff thinks it has found a macro; --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index eb5ea47d..e59b9a6e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3581,7 +3581,7 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed -.It Em Ss , Se +.It Em \&Ss , Se Change the cursor style. If set, a sequence such as this may be used to change the cursor to an underline: From d6debc21c777dc0e993fd7928bbba51075685491 Mon Sep 17 00:00:00 2001 From: Stuart Henderson Date: Tue, 11 Jun 2013 19:18:02 +0000 Subject: [PATCH 053/949] revert r1.156 "Add support for focus notifications when tmux pane changes" beck@ found annoying beeps if a machine was shutdown while tmux is running and you then focus in/out of an xterm; kettenis tracked it down to 1.156. --- tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty.c b/tty.c index 652d2b77..cf6e9652 100644 --- a/tty.c +++ b/tty.c @@ -220,7 +220,7 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) - tty_puts(tty, "\033[c\033[>4;1m\033[?1004h\033[m"); + tty_puts(tty, "\033[c\033[>4;1m\033[m"); tty->cx = UINT_MAX; tty->cy = UINT_MAX; @@ -283,7 +283,7 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) - tty_raw(tty, "\033[>4m\033[?1004l\033[m"); + tty_raw(tty, "\033[>4m\033[m"); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); From 662d471215d66d3c44a9251cef7d00b25587851d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 12:41:54 +0000 Subject: [PATCH 054/949] Mark control commands specially so the client can identify them, based on a diff from George Nachman a while back. --- cmd-queue.c | 7 +++++-- control.c | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index a64d332c..904b092c 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -157,14 +157,17 @@ int cmdq_guard(struct cmd_q *cmdq, const char *guard) { struct client *c = cmdq->client; + int flags; if (c == NULL) return 0; if (!(c->flags & CLIENT_CONTROL)) return 0; - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, - (long) cmdq->time, cmdq->number); + flags = !!(cmdq->cmd->flags & CMD_CONTROL); + + evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, + (long) cmdq->time, cmdq->number, flags); server_push_stdout(c); return 1; } diff --git a/control.c b/control.c index 8986f5c6..1f3739fc 100644 --- a/control.c +++ b/control.c @@ -55,6 +55,7 @@ control_callback(struct client *c, int closed, unused void *data) { char *line, *cause; struct cmd_list *cmdlist; + struct cmd *cmd; if (closed) c->flags |= CLIENT_EXIT; @@ -78,6 +79,8 @@ control_callback(struct client *c, int closed, unused void *data) free(cause); } else { + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) + cmd->flags |= CMD_CONTROL; cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); } From a41cd8d75b55da5a1e75e187b30b33917c18c1b2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 12:51:28 +0000 Subject: [PATCH 055/949] Always push a focus event when the application turns it on, prompted by discussion with Hayaki Saito a while ago. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 0dcdee96..5f70f872 100644 --- a/input.c +++ b/input.c @@ -1333,7 +1333,7 @@ input_csi_dispatch(struct input_ctx *ictx) if (s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - wp->flags &= ~PANE_FOCUSED; /* force update if needed */ + wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); From 3977dba76139b5d4b0a6e60458d39a374beeedf3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 13:10:46 +0000 Subject: [PATCH 056/949] Focus events can cause trouble if left on and they can't be turned off during idle periods (like the other states are) because we'd miss events. So add a server option to control them. Defaults to off. --- options-table.c | 5 +++++ server-client.c | 13 +++++++++++-- tmux.1 | 8 ++++++++ tmux.h | 5 +++++ tty.c | 14 ++++++++++++-- 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/options-table.c b/options-table.c index c73842f7..cf0202b7 100644 --- a/options-table.c +++ b/options-table.c @@ -76,6 +76,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "focus-events", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + { .name = "quiet", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ diff --git a/server-client.c b/server-client.c index 3b7b988a..5f61f5c0 100644 --- a/server-client.c +++ b/server-client.c @@ -548,6 +548,15 @@ server_client_check_focus(struct window_pane *wp) { u_int i; struct client *c; + int push; + + /* Are focus events off? */ + if (!options_get_number(&global_options, "focus-events")) + return; + + /* Do we need to push the focus state? */ + push = wp->flags & PANE_FOCUSPUSH; + wp->flags &= ~PANE_FOCUSPUSH; /* If we don't care about focus, forget it. */ if (!(wp->base.mode & MODE_FOCUSON)) @@ -580,13 +589,13 @@ server_client_check_focus(struct window_pane *wp) } not_focused: - if (wp->flags & PANE_FOCUSED) + if (push || (wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[O", 3); wp->flags &= ~PANE_FOCUSED; return; focused: - if (!(wp->flags & PANE_FOCUSED)) + if (push || !(wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[I", 3); wp->flags |= PANE_FOCUSED; } diff --git a/tmux.1 b/tmux.1 index e59b9a6e..012f2069 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2092,6 +2092,14 @@ The default is 500 milliseconds. .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. +.It Xo Ic focus-events +.Op Ic on | off +.Xc +When enabled, focus events are requested from the terminal if supported and +passed through to applications running in +.Nm . +Attached clients should be detached and attached again after changing this +option. .It Xo Ic quiet .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index b1624b6a..b3f314c5 100644 --- a/tmux.h +++ b/tmux.h @@ -941,6 +941,7 @@ struct window_pane { #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 #define PANE_RESIZE 0x8 +#define PANE_FOCUSPUSH 0x10 char *cmd; char *shell; @@ -1232,6 +1233,7 @@ struct tty { #define TTY_UTF8 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 +#define TTY_FOCUS 0x40 int flags; int term_flags; @@ -1383,6 +1385,9 @@ struct cmd { char *file; u_int line; +#define CMD_CONTROL 0x1 + int flags; + TAILQ_ENTRY(cmd) qentry; }; struct cmd_list { diff --git a/tty.c b/tty.c index cf6e9652..c989aaaa 100644 --- a/tty.c +++ b/tty.c @@ -219,8 +219,13 @@ tty_start_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) + if (tty_term_has(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\033[>4;1m\033[m"); + } tty->cx = UINT_MAX; tty->cy = UINT_MAX; @@ -282,8 +287,13 @@ tty_stop_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) + if (tty_term_has(tty->term, TTYC_XT)) { + if (tty->flags & TTY_FOCUS) { + tty->flags &= ~TTY_FOCUS; + tty_puts(tty, "\033[?1004l"); + } tty_raw(tty, "\033[>4m\033[m"); + } tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); From 097a046e4a2e4e637a53cfc85efca1c4268242c2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 25 Jun 2013 09:35:42 +0100 Subject: [PATCH 057/949] Use _XPG6 on Solaris with GCC because the headers are insane and don't like GCC with -std=gnu99 and _XPG4_2. We should not really be using _XPG* at all but there doesn't seem to be a magic define that says "give me the latest standard", and things are further confused by the CMSG_DATA check in configure.ac which sets _XOPEN_SOURCE _XOPEN_SOURCE_EXTENDED. While here add COPYING to EXTRA_DIST. --- Makefile.am | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 84261d41..fb707df0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ CLEANFILES = tmux.1.mdoc tmux.1.man # Distribution tarball options. EXTRA_DIST = \ - CHANGES FAQ README TODO examples compat \ + CHANGES FAQ README TODO COPYING examples compat \ array.h compat.h tmux.h osdep-*.c mdoc2man.awk tmux.1 dist-hook: grep "^#found_debug=" configure @@ -48,8 +48,12 @@ endif # Set flags for Solaris. if IS_SUNOS +if IS_GCC +CPPFLAGS += -D_XPG6 -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS +else CPPFLAGS += -D_XPG4_2 -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS endif +endif # Set flags for Sun CC. if IS_SUNCC From 828145456c4c1ab8340c2a38ee7e66333cff1d27 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 25 Jun 2013 09:48:09 +0100 Subject: [PATCH 058/949] Don't set TMUX_CONF in tmux.h. --- tmux.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/tmux.h b/tmux.h index 12310361..cd40d4ef 100644 --- a/tmux.h +++ b/tmux.h @@ -39,9 +39,6 @@ extern char *__progname; extern char **environ; -/* Default global configuration file. */ -#define TMUX_CONF "/etc/tmux.conf" - /* Default prompt history length. */ #define PROMPT_HISTORY 100 From 1099442c0a9751a00524895c42ba1508eced0b25 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 25 Jun 2013 09:57:49 +0100 Subject: [PATCH 059/949] +strings.h in compat/. --- compat/forkpty-sunos.c | 1 + compat/imsg-buffer.c | 1 + compat/imsg.c | 1 + 3 files changed, 3 insertions(+) diff --git a/compat/forkpty-sunos.c b/compat/forkpty-sunos.c index 90452f8d..7b254d82 100644 --- a/compat/forkpty-sunos.c +++ b/compat/forkpty-sunos.c @@ -21,6 +21,7 @@ #include #include +#include #include #include diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 24db311d..2ddf0f77 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "tmux.h" diff --git a/compat/imsg.c b/compat/imsg.c index 0feff660..c4dd191d 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "tmux.h" From a0172a6ae561fd6764fcfcdd975fc7a07f43dc79 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 12:41:54 +0000 Subject: [PATCH 060/949] Mark control commands specially so the client can identify them, based on a diff from George Nachman a while back. --- cmd-queue.c | 7 +++++-- control.c | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 671f1e95..243f73dd 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -157,14 +157,17 @@ int cmdq_guard(struct cmd_q *cmdq, const char *guard) { struct client *c = cmdq->client; + int flags; if (c == NULL) return 0; if (!(c->flags & CLIENT_CONTROL)) return 0; - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, - (long) cmdq->time, cmdq->number); + flags = !!(cmdq->cmd->flags & CMD_CONTROL); + + evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, + (long) cmdq->time, cmdq->number, flags); server_push_stdout(c); return 1; } diff --git a/control.c b/control.c index ba243fd3..aa79085a 100644 --- a/control.c +++ b/control.c @@ -55,6 +55,7 @@ control_callback(struct client *c, int closed, unused void *data) { char *line, *cause; struct cmd_list *cmdlist; + struct cmd *cmd; if (closed) c->flags |= CLIENT_EXIT; @@ -78,6 +79,8 @@ control_callback(struct client *c, int closed, unused void *data) free(cause); } else { + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) + cmd->flags |= CMD_CONTROL; cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); } From 777be296ee041e7d9131110e0489e791ed389ac1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 12:51:28 +0000 Subject: [PATCH 061/949] Always push a focus event when the application turns it on, prompted by discussion with Hayaki Saito a while ago. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 4aa02e90..30d3bb98 100644 --- a/input.c +++ b/input.c @@ -1333,7 +1333,7 @@ input_csi_dispatch(struct input_ctx *ictx) if (s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - wp->flags &= ~PANE_FOCUSED; /* force update if needed */ + wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); From c7a121cfc0137c907b7bfb0c3fd1bdee395af8aa Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 13:10:46 +0000 Subject: [PATCH 062/949] Focus events can cause trouble if left on and they can't be turned off during idle periods (like the other states are) because we'd miss events. So add a server option to control them. Defaults to off. --- options-table.c | 5 +++++ server-client.c | 13 +++++++++++-- tmux.1 | 8 ++++++++ tmux.h | 5 +++++ tty.c | 14 ++++++++++++-- 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/options-table.c b/options-table.c index fe21f165..2281d652 100644 --- a/options-table.c +++ b/options-table.c @@ -75,6 +75,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "focus-events", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + { .name = "quiet", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ diff --git a/server-client.c b/server-client.c index 1c15a555..01616cbf 100644 --- a/server-client.c +++ b/server-client.c @@ -548,6 +548,15 @@ server_client_check_focus(struct window_pane *wp) { u_int i; struct client *c; + int push; + + /* Are focus events off? */ + if (!options_get_number(&global_options, "focus-events")) + return; + + /* Do we need to push the focus state? */ + push = wp->flags & PANE_FOCUSPUSH; + wp->flags &= ~PANE_FOCUSPUSH; /* If we don't care about focus, forget it. */ if (!(wp->base.mode & MODE_FOCUSON)) @@ -580,13 +589,13 @@ server_client_check_focus(struct window_pane *wp) } not_focused: - if (wp->flags & PANE_FOCUSED) + if (push || (wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[O", 3); wp->flags &= ~PANE_FOCUSED; return; focused: - if (!(wp->flags & PANE_FOCUSED)) + if (push || !(wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[I", 3); wp->flags |= PANE_FOCUSED; } diff --git a/tmux.1 b/tmux.1 index f644587b..0aeb5a17 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2096,6 +2096,14 @@ The default is 500 milliseconds. .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. +.It Xo Ic focus-events +.Op Ic on | off +.Xc +When enabled, focus events are requested from the terminal if supported and +passed through to applications running in +.Nm . +Attached clients should be detached and attached again after changing this +option. .It Xo Ic quiet .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index cd40d4ef..5dd86d38 100644 --- a/tmux.h +++ b/tmux.h @@ -934,6 +934,7 @@ struct window_pane { #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 #define PANE_RESIZE 0x8 +#define PANE_FOCUSPUSH 0x10 char *cmd; char *shell; @@ -1225,6 +1226,7 @@ struct tty { #define TTY_UTF8 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 +#define TTY_FOCUS 0x40 int flags; int term_flags; @@ -1376,6 +1378,9 @@ struct cmd { char *file; u_int line; +#define CMD_CONTROL 0x1 + int flags; + TAILQ_ENTRY(cmd) qentry; }; struct cmd_list { diff --git a/tty.c b/tty.c index 79887fb3..d5b1aec0 100644 --- a/tty.c +++ b/tty.c @@ -219,8 +219,13 @@ tty_start_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) + if (tty_term_has(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\033[>4;1m\033[m"); + } tty->cx = UINT_MAX; tty->cy = UINT_MAX; @@ -282,8 +287,13 @@ tty_stop_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) + if (tty_term_has(tty->term, TTYC_XT)) { + if (tty->flags & TTY_FOCUS) { + tty->flags &= ~TTY_FOCUS; + tty_puts(tty, "\033[?1004l"); + } tty_raw(tty, "\033[>4m\033[m"); + } tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); From f884fff8693de02cea0347f1c267dbd925e3b5f5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 14:38:23 +0000 Subject: [PATCH 063/949] Implement s, S, C mode switch commands in vi(1) mode, from Ben Boeckel. --- tmux.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tmux.h b/tmux.h index b3f314c5..b5608da3 100644 --- a/tmux.h +++ b/tmux.h @@ -548,6 +548,9 @@ enum mode_key_cmd { MODEKEYEDIT_SWITCHMODEAPPEND, MODEKEYEDIT_SWITCHMODEAPPENDLINE, MODEKEYEDIT_SWITCHMODEBEGINLINE, + MODEKEYEDIT_SWITCHMODECHANGELINE, + MODEKEYEDIT_SWITCHMODESUBSTITUTE, + MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, MODEKEYEDIT_TRANSPOSECHARS, /* Menu (choice) keys. */ From 7af5fec0387008c71bf29ad1e394871873acaf40 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 14:44:06 +0000 Subject: [PATCH 064/949] Whitespace nits, from Ben Boeckel. --- mode-key.c | 21 ++++++++++++++------- status.c | 13 ++++++++++--- window-copy.c | 7 ++++++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/mode-key.c b/mode-key.c index 54abcf32..78a8bdf3 100644 --- a/mode-key.c +++ b/mode-key.c @@ -35,9 +35,7 @@ * * vi command mode is handled by having a mode flag in the struct which allows * two sets of bindings to be swapped between. A couple of editing commands - * (MODEKEYEDIT_SWITCHMODE, MODEKEYEDIT_SWITCHMODEAPPEND, - * MODEKEYEDIT_SWITCHMODEAPPENDLINE, and MODEKEYEDIT_SWITCHMODEBEGINLINE) - * are special-cased to do this. + * (any matching MODEKEYEDIT_SWITCHMODE*) are special-cased to do this. */ /* Edit keys command strings. */ @@ -67,6 +65,9 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" }, { MODEKEYEDIT_SWITCHMODEAPPENDLINE, "switch-mode-append-line" }, { MODEKEYEDIT_SWITCHMODEBEGINLINE, "switch-mode-begin-line" }, + { MODEKEYEDIT_SWITCHMODECHANGELINE, "switch-mode-change-line" }, + { MODEKEYEDIT_SWITCHMODESUBSTITUTE, "switch-mode-substitute" }, + { MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, "switch-mode-substitute-line" }, { MODEKEYEDIT_TRANSPOSECHARS, "transpose-chars" }, { 0, NULL } @@ -166,9 +167,11 @@ const struct mode_key_entry mode_key_vi_edit[] = { { '0', 1, MODEKEYEDIT_STARTOFLINE }, { 'A', 1, MODEKEYEDIT_SWITCHMODEAPPENDLINE }, { 'B', 1, MODEKEYEDIT_PREVIOUSSPACE }, + { 'C', 1, MODEKEYEDIT_SWITCHMODECHANGELINE }, { 'D', 1, MODEKEYEDIT_DELETETOENDOFLINE }, { 'E', 1, MODEKEYEDIT_NEXTSPACEEND }, { 'I', 1, MODEKEYEDIT_SWITCHMODEBEGINLINE }, + { 'S', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTELINE }, { 'W', 1, MODEKEYEDIT_NEXTSPACE }, { 'X', 1, MODEKEYEDIT_BACKSPACE }, { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL }, @@ -185,6 +188,7 @@ const struct mode_key_entry mode_key_vi_edit[] = { { 'k', 1, MODEKEYEDIT_HISTORYUP }, { 'l', 1, MODEKEYEDIT_CURSORRIGHT }, { 'p', 1, MODEKEYEDIT_PASTE }, + { 's', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTE }, { 'w', 1, MODEKEYEDIT_NEXTWORD }, { 'x', 1, MODEKEYEDIT_DELETE }, { KEYC_BSPACE, 1, MODEKEYEDIT_BACKSPACE }, @@ -227,8 +231,8 @@ const struct mode_key_entry mode_key_vi_choice[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, + { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, + { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, @@ -369,8 +373,8 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, + { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, + { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, @@ -545,6 +549,9 @@ mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) case MODEKEYEDIT_SWITCHMODEAPPEND: case MODEKEYEDIT_SWITCHMODEAPPENDLINE: case MODEKEYEDIT_SWITCHMODEBEGINLINE: + case MODEKEYEDIT_SWITCHMODECHANGELINE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: mdata->mode = 1 - mdata->mode; /* FALLTHROUGH */ default: diff --git a/status.c b/status.c index ecfd7e58..c5572b74 100644 --- a/status.c +++ b/status.c @@ -142,10 +142,8 @@ status_set_window_at(struct client *c, u_int x) x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { - if (x < wl->status_width && - session_select(s, wl->idx) == 0) { + if (x < wl->status_width && session_select(s, wl->idx) == 0) server_redraw_session(s); - } x -= wl->status_width + 1; } } @@ -938,6 +936,7 @@ status_prompt_redraw(struct client *c) off = 0; memcpy(&gc, &grid_default_cell, sizeof gc); + /* Change colours for command mode. */ if (c->prompt_mdata.mode == 1) { colour_set_fg(&gc, options_get_number(&s->options, "message-command-fg")); @@ -1099,6 +1098,7 @@ status_prompt_key(struct client *c, int key) } break; case MODEKEYEDIT_DELETE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTE: if (c->prompt_index != size) { memmove(c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index + 1, @@ -1107,11 +1107,13 @@ status_prompt_key(struct client *c, int key) } break; case MODEKEYEDIT_DELETELINE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: *c->prompt_buffer = '\0'; c->prompt_index = 0; c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_DELETETOENDOFLINE: + case MODEKEYEDIT_SWITCHMODECHANGELINE: if (c->prompt_index < size) { c->prompt_buffer[c->prompt_index] = '\0'; c->flags |= CLIENT_STATUS; @@ -1190,6 +1192,11 @@ status_prompt_key(struct client *c, int key) break; } + /* Back up to the end-of-word like vi. */ + if (options_get_number(oo, "status-keys") == MODEKEY_VI && + c->prompt_index != 0) + c->prompt_index--; + c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PREVIOUSSPACE: diff --git a/window-copy.c b/window-copy.c index f5f78cf2..9d4ff20e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1116,7 +1116,7 @@ window_copy_write_line( struct options *oo = &wp->window->options; struct grid_cell gc; char hdr[32]; - size_t last, xoff = 0, size = 0; + size_t last, xoff = 0, size = 0; window_mode_attrs(&gc, oo); @@ -1894,6 +1894,7 @@ void window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) { struct window_copy_mode_data *data = wp->modedata; + struct options *oo = &wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; int expected = 1; @@ -1927,6 +1928,10 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) expected = !expected; } while (expected == 0); + /* Back up to the end-of-word like vi. */ + if (options_get_number(oo, "status-keys") == MODEKEY_VI && px != 0) + px--; + window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); From 064124cc5f3df250f0f866ae1df7bc4cd19bf833 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 14:52:33 +0000 Subject: [PATCH 065/949] When the session option renumber-window is used, ensure we iterate over all sessions in that group when the winlinks are reordered, otherwise the winlink lists are out of sync with one another. From Thomas Adam. --- server-fn.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server-fn.c b/server-fn.c index d92754e1..efb95acd 100644 --- a/server-fn.c +++ b/server-fn.c @@ -262,8 +262,9 @@ server_lock_client(struct client *c) void server_kill_window(struct window *w) { - struct session *s, *next_s; - struct winlink *wl; + struct session *s, *next_s, *target_s; + struct session_group *sg; + struct winlink *wl; next_s = RB_MIN(sessions, &sessions); while (next_s != NULL) { @@ -280,8 +281,13 @@ server_kill_window(struct window *w) server_redraw_session_group(s); } - if (options_get_number(&s->options, "renumber-windows")) - session_renumber_windows(s); + if (options_get_number(&s->options, "renumber-windows")) { + if ((sg = session_group_find(s)) != NULL) { + TAILQ_FOREACH(target_s, &sg->sessions, gentry) + session_renumber_windows(target_s); + } else + session_renumber_windows(s); + } } recalculate_sizes(); } From a96a8a1aabd64f0d79602ed7248cf73655066d92 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 15:10:38 +0000 Subject: [PATCH 066/949] Clarify error messages when setting options, from Thomas Adam. --- cmd-set-option.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 6c0fefbf..3b822d8b 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -128,8 +128,13 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) oo = &global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) + if (wl == NULL) { + cmdq_error(cmdq, + "couldn't set '%s'%s", optstr, + (!args_has(args, 't') && !args_has(args, + 'g')) ? " need target window or -g" : ""); return (CMD_RETURN_ERROR); + } oo = &wl->window->options; } } else if (table == session_options_table) { @@ -137,8 +142,13 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) oo = &global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) + if (s == NULL) { + cmdq_error(cmdq, + "couldn't set '%s'%s", optstr, + (!args_has(args, 't') && !args_has(args, + 'g')) ? " need target session or -g" : ""); return (CMD_RETURN_ERROR); + } oo = &s->options; } } else { From 3d39b18e319cdd96bf25debcfcf52c03120e3d8e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 16:23:33 +0100 Subject: [PATCH 067/949] Section on clipboard, from Thomas Adam. --- FAQ | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/FAQ b/FAQ index 41b917c8..81421bc0 100644 --- a/FAQ +++ b/FAQ @@ -396,5 +396,29 @@ configuration file: Or the default window options: $ tmux -Lfoo -f/dev/null start\; show -gw + +* How do I copy a selection from tmux to the system's clipboard? + +When running in xterm(1), tmux can automatically send copied text to the +clipboard. This is controlled by the set-clipboard option and also needs this +X resource to be set: + + XTerm*disallowedWindowOps: 20,21,SetXprop + +For rxvt-unicode (urxvt), there is an unofficial Perl extension here: + + http://anti.teamidiot.de/static/nei/*/Code/urxvt/ + +Otherwise a key binding for copy mode using xclip (or xsel) works: + + bind -temacs-copy C-y copy-pipe "xclip -i >/dev/null" + +Or for inside and outside copy mode with the prefix key: + + bind C-y run -b "tmux save-buffer - | xclip -i" + +On OS X, reattach-to-usernamespace lets pbcopy/pbpaste work: + + https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard $Id$ From f5b041e3949e9a129d68d9919725c3afcd81ed5a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 15:27:14 +0000 Subject: [PATCH 068/949] Add pane_synchronized format, from Romain Francoise. --- format.c | 2 ++ tmux.1 | 1 + 2 files changed, 3 insertions(+) diff --git a/format.c b/format.c index 8ed1c1be..168ff5b9 100644 --- a/format.c +++ b/format.c @@ -460,6 +460,8 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_dead", "%d", wp->fd == -1); format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); + format_add(ft, "pane_synchronized", "%d", + !!options_get_number(&wp->window->options, "synchronize-panes")); if (wp->tty != NULL) format_add(ft, "pane_tty", "%s", wp->tty); diff --git a/tmux.1 b/tmux.1 index 012f2069..da010d17 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3068,6 +3068,7 @@ The following variables are available, where appropriate: .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 "If pane is in a mode" +.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" .It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" From a9ebb62d54650dd2e8e36acea8f62f3b3f108477 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 12 Jul 2013 09:52:36 +0000 Subject: [PATCH 069/949] Make next-word-end work properly with vi(1) keys, reported by patrick keshishian. --- window-copy.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/window-copy.c b/window-copy.c index 9d4ff20e..7a647c51 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1897,13 +1897,17 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) struct options *oo = &wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; - int expected = 1; + int keys, expected = 1; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, 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(wp, px, py, separators)) + px++; + /* * First skip past any word characters, then any nonword characters. * @@ -1928,8 +1932,7 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) expected = !expected; } while (expected == 0); - /* Back up to the end-of-word like vi. */ - if (options_get_number(oo, "status-keys") == MODEKEY_VI && px != 0) + if (keys == MODEKEY_VI && px != 0) px--; window_copy_update_cursor(wp, px, data->cy); From e8567098a4eaa81306effeaef430d9d74f603f7a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 12 Jul 2013 22:21:42 +0100 Subject: [PATCH 070/949] Add support for Cgywin, apparently it is enough just to open the tty again in the server and fd passing is not necessary. Needs some ifdefs unfortunately but no way around that and some of them can go next time we're willing to do a protocol bump. Patch from J Raynor jxraynor at gmail dot com. --- client.c | 5 +++++ compat.h | 5 +++++ server-client.c | 5 ++++- tmux.h | 4 ++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/client.c b/client.c index 691ace31..b9e3b30a 100644 --- a/client.c +++ b/client.c @@ -328,8 +328,13 @@ client_send_identify(int flags) strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) *data.term = '\0'; +#ifdef __CYGWIN__ + snprintf(&data.ttyname, sizeof data.ttyname, "%s", + ttyname(STDIN_FILENO)); +#else if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); +#endif imsg_compose(&client_ibuf, MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); client_update_event(); diff --git a/compat.h b/compat.h index d3973797..b84ff400 100644 --- a/compat.h +++ b/compat.h @@ -30,6 +30,10 @@ #define __packed __attribute__ ((__packed__)) #endif +#ifndef ECHOPRT +#define ECHOPRT 0 +#endif + #ifndef HAVE_BSD_TYPES typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; @@ -210,6 +214,7 @@ int daemon(int, int); #ifndef HAVE_B64_NTOP /* b64_ntop.c */ +#undef b64_ntop /* for Cygwin */ int b64_ntop(const char *, size_t, char *, size_t); #endif diff --git a/server-client.c b/server-client.c index 01616cbf..5923eb0e 100644 --- a/server-client.c +++ b/server-client.c @@ -825,9 +825,12 @@ server_client_msg_dispatch(struct client *c) case MSG_IDENTIFY: if (datalen != sizeof identifydata) fatalx("bad MSG_IDENTIFY size"); + memcpy(&identifydata, imsg.data, sizeof identifydata); +#ifdef __CYGWIN__ + imsg.fd = open(identifydata.ttyname, O_RDWR|O_NOCTTY); +#endif if (imsg.fd == -1) fatalx("MSG_IDENTIFY missing fd"); - memcpy(&identifydata, imsg.data, sizeof identifydata); server_client_msg_identify(c, &identifydata, imsg.fd); break; diff --git a/tmux.h b/tmux.h index eb53d3b4..cc1c79e8 100644 --- a/tmux.h +++ b/tmux.h @@ -471,6 +471,10 @@ struct msg_identify_data { char term[TERMINAL_LENGTH]; +#ifdef __CYGWIN__ + char ttyname[TTY_NAME_MAX]; +#endif + #define IDENTIFY_UTF8 0x1 #define IDENTIFY_256COLOURS 0x2 /* 0x4 unused */ From 304ea079d2157b6625c960e0daa817213ba1ffd6 Mon Sep 17 00:00:00 2001 From: schwarze Date: Tue, 16 Jul 2013 00:07:52 +0000 Subject: [PATCH 071/949] use .Mt for email addresses; from Jan Stary ; ok jmc@ --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index da010d17..7a01b394 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3799,4 +3799,4 @@ bind-key S command-prompt "new-window -n %1 'ssh %1'" .Sh SEE ALSO .Xr pty 4 .Sh AUTHORS -.An Nicholas Marriott Aq nicm@users.sourceforge.net +.An Nicholas Marriott Aq Mt nicm@users.sourceforge.net From 7ea560261c72cd8e8453e7ea0e4f3922a2bdfeb3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 27 Jul 2013 19:57:21 +0100 Subject: [PATCH 072/949] Add destroy entry to TODO. --- TODO | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TODO b/TODO index 92b60a9a..e8e8d073 100644 --- a/TODO +++ b/TODO @@ -115,3 +115,12 @@ - we need a tmux terminfo entry to document the extensions we are using in upstream terminfo + +- the way pane, window, session destroy is handled is too complicated and the + distinction between session.c, window.c and server-fn.c functions is not + clear. could we just have kill_pane(), kill_window(), unlink_window(), + kill_session() that fix up all data structures (flagging sessions as dead) + and return a value to say whether clients need to be checked for dead + sessions? sort of like session_detach now but more so. or some other scheme + to make it simpler and clearer? also would be nice to remove/rename + server-fn.c. From 1c271852fc7464ecdff1697a66a8bc0eac44e90d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:35:03 +0100 Subject: [PATCH 073/949] Pass flags into cmdq_guard as an argument since sometimes cmdq->cmd can be NULL. Avoids crash when a command in a command client can't be parsed. --- cmd-queue.c | 16 ++++++++-------- control.c | 4 ++-- tmux.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 243f73dd..38a88d23 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -154,18 +154,15 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) /* Print a guard line. */ int -cmdq_guard(struct cmd_q *cmdq, const char *guard) +cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) { struct client *c = cmdq->client; - int flags; if (c == NULL) return 0; if (!(c->flags & CLIENT_CONTROL)) return 0; - flags = !!(cmdq->cmd->flags & CMD_CONTROL); - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long) cmdq->time, cmdq->number, flags); server_push_stdout(c); @@ -202,7 +199,7 @@ cmdq_continue(struct cmd_q *cmdq) { struct cmd_q_item *next; enum cmd_retval retval; - int empty, guard; + int empty, guard, flags; char s[1024]; notify_disable(); @@ -228,13 +225,16 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->time = time(NULL); cmdq->number++; - guard = cmdq_guard(cmdq, "begin"); + flags = !!(cmdq->cmd->flags & CMD_CONTROL); + guard = cmdq_guard(cmdq, "begin", flags); + retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); + if (guard) { if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error"); + cmdq_guard(cmdq, "error", flags); else - cmdq_guard(cmdq, "end"); + cmdq_guard(cmdq, "end", flags); } if (retval == CMD_RETURN_ERROR) diff --git a/control.c b/control.c index aa79085a..52fdb524 100644 --- a/control.c +++ b/control.c @@ -73,9 +73,9 @@ control_callback(struct client *c, int closed, unused void *data) c->cmdq->time = time(NULL); c->cmdq->number++; - cmdq_guard(c->cmdq, "begin"); + cmdq_guard(c->cmdq, "begin", 1); control_write(c, "parse error: %s", cause); - cmdq_guard(c->cmdq, "error"); + cmdq_guard(c->cmdq, "error", 1); free(cause); } else { diff --git a/tmux.h b/tmux.h index cc1c79e8..1cb5cd8e 100644 --- a/tmux.h +++ b/tmux.h @@ -1868,7 +1868,7 @@ int cmdq_free(struct cmd_q *); void printflike2 cmdq_print(struct cmd_q *, const char *, ...); void printflike2 cmdq_info(struct cmd_q *, const char *, ...); void printflike2 cmdq_error(struct cmd_q *, const char *, ...); -int cmdq_guard(struct cmd_q *, const char *); +int cmdq_guard(struct cmd_q *, const char *, int); void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); From 965edf8a5c985d760b1e037be8182b353e9139c9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:37:45 +0100 Subject: [PATCH 074/949] Make recalculate_sizes() handle an empty window with no active pane. This can happen when a window is in two sessions - it isn't destroyed immediately when the pane goes away but is left until the last session is destroyed. Fixes problems with grouped sessions reported by Daniel Ralston. --- resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resize.c b/resize.c index 5c365dfe..8d0bd275 100644 --- a/resize.c +++ b/resize.c @@ -92,7 +92,7 @@ recalculate_sizes(void) for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); - if (w == NULL) + if (w == NULL || w->active == NULL) continue; flag = options_get_number(&w->options, "aggressive-resize"); From b0b5cad4968b09b805b475b41c0a9005f79c9563 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:38:35 +0100 Subject: [PATCH 075/949] Grouped sessions were being leaked on destroy, correctly free them. --- server-fn.c | 7 ++++--- session.c | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server-fn.c b/server-fn.c index 86e2054e..738a61da 100644 --- a/server-fn.c +++ b/server-fn.c @@ -398,14 +398,15 @@ void server_destroy_session_group(struct session *s) { struct session_group *sg; + struct session *s1; if ((sg = session_group_find(s)) == NULL) server_destroy_session(s); else { - TAILQ_FOREACH(s, &sg->sessions, gentry) + TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { server_destroy_session(s); - TAILQ_REMOVE(&session_groups, sg, entry); - free(sg); + session_destroy(s); + } } } diff --git a/session.c b/session.c index 74eb06a5..24e2e5e7 100644 --- a/session.c +++ b/session.c @@ -150,6 +150,7 @@ void session_destroy(struct session *s) { struct winlink *wl; + log_debug("session %s destroyed", s->name); RB_REMOVE(sessions, &sessions, s); From 35c19ffc284da8f37f62b1696f5d5d5e6ca2329c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:38:53 +0100 Subject: [PATCH 076/949] Missing space in refresh-client synopsis. --- cmd-refresh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 7d9d539f..70e6888d 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -29,7 +29,7 @@ enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { "refresh-client", "refresh", "C:St:", 0, 0, - "[-S] [-C size]" CMD_TARGET_CLIENT_USAGE, + "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, From 27364345bf68785af58131c47ef46ff081622537 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:39:09 +0100 Subject: [PATCH 077/949] Don't add client formats when they are NULL. --- format.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 8c4c7842..16ac53d6 100644 --- a/format.c +++ b/format.c @@ -346,8 +346,10 @@ format_client(struct format_tree *ft, struct client *c) format_add(ft, "client_cwd", "%s", c->cwd); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); - format_add(ft, "client_tty", "%s", c->tty.path); - format_add(ft, "client_termname", "%s", c->tty.termname); + if (c->tty.path != NULL) + format_add(ft, "client_tty", "%s", c->tty.path); + if (c->tty.termname != NULL) + format_add(ft, "client_termname", "%s", c->tty.termname); t = c->creation_time.tv_sec; format_add(ft, "client_created", "%lld", (long long) t); From 939f796f086cac17ad0558b36fa87c55a9cb1a22 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:40:44 +0100 Subject: [PATCH 078/949] Don't leak formats if they are added multiple times. --- format.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 16ac53d6..2275090e 100644 --- a/format.c +++ b/format.c @@ -151,6 +151,7 @@ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) { struct format_entry *fe; + struct format_entry *fe_now; va_list ap; fe = xmalloc(sizeof *fe); @@ -160,7 +161,13 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) xvasprintf(&fe->value, fmt, ap); va_end(ap); - RB_INSERT(format_tree, ft, fe); + fe_now = RB_INSERT(format_tree, ft, fe); + if (fe_now != NULL) { + free(fe_now->value); + fe_now->value = fe->value; + free(fe->key); + free(fe); + } } /* Find a format entry. */ From bcd9bcae2a9aa3a2553f546107b99431dbb6931f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:41:39 +0100 Subject: [PATCH 079/949] Add formats for window flags. --- format.c | 9 +++++++++ tmux.1 | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/format.c b/format.c index 2275090e..0845df6d 100644 --- a/format.c +++ b/format.c @@ -410,6 +410,15 @@ format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) format_add(ft, "window_active", "%d", wl == s->curw); format_add(ft, "window_panes", "%u", window_count_panes(w)); + format_add(ft, "window_bell_flag", "%u", + !!(wl->flags & WINLINK_BELL)); + format_add(ft, "window_content_flag", "%u", + !!(wl->flags & WINLINK_CONTENT)); + format_add(ft, "window_activity_flag", "%u", + !!(wl->flags & WINLINK_ACTIVITY)); + format_add(ft, "window_silence_flag", "%u", + !!(wl->flags & WINLINK_SILENCE)); + free(flags); free(layout); } diff --git a/tmux.1 b/tmux.1 index 11251790..340109b6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3096,6 +3096,9 @@ The following variables are available, where appropriate: .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" +.It Li "window_bell_flag" Ta "" Ta "1 if window has bell" +.It Li "window_content_flag" Ta "" Ta "1 if window has content alert" .It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" .It Li "window_flags" Ta "#F" Ta "Window flags" .It Li "window_height" Ta "" Ta "Height of window" @@ -3104,6 +3107,7 @@ The following variables are available, where appropriate: .It Li "window_layout" Ta "" Ta "Window layout description" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_width" Ta "" Ta "Width of window" .It Li "wrap_flag" Ta "" Ta "Pane wrap flag" .El From 3a13e066ba184b1051167b0675bf9b4364af021e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:42:39 +0100 Subject: [PATCH 080/949] Allow the file descriptor received from the client to be -1 - it can be on Cygwin when stdin is not a terminal. Reported by A Young, SF bug 52. --- server-client.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server-client.c b/server-client.c index 5923eb0e..fa4d2688 100644 --- a/server-client.c +++ b/server-client.c @@ -829,9 +829,6 @@ server_client_msg_dispatch(struct client *c) #ifdef __CYGWIN__ imsg.fd = open(identifydata.ttyname, O_RDWR|O_NOCTTY); #endif - if (imsg.fd == -1) - fatalx("MSG_IDENTIFY missing fd"); - server_client_msg_identify(c, &identifydata, imsg.fd); break; case MSG_STDIN: @@ -975,6 +972,8 @@ server_client_msg_identify( return; } + if (fd == -1) + return; if (!isatty(fd)) { close(fd); return; From 2dfd3fbd71560aa34cffd2a8de65425876188a4f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Aug 2013 23:47:45 +0100 Subject: [PATCH 081/949] Only include actual trailing spaces not unused cells with capturep -J, from George Nachman. --- grid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 2955e8ba..5cc6c149 100644 --- a/grid.c +++ b/grid.c @@ -592,6 +592,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, char *buf, code[128]; size_t len, off, size, codelen; u_int xx; + const struct grid_line *gl; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); @@ -604,8 +605,11 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, buf = xmalloc(len); off = 0; + gl = grid_peek_line(gd, py); for (xx = px; xx < px + nx; xx++) { - gc = grid_peek_cell(gd, xx, py); + if (gl == NULL || xx >= gl->cellsize) + break; + gc = &gl->celldata[xx]; if (gc->flags & GRID_FLAG_PADDING) continue; grid_cell_get(gc, &ud); From 7673732c0f6006f2afa5195fc0d8e6032a5580a2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 2 Aug 2013 08:51:57 +0100 Subject: [PATCH 082/949] Handle input mouse positions <33 (we already can generate them). --- tty-keys.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 3055f399..d1c9d875 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -676,11 +676,17 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) log_debug("mouse input: %.*s", (int) *size, buf); /* Check and return the mouse input. */ - if (b < 32 || x < 33 || y < 33) + if (b < 32) return (-1); b -= 32; - x -= 33; - y -= 33; + if (x >= 33) + x -= 33; + else + x = 256 - x; + if (y >= 33) + y -= 33; + else + y = 256 - y; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; From 7581762c8e07b18726933616ac92458f9e74d4c6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 2 Aug 2013 13:53:17 +0100 Subject: [PATCH 083/949] + to TODO. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index e8e8d073..e72e83a2 100644 --- a/TODO +++ b/TODO @@ -8,6 +8,8 @@ * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions + * exact match operator for targets (or break the substring match + and require eg x* instead of just x) - make command sequences more usable * don't require space after ; From 84c22d053b5ec1cd25e1694fa3889922b9cbe11b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 3 Aug 2013 21:06:38 +0100 Subject: [PATCH 084/949] Add length limit operator for formats. --- format.c | 40 ++++++++++++++++++++++++++++++++++------ options-table.c | 2 +- tmux.1 | 6 ++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/format.c b/format.c index 0845df6d..ee3339dc 100644 --- a/format.c +++ b/format.c @@ -18,6 +18,8 @@ #include +#include +#include #include #include #include @@ -188,18 +190,40 @@ format_find(struct format_tree *ft, const char *key) * #{?blah,a,b} is replace with a if blah exists and is nonzero else b. */ int -format_replace(struct format_tree *ft, - const char *key, size_t keylen, char **buf, size_t *len, size_t *off) +format_replace(struct format_tree *ft, const char *key, size_t keylen, + char **buf, size_t *len, size_t *off) { - char *copy, *ptr; + char *copy, *copy0, *endptr, *ptr; const char *value; size_t valuelen; + u_long limit = ULONG_MAX; /* Make a copy of the key. */ - copy = xmalloc(keylen + 1); + copy0 = copy = xmalloc(keylen + 1); memcpy(copy, key, keylen); copy[keylen] = '\0'; + /* Is there a length limit or whatnot? */ + if (!islower((u_char) *copy) && *copy != '?') { + while (*copy != ':' && *copy != '\0') { + switch (*copy) { + case '=': + errno = 0; + limit = strtoul(copy + 1, &endptr, 10); + if (errno == ERANGE && limit == ULONG_MAX) + goto fail; + copy = endptr; + break; + default: + copy++; + break; + } + } + if (*copy != ':') + goto fail; + copy++; + } + /* * Is this a conditional? If so, check it exists and extract either the * first or second element. If not, look up the key directly. @@ -230,6 +254,10 @@ format_replace(struct format_tree *ft, } valuelen = strlen(value); + /* Truncate the value if needed. */ + if (valuelen > limit) + valuelen = limit; + /* Expand the buffer and copy in the value. */ while (*len - *off < valuelen + 1) { *buf = xrealloc(*buf, 2, *len); @@ -238,11 +266,11 @@ format_replace(struct format_tree *ft, memcpy(*buf + *off, value, valuelen); *off += valuelen; - free(copy); + free(copy0); return (0); fail: - free(copy); + free(copy0); return (-1); } diff --git a/options-table.c b/options-table.c index 2281d652..0b86ef7c 100644 --- a/options-table.c +++ b/options-table.c @@ -386,7 +386,7 @@ const struct options_table_entry session_options_table[] = { { .name = "status-right", .type = OPTIONS_TABLE_STRING, - .default_str = "\"#22T\" %H:%M %d-%b-%y" + .default_str = "\"#{=22:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", diff --git a/tmux.1 b/tmux.1 index 340109b6..19ae4a9b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3026,6 +3026,12 @@ will include the string if the session is attached and the string .Ql not attached if it is unattached. +A limit may be placed on the length of the resultant string by prefixing it +by an +.Ql = , +a number and a colon, so +.Ql #{=10:pane_title} +will include at most the first 10 characters of the pane title. .Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" From 04288fcd4c99117f730f37f8a63ded96fcc04c9d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 19 Aug 2013 22:14:55 +0100 Subject: [PATCH 085/949] Allow nested format expansion. --- format.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/format.c b/format.c index ee3339dc..98fc55d2 100644 --- a/format.c +++ b/format.c @@ -193,7 +193,7 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr; + char *copy, *copy0, *endptr, *ptr, *saved; const char *value; size_t valuelen; u_long limit = ULONG_MAX; @@ -247,10 +247,13 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; value = ptr + 1; } + saved = format_expand(ft, value); + value = saved; } else { value = format_find(ft, copy); if (value == NULL) value = ""; + saved = NULL; } valuelen = strlen(value); @@ -266,6 +269,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, memcpy(*buf + *off, value, valuelen); *off += valuelen; + free(saved); free(copy0); return (0); @@ -278,10 +282,10 @@ fail: char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *ptr; - const char *s; + char *buf; + const char *ptr, *s; size_t off, len, n; - int ch; + int ch, brackets; len = 64; buf = xmalloc(len); @@ -299,11 +303,16 @@ format_expand(struct format_tree *ft, const char *fmt) fmt++; ch = (u_char) *fmt++; - switch (ch) { case '{': - ptr = strchr(fmt, '}'); - if (ptr == NULL) + brackets = 1; + for (ptr = fmt; *ptr != '\0'; ptr++) { + if (*ptr == '{') + brackets++; + if (*ptr == '}' && --brackets == 0) + break; + } + if (*ptr != '}' || brackets != 0) break; n = ptr - fmt; From 23519fc0b440ccfe13d52ab3e09ea98c792d4fa7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 19 Aug 2013 22:16:11 +0100 Subject: [PATCH 086/949] Add automatic-rename-format option allowing automatic rename to use something other than pane_current_command. --- format.c | 55 +++++++++++++++++++++++++++++++++++----------- names.c | 58 ++++++++++++++++++++----------------------------- options-table.c | 5 +++++ tmux.1 | 11 ++++++++-- tmux.h | 21 +++++++++++------- 5 files changed, 92 insertions(+), 58 deletions(-) diff --git a/format.c b/format.c index 98fc55d2..da939f04 100644 --- a/format.c +++ b/format.c @@ -34,9 +34,10 @@ * string. */ -int format_replace(struct format_tree *, const char *, size_t, char **, - size_t *, size_t *); -void format_window_pane_tabs(struct format_tree *, struct window_pane *); +int format_replace(struct format_tree *, const char *, size_t, char **, + size_t *, size_t *); +char *format_get_command(struct window_pane *); +void format_window_pane_tabs(struct format_tree *, struct window_pane *); /* Format key-value replacement entry. */ RB_GENERATE(format_tree, format_entry, entry, format_cmp); @@ -348,6 +349,21 @@ format_expand(struct format_tree *ft, const char *fmt) return (buf); } +/* Get command name for format. */ +char * +format_get_command(struct window_pane *wp) +{ + char *cmd; + + cmd = osdep_get_name(wp->fd, wp->tty); + if (cmd == NULL || *cmd == '\0') { + cmd = wp->cmd; + if (cmd == NULL || *cmd == '\0') + cmd = wp->shell; + } + return (parse_window_name(cmd)); +} + /* Set default format keys for a session. */ void format_session(struct format_tree *ft, struct session *s) @@ -427,25 +443,38 @@ format_client(struct format_tree *ft, struct client *c) format_add(ft, "client_last_session", "%s", s->name); } +/* Set default format keys for a window. */ +void +format_window(struct format_tree *ft, struct window *w) +{ + char *layout; + + layout = layout_dump(w); + + format_add(ft, "window_id", "@%u", w->id); + 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_layout", "%s", layout); + format_add(ft, "window_panes", "%u", window_count_panes(w)); + + free(layout); +} + /* Set default format keys for a winlink. */ void format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) { struct window *w = wl->window; - char *layout, *flags; + char *flags; - layout = layout_dump(w); flags = window_printable_flags(s, wl); - format_add(ft, "window_id", "@%u", w->id); + format_window(ft, w); + format_add(ft, "window_index", "%d", wl->idx); - 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_flags", "%s", flags); - format_add(ft, "window_layout", "%s", layout); format_add(ft, "window_active", "%d", wl == s->curw); - format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_bell_flag", "%u", !!(wl->flags & WINLINK_BELL)); @@ -456,8 +485,8 @@ format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) format_add(ft, "window_silence_flag", "%u", !!(wl->flags & WINLINK_SILENCE)); + free(flags); - free(layout); } /* Add window pane tabs. */ @@ -527,7 +556,7 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_start_path", "%s", wp->cwd); if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); - if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) { + if ((cmd = format_get_command(wp)) != NULL) { format_add(ft, "pane_current_command", "%s", cmd); free(cmd); } diff --git a/names.c b/names.c index f536d2fc..7c02961c 100644 --- a/names.c +++ b/names.c @@ -22,12 +22,10 @@ #include #include #include -#include #include "tmux.h" void window_name_callback(unused int, unused short, void *); -char *parse_window_name(const char *); void queue_window_name(struct window *w) @@ -47,7 +45,7 @@ void window_name_callback(unused int fd, unused short events, void *data) { struct window *w = data; - char *name, *wname; + char *name; if (w->active == NULL) return; @@ -59,49 +57,39 @@ window_name_callback(unused int fd, unused short events, void *data) } queue_window_name(w); - if (w->active->screen != &w->active->base) - name = NULL; - else - name = osdep_get_name(w->active->fd, w->active->tty); - if (name == NULL) - wname = default_window_name(w); - else { - /* - * If tmux is using the default command, it will be a login - * shell and argv[0] may have a - prefix. Remove this if it is - * present. Ick. - */ - if (w->active->cmd != NULL && *w->active->cmd == '\0' && - name != NULL && name[0] == '-' && name[1] != '\0') - wname = parse_window_name(name + 1); - else - wname = parse_window_name(name); - free(name); - } - - if (w->active->fd == -1) { - xasprintf(&name, "%s[dead]", wname); - free(wname); - wname = name; - } - - if (strcmp(wname, w->name)) { - window_set_name(w, wname); + name = format_window_name(w); + if (strcmp(name, w->name) != 0) { + window_set_name(w, name); server_status_window(w); } - free(wname); + free(name); } char * default_window_name(struct window *w) { - if (w->active->screen != &w->active->base) - return (xstrdup("[tmux]")); if (w->active->cmd != NULL && *w->active->cmd != '\0') return (parse_window_name(w->active->cmd)); return (parse_window_name(w->active->shell)); } +char * +format_window_name(struct window *w) +{ + struct format_tree *ft; + char *fmt, *name; + + ft = format_create(); + format_window(ft, w); + format_window_pane(ft, w->active); + + fmt = options_get_string(&w->options, "automatic-rename-format"); + name = format_expand(ft, fmt); + + format_free(ft); + return (name); +} + char * parse_window_name(const char *in) { @@ -111,7 +99,7 @@ parse_window_name(const char *in) if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; - while (*name == ' ') + while (*name == ' ' || *name == '-') name++; if ((ptr = strchr(name, ' ')) != NULL) *ptr = '\0'; diff --git a/options-table.c b/options-table.c index 0b86ef7c..f6a15472 100644 --- a/options-table.c +++ b/options-table.c @@ -481,6 +481,11 @@ const struct options_table_entry window_options_table[] = { .default_num = 1 }, + { .name = "automatic-rename-format", + .type = OPTIONS_TABLE_STRING, + .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}#{?pane_dead,[dead],}" + }, + { .name = "c0-change-trigger", .type = OPTIONS_TABLE_NUMBER, .default_num = 250, diff --git a/tmux.1 b/tmux.1 index 19ae4a9b..c93f4d97 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2710,8 +2710,8 @@ The default is on. Control automatic window renaming. When this setting is enabled, .Nm -will attempt - on supported platforms - to rename the window to reflect the -command currently running in it. +will rename the window automatically using the format specified by +.Ic automatic-rename-format . This flag is automatically disabled for an individual window when a name is specified at creation with .Ic new-window @@ -2725,6 +2725,13 @@ It may be switched off globally with: set-window-option -g automatic-rename off .Ed .Pp +.It Ic automatic-rename-format Ar format +The format (see +.Sx FORMATS ) +used when the +.Ic automatic-rename +option is enabled. +.Pp .It Ic c0-change-interval Ar interval .It Ic c0-change-trigger Ar trigger These two options configure a simple form of rate limiting for a pane. diff --git a/tmux.h b/tmux.h index 1cb5cd8e..6ab9861b 100644 --- a/tmux.h +++ b/tmux.h @@ -1549,16 +1549,19 @@ int format_cmp(struct format_entry *, struct format_entry *); RB_PROTOTYPE(format_tree, format_entry, entry, format_cmp); struct format_tree *format_create(void); void format_free(struct format_tree *); -void printflike3 format_add( - struct format_tree *, const char *, const char *, ...); +void printflike3 format_add(struct format_tree *, const char *, const char *, + ...); const char *format_find(struct format_tree *, const char *); char *format_expand(struct format_tree *, const char *); void format_session(struct format_tree *, struct session *); void format_client(struct format_tree *, struct client *); -void format_winlink( - struct format_tree *, struct session *, struct winlink *); -void format_window_pane(struct format_tree *, struct window_pane *); -void format_paste_buffer(struct format_tree *, struct paste_buffer *); +void format_window(struct format_tree *, struct window *); +void format_winlink(struct format_tree *, struct session *, + struct winlink *); +void format_window_pane(struct format_tree *, + struct window_pane *); +void format_paste_buffer(struct format_tree *, + struct paste_buffer *); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -2272,8 +2275,10 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void queue_window_name(struct window *); -char *default_window_name(struct window *); +void queue_window_name(struct window *); +char *default_window_name(struct window *); +char *format_window_name(struct window *); +char *parse_window_name(const char *); /* signal.c */ void set_signals(void(*)(int, short, void *)); From ddf929390e2780f8e53536a7c0b2f974a3ce2721 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 19 Aug 2013 22:31:38 +0100 Subject: [PATCH 087/949] Accept multiple parameters to SM/RM/DECSET/DECRST, based on a diff from Hayaki Saito. --- input.c | 151 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/input.c b/input.c index 30d3bb98..259fad16 100644 --- a/input.c +++ b/input.c @@ -70,6 +70,10 @@ int input_input(struct input_ctx *); int input_c0_dispatch(struct input_ctx *); int input_esc_dispatch(struct input_ctx *); int input_csi_dispatch(struct input_ctx *); +void input_csi_dispatch_rm(struct input_ctx *); +void input_csi_dispatch_rm_private(struct input_ctx *); +void input_csi_dispatch_sm(struct input_ctx *); +void input_csi_dispatch_sm_private(struct input_ctx *); void input_csi_dispatch_sgr(struct input_ctx *); int input_dcs_dispatch(struct input_ctx *); int input_utf8_open(struct input_ctx *); @@ -1071,7 +1075,6 @@ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; @@ -1230,7 +1233,60 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_CSI_RM: - switch (input_get(ictx, 0, 0, -1)) { + input_csi_dispatch_rm(ictx); + break; + case INPUT_CSI_RM_PRIVATE: + input_csi_dispatch_rm_private(ictx); + break; + case INPUT_CSI_SCP: + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = s->cx; + ictx->old_cy = s->cy; + break; + case INPUT_CSI_SGR: + input_csi_dispatch_sgr(ictx); + break; + case INPUT_CSI_SM: + input_csi_dispatch_sm(ictx); + break; + case INPUT_CSI_SM_PRIVATE: + input_csi_dispatch_sm_private(ictx); + break; + case INPUT_CSI_TBC: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + if (s->cx < screen_size_x(s)) + bit_clear(s->tabs, s->cx); + break; + case 3: + bit_nclear(s->tabs, 0, screen_size_x(s) - 1); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_VPA: + n = input_get(ictx, 0, 1, 1); + screen_write_cursormove(sctx, s->cx, n - 1); + break; + case INPUT_CSI_DECSCUSR: + n = input_get(ictx, 0, 0, 0); + screen_set_cursor_style(s, n); + break; + } + + return (0); +} + +/* Handle CSI RM. */ +void +input_csi_dispatch_rm(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { case 4: /* IRM */ screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; @@ -1238,10 +1294,18 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_RM_PRIVATE: - switch (input_get(ictx, 0, 0, -1)) { - case 1: /* GATM */ + } +} + +/* Handle CSI private RM. */ +void +input_csi_dispatch_rm_private(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 1: /* DECCKM */ screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ @@ -1271,10 +1335,10 @@ input_csi_dispatch(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_off(wp, &ictx->cell, 0); + window_pane_alternate_off(ictx->wp, &ictx->cell, 0); break; case 1049: - window_pane_alternate_off(wp, &ictx->cell, 1); + window_pane_alternate_off(ictx->wp, &ictx->cell, 1); break; case 2004: screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); @@ -1283,17 +1347,17 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_SCP: - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = s->cx; - ictx->old_cy = s->cy; - break; - case INPUT_CSI_SGR: - input_csi_dispatch_sgr(ictx); - break; - case INPUT_CSI_SM: - switch (input_get(ictx, 0, 0, -1)) { + } +} + +/* Handle CSI SM. */ +void +input_csi_dispatch_sm(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { case 4: /* IRM */ screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; @@ -1301,10 +1365,18 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_SM_PRIVATE: - switch (input_get(ictx, 0, 0, -1)) { - case 1: /* GATM */ + } +} + +/* Handle CSI private SM. */ +void +input_csi_dispatch_sm_private(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 1: /* DECCKM */ screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ @@ -1330,10 +1402,10 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY); break; case 1004: - if (s->mode & MODE_FOCUSON) + if (ictx->ctx.s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - wp->flags |= PANE_FOCUSPUSH; /* force update */ + ictx->wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); @@ -1343,10 +1415,10 @@ input_csi_dispatch(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_on(wp, &ictx->cell, 0); + window_pane_alternate_on(ictx->wp, &ictx->cell, 0); break; case 1049: - window_pane_alternate_on(wp, &ictx->cell, 1); + window_pane_alternate_on(ictx->wp, &ictx->cell, 1); break; case 2004: screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); @@ -1355,32 +1427,7 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_TBC: - switch (input_get(ictx, 0, 0, 0)) { - case 0: - if (s->cx < screen_size_x(s)) - bit_clear(s->tabs, s->cx); - break; - case 3: - bit_nclear(s->tabs, 0, screen_size_x(s) - 1); - break; - default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); - break; - } - break; - case INPUT_CSI_VPA: - n = input_get(ictx, 0, 1, 1); - screen_write_cursormove(sctx, s->cx, n - 1); - break; - case INPUT_CSI_DECSCUSR: - n = input_get(ictx, 0, 0, 0); - screen_set_cursor_style(s, n); - break; } - - return (0); } /* Handle CSI SGR. */ From 5dbf3cb036d8093599494b8c02407e8d2d079f0d Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 16 Aug 2013 11:58:32 +0100 Subject: [PATCH 088/949] Assign mouse x/y coords before checking them When receiving mouse inputs, we should set the x/y coordinates earlier than we currently do, so that we aren't off-by-one in the case when the statusbar is at the top of the screen. --- tty-keys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index d1c9d875..595ad6e1 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -746,6 +746,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->sgr = sgr; m->sgr_xb = sgr_b; m->sgr_rel = sgr_rel; + m->x = x; + m->y = y; if (b & 64) { /* wheel button */ b &= 3; if (b == 0) @@ -773,8 +775,6 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) } m->button = (b & 3); } - m->x = x; - m->y = y; return (0); } From 5532766b1935a8888fb60fb9a238f0849b96050b Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Tue, 13 Aug 2013 21:56:41 +0100 Subject: [PATCH 089/949] Renumber windows: Lookup lastw via window not index When calling 'movew -r' on a session to reorder the winlinks, ensure when adding back in the information for the lastw stack that we look up the winlink based on the window and not its index. Using the index doesn't make sense here because when comparing it to the old set, it will never match since the winlink has been renumbered. Bug reported by Ben Boeckel. --- session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.c b/session.c index 24e2e5e7..bb742d8f 100644 --- a/session.c +++ b/session.c @@ -615,7 +615,7 @@ session_renumber_windows(struct session *s) memcpy(&old_lastw, &s->lastw, sizeof old_lastw); TAILQ_INIT(&s->lastw); TAILQ_FOREACH(wl, &old_lastw, sentry) { - wl_new = winlink_find_by_index(&s->windows, wl->idx); + wl_new = winlink_find_by_window(&s->windows, wl->window); if (wl_new != NULL) TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry); } From aa4920fea3dcdcaa0dbe4d373160195b1a3ca892 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 9 Aug 2013 10:37:46 +0100 Subject: [PATCH 090/949] choose-tree: Reset top when toggling items When choose-tree is told to expand/collapse items (especially when first rendering collapsed to just show sessions), ensure that in addition to setting the selected item, that the item itself appears on the bottom of the screen, rather than off screen. This was causing rendering glitches when a very small tmux window tried to render a list of items in choose-tree much larger than itself, and the selected item appeared off screen, and didn't show the selection until the selection had wrapped around to the top of the screen. --- cmd-choose-tree.c | 1 - window-choose.c | 25 +++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index a9b6ffbc..2fe6e171 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -228,7 +228,6 @@ windows_only: free(final_win_template_last); window_choose_ready(wl->window->active, cur_win, NULL); - window_choose_collapse_all(wl->window->active); if (args_has(args, 'u')) { window_choose_expand_all(wl->window->active); diff --git a/window-choose.c b/window-choose.c index 5ed85f0e..572581a5 100644 --- a/window-choose.c +++ b/window-choose.c @@ -81,6 +81,7 @@ int window_choose_key_index(struct window_choose_mode_data *, u_int); int window_choose_index_key(struct window_choose_mode_data *, int); void window_choose_prompt_input(enum window_choose_input_type, const char *, struct window_pane *, int); +void window_choose_reset_top(struct window_pane *, u_int); void window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) @@ -107,8 +108,17 @@ window_choose_set_current(struct window_pane *wp, u_int cur) struct screen *s = &data->screen; data->selected = cur; - if (data->selected > screen_size_y(s) - 1) - data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); + window_choose_reset_top(wp, screen_size_y(s)); +} + +void +window_choose_reset_top(struct window_pane *wp, u_int sy) +{ + struct window_choose_mode_data *data = wp->modedata; + + data->top = 0; + if (data->selected > sy - 1) + data->top = data->selected - (sy - 1); window_choose_redraw_screen(wp); } @@ -277,10 +287,7 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; - data->top = 0; - if (data->selected > sy - 1) - data->top = data->selected - (sy - 1); - + window_choose_reset_top(wp, sy); screen_resize(s, sx, sy, 0); window_choose_redraw_screen(wp); } @@ -373,6 +380,7 @@ window_choose_collapse_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; + struct screen *scr = &data->screen; struct session *s, *chosen; u_int i; @@ -391,7 +399,7 @@ window_choose_collapse_all(struct window_pane *wp) if (item->wcd->type & TREE_SESSION) data->selected = i; } - window_choose_redraw_screen(wp); + window_choose_reset_top(wp, screen_size_y(scr)); } void @@ -399,6 +407,7 @@ window_choose_expand_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; + struct screen *scr = &data->screen; struct session *s; u_int i; @@ -414,7 +423,7 @@ window_choose_expand_all(struct window_pane *wp) } } - window_choose_redraw_screen(wp); + window_choose_reset_top(wp, screen_size_y(scr)); } void From c103f2fbcbeb82019b694bed6869942bc1cbe966 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 20 Aug 2013 10:37:55 +0100 Subject: [PATCH 091/949] Clear window->flags when clearing winlinks When clearing WINLINK_ALERTFLAGS for all sessions, we must also, for that window, clear the window->flags as well, otherwise sessions may well still see flags for winlinks long since cleared. This therfore introduces WINDOW_ALERTFLAGS to help with this. --- tmux.h | 1 + window.c | 1 + 2 files changed, 2 insertions(+) diff --git a/tmux.h b/tmux.h index 6ab9861b..0a17f72b 100644 --- a/tmux.h +++ b/tmux.h @@ -1005,6 +1005,7 @@ struct window { #define WINDOW_REDRAW 0x4 #define WINDOW_SILENCE 0x8 #define WINDOW_ZOOMED 0x10 +#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) struct options options; diff --git a/window.c b/window.c index 7678adc6..7912bd37 100644 --- a/window.c +++ b/window.c @@ -1243,6 +1243,7 @@ winlink_clear_flags(struct winlink *wl) continue; wm->flags &= ~WINLINK_ALERTFLAGS; + wm->window->flags &= ~WINDOW_ALERTFLAGS; server_status_session(s); } } From a36da3a878a76f9570bee1b180a07243df4d382c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:01:40 +0100 Subject: [PATCH 092/949] Remove the barely-used and unnecessary command check() function. --- cmd-attach-session.c | 1 - cmd-bind-key.c | 27 ++++++++++++--------------- cmd-break-pane.c | 1 - cmd-capture-pane.c | 1 - cmd-choose-buffer.c | 1 - cmd-choose-client.c | 1 - cmd-choose-list.c | 1 - cmd-choose-tree.c | 3 --- cmd-clear-history.c | 1 - cmd-clock-mode.c | 1 - cmd-command-prompt.c | 2 -- cmd-confirm-before.c | 1 - cmd-copy-mode.c | 1 - cmd-delete-buffer.c | 1 - cmd-detach-client.c | 1 - cmd-display-message.c | 1 - cmd-display-panes.c | 1 - cmd-find-window.c | 1 - cmd-has-session.c | 1 - cmd-if-shell.c | 1 - cmd-join-pane.c | 2 -- cmd-kill-pane.c | 1 - cmd-kill-server.c | 1 - cmd-kill-session.c | 1 - cmd-kill-window.c | 1 - cmd-link-window.c | 1 - cmd-list-buffers.c | 1 - cmd-list-clients.c | 1 - cmd-list-commands.c | 1 - cmd-list-keys.c | 1 - cmd-list-panes.c | 1 - cmd-list-sessions.c | 1 - cmd-list-windows.c | 1 - cmd-load-buffer.c | 1 - cmd-lock-server.c | 3 --- cmd-move-window.c | 1 - cmd-new-session.c | 15 +++++---------- cmd-new-window.c | 1 - cmd-paste-buffer.c | 1 - cmd-pipe-pane.c | 1 - cmd-refresh-client.c | 1 - cmd-rename-session.c | 1 - cmd-rename-window.c | 1 - cmd-resize-pane.c | 1 - cmd-respawn-pane.c | 1 - cmd-respawn-window.c | 1 - cmd-rotate-window.c | 1 - cmd-run-shell.c | 1 - cmd-save-buffer.c | 2 -- cmd-select-layout.c | 3 --- cmd-select-pane.c | 2 -- cmd-select-window.c | 4 ---- cmd-send-keys.c | 2 -- cmd-server-info.c | 1 - cmd-set-buffer.c | 1 - cmd-set-environment.c | 1 - cmd-set-option.c | 2 -- cmd-show-environment.c | 1 - cmd-show-messages.c | 1 - cmd-show-options.c | 2 -- cmd-source-file.c | 1 - cmd-split-window.c | 1 - cmd-start-server.c | 1 - cmd-suspend-client.c | 1 - cmd-swap-pane.c | 1 - cmd-swap-window.c | 1 - cmd-switch-client.c | 1 - cmd-unbind-key.c | 24 +++++++++++------------- cmd-unlink-window.c | 1 - cmd-wait-for.c | 1 - cmd.c | 2 -- tmux.h | 1 - 72 files changed, 28 insertions(+), 124 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 07185737..cf5f30a4 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_attach_session_entry = { "[-dr] " CMD_TARGET_SESSION_USAGE, CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON, NULL, - NULL, cmd_attach_session_exec }; diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 71e79ea0..4ff3ac84 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -27,7 +27,6 @@ * Bind a key to a command, this recurses through cmd_*. */ -enum cmd_retval cmd_bind_key_check(struct args *); enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); @@ -38,23 +37,9 @@ const struct cmd_entry cmd_bind_key_entry = { "[-cnr] [-t key-table] key command [arguments]", 0, NULL, - cmd_bind_key_check, cmd_bind_key_exec }; -enum cmd_retval -cmd_bind_key_check(struct args *args) -{ - if (args_has(args, 't')) { - if (args->argc != 2 && args->argc != 3) - return (CMD_RETURN_ERROR); - } else { - if (args->argc < 2) - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); -} - enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -63,6 +48,18 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_list *cmdlist; int key; + if (args_has(args, 't')) { + if (args->argc != 2 && args->argc != 3) { + cmdq_error(cmdq, "not enough arguments"); + return (CMD_RETURN_ERROR); + } + } else { + if (args->argc < 2) { + cmdq_error(cmdq, "not enough arguments"); + return (CMD_RETURN_ERROR); + } + } + key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 8ed9a1a6..d0a5a450 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_break_pane_entry = { "[-dP] [-F format] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_break_pane_exec }; diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index f59dc2d6..e157e3cb 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -42,7 +42,6 @@ const struct cmd_entry cmd_capture_pane_entry = { CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_capture_pane_exec }; diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index e6b79d91..d79f6fdc 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_choose_buffer_entry = { CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, - NULL, cmd_choose_buffer_exec }; diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 40752a70..93671987 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -37,7 +37,6 @@ const struct cmd_entry cmd_choose_client_entry = { CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, - NULL, cmd_choose_client_exec }; diff --git a/cmd-choose-list.c b/cmd-choose-list.c index 15f87294..c3caabba 100644 --- a/cmd-choose-list.c +++ b/cmd-choose-list.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_choose_list_entry = { "[-l items] " CMD_TARGET_WINDOW_USAGE "[template]", 0, NULL, - NULL, cmd_choose_list_exec }; diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index a9b6ffbc..75ed5d2f 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_choose_tree_entry = { "[-W format] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_choose_tree_exec }; @@ -51,7 +50,6 @@ const struct cmd_entry cmd_choose_session_entry = { CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, - NULL, cmd_choose_tree_exec }; @@ -61,7 +59,6 @@ const struct cmd_entry cmd_choose_window_entry = { CMD_TARGET_WINDOW_USAGE "[-F format] [template]", 0, NULL, - NULL, cmd_choose_tree_exec }; diff --git a/cmd-clear-history.c b/cmd-clear-history.c index aebaa27d..cce3ea18 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_clear_history_entry = { CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_clear_history_exec }; diff --git a/cmd-clock-mode.c b/cmd-clock-mode.c index 872f3d53..50e4ab8b 100644 --- a/cmd-clock-mode.c +++ b/cmd-clock-mode.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_clock_mode_entry = { CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_clock_mode_exec }; diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 3bb79ed9..759d578b 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -30,7 +30,6 @@ */ void cmd_command_prompt_key_binding(struct cmd *, int); -int cmd_command_prompt_check(struct args *); enum cmd_retval cmd_command_prompt_exec(struct cmd *, struct cmd_q *); int cmd_command_prompt_callback(void *, const char *); @@ -42,7 +41,6 @@ const struct cmd_entry cmd_command_prompt_entry = { "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", 0, cmd_command_prompt_key_binding, - NULL, cmd_command_prompt_exec }; diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index e670f69c..9266721f 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -38,7 +38,6 @@ const struct cmd_entry cmd_confirm_before_entry = { "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", 0, cmd_confirm_before_key_binding, - NULL, cmd_confirm_before_exec }; diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 40584a28..f11c7aff 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_copy_mode_entry = { "[-u] " CMD_TARGET_PANE_USAGE, 0, cmd_copy_mode_key_binding, - NULL, cmd_copy_mode_exec }; diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c index bc3982ca..b8f55db4 100644 --- a/cmd-delete-buffer.c +++ b/cmd-delete-buffer.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_delete_buffer_entry = { CMD_BUFFER_USAGE, 0, NULL, - NULL, cmd_delete_buffer_exec }; diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 6e00e079..ea9e3816 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_detach_client_entry = { "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, CMD_READONLY, NULL, - NULL, cmd_detach_client_exec }; diff --git a/cmd-display-message.c b/cmd-display-message.c index 485ccf08..006c49a5 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_display_message_entry = { " [message]", 0, NULL, - NULL, cmd_display_message_exec }; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 4a8731a4..c137feef 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_display_panes_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_display_panes_exec }; diff --git a/cmd-find-window.c b/cmd-find-window.c index 02f19307..647cc8fb 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -48,7 +48,6 @@ const struct cmd_entry cmd_find_window_entry = { "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", 0, NULL, - NULL, cmd_find_window_exec }; diff --git a/cmd-has-session.c b/cmd-has-session.c index c4286b86..38a92f61 100644 --- a/cmd-has-session.c +++ b/cmd-has-session.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_has_session_entry = { CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_has_session_exec }; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index d1cbd7f3..a074341b 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_if_shell_entry = { "[-b] " CMD_TARGET_PANE_USAGE " shell-command command [command]", 0, NULL, - NULL, cmd_if_shell_exec }; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 2cf587e0..1a710cec 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_join_pane_entry = { "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", 0, cmd_join_pane_key_binding, - NULL, cmd_join_pane_exec }; @@ -49,7 +48,6 @@ const struct cmd_entry cmd_move_pane_entry = { "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", 0, NULL, - NULL, cmd_join_pane_exec }; diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 40761350..bf486eb3 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_kill_pane_entry = { "[-a] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_kill_pane_exec }; diff --git a/cmd-kill-server.c b/cmd-kill-server.c index a6065460..ba63faa3 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_kill_server_entry = { "", 0, NULL, - NULL, cmd_kill_server_exec }; diff --git a/cmd-kill-session.c b/cmd-kill-session.c index a12cc8a4..54b0c31b 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_kill_session_entry = { "[-a] " CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_kill_session_exec }; diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 34b97499..ca8fe3d6 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_kill_window_entry = { "[-a] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_kill_window_exec }; diff --git a/cmd-link-window.c b/cmd-link-window.c index 2be8ace0..d94eb38a 100644 --- a/cmd-link-window.c +++ b/cmd-link-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_link_window_entry = { "[-dk] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, - NULL, cmd_link_window_exec }; diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 58af0020..02a4183e 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_list_buffers_entry = { "[-F format]", 0, NULL, - NULL, cmd_list_buffers_exec }; diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 59f63099..904ec005 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_list_clients_entry = { "[-F format] " CMD_TARGET_SESSION_USAGE, CMD_READONLY, NULL, - NULL, cmd_list_clients_exec }; diff --git a/cmd-list-commands.c b/cmd-list-commands.c index 1bf6e703..06e48378 100644 --- a/cmd-list-commands.c +++ b/cmd-list-commands.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_list_commands_entry = { "", 0, NULL, - NULL, cmd_list_commands_exec }; diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 78998b66..615c5ce1 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_list_keys_entry = { "[-t key-table]", 0, NULL, - NULL, cmd_list_keys_exec }; diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 0d498e28..c989eba1 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_list_panes_entry = { "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_list_panes_exec }; diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 61c12f4e..a3613c87 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_list_sessions_entry = { "[-F format]", 0, NULL, - NULL, cmd_list_sessions_exec }; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 5c2a2b95..ce122f45 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_list_windows_entry = { "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_list_windows_exec }; diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 3be14d6a..82b8f574 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_load_buffer_entry = { CMD_BUFFER_USAGE " path", 0, NULL, - NULL, cmd_load_buffer_exec }; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 0b6aafe8..2b591ecf 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_lock_server_entry = { "", 0, NULL, - NULL, cmd_lock_server_exec }; @@ -46,7 +45,6 @@ const struct cmd_entry cmd_lock_session_entry = { CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_lock_server_exec }; @@ -56,7 +54,6 @@ const struct cmd_entry cmd_lock_client_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_lock_server_exec }; diff --git a/cmd-move-window.c b/cmd-move-window.c index 1a147c7e..21606755 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_move_window_entry = { "[-dkr] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, - NULL, cmd_move_window_exec }; diff --git a/cmd-new-session.c b/cmd-new-session.c index 4eebe632..b1e1aa45 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -30,7 +30,6 @@ * Create a new session and attach to the current terminal unless -d is given. */ -enum cmd_retval cmd_new_session_check(struct args *); enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { @@ -40,18 +39,9 @@ const struct cmd_entry cmd_new_session_entry = { CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, NULL, - cmd_new_session_check, cmd_new_session_exec }; -enum cmd_retval -cmd_new_session_check(struct args *args) -{ - if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) - return (CMD_RETURN_ERROR); - return (CMD_RETURN_NORMAL); -} - enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -70,6 +60,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) int already_attached; struct format_tree *ft; + if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { + cmdq_error(cmdq, "command or window name given with target"); + return (CMD_RETURN_ERROR); + } + newname = args_get(args, 's'); if (newname != NULL) { if (!session_check_name(newname)) { diff --git a/cmd-new-window.c b/cmd-new-window.c index cfc0b8bd..2ca0cecd 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_new_window_entry = { CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, - NULL, cmd_new_window_exec }; diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index b07c9faf..5305b7e6 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -38,7 +38,6 @@ const struct cmd_entry cmd_paste_buffer_entry = { "[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_paste_buffer_exec }; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index aa72c699..611ad8cf 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_pipe_pane_entry = { "[-o] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, - NULL, cmd_pipe_pane_exec }; diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 70e6888d..45804fbe 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_refresh_client_entry = { "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_refresh_client_exec }; diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 3f8a9d8f..4c8ae122 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_rename_session_entry = { CMD_TARGET_SESSION_USAGE " new-name", 0, NULL, - NULL, cmd_rename_session_exec }; diff --git a/cmd-rename-window.c b/cmd-rename-window.c index c756ba1f..f647abb3 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_rename_window_entry = { CMD_TARGET_WINDOW_USAGE " new-name", 0, NULL, - NULL, cmd_rename_window_exec }; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index ca2a6cd3..d31f8868 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_resize_pane_entry = { "[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", 0, cmd_resize_pane_key_binding, - NULL, cmd_resize_pane_exec }; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 4486c91f..36f8783b 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_respawn_pane_entry = { "[-k] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, - NULL, cmd_respawn_pane_exec }; diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 35bd3471..6026ca64 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_respawn_window_entry = { "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, - NULL, cmd_respawn_window_exec }; diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 75ca7292..9f4cc751 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_rotate_window_entry = { "[-DU] " CMD_TARGET_WINDOW_USAGE, 0, cmd_rotate_window_key_binding, - NULL, cmd_rotate_window_exec }; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 7c7d333c..f5231814 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_run_shell_entry = { "[-b] " CMD_TARGET_PANE_USAGE " shell-command", 0, NULL, - NULL, cmd_run_shell_exec }; diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 52914a94..c6c54019 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -37,7 +37,6 @@ const struct cmd_entry cmd_save_buffer_entry = { "[-a] " CMD_BUFFER_USAGE " path", 0, NULL, - NULL, cmd_save_buffer_exec }; @@ -47,7 +46,6 @@ const struct cmd_entry cmd_show_buffer_entry = { CMD_BUFFER_USAGE, 0, NULL, - NULL, cmd_save_buffer_exec }; diff --git a/cmd-select-layout.c b/cmd-select-layout.c index ae1be4c4..053f3e40 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_select_layout_entry = { "[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]", 0, cmd_select_layout_key_binding, - NULL, cmd_select_layout_exec }; @@ -43,7 +42,6 @@ const struct cmd_entry cmd_next_layout_entry = { CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_select_layout_exec }; @@ -53,7 +51,6 @@ const struct cmd_entry cmd_previous_layout_entry = { CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_select_layout_exec }; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index b8a12671..c342fef3 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_select_pane_entry = { "[-lDLRU] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_key_binding, - NULL, cmd_select_pane_exec }; @@ -43,7 +42,6 @@ const struct cmd_entry cmd_last_pane_entry = { CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_select_pane_exec }; diff --git a/cmd-select-window.c b/cmd-select-window.c index c15d5858..73196200 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_select_window_entry = { "[-lnpT] " CMD_TARGET_WINDOW_USAGE, 0, cmd_select_window_key_binding, - NULL, cmd_select_window_exec }; @@ -45,7 +44,6 @@ const struct cmd_entry cmd_next_window_entry = { "[-a] " CMD_TARGET_SESSION_USAGE, 0, cmd_select_window_key_binding, - NULL, cmd_select_window_exec }; @@ -55,7 +53,6 @@ const struct cmd_entry cmd_previous_window_entry = { "[-a] " CMD_TARGET_SESSION_USAGE, 0, cmd_select_window_key_binding, - NULL, cmd_select_window_exec }; @@ -65,7 +62,6 @@ const struct cmd_entry cmd_last_window_entry = { CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_select_window_exec }; diff --git a/cmd-send-keys.c b/cmd-send-keys.c index a2041656..50cb70e2 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_send_keys_entry = { "[-lR] " CMD_TARGET_PANE_USAGE " key ...", 0, NULL, - NULL, cmd_send_keys_exec }; @@ -45,7 +44,6 @@ const struct cmd_entry cmd_send_prefix_entry = { "[-2] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_send_keys_exec }; diff --git a/cmd-server-info.c b/cmd-server-info.c index 8eba172a..3aa5df8a 100644 --- a/cmd-server-info.c +++ b/cmd-server-info.c @@ -38,7 +38,6 @@ const struct cmd_entry cmd_server_info_entry = { "", 0, NULL, - NULL, cmd_server_info_exec }; diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 46d28ff2..fade4fe3 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_set_buffer_entry = { CMD_BUFFER_USAGE " data", 0, NULL, - NULL, cmd_set_buffer_exec }; diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 0f0365aa..8d067c35 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_set_environment_entry = { "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", 0, NULL, - NULL, cmd_set_environment_exec }; diff --git a/cmd-set-option.c b/cmd-set-option.c index c246743c..3acf125d 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -67,7 +67,6 @@ const struct cmd_entry cmd_set_option_entry = { "[-agosquw] [-t target-session|target-window] option [value]", 0, NULL, - NULL, cmd_set_option_exec }; @@ -77,7 +76,6 @@ const struct cmd_entry cmd_set_window_option_entry = { "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", 0, NULL, - NULL, cmd_set_option_exec }; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index ffe98bcc..2238929e 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_show_environment_entry = { "[-g] " CMD_TARGET_SESSION_USAGE " [name]", 0, NULL, - NULL, cmd_show_environment_exec }; diff --git a/cmd-show-messages.c b/cmd-show-messages.c index bc2424ad..256570cd 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_show_messages_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_show_messages_exec }; diff --git a/cmd-show-options.c b/cmd-show-options.c index e2f78e12..943353f6 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -40,7 +40,6 @@ const struct cmd_entry cmd_show_options_entry = { "[-gqsvw] [-t target-session|target-window] [option]", 0, NULL, - NULL, cmd_show_options_exec }; @@ -50,7 +49,6 @@ const struct cmd_entry cmd_show_window_options_entry = { "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", 0, NULL, - NULL, cmd_show_options_exec }; diff --git a/cmd-source-file.c b/cmd-source-file.c index 45a3a39b..d636643d 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -37,7 +37,6 @@ const struct cmd_entry cmd_source_file_entry = { "path", 0, NULL, - NULL, cmd_source_file_exec }; diff --git a/cmd-split-window.c b/cmd-split-window.c index 601dcb17..5b5140be 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_split_window_entry = { "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", 0, - cmd_split_window_key_binding, NULL, cmd_split_window_exec }; diff --git a/cmd-start-server.c b/cmd-start-server.c index cba2403b..33b28b4a 100644 --- a/cmd-start-server.c +++ b/cmd-start-server.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_start_server_entry = { "", CMD_STARTSERVER, NULL, - NULL, cmd_start_server_exec }; diff --git a/cmd-suspend-client.c b/cmd-suspend-client.c index 101658b1..e0e375fc 100644 --- a/cmd-suspend-client.c +++ b/cmd-suspend-client.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_suspend_client_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_suspend_client_exec }; diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 05317260..b8ff7690 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_swap_pane_entry = { "[-dDU] " CMD_SRCDST_PANE_USAGE, 0, cmd_swap_pane_key_binding, - NULL, cmd_swap_pane_exec }; diff --git a/cmd-swap-window.c b/cmd-swap-window.c index f9a2cb1b..1591d403 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_swap_window_entry = { "[-d] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, - NULL, cmd_swap_window_exec }; diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 9adb2146..d101c52b 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_switch_client_entry = { "[-lnpr] [-c target-client] [-t target-session]", CMD_READONLY, cmd_switch_client_key_binding, - NULL, cmd_switch_client_exec }; diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index dc037dde..9bd068e1 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -26,7 +26,6 @@ * Unbind key from command. */ -enum cmd_retval cmd_unbind_key_check(struct args *); enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); @@ -36,20 +35,9 @@ const struct cmd_entry cmd_unbind_key_entry = { "[-acn] [-t key-table] key", 0, NULL, - cmd_unbind_key_check, cmd_unbind_key_exec }; -enum cmd_retval -cmd_unbind_key_check(struct args *args) -{ - if (args_has(args, 'a') && args->argc != 0) - return (CMD_RETURN_ERROR); - if (!args_has(args, 'a') && args->argc != 1) - return (CMD_RETURN_ERROR); - return (CMD_RETURN_NORMAL); -} - enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -58,13 +46,23 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) int key; if (!args_has(args, 'a')) { + if (args->argc != 1) { + cmdq_error(cmdq, "missing key"); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_ERROR); key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - } else + } else { + if (args->argc != 0) { + cmdq_error(cmdq, "key given with -a"); + return (CMD_RETURN_ERROR); + } key = KEYC_NONE; + } if (args_has(args, 't')) return (cmd_unbind_key_table(self, cmdq, key)); diff --git a/cmd-unlink-window.c b/cmd-unlink-window.c index 39cdd8ed..ec69b91f 100644 --- a/cmd-unlink-window.c +++ b/cmd-unlink-window.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_unlink_window_entry = { "[-k] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_unlink_window_exec }; diff --git a/cmd-wait-for.c b/cmd-wait-for.c index d40ba49e..e87e197a 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_wait_for_entry = { "[-LSU] channel", 0, NULL, - NULL, cmd_wait_for_exec }; diff --git a/cmd.c b/cmd.c index 282fb112..c9eb41a1 100644 --- a/cmd.c +++ b/cmd.c @@ -254,8 +254,6 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) goto usage; if (entry->args_upper != -1 && args->argc > entry->args_upper) goto usage; - if (entry->check != NULL && entry->check(args) != 0) - goto usage; cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; diff --git a/tmux.h b/tmux.h index 6ab9861b..5b2aac5b 100644 --- a/tmux.h +++ b/tmux.h @@ -1451,7 +1451,6 @@ struct cmd_entry { int flags; void (*key_binding)(struct cmd *, int); - int (*check)(struct args *); enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; From 8954d01f968d73f5e60bdff200b7549b09d511c3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:28:31 +0100 Subject: [PATCH 093/949] + to TODO. --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index e72e83a2..ea672fa4 100644 --- a/TODO +++ b/TODO @@ -30,6 +30,7 @@ * choose-mode and copy-mode are very similar, make choose-mode a subset? * flag to choose-* for sort order * choose mode would be better per client than per window + * two choices (first one then second, for swap-pane and join-pane) - improve monitor-*: * straighten out rules for multiple clients From 25c0dc5e6ed467f99aeb63f628f9d185a39e6d3b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:30:27 +0100 Subject: [PATCH 094/949] ++ to TODO. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index ea672fa4..c623c8b5 100644 --- a/TODO +++ b/TODO @@ -18,6 +18,8 @@ - options bits and pieces: * set-remain-on-exit is a complete hack * way to set socket path from config file + * -fg/-bg/-attr is crap - better just foo-style options which accept + fg=,bg=,bright and so on like #[] - format improvements: * option to quote format (#{session_name:quoted}) From e3864c383f2678b0391a897cdccab7ad3bb98232 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:33:34 +0100 Subject: [PATCH 095/949] Remove from TODO. --- TODO | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO b/TODO index c623c8b5..f4cf7b6d 100644 --- a/TODO +++ b/TODO @@ -94,8 +94,6 @@ - mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the flag on w/o checking the others before calling tty_update_mode) -- panes should have names like windows - - way to tag a layout as a number/name - optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx From f2675cdf04f9b01d86ce98df620c91393fbb8db4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:35:01 +0100 Subject: [PATCH 096/949] Trivial style and spacing nits. --- cmd-display-message.c | 6 +++--- grid.c | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 006c49a5..78752f86 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -70,9 +70,9 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 'c')) { - c = cmd_find_client(cmdq, args_get(args, 'c'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); + c = cmd_find_client(cmdq, args_get(args, 'c'), 0); + if (c == NULL) + return (CMD_RETURN_ERROR); } else { c = cmd_current_client(cmdq); if (c == NULL && !args_has(self->args, 'p')) { diff --git a/grid.c b/grid.c index 5cc6c149..9e800243 100644 --- a/grid.c +++ b/grid.c @@ -268,8 +268,7 @@ grid_get_cell(struct grid *gd, u_int px, u_int py) /* Set cell at relative position. */ void -grid_set_cell( - struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { if (grid_check_y(gd, py) != 0) return; @@ -657,8 +656,8 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, * available. */ void -grid_duplicate_lines( - struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) +grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, + u_int ny) { struct grid_line *dstl, *srcl; u_int yy; From a0802dd4869bfefe548734a840dc2e1d3cb15c90 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 22 Aug 2013 00:32:55 +0100 Subject: [PATCH 097/949] A couple of manpage fixes from Tiago Cunha. --- tmux.1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index c93f4d97..ede1bb20 100644 --- a/tmux.1 +++ b/tmux.1 @@ -99,7 +99,9 @@ Force .Nm to assume the terminal supports 256 colours. .It Fl C -Start in control mode. +Start in control mode (see the +.Sx CONTROL MODE +section). Given twice .Xo ( Fl CC ) Xc disables echo. @@ -626,9 +628,10 @@ If it does exist, exit with 0. Kill the .Nm server and clients and destroy all sessions. -.It Ic kill-session +.It Xo Ic kill-session .Op Fl a .Op Fl t Ar target-session +.Xc Destroy the given session, closing any windows linked to it and no other sessions, and detaching all clients attached to it. If From 3ed5e56a3974016dad9c28f42356aba9c05e0f4c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 23 Aug 2013 15:25:05 +0100 Subject: [PATCH 098/949] Set EVENT_NOEPOLL on Linux again. --- osdep-linux.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/osdep-linux.c b/osdep-linux.c index b65acffc..ccac2670 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -84,14 +84,7 @@ osdep_get_cwd(int fd) struct event_base * osdep_event_init(void) { - /* - * On Linux, epoll doesn't work on /dev/null (yes, really). - * - * This has been commented because libevent versions up until the very - * latest (1.4 git or 2.0.10) do not handle signals properly when using - * poll or select, causing hangs. - * - */ - /* setenv("EVENT_NOEPOLL", "1", 1); */ + /* On Linux, epoll doesn't work on /dev/null (yes, really). */ + setenv("EVENT_NOEPOLL", "1", 1); return (event_init()); } From 06d101657f40ef4bd6cfeb4524c73eb533620542 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Aug 2013 12:59:13 +0100 Subject: [PATCH 099/949] No space in lsw -a targets. --- tmux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.h b/tmux.h index 63eddd19..ae1f38aa 100644 --- a/tmux.h +++ b/tmux.h @@ -144,7 +144,7 @@ extern char **environ; "[layout #{window_layout}] #{window_id}" \ "#{?window_active, (active),}"; #define LIST_WINDOWS_WITH_SESSION_TEMPLATE \ - "#{session_name}: " \ + "#{session_name}:" \ "#{window_index}: #{window_name}#{window_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " From d62121e7bbab6c84faa7beb2117cb9e444866db3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 31 Aug 2013 10:42:09 +0100 Subject: [PATCH 100/949] Add entry about smaller clients based on text from Thomas Adam. --- FAQ | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/FAQ b/FAQ index 81421bc0..da72d433 100644 --- a/FAQ +++ b/FAQ @@ -421,4 +421,19 @@ On OS X, reattach-to-usernamespace lets pbcopy/pbpaste work: https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard +* Why do I see dots around a session when I attach to it? + +tmux limits the size of the window to the smallest attached session. If +it didn't do this then it would be impossible to see the entire window. +The dots mark the size of the window tmux can display. + +To avoid this, detach all other clients when attaching: + + $ tmux attach -d + +Or from inside tmux by detaching individual clients with C-b D or all +using: + + C-b : attach -d + $Id$ From 75ec17f0b5204a12d15282a5167918416cd05276 Mon Sep 17 00:00:00 2001 From: Tiago Cunha Date: Sat, 31 Aug 2013 11:16:47 +0100 Subject: [PATCH 101/949] Mark flags as optional and mutually exclusive. --- cmd-wait-for.c | 2 +- tmux.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index e87e197a..e251863d 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -33,7 +33,7 @@ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_wait_for_entry = { "wait-for", "wait", "LSU", 1, 1, - "[-LSU] channel", + "[-L|-S|-U] channel", 0, NULL, cmd_wait_for_exec diff --git a/tmux.1 b/tmux.1 index ede1bb20..0b7d45f7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3582,7 +3582,7 @@ If the command doesn't return success, the exit status is also displayed. .D1 (alias: Ic info ) Show server information and terminal details. .It Xo Ic wait-for -.Fl LSU +.Op Fl L | S | U .Ar channel .Xc .D1 (alias: Ic wait ) From bda970b3b14d145fcaa25cf8f3f9a3cb70a864c3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 26 Sep 2013 12:00:48 +0100 Subject: [PATCH 102/949] Don't treat TMUX_TMPDIR as a potential file The point of setting TMUX_TMPDIR is to then make any labels from -L go to that directory. In the case of makesocketpath() with no TMUX_TMPDIR set, would set both the path and the default socket to a file. The checking of the permissions on the file worked fine in that case, but when TMUX_TMPDIR is set, won't work on a directory. This fixes the problem by ensuring the check on the permissions is performed on directories only. --- tmux.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tmux.c b/tmux.c index 606c574f..9c81cff5 100644 --- a/tmux.c +++ b/tmux.c @@ -184,7 +184,8 @@ makesocketpath(const char *label) errno = ENOTDIR; return (NULL); } - if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { + if (sb.st_uid != uid || (!S_ISDIR(sb.st_mode) && + sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { errno = EACCES; return (NULL); } @@ -387,7 +388,8 @@ main(int argc, char **argv) /* -L or default set. */ if (label != NULL) { if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket\n"); + fprintf(stderr, "can't create socket: %s\n", + strerror(errno)); exit(1); } } From 21bca549d35b3d0b4ff1f22f66e5e108c103ac4b Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 10 Sep 2013 13:17:39 +0100 Subject: [PATCH 103/949] layout-resize-pane-mouse: Consider visible panes only When a pane is maximized, and text is selected, we end up checking if a pane switch is needed. This therefore means we might end up selecting panes which aren't visible. --- layout.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/layout.c b/layout.c index b74bd789..cf554c38 100644 --- a/layout.c +++ b/layout.c @@ -533,6 +533,9 @@ layout_resize_pane_mouse(struct client *c) pane_border = 0; if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) { TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + if (wp->xoff + wp->sx == m->lx && wp->yoff <= 1 + m->ly && wp->yoff + wp->sy >= m->ly) { From 884a21d0f5b5b5b1d19a84b3e2f40a42a0b6105c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 1 Oct 2013 23:24:39 +0100 Subject: [PATCH 104/949] First period not last for host_short, from Michael Scholz. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index da939f04..ba4b95b2 100644 --- a/format.c +++ b/format.c @@ -121,7 +121,7 @@ format_create(void) if (gethostname(host, sizeof host) == 0) { format_add(ft, "host", "%s", host); - if ((ptr = strrchr(host, '.')) != NULL) + if ((ptr = strchr(host, '.')) != NULL) *ptr = '\0'; format_add(ft, "host_short", "%s", host); } From d0fa48db1eaea5fc2fa62111c2bfb4836f2b5f03 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 1 Oct 2013 23:27:36 +0100 Subject: [PATCH 105/949] Restore missing key binding for %, from Chris Johnsen. --- cmd-split-window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index 5b5140be..40f7966b 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_split_window_entry = { "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", 0, - NULL, + cmd_split_window_key_binding, cmd_split_window_exec }; From 7be152412ea0a614df11bce9ba5097574369a5f6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 1 Oct 2013 23:31:09 +0100 Subject: [PATCH 106/949] Make cmdq->client_exit a tristate (-1 means "not set") so that if explicitly set it can be copied from child to parent cmdq by if-shell and source-file. This fixes using attach or new. From Chris Johnsen. --- cmd-if-shell.c | 3 +++ cmd-queue.c | 4 ++-- cmd-source-file.c | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a074341b..9b6dcf30 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -147,6 +147,9 @@ cmd_if_shell_done(struct cmd_q *cmdq1) struct cmd_if_shell_data *cdata = cmdq1->data; struct cmd_q *cmdq = cdata->cmdq; + if (cmdq1->client_exit >= 0) + cmdq->client_exit = cmdq1->client_exit; + if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); diff --git a/cmd-queue.c b/cmd-queue.c index 38a88d23..19d98190 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -35,7 +35,7 @@ cmdq_new(struct client *c) cmdq->dead = 0; cmdq->client = c; - cmdq->client_exit = 0; + cmdq->client_exit = -1; TAILQ_INIT(&cmdq->queue); cmdq->item = NULL; @@ -259,7 +259,7 @@ cmdq_continue(struct cmd_q *cmdq) } while (cmdq->item != NULL); empty: - if (cmdq->client_exit) + if (cmdq->client_exit > 0) cmdq->client->flags |= CLIENT_EXIT; if (cmdq->emptyfn != NULL) cmdq->emptyfn(cmdq); /* may free cmdq */ diff --git a/cmd-source-file.c b/cmd-source-file.c index d636643d..eb7e1490 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -95,6 +95,9 @@ cmd_source_file_done(struct cmd_q *cmdq1) { struct cmd_q *cmdq = cmdq1->data; + if (cmdq1->client_exit >= 0) + cmdq->client_exit = cmdq1->client_exit; + cmdq_free(cmdq1); cfg_references--; From 9389cfbec9d7f4a7b5f9d4a580428949db4f6e67 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 1 Oct 2013 23:48:03 +0100 Subject: [PATCH 107/949] Support -c for new-session, based on code from J Raynor. --- cmd-new-session.c | 22 ++++++++++------- cmd-new-window.c | 2 +- cmd-queue.c | 24 +++++++++++++++++++ cmd-split-window.c | 2 +- cmd.c | 59 ++++++++++++++++++---------------------------- tmux.1 | 1 + tmux.h | 3 ++- 7 files changed, 66 insertions(+), 47 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index b1e1aa45..1c743a8e 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -34,9 +34,10 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", - "AdDF:n:Ps:t:x:y:", 0, 1, - "[-AdDP] [-F format] [-n window-name] [-s session-name] " - CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", + "Ac:dDF:n:Ps:t:x:y:", 0, 1, + "[-AdDP] [-c start-directory] [-F format] [-n window-name] " + "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] " + "[command]", CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, NULL, cmd_new_session_exec @@ -52,8 +53,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ env; struct termios tio, *tiop; struct passwd *pw; - const char *newname, *target, *update, *cwd, *errstr; - const char *template; + const char *newname, *target, *update, *base, *cwd; + const char *errstr, *template; char *cmd, *cause, *cp; int detached, idx; u_int sx, sy; @@ -126,14 +127,19 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Get the new session working directory. */ if (c != NULL && c->cwd != NULL) - cwd = c->cwd; + base = c->cwd; else { pw = getpwuid(getuid()); if (pw->pw_dir != NULL && *pw->pw_dir != '\0') - cwd = pw->pw_dir; + base = pw->pw_dir; else - cwd = "/"; + base = "/"; } + if (args_has(args, 'c')) + cwd = args_get(args, 'c'); + else + cwd = options_get_string(&global_s_options, "default-path"); + cwd = cmd_default_path(base, base, cwd); /* Find new session size. */ if (c != NULL) { diff --git a/cmd-new-window.c b/cmd-new-window.c index 2ca0cecd..e5658b37 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -102,7 +102,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); + cwd = cmdq_default_path(cmdq, args_get(args, 'c')); if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); diff --git a/cmd-queue.c b/cmd-queue.c index 38a88d23..808d4dc0 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -283,3 +283,27 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } + +/* Get default path using command queue. */ +const char * +cmdq_default_path(struct cmd_q *cmdq, const char *cwd) +{ + struct client *c = cmdq->client; + struct session *s; + const char *current; + + if ((s = cmd_current_session(cmdq, 0)) == NULL) + return (NULL); + + if (cwd == NULL) + cwd = options_get_string(&s->options, "default-path"); + + if (c != NULL && c->session == NULL && c->cwd != NULL) + current = c->cwd; + else if (s->curw != NULL) + current = osdep_get_cwd(s->curw->window->active->fd); + else + current = NULL; + + return (cmd_default_path(s->cwd, current, cwd)); +} diff --git a/cmd-split-window.c b/cmd-split-window.c index 5b5140be..039d7fce 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -82,7 +82,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); + cwd = cmdq_default_path(cmdq, args_get(args, 'c')); type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) diff --git a/cmd.c b/cmd.c index c9eb41a1..6bf7cecc 100644 --- a/cmd.c +++ b/cmd.c @@ -1278,68 +1278,55 @@ cmd_template_replace(const char *template, const char *s, int idx) } /* - * Return the default path for a new pane, using the given path or the - * default-path option if it is NULL. Several special values are accepted: the - * empty string or relative path for the current pane's working directory, ~ - * for the user's home, - for the session working directory, . for the tmux - * server's working directory. The default on failure is the session's working - * directory. + * Return the default path for a new pane. Several special values are accepted: + * the empty string or relative path for the current working directory, + * ~ for the user's home, - for the base working directory, . for the server + * working directory. */ const char * -cmd_get_default_path(struct cmd_q *cmdq, const char *cwd) +cmd_default_path(const char *base, const char *current, const char *in) { - struct client *c = cmdq->client; - struct session *s; - struct environ_entry *envent; const char *root; + struct environ_entry *envent; char tmp[MAXPATHLEN]; struct passwd *pw; int n; size_t skip; static char path[MAXPATHLEN]; - if ((s = cmd_current_session(cmdq, 0)) == NULL) - return (NULL); - - if (cwd == NULL) - cwd = options_get_string(&s->options, "default-path"); - skip = 1; - if (strcmp(cwd, "$HOME") == 0 || strncmp(cwd, "$HOME/", 6) == 0) { + if (strcmp(in, "$HOME") == 0 || strncmp(in, "$HOME/", 6) == 0) { /* User's home directory - $HOME. */ skip = 5; goto find_home; - } else if (cwd[0] == '~' && (cwd[1] == '\0' || cwd[1] == '/')) { + } else if (in[0] == '~' && (in[1] == '\0' || in[1] == '/')) { /* User's home directory - ~. */ goto find_home; - } else if (cwd[0] == '-' && (cwd[1] == '\0' || cwd[1] == '/')) { - /* Session working directory. */ - root = s->cwd; + } else if (in[0] == '-' && (in[1] == '\0' || in[1] == '/')) { + /* Base working directory. */ + root = base; goto complete_path; - } else if (cwd[0] == '.' && (cwd[1] == '\0' || cwd[1] == '/')) { + } else if (in[0] == '.' && (in[1] == '\0' || in[1] == '/')) { /* Server working directory. */ if (getcwd(tmp, sizeof tmp) != NULL) { root = tmp; goto complete_path; } - return (s->cwd); - } else if (*cwd == '/') { + return ("/"); + } else if (*in == '/') { /* Absolute path. */ - return (cwd); + return (in); } else { /* Empty or relative path. */ - if (c != NULL && c->session == NULL && c->cwd != NULL) - root = c->cwd; - else if (s->curw != NULL) - root = osdep_get_cwd(s->curw->window->active->fd); + if (current != NULL) + root = current; else - return (s->cwd); + return (base); skip = 0; - if (root != NULL) - goto complete_path; + goto complete_path; } - return (s->cwd); + return (base); find_home: envent = environ_find(&global_environ, "HOME"); @@ -1348,15 +1335,15 @@ find_home: else if ((pw = getpwuid(getuid())) != NULL) root = pw->pw_dir; else - return (s->cwd); + return (base); complete_path: if (root[skip] == '\0') { strlcpy(path, root, sizeof path); return (path); } - n = snprintf(path, sizeof path, "%s/%s", root, cwd + skip); + n = snprintf(path, sizeof path, "%s/%s", root, in + skip); if (n > 0 && (size_t)n < sizeof path) return (path); - return (s->cwd); + return (base); } diff --git a/tmux.1 b/tmux.1 index ede1bb20..bbe9beb5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -676,6 +676,7 @@ Lock all clients attached to .Ar target-session . .It Xo Ic new-session .Op Fl AdDP +.Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name diff --git a/tmux.h b/tmux.h index ae1f38aa..b6b66fba 100644 --- a/tmux.h +++ b/tmux.h @@ -1767,7 +1767,7 @@ int cmd_find_index(struct cmd_q *, const char *, struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); char *cmd_template_replace(const char *, const char *, int); -const char *cmd_get_default_path(struct cmd_q *, const char *); +const char *cmd_default_path(const char *, const char *, const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; @@ -1876,6 +1876,7 @@ void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); void cmdq_flush(struct cmd_q *); +const char *cmdq_default_path(struct cmd_q *, const char *); /* cmd-string.c */ int cmd_string_parse(const char *, struct cmd_list **, const char *, From 2057812c8f293b0295740f9b31f877629fc43888 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 24 Aug 2013 22:37:42 +0100 Subject: [PATCH 108/949] mouse-resize-pane: Only resize on border select The current behaviour of mouse-resize-pane is such that if the mouse button is held down and a selection takes place within a pane, that if the mouse pointer then hits a border edge, that pane-resize would initiate. This seems counter-intuitive; instead, check for a resize condition if the border of a pane is selected, and in the case of mouse selection within a pane, no longer resize the pane if edge of the border is hit. --- layout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout.c b/layout.c index cf554c38..646d1bd9 100644 --- a/layout.c +++ b/layout.c @@ -553,7 +553,7 @@ layout_resize_pane_mouse(struct client *c) } if (pane_border) server_redraw_window(w); - } else if (~m->event & MOUSE_EVENT_UP) { + } else if (m->event & MOUSE_EVENT_DOWN) { TAILQ_FOREACH(wp, &w->panes, entry) { if ((wp->xoff + wp->sx == m->x && wp->yoff <= 1 + m->y && From 3d8a8ea0c68d6ab343568559f7d5879170c2e78c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 5 Oct 2013 08:12:39 +0000 Subject: [PATCH 109/949] Use open(".")/fchdir() to save and restore current directory rather than getcwd()/chdir(). --- tmux.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tmux.c b/tmux.c index b229967d..8726a55f 100644 --- a/tmux.c +++ b/tmux.c @@ -127,16 +127,22 @@ areshell(const char *shell) const char* get_full_path(const char *wd, const char *path) { + int fd; static char newpath[MAXPATHLEN]; - char oldpath[MAXPATHLEN]; - if (getcwd(oldpath, sizeof oldpath) == NULL) + fd = open(".", O_RDONLY); + if (fd == -1) return (NULL); + if (chdir(wd) != 0) return (NULL); if (realpath(path, newpath) != 0) return (NULL); - chdir(oldpath); + + if (fchdir(fd) != 0) + chdir("/"); + close(fd); + return (newpath); } From d51b4f92d7c9899694e9ef8334030ceb512cb2de Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 5 Oct 2013 09:14:11 +0100 Subject: [PATCH 110/949] Use open(".")/fchdir() to save and restore current directory rather than getcwd()/chdir(). --- osdep-openbsd.c | 2 +- tmux.c | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osdep-openbsd.c b/osdep-openbsd.c index 7be38a91..0a4c1445 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -135,7 +135,7 @@ error: return (NULL); } -char* +char * osdep_get_cwd(int fd) { int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; diff --git a/tmux.c b/tmux.c index 9c81cff5..4ff45d55 100644 --- a/tmux.c +++ b/tmux.c @@ -127,19 +127,25 @@ areshell(const char *shell) return (0); } -const char* +const char * get_full_path(const char *wd, const char *path) { + int fd; static char newpath[MAXPATHLEN]; - char oldpath[MAXPATHLEN]; - if (getcwd(oldpath, sizeof oldpath) == NULL) + fd = open(".", O_RDONLY); + if (fd == -1) return (NULL); + if (chdir(wd) != 0) return (NULL); if (realpath(path, newpath) != 0) return (NULL); - chdir(oldpath); + + if (fchdir(fd) != 0) + chdir("/"); + close(fd); + return (newpath); } From 710eeb2a33cb91c05251c69d7eb1a572aed44cb4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 5 Oct 2013 11:40:47 +0100 Subject: [PATCH 111/949] Fix previous not to lead fd on failure. --- tmux.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tmux.c b/tmux.c index 4ff45d55..f68beeea 100644 --- a/tmux.c +++ b/tmux.c @@ -130,23 +130,25 @@ areshell(const char *shell) const char * get_full_path(const char *wd, const char *path) { - int fd; - static char newpath[MAXPATHLEN]; + int fd; + static char newpath[MAXPATHLEN]; + const char *retval; fd = open(".", O_RDONLY); if (fd == -1) return (NULL); - if (chdir(wd) != 0) - return (NULL); - if (realpath(path, newpath) != 0) - return (NULL); + retval = NULL; + if (chdir(wd) == 0) { + if (realpath(path, newpath) == 0) + retval = newpath; + } if (fchdir(fd) != 0) chdir("/"); close(fd); - return (newpath); + return (retval); } void From 9f330897a8fc4f927d355eacf9cc58f3be2092f7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 5 Oct 2013 10:40:49 +0000 Subject: [PATCH 112/949] Fix previous not to leak fd on failure, whoops. --- tmux.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tmux.c b/tmux.c index 8726a55f..73843a96 100644 --- a/tmux.c +++ b/tmux.c @@ -127,23 +127,25 @@ areshell(const char *shell) const char* get_full_path(const char *wd, const char *path) { - int fd; - static char newpath[MAXPATHLEN]; + int fd; + static char newpath[MAXPATHLEN]; + const char *retval; fd = open(".", O_RDONLY); if (fd == -1) return (NULL); - if (chdir(wd) != 0) - return (NULL); - if (realpath(path, newpath) != 0) - return (NULL); + retval = NULL; + if (chdir(wd) == 0) { + if (realpath(path, newpath) == 0) + retval = newpath; + } if (fchdir(fd) != 0) chdir("/"); close(fd); - return (newpath); + return (retval); } void From 75f5b3dab669ab79a6ff11f70ff75589fc1946c5 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 3 Oct 2013 22:31:26 +0100 Subject: [PATCH 113/949] Add subdir-objects to shut automake up automake 1.14 onwards has started emitting lots of warnings about this option: automake: warning: possible forward-incompatibility. automake: At least a source file is in a subdirectory, but the 'subdir-objects' automake: automake option hasn't been enabled. For now, the corresponding output automake: object file(s) will be placed in the top-level directory. However, automake: this behaviour will change in future Automake versions: they will automake: unconditionally cause object files to be placed in the same subdirectory automake: of the corresponding sources. automake: You are advised to start using 'subdir-objects' option throughout your automake: project, to avoid future incompatibilities. So enable this in AM_INIT_AUTOMAKE. This doesn't seem to break older automake versions. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 590b9db0..186d7536 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_INIT(tmux, 1.9) AC_CONFIG_AUX_DIR(etc) -AM_INIT_AUTOMAKE([foreign]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) AC_CANONICAL_HOST From 796974ddf65604f0da4b53039f11ebf886957cc9 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 3 Oct 2013 16:56:12 +0100 Subject: [PATCH 114/949] Check setupterm() in libtinfo also Some ncurses packages have build time configuration options to separate its different parts into separate libraries. Some Linux distributions in particular separate out the terminfo routines in to libtinfo. This change teaches configure that setupterm() can also be found there. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 186d7536..68c50fba 100644 --- a/configure.ac +++ b/configure.ac @@ -135,7 +135,7 @@ fi # Look for curses. AC_SEARCH_LIBS( setupterm, - [terminfo curses ncurses], + [terminfo curses ncurses tinfo], found_curses=yes, found_curses=no ) From 5eeee39cc18a1205f69dd42c626312b9456ead4b Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 5 Oct 2013 12:45:24 +0100 Subject: [PATCH 115/949] Ignore .dirstamp files GNU automake 1.14+ uses these files for subdir-option detection. We don't want to accidentally commit these. They're not useful to us. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 20a3bbf4..ba580eda 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ core tags .deps/ +compat/.dirstamp aclocal.m4 autom4te.cache/ config.log From 7c71c3e27de71e8a91590a4e2ed1945873cfc538 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 5 Oct 2013 13:56:48 +0000 Subject: [PATCH 116/949] Change the default for the default-path option to ~. This is a quick change to turn off the KERN_PROC_CWD code which is unpredictable. Later it will go away and there may be other changes to how this works. --- options-table.c | 2 +- tmux.1 | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index cf0202b7..138a307e 100644 --- a/options-table.c +++ b/options-table.c @@ -128,7 +128,7 @@ const struct options_table_entry session_options_table[] = { { .name = "default-path", .type = OPTIONS_TABLE_STRING, - .default_str = "" + .default_str = "~" }, { .name = "default-shell", diff --git a/tmux.1 b/tmux.1 index 7a01b394..61158872 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2173,13 +2173,15 @@ to create a login shell using the value of the option. .It Ic default-path Ar path Set the default working directory for new panes. -If empty (the default), the working directory is determined from the process +If empty, the working directory is determined from the process running in the active pane, from the command line environment or from the working directory where the session was created. Otherwise the same options are available as for the .Fl c flag to .Ic new-window . +The default is +.Ql ~ . .It Ic default-shell Ar path Specify the default shell. This is used as the login shell for new windows when the From 7e4314eccbc9dd36340e6ca3d0eccfc7e678b219 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 00:02:52 +0100 Subject: [PATCH 117/949] Remove CMD_SENDENVIRON. --- client.c | 7 ++----- cmd-attach-session.c | 2 +- cmd-new-session.c | 2 +- tmux.h | 3 +-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/client.c b/client.c index b9e3b30a..bb1b34cf 100644 --- a/client.c +++ b/client.c @@ -179,7 +179,7 @@ client_main(int argc, char **argv, int flags) cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; - cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST; + cmdflags = CMD_STARTSERVER|CMD_CANTNEST; } else { msg = MSG_COMMAND; @@ -197,8 +197,6 @@ client_main(int argc, char **argv, int flags) TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; - if (cmd->entry->flags & CMD_SENDENVIRON) - cmdflags |= CMD_SENDENVIRON; if (cmd->entry->flags & CMD_CANTNEST) cmdflags |= CMD_CANTNEST; } @@ -262,8 +260,7 @@ client_main(int argc, char **argv, int flags) set_signals(client_signal); /* Send initial environment. */ - if (cmdflags & CMD_SENDENVIRON) - client_send_environ(); + client_send_environ(); client_send_identify(flags); /* Send first command. */ diff --git a/cmd-attach-session.c b/cmd-attach-session.c index cf5f30a4..7a952e59 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", "drt:", 0, 0, "[-dr] " CMD_TARGET_SESSION_USAGE, - CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON, + CMD_CANTNEST|CMD_STARTSERVER, NULL, cmd_attach_session_exec }; diff --git a/cmd-new-session.c b/cmd-new-session.c index 1c743a8e..38c79a6d 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_new_session_entry = { "[-AdDP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] " "[command]", - CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, + CMD_STARTSERVER|CMD_CANTNEST, NULL, cmd_new_session_exec }; diff --git a/tmux.h b/tmux.h index b6b66fba..dd351950 100644 --- a/tmux.h +++ b/tmux.h @@ -1447,8 +1447,7 @@ struct cmd_entry { #define CMD_STARTSERVER 0x1 #define CMD_CANTNEST 0x2 -#define CMD_SENDENVIRON 0x4 -#define CMD_READONLY 0x8 +#define CMD_READONLY 0x4 int flags; void (*key_binding)(struct cmd *, int); From d66cbf20f7ddeca5e350a79ffaf3a18e1da231fb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 00:06:01 +0100 Subject: [PATCH 118/949] Bump protocol version and add new message types. --- tmux.h | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/tmux.h b/tmux.h index dd351950..345efaba 100644 --- a/tmux.h +++ b/tmux.h @@ -19,7 +19,7 @@ #ifndef TMUX_H #define TMUX_H -#define PROTOCOL_VERSION 7 +#define PROTOCOL_VERSION 8 #include #include @@ -430,27 +430,36 @@ ARRAY_DECL(causelist, char *); /* Message codes. */ enum msgtype { - MSG_COMMAND, + MSG_VERSION = 12, + + MSG_IDENTIFY_FLAGS = 100, + MSG_IDENTIFY_TERM, + MSG_IDENTIFY_TTYNAME, + MSG_IDENTIFY_CWD, + MSG_IDENTIFY_STDIN, + MSG_IDENTIFY_ENVIRON, + MSG_IDENTIFY_DONE, + + MSG_COMMAND = 200, MSG_DETACH, - MSG_ERROR, + MSG_DETACHKILL, MSG_EXIT, MSG_EXITED, MSG_EXITING, - MSG_IDENTIFY, - MSG_STDIN, + MSG_LOCK, MSG_READY, MSG_RESIZE, - MSG_SHUTDOWN, - MSG_SUSPEND, - MSG_VERSION, - MSG_WAKEUP, - MSG_ENVIRON, - MSG_UNLOCK, - MSG_LOCK, MSG_SHELL, + MSG_SHUTDOWN, MSG_STDERR, + MSG_STDIN, MSG_STDOUT, - MSG_DETACHKILL + MSG_SUSPEND, + MSG_UNLOCK, + MSG_WAKEUP, + + MSG_IDENTIFY = 300, + MSG_ENVIRON }; /* From 01a47525030ec6fa4f53dc2c0cb6786f63881679 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 00:10:40 +0100 Subject: [PATCH 119/949] Merge IDENTIFY_* flags with CLIENT_* flags. --- client.c | 16 +++++++--------- server-client.c | 10 +++++----- tmux.c | 16 ++++++++-------- tmux.h | 11 +++++------ 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/client.c b/client.c index bb1b34cf..13c9ca9e 100644 --- a/client.c +++ b/client.c @@ -236,7 +236,7 @@ client_main(int argc, char **argv, int flags) setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); - if (flags & IDENTIFY_TERMIOS) { + if (flags & CLIENT_CONTROLCONTROL) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); @@ -293,14 +293,12 @@ client_main(int argc, char **argv, int flags) ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } else if (flags & IDENTIFY_TERMIOS) { - if (flags & IDENTIFY_CONTROL) { - if (client_exitreason != CLIENT_EXIT_NONE) - printf("%%exit %s\n", client_exit_message()); - else - printf("%%exit\n"); - printf("\033\\"); - } + } else if (flags & CLIENT_CONTROLCONTROL) { + if (client_exitreason != CLIENT_EXIT_NONE) + printf("%%exit %s\n", client_exit_message()); + else + printf("%%exit\n"); + printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); } setblocking(STDIN_FILENO, 1); diff --git a/server-client.c b/server-client.c index fa4d2688..7a9f9115 100644 --- a/server-client.c +++ b/server-client.c @@ -956,12 +956,12 @@ server_client_msg_identify( if (*data->cwd != '\0') c->cwd = xstrdup(data->cwd); - if (data->flags & IDENTIFY_CONTROL) { + if (data->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; c->flags |= CLIENT_CONTROL; - if (data->flags & IDENTIFY_TERMIOS) + if (data->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); server_write_client(c, MSG_STDIN, NULL, 0); @@ -980,14 +980,14 @@ server_client_msg_identify( } data->term[(sizeof data->term) - 1] = '\0'; tty_init(&c->tty, c, fd, data->term); - if (data->flags & IDENTIFY_UTF8) + if (data->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; - if (data->flags & IDENTIFY_256COLOURS) + if (data->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); - if (!(data->flags & IDENTIFY_CONTROL)) + if (!(data->flags & CLIENT_CONTROL)) c->flags |= CLIENT_TERMINAL; } diff --git a/tmux.c b/tmux.c index f68beeea..76b09cf4 100644 --- a/tmux.c +++ b/tmux.c @@ -264,17 +264,17 @@ main(int argc, char **argv) while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { switch (opt) { case '2': - flags |= IDENTIFY_256COLOURS; + flags |= CLIENT_256COLOURS; break; case 'c': free(shell_cmd); shell_cmd = xstrdup(optarg); break; case 'C': - if (flags & IDENTIFY_CONTROL) - flags |= IDENTIFY_TERMIOS; + if (flags & CLIENT_CONTROL) + flags |= CLIENT_CONTROLCONTROL; else - flags |= IDENTIFY_CONTROL; + flags |= CLIENT_CONTROL; break; case 'V': printf("%s %s\n", __progname, VERSION); @@ -298,7 +298,7 @@ main(int argc, char **argv) path = xstrdup(optarg); break; case 'u': - flags |= IDENTIFY_UTF8; + flags |= CLIENT_UTF8; break; case 'v': debug_level++; @@ -313,7 +313,7 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (!(flags & IDENTIFY_UTF8)) { + if (!(flags & CLIENT_UTF8)) { /* * If the user has set whichever of LC_ALL, LC_CTYPE or LANG * exist (in that order) to contain UTF-8, it is a safe @@ -327,7 +327,7 @@ main(int argc, char **argv) } if (s != NULL && (strcasestr(s, "UTF-8") != NULL || strcasestr(s, "UTF8") != NULL)) - flags |= IDENTIFY_UTF8; + flags |= CLIENT_UTF8; } environ_init(&global_environ); @@ -346,7 +346,7 @@ main(int argc, char **argv) options_table_populate_tree(window_options_table, &global_w_options); /* Enable UTF-8 if the first client is on UTF-8 terminal. */ - if (flags & IDENTIFY_UTF8) { + if (flags & CLIENT_UTF8) { options_set_number(&global_s_options, "status-utf8", 1); options_set_number(&global_s_options, "mouse-utf8", 1); options_set_number(&global_w_options, "utf8", 1); diff --git a/tmux.h b/tmux.h index 345efaba..3b4f8150 100644 --- a/tmux.h +++ b/tmux.h @@ -484,11 +484,6 @@ struct msg_identify_data { char ttyname[TTY_NAME_MAX]; #endif -#define IDENTIFY_UTF8 0x1 -#define IDENTIFY_256COLOURS 0x2 -/* 0x4 unused */ -#define IDENTIFY_CONTROL 0x8 -#define IDENTIFY_TERMIOS 0x10 int flags; }; @@ -1341,7 +1336,11 @@ struct client { #define CLIENT_READONLY 0x800 #define CLIENT_REDRAWWINDOW 0x1000 #define CLIENT_CONTROL 0x2000 -#define CLIENT_FOCUSED 0x4000 +#define CLIENT_CONTROLCONTROL 0x4000 +#define CLIENT_FOCUSED 0x8000 +#define CLIENT_UTF8 0x10000 +#define CLIENT_256COLOURS 0x20000 +#define CLIENT_IDENTIFIED 0x40000 int flags; struct event identify_timer; From 3fba377ddd6fd234c614b53d5402153c6489b505 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 00:18:00 +0100 Subject: [PATCH 120/949] retcode -> retval for exit message. --- cmd-queue.c | 2 +- server-client.c | 2 +- tmux.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index bbc9971f..7feb25ef 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -143,7 +143,7 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) evbuffer_add(c->stderr_data, "\n", 1); server_push_stderr(c); - c->retcode = 1; + c->retval = 1; } else { *msg = toupper((u_char) *msg); status_message_set(c, "%s", msg); diff --git a/server-client.c b/server-client.c index 7a9f9115..41d1bb22 100644 --- a/server-client.c +++ b/server-client.c @@ -707,7 +707,7 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; - exitdata.retcode = c->retcode; + exitdata.retcode = c->retval; server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); c->flags &= ~CLIENT_EXIT; diff --git a/tmux.h b/tmux.h index 3b4f8150..7d082796 100644 --- a/tmux.h +++ b/tmux.h @@ -1295,8 +1295,9 @@ RB_HEAD(status_out_tree, status_out); /* Client connection. */ struct client { struct imsgbuf ibuf; + struct event event; - int retcode; + int retval; struct timeval creation_time; struct timeval activity_time; From f141e9b37a8d8a0c89c4f74137df9e5088b4b7af Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 00:28:35 +0100 Subject: [PATCH 121/949] Instead of fixed size buffers for some messages, send only the string length. --- client.c | 66 +++++++++++++++++++++++-------------------------- server-client.c | 14 +++-------- server-fn.c | 10 +++----- tmux.h | 12 --------- 4 files changed, 38 insertions(+), 64 deletions(-) diff --git a/client.c b/client.c index 13c9ca9e..de9ec52c 100644 --- a/client.c +++ b/client.c @@ -488,33 +488,33 @@ client_write(int fd, const char *data, size_t size) /* Dispatch imsgs when in wait state (before MSG_READY). */ int -client_dispatch_wait(void *data) +client_dispatch_wait(void *data0) { - struct imsg imsg; - ssize_t n, datalen; - struct msg_shell_data shelldata; - struct msg_exit_data exitdata; - struct msg_stdout_data stdoutdata; - struct msg_stderr_data stderrdata; - const char *shellcmd = data; + struct imsg imsg; + char *data; + ssize_t n, datalen; + struct msg_stdout_data stdoutdata; + struct msg_stderr_data stderrdata; + int retval; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) fatalx("imsg_get failed"); if (n == 0) return (0); + + data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; log_debug("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: - if (datalen != sizeof exitdata) { - if (datalen != 0) - fatalx("bad MSG_EXIT size"); - } else { - memcpy(&exitdata, imsg.data, sizeof exitdata); - client_exitval = exitdata.retcode; + if (datalen != sizeof retval && datalen != 0) + fatalx("bad MSG_EXIT size"); + if (datalen == sizeof retval) { + memcpy(&retval, data, sizeof retval); + client_exitval = retval; } imsg_free(&imsg); return (-1); @@ -534,15 +534,15 @@ client_dispatch_wait(void *data) break; case MSG_STDOUT: if (datalen != sizeof stdoutdata) - fatalx("bad MSG_STDOUT"); - memcpy(&stdoutdata, imsg.data, 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"); - memcpy(&stderrdata, imsg.data, sizeof stderrdata); + fatalx("bad MSG_STDERR size"); + memcpy(&stderrdata, data, sizeof stderrdata); client_write(STDERR_FILENO, stderrdata.data, stderrdata.size); break; @@ -558,14 +558,11 @@ client_dispatch_wait(void *data) imsg_free(&imsg); return (-1); case MSG_SHELL: - if (datalen != sizeof shelldata) - fatalx("bad MSG_SHELL size"); - memcpy(&shelldata, imsg.data, sizeof shelldata); - shelldata.shell[(sizeof shelldata.shell) - 1] = '\0'; + if (data[datalen - 1] != '\0') + fatalx("bad MSG_SHELL string"); clear_signals(0); - - shell_exec(shelldata.shell, shellcmd); + shell_exec(data, data0); /* NOTREACHED */ case MSG_DETACH: client_write_server(MSG_EXITING, NULL, 0); @@ -585,16 +582,18 @@ client_dispatch_wait(void *data) int client_dispatch_attached(void) { - struct imsg imsg; - struct msg_lock_data lockdata; - struct sigaction sigact; - ssize_t n, datalen; + struct imsg imsg; + struct sigaction sigact; + char *data; + ssize_t n, datalen; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) fatalx("imsg_get failed"); if (n == 0) return (0); + + data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; log_debug("got %d from server", imsg.hdr.type); @@ -612,8 +611,7 @@ client_dispatch_attached(void) client_write_server(MSG_EXITING, NULL, 0); break; case MSG_EXIT: - if (datalen != 0 && - datalen != sizeof (struct msg_exit_data)) + if (datalen != 0 && datalen != sizeof (int)) fatalx("bad MSG_EXIT size"); client_write_server(MSG_EXITING, NULL, 0); @@ -646,12 +644,10 @@ client_dispatch_attached(void) kill(getpid(), SIGTSTP); break; case MSG_LOCK: - if (datalen != sizeof lockdata) - fatalx("bad MSG_LOCK size"); - memcpy(&lockdata, imsg.data, sizeof lockdata); + if (data[datalen - 1] != '\0') + fatalx("bad MSG_LOCK string"); - lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; - system(lockdata.cmd); + system(data); client_write_server(MSG_UNLOCK, NULL, 0); break; default: diff --git a/server-client.c b/server-client.c index 41d1bb22..31a286b7 100644 --- a/server-client.c +++ b/server-client.c @@ -695,8 +695,6 @@ server_client_repeat_timer(unused int fd, unused short events, void *data) void server_client_check_exit(struct client *c) { - struct msg_exit_data exitdata; - if (!(c->flags & CLIENT_EXIT)) return; @@ -707,9 +705,7 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; - exitdata.retcode = c->retval; - server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); - + server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval); c->flags &= ~CLIENT_EXIT; } @@ -995,16 +991,12 @@ server_client_msg_identify( void server_client_msg_shell(struct client *c) { - struct msg_shell_data data; - const char *shell; + const char *shell; shell = options_get_string(&global_s_options, "default-shell"); - if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) - strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); + server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1); - server_write_client(c, MSG_SHELL, &data, sizeof data); c->flags |= CLIENT_BAD; /* it will die after exec */ } diff --git a/server-fn.c b/server-fn.c index 738a61da..f025b3a2 100644 --- a/server-fn.c +++ b/server-fn.c @@ -235,9 +235,8 @@ server_lock_session(struct session *s) void server_lock_client(struct client *c) { - const char *cmd; - size_t cmdlen; - struct msg_lock_data lockdata; + const char *cmd; + size_t cmdlen; if (c->flags & CLIENT_CONTROL) return; @@ -246,8 +245,7 @@ server_lock_client(struct client *c) return; cmd = options_get_string(&c->session->options, "lock-command"); - cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd); - if (cmdlen >= sizeof lockdata.cmd) + if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) return; tty_stop_tty(&c->tty); @@ -256,7 +254,7 @@ server_lock_client(struct client *c) tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata); + server_write_client(c, MSG_LOCK, cmd, strlen(cmd) + 1); } void diff --git a/tmux.h b/tmux.h index 7d082796..96ce0c63 100644 --- a/tmux.h +++ b/tmux.h @@ -487,22 +487,10 @@ struct msg_identify_data { int flags; }; -struct msg_lock_data { - char cmd[COMMAND_LENGTH]; -}; - struct msg_environ_data { char var[ENVIRON_LENGTH]; }; -struct msg_shell_data { - char shell[MAXPATHLEN]; -}; - -struct msg_exit_data { - int retcode; -}; - struct msg_stdin_data { ssize_t size; char data[BUFSIZ]; From fa1375c09f13118c5be70aa6ba64284bdda1c03f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 00:48:24 +0100 Subject: [PATCH 122/949] Similarly for MSG_COMMAND - allow full imsg limit not arbitrary 2048. --- client.c | 66 +++++++++++++++++++++++++++++++++++++------------ server-client.c | 43 +++++++++++++++++++------------- server-fn.c | 1 - tmux.h | 21 +++++++--------- 4 files changed, 85 insertions(+), 46 deletions(-) diff --git a/client.c b/client.c index de9ec52c..0a1c8da5 100644 --- a/client.c +++ b/client.c @@ -54,7 +54,8 @@ int client_get_lock(char *); int client_connect(char *, int); void client_send_identify(int); void client_send_environ(void); -void client_write_server(enum msgtype, void *, size_t); +int client_write_one(enum msgtype, int, const void *, size_t); +int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); void client_signal(int, short, void *); void client_stdin_callback(int, short, void *); @@ -165,12 +166,13 @@ client_main(int argc, char **argv, int flags) { struct cmd *cmd; struct cmd_list *cmdlist; - struct msg_command_data cmddata; - int cmdflags, fd; + struct msg_command_data *data; + int cmdflags, fd, i; pid_t ppid; enum msgtype msg; char *cause; struct termios tio, saved_tio; + size_t size; /* Set up the initial command. */ cmdflags = 0; @@ -265,19 +267,32 @@ client_main(int argc, char **argv, int flags) /* Send first command. */ if (msg == MSG_COMMAND) { + /* How big is the command? */ + size = 0; + for (i = 0; i < argc; i++) + size += strlen(argv[i]) + 1; + data = xmalloc((sizeof *data) + size); + /* Fill in command line arguments. */ - cmddata.pid = environ_pid; - cmddata.session_id = environ_session_id; + data->pid = environ_pid; + data->session_id = environ_session_id; /* Prepare command for server. */ - cmddata.argc = argc; - if (cmd_pack_argv( - argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) { + data->argc = argc; + if (cmd_pack_argv(argc, argv, (char*)(data + 1), size) != 0) { fprintf(stderr, "command too long\n"); + free(data); return (1); } + size += sizeof *data; - client_write_server(msg, &cmddata, sizeof cmddata); + /* Send the command. */ + if (client_write_server(msg, data, size) != 0) { + fprintf(stderr, "failed to send command\n"); + free(data); + return (1); + } + free(data); } else if (msg == MSG_SHELL) client_write_server(msg, NULL, 0); @@ -349,12 +364,29 @@ client_send_environ(void) } } -/* Write a message to the server without a file descriptor. */ -void -client_write_server(enum msgtype type, void *buf, size_t len) +/* Helper to send one message. */ +int +client_write_one(enum msgtype type, int fd, const void *buf, size_t len) { - imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); - client_update_event(); + int retval; + + retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd, + (void*)buf, len); + if (retval != 1) + return (-1); + return (0); +} + +/* Write a message to the server without a file descriptor. */ +int +client_write_server(enum msgtype type, const void *buf, size_t len) +{ + int retval; + + retval = client_write_one(type, -1, buf, len); + if (retval == 0) + client_update_event(); + return (retval); } /* Update client event based on whether it needs to read or read and write. */ @@ -537,14 +569,16 @@ client_dispatch_wait(void *data0) fatalx("bad MSG_STDOUT size"); memcpy(&stdoutdata, data, sizeof stdoutdata); - client_write(STDOUT_FILENO, stdoutdata.data, stdoutdata.size); + 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); + client_write(STDERR_FILENO, stderrdata.data, + stderrdata.size); break; case MSG_VERSION: if (datalen != 0) diff --git a/server-client.c b/server-client.c index 31a286b7..11f19376 100644 --- a/server-client.c +++ b/server-client.c @@ -40,7 +40,7 @@ void server_client_reset_state(struct client *); int server_client_assume_paste(struct session *); int server_client_msg_dispatch(struct client *); -void server_client_msg_command(struct client *, struct msg_command_data *); +void server_client_msg_command(struct client *, struct imsg *); void server_client_msg_identify( struct client *, struct msg_identify_data *, int); void server_client_msg_shell(struct client *); @@ -786,10 +786,10 @@ int server_client_msg_dispatch(struct client *c) { struct imsg imsg; - struct msg_command_data commanddata; struct msg_identify_data identifydata; struct msg_environ_data environdata; struct msg_stdin_data stdindata; + const char *data; ssize_t n, datalen; if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) @@ -800,6 +800,8 @@ server_client_msg_dispatch(struct client *c) return (-1); if (n == 0) return (0); + + data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; if (imsg.hdr.peerid != PROTOCOL_VERSION) { @@ -811,13 +813,6 @@ server_client_msg_dispatch(struct client *c) log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); switch (imsg.hdr.type) { - case MSG_COMMAND: - if (datalen != sizeof commanddata) - fatalx("bad MSG_COMMAND size"); - memcpy(&commanddata, imsg.data, sizeof commanddata); - - server_client_msg_command(c, &commanddata); - break; case MSG_IDENTIFY: if (datalen != sizeof identifydata) fatalx("bad MSG_IDENTIFY size"); @@ -827,10 +822,13 @@ server_client_msg_dispatch(struct client *c) #endif server_client_msg_identify(c, &identifydata, imsg.fd); break; + case MSG_COMMAND: + server_client_msg_command(c, &imsg); + break; case MSG_STDIN: if (datalen != sizeof stdindata) fatalx("bad MSG_STDIN size"); - memcpy(&stdindata, imsg.data, sizeof stdindata); + memcpy(&stdindata, data, sizeof stdindata); if (c->stdin_callback == NULL) break; @@ -905,15 +903,26 @@ server_client_msg_dispatch(struct client *c) /* Handle command message. */ void -server_client_msg_command(struct client *c, struct msg_command_data *data) +server_client_msg_command(struct client *c, struct imsg *imsg) { - struct cmd_list *cmdlist = NULL; - int argc; - char **argv, *cause; + struct msg_command_data data; + char *buf; + size_t len; + struct cmd_list *cmdlist = NULL; + int argc; + char **argv, *cause; - argc = data->argc; - data->argv[(sizeof data->argv) - 1] = '\0'; - if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { + if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data) + fatalx("bad MSG_COMMAND size"); + memcpy(&data, imsg->data, sizeof data); + + buf = (char*)imsg->data + sizeof data; + len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data; + if (len > 0 && buf[len - 1] != '\0') + fatalx("bad MSG_COMMAND string"); + + argc = data.argc; + if (cmd_unpack_argv(buf, len, argc, &argv) != 0) { cmdq_error(c->cmdq, "command too long"); goto error; } diff --git a/server-fn.c b/server-fn.c index f025b3a2..1b49985f 100644 --- a/server-fn.c +++ b/server-fn.c @@ -236,7 +236,6 @@ void server_lock_client(struct client *c) { const char *cmd; - size_t cmdlen; if (c->flags & CLIENT_CONTROL) return; diff --git a/tmux.h b/tmux.h index 96ce0c63..5e6de257 100644 --- a/tmux.h +++ b/tmux.h @@ -55,7 +55,6 @@ extern char **environ; * Maximum sizes of strings in message data. Don't forget to bump * PROTOCOL_VERSION if any of these change! */ -#define COMMAND_LENGTH 2048 /* packed argv size */ #define TERMINAL_LENGTH 128 /* length of TERM environment variable */ #define ENVIRON_LENGTH 1024 /* environment variable length */ @@ -468,16 +467,14 @@ enum msgtype { * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { - pid_t pid; /* from $TMUX or -1 */ - int session_id; /* from $TMUX or -1 */ + pid_t pid; /* from $TMUX or -1 */ + int session_id; /* from $TMUX or -1 */ - int argc; - char argv[COMMAND_LENGTH]; -}; + int argc; +}; /* followed by packed argv */ struct msg_identify_data { char cwd[MAXPATHLEN]; - char term[TERMINAL_LENGTH]; #ifdef __CYGWIN__ @@ -1316,7 +1313,7 @@ struct client { #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 -#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ +#define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 #define CLIENT_BAD 0x80 #define CLIENT_IDENTIFY 0x100 @@ -1919,10 +1916,10 @@ void server_window_loop(void); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); void server_write_ready(struct client *); -int server_write_client( - struct client *, enum msgtype, const void *, size_t); -void server_write_session( - struct session *, enum msgtype, const void *, size_t); +int server_write_client(struct client *, enum msgtype, const void *, + size_t); +void server_write_session(struct session *, enum msgtype, const void *, + size_t); void server_redraw_client(struct client *); void server_status_client(struct client *); void server_redraw_session(struct session *); From 446eb11cdeba97a24996cee36ba331491aba6211 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 09:06:07 +0100 Subject: [PATCH 123/949] Make tilde expansion in command strings work even if it isn't terminated by /. --- cmd-string.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/cmd-string.c b/cmd-string.c index 7e84eda6..e793ea02 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -318,10 +318,13 @@ cmd_string_expand_tilde(const char *s, size_t *p) { struct passwd *pw; struct environ_entry *envent; - char *home, *path, *username; + char *home, *path, *user, *cp; + int last; home = NULL; - if (cmd_string_getc(s, p) == '/') { + + last = cmd_string_getc(s, p); + if (last == EOF || last == '/' || last == ' '|| last == '\t') { envent = environ_find(&global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') home = envent->value; @@ -329,15 +332,27 @@ cmd_string_expand_tilde(const char *s, size_t *p) home = pw->pw_dir; } else { cmd_string_ungetc(p); - if ((username = cmd_string_string(s, p, '/', 0)) == NULL) - return (NULL); - if ((pw = getpwnam(username)) != NULL) + + cp = user = xmalloc(strlen(s)); + for (;;) { + last = cmd_string_getc(s, p); + if (last == EOF || last == '/' || last == ' '|| last == '\t') + break; + *cp++ = last; + } + *cp = '\0'; + + if ((pw = getpwnam(user)) != NULL) home = pw->pw_dir; - free(username); + free(user); } + if (home == NULL) return (NULL); - xasprintf(&path, "%s/", home); + if (last != EOF) + xasprintf(&path, "%s%c", home, last); + else + xasprintf(&path, "%s", home); return (path); } From 4538c269d0b366a770a5a5ebfe0c5007569edbc1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 21:02:23 +0100 Subject: [PATCH 124/949] Alter how tmux handles the working directory to internally use file descriptors rather than strings. - Each session still has a current working directory. - New sessions still get their working directory from the client that created them or its attached session if any. - New windows are created by default in the session working directory. - The -c flag to new, neww, splitw allows the working directory to be overridden. - The -c flag to attach let's the session working directory be changed. - The default-path option has been removed. To get the equivalent to default-path '.', do: bind c neww -c $PWD To get the equivalent of default-path '', do: bind c neww -c '#{pane_current_path}' The equivalent of default-path '~' is left as an exercise for the reader. This also changes the client identify protocol to be a set of messages rather than one as well as some other changes that should make it easier to make backwards-compatible protocol changes in future. --- client.c | 62 ++++++++------------- cmd-attach-session.c | 68 +++++++++++++++++++---- cmd-load-buffer.c | 30 +++++------ cmd-new-session.c | 74 +++++++++++++++---------- cmd-new-window.c | 41 ++++++++++++-- cmd-queue.c | 24 --------- cmd-save-buffer.c | 54 +++++++++---------- cmd-split-window.c | 40 +++++++++++--- format.c | 3 -- options-table.c | 5 -- server-client.c | 125 ++++++++++++++++++++++++++++--------------- server-fn.c | 8 +-- session.c | 17 +++--- tmux.1 | 22 ++------ tmux.c | 28 ++-------- tmux.h | 56 ++++++------------- window.c | 16 +++--- 17 files changed, 364 insertions(+), 309 deletions(-) diff --git a/client.c b/client.c index 0a1c8da5..61791876 100644 --- a/client.c +++ b/client.c @@ -53,7 +53,6 @@ int client_attached; int client_get_lock(char *); int client_connect(char *, int); void client_send_identify(int); -void client_send_environ(void); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -261,8 +260,7 @@ client_main(int argc, char **argv, int flags) /* Establish signal handlers. */ set_signals(client_signal); - /* Send initial environment. */ - client_send_environ(); + /* Send identify messages. */ client_send_identify(flags); /* Send first command. */ @@ -320,50 +318,40 @@ client_main(int argc, char **argv, int flags) return (client_exitval); } -/* Send identify message to server with the file descriptors. */ +/* Send identify messages to server. */ void client_send_identify(int flags) { - struct msg_identify_data data; - char *term; - int fd; + const char *s; + char **ss; + int fd; - data.flags = flags; + client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); - if (getcwd(data.cwd, sizeof data.cwd) == NULL) - *data.cwd = '\0'; + if ((s = getenv("TERM")) == NULL) + s = ""; + client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - term = getenv("TERM"); - if (term == NULL || - strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) - *data.term = '\0'; + if ((s = ttyname(STDIN_FILENO)) == NULL) + s = ""; + client_write_one(MSG_IDENTIFY_TTYNAME, -1, s, strlen(s) + 1); + + if ((fd = open(".", O_RDONLY)) == -1) + fd = open("/", O_RDONLY); + client_write_one(MSG_IDENTIFY_CWD, fd, NULL, 0); -#ifdef __CYGWIN__ - snprintf(&data.ttyname, sizeof data.ttyname, "%s", - ttyname(STDIN_FILENO)); -#else if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); -#endif - imsg_compose(&client_ibuf, - MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); + client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + + for (ss = environ; *ss != NULL; ss++) + client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); + + client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); + client_update_event(); } -/* Forward entire environment to server. */ -void -client_send_environ(void) -{ - struct msg_environ_data data; - char **var; - - for (var = environ; *var != NULL; var++) { - if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var) - continue; - client_write_server(MSG_ENVIRON, &data, sizeof data); - } -} - /* Helper to send one message. */ int client_write_one(enum msgtype type, int fd, const void *buf, size_t len) @@ -604,8 +592,6 @@ client_dispatch_wait(void *data0) case MSG_EXITED: imsg_free(&imsg); return (-1); - default: - fatalx("unexpected message"); } imsg_free(&imsg); @@ -684,8 +670,6 @@ client_dispatch_attached(void) system(data); client_write_server(MSG_UNLOCK, NULL, 0); break; - default: - fatalx("unexpected message"); } imsg_free(&imsg); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 7a952e59..f78a89fd 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -18,7 +18,11 @@ #include +#include +#include #include +#include +#include #include "tmux.h" @@ -30,21 +34,25 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", - "drt:", 0, 0, - "[-dr] " CMD_TARGET_SESSION_USAGE, + "c:drt:", 0, 0, + "[-dr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, CMD_CANTNEST|CMD_STARTSERVER, NULL, cmd_attach_session_exec }; enum cmd_retval -cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) +cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, + const char *cflag) { - struct session *s; - struct client *c; - const char *update; - char *cause; - u_int i; + struct session *s; + struct client *c; + const char *update; + char *cause; + u_int i; + int fd; + struct format_tree *ft; + char *cp; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); @@ -73,6 +81,27 @@ cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) } } + if (cflag != NULL) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, cflag); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + close(s->cwd); + s->cwd = fd; + } + cmdq->client->session = s; notify_attached_session_changed(cmdq->client); session_update_activity(s); @@ -85,6 +114,27 @@ cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) return (CMD_RETURN_ERROR); } + if (cflag != NULL) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, cflag); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + close(s->cwd); + s->cwd = fd; + } + if (rflag) cmdq->client->flags |= CLIENT_READONLY; @@ -115,5 +165,5 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; return (cmd_attach_session(cmdq, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'))); + args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'))); } diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 82b8f574..4acbab5e 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -49,11 +50,11 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path, *newpath, *wd; + const char *path; char *pdata, *new_pdata, *cause; size_t psize; u_int limit; - int ch, error, buffer, *buffer_ptr; + int ch, error, buffer, *buffer_ptr, cwd, fd; if (!args_has(args, 'b')) buffer = -1; @@ -81,20 +82,17 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_WAIT); } - if (c != NULL) - wd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) { - wd = options_get_string(&s->options, "default-path"); - if (*wd == '\0') - wd = s->cwd; - } else - wd = NULL; - if (wd != NULL && *wd != '\0') { - newpath = get_full_path(wd, path); - if (newpath != NULL) - path = newpath; - } - if ((f = fopen(path, "rb")) == NULL) { + if (c != NULL && c->session == NULL) + cwd = c->cwd; + else if ((s = cmd_current_session(cmdq, 0)) != NULL) + cwd = s->cwd; + else + cwd = AT_FDCWD; + + if ((fd = openat(cwd, path, O_RDONLY)) == -1 || + (f = fdopen(fd, "rb")) == NULL) { + if (fd != -1) + close(fd); cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 38c79a6d..3ce24397 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -18,6 +18,8 @@ #include +#include +#include #include #include #include @@ -47,18 +49,15 @@ enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c = cmdq->client; + struct client *c = cmdq->client, *c0; struct session *s, *groupwith; struct window *w; struct environ env; struct termios tio, *tiop; - struct passwd *pw; - const char *newname, *target, *update, *base, *cwd; - const char *errstr, *template; + const char *newname, *target, *update, *errstr, *template; char *cmd, *cause, *cp; - int detached, idx; + int detached, already_attached, idx, cwd, fd = -1; u_int sx, sy; - int already_attached; struct format_tree *ft; if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { @@ -75,7 +74,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (session_find(newname) != NULL) { if (args_has(args, 'A')) { return (cmd_attach_session(cmdq, newname, - args_has(args, 'D'), 0)); + args_has(args, 'D'), 0, NULL)); } cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); @@ -100,6 +99,31 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session != NULL) already_attached = 1; + /* Get the new session working directory. */ + if (args_has(args, 'c')) { + ft = format_create(); + if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c0); + cp = format_expand(ft, args_get(args, 'c')); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + cwd = fd; + } else if (c->session == NULL) + cwd = c->cwd; + else if ((c0 = cmd_current_client(cmdq)) != NULL) + cwd = c0->session->cwd; + else { + fd = open(".", O_RDONLY); + cwd = fd; + } + /* * Save the termios settings, part of which is used for new windows in * this session. @@ -121,26 +145,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (server_client_open(c, NULL, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } } - /* Get the new session working directory. */ - if (c != NULL && c->cwd != NULL) - base = c->cwd; - else { - pw = getpwuid(getuid()); - if (pw->pw_dir != NULL && *pw->pw_dir != '\0') - base = pw->pw_dir; - else - base = "/"; - } - if (args_has(args, 'c')) - cwd = args_get(args, 'c'); - else - cwd = options_get_string(&global_s_options, "default-path"); - cwd = cmd_default_path(base, base, cwd); - /* Find new session size. */ if (c != NULL) { sx = c->tty.sx; @@ -153,14 +161,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "width %s", errstr); - return (CMD_RETURN_ERROR); + goto error; } } if (detached && args_has(args, 'y')) { sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "height %s", errstr); - return (CMD_RETURN_ERROR); + goto error; } } if (sy > 0 && options_get_number(&global_s_options, "status")) @@ -190,7 +198,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } environ_free(&env); @@ -241,8 +249,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) template = NEW_SESSION_TEMPLATE; ft = format_create(); - if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) - format_client(ft, c); + if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c0); format_session(ft, s); cp = format_expand(ft, template); @@ -254,5 +262,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (!detached) cmdq->client_exit = 0; + + if (fd != -1) + close(fd); return (CMD_RETURN_NORMAL); + +error: + if (fd != -1) + close(fd); + return (CMD_RETURN_ERROR); } diff --git a/cmd-new-window.c b/cmd-new-window.c index e5658b37..f6a925b1 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -18,7 +18,11 @@ #include +#include +#include #include +#include +#include #include "tmux.h" @@ -45,9 +49,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct session *s; struct winlink *wl; struct client *c; - const char *cmd, *cwd, *template; + const char *cmd, *template; char *cause, *cp; - int idx, last, detached; + int idx, last, detached, cwd, fd = -1; struct format_tree *ft; if (args_has(args, 'a')) { @@ -102,7 +106,29 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmdq_default_path(cmdq, args_get(args, 'c')); + + if (args_has(args, 'c')) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, args_get(args, 'c')); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + cwd = fd; + } else if (cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else + cwd = s->cwd; if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); @@ -110,7 +136,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } if (!detached) { session_select(s, wl->idx); @@ -136,5 +162,12 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } + if (fd != -1) + close(fd); return (CMD_RETURN_NORMAL); + +error: + if (fd != -1) + close(fd); + return (CMD_RETURN_ERROR); } diff --git a/cmd-queue.c b/cmd-queue.c index 7feb25ef..c5905bdb 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -283,27 +283,3 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } - -/* Get default path using command queue. */ -const char * -cmdq_default_path(struct cmd_q *cmdq, const char *cwd) -{ - struct client *c = cmdq->client; - struct session *s; - const char *current; - - if ((s = cmd_current_session(cmdq, 0)) == NULL) - return (NULL); - - if (cwd == NULL) - cwd = options_get_string(&s->options, "default-path"); - - if (c != NULL && c->session == NULL && c->cwd != NULL) - current = c->cwd; - else if (s->curw != NULL) - current = osdep_get_cwd(s->curw->window->active->fd); - else - current = NULL; - - return (cmd_default_path(s->cwd, current, cwd)); -} diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index c6c54019..3788fc22 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -20,8 +20,10 @@ #include #include +#include #include #include +#include #include "tmux.h" @@ -53,17 +55,14 @@ enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *newpath, *wd; - char *cause, *start, *end; - size_t size, used; - int buffer; - mode_t mask; + const char *path; + char *cause, *start, *end, *msg; + size_t size, used, msglen; + int cwd, fd, buffer; FILE *f; - char *msg; - size_t msglen; if (!args_has(args, 'b')) { if ((pb = paste_get_top(&global_buffers)) == NULL) { @@ -90,7 +89,6 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else path = args->argv[0]; if (strcmp(path, "-") == 0) { - c = cmdq->client; if (c == NULL) { cmdq_error(cmdq, "can't write to stdout"); return (CMD_RETURN_ERROR); @@ -100,28 +98,26 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) goto do_print; } - c = cmdq->client; - if (c != NULL) - wd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) { - wd = options_get_string(&s->options, "default-path"); - if (*wd == '\0') - wd = s->cwd; - } else - wd = NULL; - if (wd != NULL && *wd != '\0') { - newpath = get_full_path(wd, path); - if (newpath != NULL) - path = newpath; - } - - mask = umask(S_IRWXG | S_IRWXO); - if (args_has(self->args, 'a')) - f = fopen(path, "ab"); + if (c != NULL && c->session == NULL) + cwd = c->cwd; + else if ((s = cmd_current_session(cmdq, 0)) != NULL) + cwd = s->cwd; else - f = fopen(path, "wb"); - umask(mask); + cwd = AT_FDCWD; + + f = NULL; + if (args_has(self->args, 'a')) { + fd = openat(cwd, path, O_CREAT|O_RDWR|O_APPEND, 0600); + if (fd != -1) + f = fdopen(fd, "ab"); + } else { + fd = openat(cwd, path, O_CREAT|O_RDWR, 0600); + if (fd != -1) + f = fdopen(fd, "wb"); + } if (f == NULL) { + if (fd != -1) + close(fd); cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } diff --git a/cmd-split-window.c b/cmd-split-window.c index a2403253..ef1d3cbc 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -18,7 +18,10 @@ #include +#include +#include #include +#include #include #include "tmux.h" @@ -57,16 +60,14 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp, *new_wp = NULL; struct environ env; - const char *cmd, *cwd, *shell; - char *cause, *new_cause; + const char *cmd, *shell, *template; + char *cause, *new_cause, *cp; u_int hlimit; - int size, percentage; + int size, percentage, cwd, fd = -1; enum layout_type type; struct layout_cell *lc; - const char *template; struct client *c; struct format_tree *ft; - char *cp; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); @@ -82,7 +83,29 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmdq_default_path(cmdq, args_get(args, 'c')); + + if (args_has(args, 'c')) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, args_get(args, 'c')); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + cwd = fd; + } else if (cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else + cwd = s->cwd; type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) @@ -155,6 +178,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } notify_window_layout_changed(w); + + if (fd != -1) + close(fd); return (CMD_RETURN_NORMAL); error: @@ -163,5 +189,7 @@ error: window_remove_pane(w, new_wp); cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); + if (fd != -1) + close(fd); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index ba4b95b2..a7f1fdee 100644 --- a/format.c +++ b/format.c @@ -403,7 +403,6 @@ format_client(struct format_tree *ft, struct client *c) time_t t; struct session *s; - format_add(ft, "client_cwd", "%s", c->cwd); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); if (c->tty.path != NULL) @@ -552,8 +551,6 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_pid", "%ld", (long) wp->pid); if (wp->cmd != NULL) format_add(ft, "pane_start_command", "%s", wp->cmd); - if (wp->cwd != NULL) - format_add(ft, "pane_start_path", "%s", wp->cwd); if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); if ((cmd = format_get_command(wp)) != NULL) { diff --git a/options-table.c b/options-table.c index f6a15472..5da095b1 100644 --- a/options-table.c +++ b/options-table.c @@ -125,11 +125,6 @@ const struct options_table_entry session_options_table[] = { .default_str = "" }, - { .name = "default-path", - .type = OPTIONS_TABLE_STRING, - .default_str = "" - }, - { .name = "default-shell", .type = OPTIONS_TABLE_STRING, .default_str = _PATH_BSHELL diff --git a/server-client.c b/server-client.c index 11f19376..8a99367c 100644 --- a/server-client.c +++ b/server-client.c @@ -41,8 +41,7 @@ int server_client_assume_paste(struct session *); int server_client_msg_dispatch(struct client *); void server_client_msg_command(struct client *, struct imsg *); -void server_client_msg_identify( - struct client *, struct msg_identify_data *, int); +void server_client_msg_identify(struct client *, struct imsg *); void server_client_msg_shell(struct client *); /* Create a new client. */ @@ -151,6 +150,8 @@ server_client_lost(struct client *c) */ if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); + free(c->ttyname); + free(c->term); evbuffer_free (c->stdin_data); evbuffer_free (c->stdout_data); @@ -162,6 +163,7 @@ server_client_lost(struct client *c) screen_free(&c->status); free(c->title); + close(c->cwd); evtimer_del(&c->repeat_timer); @@ -179,7 +181,6 @@ server_client_lost(struct client *c) free(c->prompt_string); free(c->prompt_buffer); - free(c->cwd); c->cmdq->dead = 1; cmdq_free(c->cmdq); @@ -786,8 +787,6 @@ int server_client_msg_dispatch(struct client *c) { struct imsg imsg; - struct msg_identify_data identifydata; - struct msg_environ_data environdata; struct msg_stdin_data stdindata; const char *data; ssize_t n, datalen; @@ -813,14 +812,14 @@ server_client_msg_dispatch(struct client *c) log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); switch (imsg.hdr.type) { - case MSG_IDENTIFY: - if (datalen != sizeof identifydata) - fatalx("bad MSG_IDENTIFY size"); - memcpy(&identifydata, imsg.data, sizeof identifydata); -#ifdef __CYGWIN__ - imsg.fd = open(identifydata.ttyname, O_RDWR|O_NOCTTY); -#endif - server_client_msg_identify(c, &identifydata, imsg.fd); + case MSG_IDENTIFY_FLAGS: + case MSG_IDENTIFY_TERM: + case MSG_IDENTIFY_TTYNAME: + case MSG_IDENTIFY_CWD: + case MSG_IDENTIFY_STDIN: + case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_DONE: + server_client_msg_identify(c, &imsg); break; case MSG_COMMAND: server_client_msg_command(c, &imsg); @@ -878,23 +877,12 @@ server_client_msg_dispatch(struct client *c) server_redraw_client(c); recalculate_sizes(); break; - case MSG_ENVIRON: - if (datalen != sizeof environdata) - fatalx("bad MSG_ENVIRON size"); - memcpy(&environdata, imsg.data, sizeof environdata); - - environdata.var[(sizeof environdata.var) - 1] = '\0'; - if (strchr(environdata.var, '=') != NULL) - environ_put(&c->environ, environdata.var); - break; case MSG_SHELL: if (datalen != 0) fatalx("bad MSG_SHELL size"); server_client_msg_shell(c); break; - default: - fatalx("unexpected message"); } imsg_free(&imsg); @@ -953,46 +941,99 @@ error: /* Handle identify message. */ void -server_client_msg_identify( - struct client *c, struct msg_identify_data *data, int fd) +server_client_msg_identify(struct client *c, struct imsg *imsg) { - c->cwd = NULL; - data->cwd[(sizeof data->cwd) - 1] = '\0'; - if (*data->cwd != '\0') - c->cwd = xstrdup(data->cwd); + const char *data; + size_t datalen; + int flags; - if (data->flags & CLIENT_CONTROL) { + if (c->flags & CLIENT_IDENTIFIED) + fatalx("out-of-order identify message"); + + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_IDENTIFY_FLAGS: + if (datalen != sizeof flags) + fatalx("bad MSG_IDENTIFY_FLAGS size"); + memcpy(&flags, data, sizeof flags); + c->flags |= flags; + break; + case MSG_IDENTIFY_TERM: + if (data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_TERM string"); + c->term = xstrdup(data); + break; + case MSG_IDENTIFY_TTYNAME: + if (data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_TTYNAME string"); + c->ttyname = xstrdup(data); + break; + case MSG_IDENTIFY_CWD: + if (datalen != 0) + fatalx("bad MSG_IDENTIFY_CWD size"); + c->cwd = imsg->fd; + break; + case MSG_IDENTIFY_STDIN: + if (datalen != 0) + fatalx("bad MSG_IDENTIFY_STDIN size"); + c->fd = imsg->fd; + break; + case MSG_IDENTIFY_ENVIRON: + if (data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_ENVIRON string"); + if (strchr(data, '=') != NULL) + environ_put(&c->environ, data); + break; + default: + break; + } + + if (imsg->hdr.type != MSG_IDENTIFY_DONE) + return; + c->flags |= CLIENT_IDENTIFIED; + +#ifdef __CYGWIN__ + c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); + c->cwd = open(".", O_RDONLY); +#endif + + if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; + evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; - c->flags |= CLIENT_CONTROL; - if (data->flags & CLIENT_CONTROLCONTROL) + + if (c->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); server_write_client(c, MSG_STDIN, NULL, 0); c->tty.fd = -1; c->tty.log_fd = -1; - close(fd); + close(c->fd); + c->fd = -1; + return; } - if (fd == -1) + if (c->fd == -1) return; - if (!isatty(fd)) { - close(fd); + if (!isatty(c->fd)) { + close(c->fd); + c->fd = -1; return; } - data->term[(sizeof data->term) - 1] = '\0'; - tty_init(&c->tty, c, fd, data->term); - if (data->flags & CLIENT_UTF8) + tty_init(&c->tty, c, c->fd, c->term); + if (c->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; - if (data->flags & CLIENT_256COLOURS) + if (c->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); - if (!(data->flags & CLIENT_CONTROL)) + if (!(c->flags & CLIENT_CONTROL)) c->flags |= CLIENT_TERMINAL; } diff --git a/server-fn.c b/server-fn.c index 1b49985f..4fc4eb5c 100644 --- a/server-fn.c +++ b/server-fn.c @@ -56,8 +56,8 @@ server_write_ready(struct client *c) } int -server_write_client( - struct client *c, enum msgtype type, const void *buf, size_t len) +server_write_client(struct client *c, enum msgtype type, const void *buf, + size_t len) { struct imsgbuf *ibuf = &c->ibuf; int error; @@ -73,8 +73,8 @@ server_write_client( } void -server_write_session( - struct session *s, enum msgtype type, const void *buf, size_t len) +server_write_session(struct session *s, enum msgtype type, const void *buf, + size_t len) { struct client *c; u_int i; diff --git a/session.c b/session.c index bb742d8f..66a52bc6 100644 --- a/session.c +++ b/session.c @@ -84,9 +84,8 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *name, const char *cmd, const char *cwd, - struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, - char **cause) +session_create(const char *name, const char *cmd, int cwd, struct environ *env, + struct termios *tio, int idx, u_int sx, u_int sy, char **cause) { struct session *s; @@ -98,7 +97,7 @@ session_create(const char *name, const char *cmd, const char *cwd, fatal("gettimeofday failed"); session_update_activity(s); - s->cwd = xstrdup(cwd); + s->cwd = dup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); @@ -170,7 +169,7 @@ session_destroy(struct session *s) winlink_remove(&s->windows, wl); } - free(s->cwd); + close(s->cwd); RB_INSERT(sessions, &dead_sessions, s); } @@ -226,8 +225,8 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * -session_new(struct session *s, - const char *name, const char *cmd, const char *cwd, int idx, char **cause) +session_new(struct session *s, const char *name, const char *cmd, int cwd, + int idx, char **cause) { struct window *w; struct winlink *wl; @@ -250,8 +249,8 @@ session_new(struct session *s, shell = _PATH_BSHELL; hlimit = options_get_number(&s->options, "history-limit"); - w = window_create( - name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause); + w = window_create(name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, + hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(&env); diff --git a/tmux.1 b/tmux.1 index ba5fe187..07eb93fb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -568,6 +568,7 @@ The following commands are available to manage clients and sessions: .Bl -tag -width Ds .It Xo Ic attach-session .Op Fl dr +.Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) @@ -601,6 +602,10 @@ needs to select the most recently used session, it will prefer the most recently used .Em unattached session. +.Pp +.Fl c +will set the session working directory (used for new windows) to +.Ar working-directory . .It Xo Ic detach-client .Op Fl P .Op Fl a @@ -1513,13 +1518,6 @@ is not specified, the value of the option is used. .Fl c specifies the working directory in which the new window is created. -It may have an absolute path or one of the following values (or a subdirectory): -.Bl -column "XXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXX" -offset indent -.It Li "Empty string" Ta "Current pane's directory" -.It Li "~" Ta "User's home directory" -.It Li "-" Ta "Where session was started" -.It Li "." Ta "Where server was started" -.El .Pp When the shell command completes, the window closes. See the @@ -2179,15 +2177,6 @@ The default is an empty string, which instructs to create a login shell using the value of the .Ic default-shell option. -.It Ic default-path Ar path -Set the default working directory for new panes. -If empty (the default), the working directory is determined from the process -running in the active pane, from the command line environment or from the -working directory where the session was created. -Otherwise the same options are available as for the -.Fl c -flag to -.Ic new-window . .It Ic default-shell Ar path Specify the default shell. This is used as the login shell for new windows when the @@ -3056,7 +3045,6 @@ The following variables are available, where appropriate: .It Li "client_activity_string" Ta "" Ta "String time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" .It Li "client_created_string" Ta "" Ta "String time client created" -.It Li "client_cwd" Ta "" Ta "Working directory of client" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" diff --git a/tmux.c b/tmux.c index 76b09cf4..a0359969 100644 --- a/tmux.c +++ b/tmux.c @@ -127,30 +127,6 @@ areshell(const char *shell) return (0); } -const char * -get_full_path(const char *wd, const char *path) -{ - int fd; - static char newpath[MAXPATHLEN]; - const char *retval; - - fd = open(".", O_RDONLY); - if (fd == -1) - return (NULL); - - retval = NULL; - if (chdir(wd) == 0) { - if (realpath(path, newpath) == 0) - retval = newpath; - } - - if (fchdir(fd) != 0) - chdir("/"); - close(fd); - - return (retval); -} - void parseenvironment(void) { @@ -249,7 +225,7 @@ int main(int argc, char **argv) { struct passwd *pw; - char *s, *path, *label, *home, **var; + char *s, *path, *label, *home, **var, tmp[MAXPATHLEN]; int opt, flags, quiet, keys; #if defined(DEBUG) && defined(__OpenBSD__) @@ -333,6 +309,8 @@ main(int argc, char **argv) environ_init(&global_environ); for (var = environ; *var != NULL; var++) environ_put(&global_environ, *var); + if (getcwd(tmp, sizeof tmp) != NULL) + environ_set(&global_environ, "PWD", tmp); options_init(&global_options, NULL); options_table_populate_tree(server_options_table, &global_options); diff --git a/tmux.h b/tmux.h index 5e6de257..a76d1024 100644 --- a/tmux.h +++ b/tmux.h @@ -51,13 +51,6 @@ extern char **environ; /* Automatic name refresh interval, in milliseconds. */ #define NAME_INTERVAL 500 -/* - * Maximum sizes of strings in message data. Don't forget to bump - * PROTOCOL_VERSION if any of these change! - */ -#define TERMINAL_LENGTH 128 /* length of TERM environment variable */ -#define ENVIRON_LENGTH 1024 /* environment variable length */ - /* * UTF-8 data size. This must be big enough to hold combined characters as well * as single. @@ -456,9 +449,6 @@ enum msgtype { MSG_SUSPEND, MSG_UNLOCK, MSG_WAKEUP, - - MSG_IDENTIFY = 300, - MSG_ENVIRON }; /* @@ -473,21 +463,6 @@ struct msg_command_data { int argc; }; /* followed by packed argv */ -struct msg_identify_data { - char cwd[MAXPATHLEN]; - char term[TERMINAL_LENGTH]; - -#ifdef __CYGWIN__ - char ttyname[TTY_NAME_MAX]; -#endif - - int flags; -}; - -struct msg_environ_data { - char var[ENVIRON_LENGTH]; -}; - struct msg_stdin_data { ssize_t size; char data[BUFSIZ]; @@ -934,7 +909,7 @@ struct window_pane { char *cmd; char *shell; - char *cwd; + int cwd; pid_t pid; char tty[TTY_NAME_MAX]; @@ -1081,7 +1056,7 @@ struct session { u_int id; char *name; - char *cwd; + int cwd; struct timeval creation_time; struct timeval activity_time; @@ -1281,6 +1256,7 @@ RB_HEAD(status_out_tree, status_out); struct client { struct imsgbuf ibuf; + int fd; struct event event; int retval; @@ -1290,8 +1266,10 @@ struct client { struct environ environ; char *title; - char *cwd; + int cwd; + char *term; + char *ttyname; struct tty tty; void (*stdin_callback)(struct client *, int, void *); @@ -1524,7 +1502,6 @@ void logfile(const char *); const char *getshell(void); int checkshell(const char *); int areshell(const char *); -const char* get_full_path(const char *, const char *); void setblocking(int, int); __dead void shell_exec(const char *, const char *); @@ -1760,7 +1737,6 @@ int cmd_find_index(struct cmd_q *, const char *, struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); char *cmd_template_replace(const char *, const char *, int); -const char *cmd_default_path(const char *, const char *, const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; @@ -1851,7 +1827,8 @@ extern const struct cmd_entry cmd_up_pane_entry; extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ -enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int); +enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, + const char *); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); @@ -1869,7 +1846,6 @@ void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); void cmdq_flush(struct cmd_q *); -const char *cmdq_default_path(struct cmd_q *, const char *); /* cmd-string.c */ int cmd_string_parse(const char *, struct cmd_list **, const char *, @@ -2141,9 +2117,9 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *); int window_index(struct window *, u_int *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); -struct window *window_create(const char *, const char *, const char *, - const char *, struct environ *, struct termios *, - u_int, u_int, u_int, char **); +struct window *window_create(const char *, const char *, const char *, int, + struct environ *, struct termios *, u_int, u_int, u_int, + char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); void window_set_active_at(struct window *, u_int, u_int); @@ -2167,8 +2143,8 @@ struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); void window_pane_timer_start(struct window_pane *); int window_pane_spawn(struct window_pane *, const char *, - const char *, const char *, struct environ *, - struct termios *, char **); + const char *, int, struct environ *, struct termios *, + char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); @@ -2304,7 +2280,7 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, const char *, +struct session *session_create(const char *, const char *, int, struct environ *, struct termios *, int, u_int, u_int, char **); void session_destroy(struct session *); @@ -2312,8 +2288,8 @@ int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); -struct winlink *session_new(struct session *, - const char *, const char *, const char *, int, char **); +struct winlink *session_new(struct session *, const char *, const char *, int, + int, char **); struct winlink *session_attach( struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); diff --git a/window.c b/window.c index 7912bd37..9f47f444 100644 --- a/window.c +++ b/window.c @@ -306,7 +306,7 @@ window_create1(u_int sx, u_int sy) struct window * window_create(const char *name, const char *cmd, const char *shell, - const char *cwd, struct environ *env, struct termios *tio, + int cwd, struct environ *env, struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; @@ -672,7 +672,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->cmd = NULL; wp->shell = NULL; - wp->cwd = NULL; + wp->cwd = -1; wp->fd = -1; wp->event = NULL; @@ -727,7 +727,7 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); - free(wp->cwd); + close(wp->cwd); free(wp->shell); free(wp->cmd); free(wp); @@ -735,7 +735,7 @@ window_pane_destroy(struct window_pane *wp) int window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, - const char *cwd, struct environ *env, struct termios *tio, char **cause) + int cwd, struct environ *env, struct termios *tio, char **cause) { struct winsize ws; char *argv0, paneid[16]; @@ -754,9 +754,9 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, free(wp->shell); wp->shell = xstrdup(shell); } - if (cwd != NULL) { - free(wp->cwd); - wp->cwd = xstrdup(cwd); + if (cwd != -1) { + close(wp->cwd); + wp->cwd = dup(cwd); } log_debug("spawn: %s -- %s", wp->shell, wp->cmd); @@ -771,7 +771,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, xasprintf(cause, "%s: %s", cmd, strerror(errno)); return (-1); case 0: - if (chdir(wp->cwd) != 0) + if (fchdir(wp->cwd) != 0) chdir("/"); if (tcgetattr(STDIN_FILENO, &tio2) != 0) From d86c70af965c862651017ab4c288160f37ec654b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 21:20:11 +0100 Subject: [PATCH 125/949] Don't look at string[length - 1] if length == 0. --- client.c | 4 ++-- server-client.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client.c b/client.c index 61791876..90bd40cb 100644 --- a/client.c +++ b/client.c @@ -580,7 +580,7 @@ client_dispatch_wait(void *data0) imsg_free(&imsg); return (-1); case MSG_SHELL: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_SHELL string"); clear_signals(0); @@ -664,7 +664,7 @@ client_dispatch_attached(void) kill(getpid(), SIGTSTP); break; case MSG_LOCK: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_LOCK string"); system(data); diff --git a/server-client.c b/server-client.c index 8a99367c..090916c8 100644 --- a/server-client.c +++ b/server-client.c @@ -961,12 +961,12 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) c->flags |= flags; break; case MSG_IDENTIFY_TERM: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); c->term = xstrdup(data); break; case MSG_IDENTIFY_TTYNAME: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TTYNAME string"); c->ttyname = xstrdup(data); break; @@ -981,7 +981,7 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) c->fd = imsg->fd; break; case MSG_IDENTIFY_ENVIRON: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) environ_put(&c->environ, data); From aa0a57fd5681ffbae652bebebea04e1d90ac40ce Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 21:21:52 +0100 Subject: [PATCH 126/949] Show session name in detached message. Requested by somebody a few months ago who didn't bother testing it. But it works for me anyway. --- client.c | 21 ++++++++++++++++++--- cmd-attach-session.c | 10 +++++++--- cmd-detach-client.c | 22 +++++++++++++++------- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/client.c b/client.c index 90bd40cb..8884f637 100644 --- a/client.c +++ b/client.c @@ -48,6 +48,7 @@ enum { } client_exitreason = CLIENT_EXIT_NONE; int client_exitval; enum msgtype client_exittype; +const char *client_exitsession; int client_attached; int client_get_lock(char *); @@ -138,12 +139,24 @@ failed: const char * client_exit_message(void) { + static char msg[256]; + switch (client_exitreason) { case CLIENT_EXIT_NONE: break; case CLIENT_EXIT_DETACHED: + if (client_exitsession != NULL) { + xsnprintf(msg, sizeof msg, "detached " + "(from session %s)", client_exitsession); + return (msg); + } return ("detached"); case CLIENT_EXIT_DETACHED_HUP: + if (client_exitsession != NULL) { + xsnprintf(msg, sizeof msg, "detached and SIGHUP " + "(from session %s)", client_exitsession); + return (msg); + } return ("detached and SIGHUP"); case CLIENT_EXIT_LOST_TTY: return ("lost tty"); @@ -587,6 +600,7 @@ client_dispatch_wait(void *data0) shell_exec(data, data0); /* NOTREACHED */ case MSG_DETACH: + case MSG_DETACHKILL: client_write_server(MSG_EXITING, NULL, 0); break; case MSG_EXITED: @@ -618,11 +632,12 @@ client_dispatch_attached(void) log_debug("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { - case MSG_DETACHKILL: case MSG_DETACH: - if (datalen != 0) - fatalx("bad MSG_DETACH size"); + case MSG_DETACHKILL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_DETACH string"); + client_exitsession = xstrdup(data); client_exittype = imsg.hdr.type; if (imsg.hdr.type == MSG_DETACHKILL) client_exitreason = CLIENT_EXIT_DETACHED_HUP; diff --git a/cmd-attach-session.c b/cmd-attach-session.c index f78a89fd..e4c0b232 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -77,7 +77,9 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, continue; if (c == cmdq->client) continue; - server_write_client(c, MSG_DETACH, NULL, 0); + server_write_client(c, MSG_DETACH, + c->session->name, + strlen(c->session->name) + 1); } } @@ -138,8 +140,10 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (rflag) cmdq->client->flags |= CLIENT_READONLY; - if (dflag) - server_write_session(s, MSG_DETACH, NULL, 0); + if (dflag) { + server_write_session(s, MSG_DETACH, s->name, + strlen(s->name) + 1); + } update = options_get_string(&s->options, "update-environment"); environ_update(update, &cmdq->client->environ, &s->environ); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index ea9e3816..f0867364 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -18,6 +18,8 @@ #include +#include + #include "tmux.h" /* @@ -40,8 +42,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c, *c2; - struct session *s; - enum msgtype msgtype; + struct session *s; + enum msgtype msgtype; u_int i; if (args_has(args, 'P')) @@ -56,8 +58,10 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session == s) - server_write_client(c, msgtype, NULL, 0); + if (c == NULL || c->session != s) + continue; + server_write_client(c, msgtype, c->session->name, + strlen(c->session->name) + 1); } } else { c = cmd_find_client(cmdq, args_get(args, 't'), 0); @@ -69,10 +73,14 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) c2 = ARRAY_ITEM(&clients, i); if (c2 == NULL || c == c2) continue; - server_write_client(c2, msgtype, NULL, 0); + server_write_client(c2, msgtype, + c2->session->name, + strlen(c2->session->name) + 1); } - } else - server_write_client(c, msgtype, NULL, 0); + } else { + server_write_client(c, msgtype, c->session->name, + strlen(c->session->name) + 1); + } } return (CMD_RETURN_STOP); From 9e0d7bddc0cef400077f9d2d84633a8bd4eddc01 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 21:31:55 +0100 Subject: [PATCH 127/949] Don't boke when figuring out working directory from configuration file. --- cmd-new-session.c | 2 +- cmd-new-window.c | 2 +- cmd-split-window.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 3ce24397..ad083a44 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -115,7 +115,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } cwd = fd; - } else if (c->session == NULL) + } else if (c != NULL && c->session == NULL) cwd = c->cwd; else if ((c0 = cmd_current_client(cmdq)) != NULL) cwd = c0->session->cwd; diff --git a/cmd-new-window.c b/cmd-new-window.c index f6a925b1..5c2cbe40 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -125,7 +125,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } cwd = fd; - } else if (cmdq->client->session == NULL) + } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else cwd = s->cwd; diff --git a/cmd-split-window.c b/cmd-split-window.c index ef1d3cbc..4bb069f0 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -102,7 +102,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } cwd = fd; - } else if (cmdq->client->session == NULL) + } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else cwd = s->cwd; From 5ea6148362e6f80019b0f41c29bdc395c69e41dc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 21:35:44 +0100 Subject: [PATCH 128/949] Remove now unused cmd_get_default_path. --- cmd.c | 71 ----------------------------------------------------------- 1 file changed, 71 deletions(-) diff --git a/cmd.c b/cmd.c index 6bf7cecc..90756951 100644 --- a/cmd.c +++ b/cmd.c @@ -1276,74 +1276,3 @@ cmd_template_replace(const char *template, const char *s, int idx) return (buf); } - -/* - * Return the default path for a new pane. Several special values are accepted: - * the empty string or relative path for the current working directory, - * ~ for the user's home, - for the base working directory, . for the server - * working directory. - */ -const char * -cmd_default_path(const char *base, const char *current, const char *in) -{ - const char *root; - struct environ_entry *envent; - char tmp[MAXPATHLEN]; - struct passwd *pw; - int n; - size_t skip; - static char path[MAXPATHLEN]; - - skip = 1; - if (strcmp(in, "$HOME") == 0 || strncmp(in, "$HOME/", 6) == 0) { - /* User's home directory - $HOME. */ - skip = 5; - goto find_home; - } else if (in[0] == '~' && (in[1] == '\0' || in[1] == '/')) { - /* User's home directory - ~. */ - goto find_home; - } else if (in[0] == '-' && (in[1] == '\0' || in[1] == '/')) { - /* Base working directory. */ - root = base; - goto complete_path; - } else if (in[0] == '.' && (in[1] == '\0' || in[1] == '/')) { - /* Server working directory. */ - if (getcwd(tmp, sizeof tmp) != NULL) { - root = tmp; - goto complete_path; - } - return ("/"); - } else if (*in == '/') { - /* Absolute path. */ - return (in); - } else { - /* Empty or relative path. */ - if (current != NULL) - root = current; - else - return (base); - skip = 0; - goto complete_path; - } - - return (base); - -find_home: - envent = environ_find(&global_environ, "HOME"); - if (envent != NULL && *envent->value != '\0') - root = envent->value; - else if ((pw = getpwuid(getuid())) != NULL) - root = pw->pw_dir; - else - return (base); - -complete_path: - if (root[skip] == '\0') { - strlcpy(path, root, sizeof path); - return (path); - } - n = snprintf(path, sizeof path, "%s/%s", root, in + skip); - if (n > 0 && (size_t)n < sizeof path) - return (path); - return (base); -} From e9b09faab262f179fec936c4036713866b98e3d0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 22:38:33 +0100 Subject: [PATCH 129/949] We accidentally haven't been using $TMUX to work out the session for a while and in fact it is less useful that using the client ttyname. So don't bother and don't pass it from the client. If we need it in future it is in c->environ. --- client.c | 4 ---- cmd.c | 8 -------- server-client.c | 2 ++ tmux.c | 30 ++++++++---------------------- tmux.h | 7 ------- 5 files changed, 10 insertions(+), 41 deletions(-) diff --git a/client.c b/client.c index 8884f637..d1a3b177 100644 --- a/client.c +++ b/client.c @@ -284,10 +284,6 @@ client_main(int argc, char **argv, int flags) size += strlen(argv[i]) + 1; data = xmalloc((sizeof *data) + size); - /* Fill in command line arguments. */ - data->pid = environ_pid; - data->session_id = environ_session_id; - /* Prepare command for server. */ data->argc = argc; if (cmd_pack_argv(argc, argv, (char*)(data + 1), size) != 0) { diff --git a/cmd.c b/cmd.c index 90756951..414c9067 100644 --- a/cmd.c +++ b/cmd.c @@ -313,7 +313,6 @@ cmd_print(struct cmd *cmd, char *buf, size_t len) struct session * cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) { - struct msg_command_data *data = cmdq->msgdata; struct client *c = cmdq->client; struct session *s; struct sessionslist ss; @@ -355,13 +354,6 @@ cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) return (s); } - /* Use the session from the TMUX environment variable. */ - if (data != NULL && data->pid == getpid() && data->session_id != -1) { - s = session_find_by_id(data->session_id); - if (s != NULL) - return (s); - } - return (cmd_choose_session(prefer_unattached)); } diff --git a/server-client.c b/server-client.c index 090916c8..aceef6e2 100644 --- a/server-client.c +++ b/server-client.c @@ -62,6 +62,8 @@ server_client_create(int fd) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); + environ_init(&c->environ); + c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; diff --git a/tmux.c b/tmux.c index a0359969..1e6edd90 100644 --- a/tmux.c +++ b/tmux.c @@ -48,11 +48,8 @@ time_t start_time; char socket_path[MAXPATHLEN]; int login_shell; char *environ_path; -pid_t environ_pid = -1; -int environ_session_id = -1; __dead void usage(void); -void parseenvironment(void); char *makesocketpath(const char *); #ifndef HAVE___PROGNAME @@ -127,23 +124,6 @@ areshell(const char *shell) return (0); } -void -parseenvironment(void) -{ - char *env, path[256]; - long pid; - int id; - - if ((env = getenv("TMUX")) == NULL) - return; - - if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3) - return; - environ_path = xstrdup(path); - environ_pid = pid; - environ_session_id = id; -} - char * makesocketpath(const char *label) { @@ -226,7 +206,9 @@ main(int argc, char **argv) { struct passwd *pw; char *s, *path, *label, *home, **var, tmp[MAXPATHLEN]; - int opt, flags, quiet, keys; + char in[256]; + long long pid; + int opt, flags, quiet, keys, session; #if defined(DEBUG) && defined(__OpenBSD__) malloc_options = (char *) "AFGJPX"; @@ -357,11 +339,15 @@ main(int argc, char **argv) } } + /* Get path from environment. */ + s = getenv("TMUX"); + if (s != NULL && sscanf(s, "%255[^,],%lld,%d", in, &pid, &session) == 3) + environ_path = xstrdup(in); + /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. */ - parseenvironment(); if (path == NULL) { /* If no -L, use the environment. */ if (label == NULL) { diff --git a/tmux.h b/tmux.h index a76d1024..84ad1643 100644 --- a/tmux.h +++ b/tmux.h @@ -457,9 +457,6 @@ enum msgtype { * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { - pid_t pid; /* from $TMUX or -1 */ - int session_id; /* from $TMUX or -1 */ - int argc; }; /* followed by packed argv */ @@ -1401,8 +1398,6 @@ struct cmd_q { void (*emptyfn)(struct cmd_q *); void *data; - struct msg_command_data *msgdata; - TAILQ_ENTRY(cmd_q) waitentry; }; @@ -1496,8 +1491,6 @@ extern time_t start_time; extern char socket_path[MAXPATHLEN]; extern int login_shell; extern char *environ_path; -extern pid_t environ_pid; -extern int environ_session_id; void logfile(const char *); const char *getshell(void); int checkshell(const char *); From f3ec8693e36f7a562f6ddd87acfebf2340cad2ab Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 6 Oct 2013 22:44:24 +0100 Subject: [PATCH 130/949] Pass -1 for cwd now not NULL. --- cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 36f8783b..bcde2754 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -77,7 +77,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[0]; else cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 6026ca64..e6d913cf 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -79,7 +79,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[0]; else cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(&env); From e588ddb5d683a9fd5cf595ac5ed2d8ba657a6bcf Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Oct 2013 10:27:23 +0100 Subject: [PATCH 131/949] Add openat() to compat. --- Makefile.am | 3 +++ compat.h | 8 ++++++- compat/openat.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 7 ++++++ job.c | 2 +- 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 compat/openat.c diff --git a/Makefile.am b/Makefile.am index fb707df0..690e466d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -238,6 +238,9 @@ endif if NO_CFMAKERAW nodist_tmux_SOURCES += compat/cfmakeraw.c endif +if NO_OPENAT +nodist_tmux_SOURCES += compat/openat.c +endif # Install tmux.1 in the right format. install-exec-hook: diff --git a/compat.h b/compat.h index b84ff400..ab3224b1 100644 --- a/compat.h +++ b/compat.h @@ -243,7 +243,13 @@ int unsetenv(const char *); #ifndef HAVE_CFMAKERAW /* cfmakeraw.c */ -void cfmakeraw(struct termios *tio); +void cfmakeraw(struct termios *); +#endif + +#ifndef HAVE_OPENAT +/* openat.c */ +#define AT_FDCWD -100 +int openat(int, const char *, int, ...); #endif #ifdef HAVE_GETOPT diff --git a/compat/openat.c b/compat/openat.c new file mode 100644 index 00000000..005235b4 --- /dev/null +++ b/compat/openat.c @@ -0,0 +1,63 @@ +/* $Id$ */ + +/* + * Copyright (c) 2013 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "tmux.h" + +int +openat(int fd, const char *path, int flags, ...) +{ + mode_t mode; + va_list ap; + int dotfd, retval, saved_errno; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } else + mode = 0; + + dotfd = -1; + if (fd != AT_FDCWD) { + dotfd = open(".", O_RDONLY); + if (dotfd == -1) + return (-1); + if (fchdir(fd) != 0) + return (-1); + } + + retval = open(path, flags, mode); + + if (dotfd != -1) { + if (fchdir(dotfd) != 0) { + saved_errno = errno; + close(retval); + close(dotfd); + errno = saved_errno; + return (-1); + } + close(dotfd); + } + + return (retval); +} diff --git a/configure.ac b/configure.ac index 68c50fba..ceb37db8 100644 --- a/configure.ac +++ b/configure.ac @@ -323,6 +323,13 @@ if test "x$found_cfmakeraw" = xyes; then fi AM_CONDITIONAL(NO_CFMAKERAW, [test "x$found_cfmakeraw" = xno]) +# Look for openat, compat/openat.c used if missing. +AC_CHECK_FUNC(openat, found_openat=yes, found_openat=no) +if test "x$found_openat" = xyes; then + AC_DEFINE(HAVE_OPENAT) +fi +AM_CONDITIONAL(NO_OPENAT, [test "x$found_openat" = xno]) + # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) diff --git a/job.c b/job.c index b2c2251c..d7bd852b 100644 --- a/job.c +++ b/job.c @@ -144,7 +144,7 @@ job_write_callback(unused struct bufferevent *bufev, void *data) size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, - (long) job->pid, len); + (long) job->pid, len); if (len == 0) { shutdown(job->fd, SHUT_WR); From 4c9f41f1adbdf2e9c5fa2def959ac13ea4a9785c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:45:28 +0000 Subject: [PATCH 132/949] Pass flags into cmdq_guard as an argument since sometimes cmdq->cmd can be NULL. Avoids crash when a command in a command client can't be parsed. --- cmd-queue.c | 16 ++++++++-------- control.c | 4 ++-- tmux.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 904b092c..f3506a34 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -154,18 +154,15 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) /* Print a guard line. */ int -cmdq_guard(struct cmd_q *cmdq, const char *guard) +cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) { struct client *c = cmdq->client; - int flags; if (c == NULL) return 0; if (!(c->flags & CLIENT_CONTROL)) return 0; - flags = !!(cmdq->cmd->flags & CMD_CONTROL); - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long) cmdq->time, cmdq->number, flags); server_push_stdout(c); @@ -202,7 +199,7 @@ cmdq_continue(struct cmd_q *cmdq) { struct cmd_q_item *next; enum cmd_retval retval; - int empty, guard; + int empty, guard, flags; char s[1024]; notify_disable(); @@ -228,13 +225,16 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->time = time(NULL); cmdq->number++; - guard = cmdq_guard(cmdq, "begin"); + flags = !!(cmdq->cmd->flags & CMD_CONTROL); + guard = cmdq_guard(cmdq, "begin", flags); + retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); + if (guard) { if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error"); + cmdq_guard(cmdq, "error", flags); else - cmdq_guard(cmdq, "end"); + cmdq_guard(cmdq, "end", flags); } if (retval == CMD_RETURN_ERROR) diff --git a/control.c b/control.c index 1f3739fc..8e24fd0e 100644 --- a/control.c +++ b/control.c @@ -73,9 +73,9 @@ control_callback(struct client *c, int closed, unused void *data) c->cmdq->time = time(NULL); c->cmdq->number++; - cmdq_guard(c->cmdq, "begin"); + cmdq_guard(c->cmdq, "begin", 1); control_write(c, "parse error: %s", cause); - cmdq_guard(c->cmdq, "error"); + cmdq_guard(c->cmdq, "error", 1); free(cause); } else { diff --git a/tmux.h b/tmux.h index b5608da3..21be2bd6 100644 --- a/tmux.h +++ b/tmux.h @@ -1871,7 +1871,7 @@ int cmdq_free(struct cmd_q *); void printflike2 cmdq_print(struct cmd_q *, const char *, ...); void printflike2 cmdq_info(struct cmd_q *, const char *, ...); void printflike2 cmdq_error(struct cmd_q *, const char *, ...); -int cmdq_guard(struct cmd_q *, const char *); +int cmdq_guard(struct cmd_q *, const char *, int); void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); From 0538676aa38914097fcf1a8eb292eac8852434aa Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:46:00 +0000 Subject: [PATCH 133/949] Make recalculate_sizes() handle an empty window with no active pane. This can happen when a window is in two sessions - it isn't destroyed immediately when the pane goes away but is left until the last session is destroyed. Fixes problems with grouped sessions reported by Daniel Ralston. --- resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resize.c b/resize.c index b5196c1c..ff3ec6c5 100644 --- a/resize.c +++ b/resize.c @@ -92,7 +92,7 @@ recalculate_sizes(void) for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); - if (w == NULL) + if (w == NULL || w->active == NULL) continue; flag = options_get_number(&w->options, "aggressive-resize"); From d3830e622f94f0826e2169eca1f920abc234a412 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:46:28 +0000 Subject: [PATCH 134/949] Grouped sessions were being leaked on destroy, correctly free them. --- server-fn.c | 7 ++++--- session.c | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server-fn.c b/server-fn.c index efb95acd..fb0eadd6 100644 --- a/server-fn.c +++ b/server-fn.c @@ -398,14 +398,15 @@ void server_destroy_session_group(struct session *s) { struct session_group *sg; + struct session *s1; if ((sg = session_group_find(s)) == NULL) server_destroy_session(s); else { - TAILQ_FOREACH(s, &sg->sessions, gentry) + TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { server_destroy_session(s); - TAILQ_REMOVE(&session_groups, sg, entry); - free(sg); + session_destroy(s); + } } } diff --git a/session.c b/session.c index c7b54a2b..7e144886 100644 --- a/session.c +++ b/session.c @@ -151,6 +151,7 @@ void session_destroy(struct session *s) { struct winlink *wl; + log_debug("session %s destroyed", s->name); RB_REMOVE(sessions, &sessions, s); From 6e665708fc13836dff9a683ccddb2ed194951767 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:46:47 +0000 Subject: [PATCH 135/949] Missing space in refresh-client synopsis. --- cmd-refresh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index eff692dd..e95e1ea3 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -29,7 +29,7 @@ enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { "refresh-client", "refresh", "C:St:", 0, 0, - "[-S] [-C size]" CMD_TARGET_CLIENT_USAGE, + "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, From d75dd2ab1c792ea06c2777a31cb83cb3ba4c47a7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:47:52 +0000 Subject: [PATCH 136/949] Add formats for window flags. --- format.c | 24 +++++++++++++++++++++--- tmux.1 | 4 ++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/format.c b/format.c index 168ff5b9..e870cd0a 100644 --- a/format.c +++ b/format.c @@ -151,6 +151,7 @@ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) { struct format_entry *fe; + struct format_entry *fe_now; va_list ap; fe = xmalloc(sizeof *fe); @@ -160,7 +161,13 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) xvasprintf(&fe->value, fmt, ap); va_end(ap); - RB_INSERT(format_tree, ft, fe); + fe_now = RB_INSERT(format_tree, ft, fe); + if (fe_now != NULL) { + free(fe_now->value); + fe_now->value = fe->value; + free(fe->key); + free(fe); + } } /* Find a format entry. */ @@ -346,8 +353,10 @@ format_client(struct format_tree *ft, struct client *c) format_add(ft, "client_cwd", "%s", c->cwd); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); - format_add(ft, "client_tty", "%s", c->tty.path); - format_add(ft, "client_termname", "%s", c->tty.termname); + if (c->tty.path != NULL) + format_add(ft, "client_tty", "%s", c->tty.path); + if (c->tty.termname != NULL) + format_add(ft, "client_termname", "%s", c->tty.termname); t = c->creation_time.tv_sec; format_add(ft, "client_created", "%lld", (long long) t); @@ -401,6 +410,15 @@ format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) format_add(ft, "window_active", "%d", wl == s->curw); format_add(ft, "window_panes", "%u", window_count_panes(w)); + format_add(ft, "window_bell_flag", "%u", + !!(wl->flags & WINLINK_BELL)); + format_add(ft, "window_content_flag", "%u", + !!(wl->flags & WINLINK_CONTENT)); + format_add(ft, "window_activity_flag", "%u", + !!(wl->flags & WINLINK_ACTIVITY)); + format_add(ft, "window_silence_flag", "%u", + !!(wl->flags & WINLINK_SILENCE)); + free(flags); free(layout); } diff --git a/tmux.1 b/tmux.1 index 61158872..fe2b1765 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3094,6 +3094,9 @@ The following variables are available, where appropriate: .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" +.It Li "window_bell_flag" Ta "" Ta "1 if window has bell" +.It Li "window_content_flag" Ta "" Ta "1 if window has content alert" .It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" .It Li "window_flags" Ta "#F" Ta "Window flags" .It Li "window_height" Ta "" Ta "Height of window" @@ -3102,6 +3105,7 @@ The following variables are available, where appropriate: .It Li "window_layout" Ta "" Ta "Window layout description" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_width" Ta "" Ta "Width of window" .It Li "wrap_flag" Ta "" Ta "Pane wrap flag" .El From 47a4a9992c59199bb55188c666eddce7895d884e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:49:07 +0000 Subject: [PATCH 137/949] Allow the file descriptor received from the client to be -1. --- server-client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server-client.c b/server-client.c index 5f61f5c0..44119237 100644 --- a/server-client.c +++ b/server-client.c @@ -825,8 +825,6 @@ server_client_msg_dispatch(struct client *c) case MSG_IDENTIFY: if (datalen != sizeof identifydata) fatalx("bad MSG_IDENTIFY size"); - if (imsg.fd == -1) - fatalx("MSG_IDENTIFY missing fd"); memcpy(&identifydata, imsg.data, sizeof identifydata); server_client_msg_identify(c, &identifydata, imsg.fd); @@ -972,6 +970,8 @@ server_client_msg_identify( return; } + if (fd == -1) + return; if (!isatty(fd)) { close(fd); return; From 7839993fe734aec38355401ce5a02d85f9dab5ba Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:49:29 +0000 Subject: [PATCH 138/949] Only include actual trailing spaces not unused cells with capturep -J, from George Nachman. --- grid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 529ce154..b2ee0c69 100644 --- a/grid.c +++ b/grid.c @@ -592,6 +592,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, char *buf, code[128]; size_t len, off, size, codelen; u_int xx; + const struct grid_line *gl; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); @@ -604,8 +605,11 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, buf = xmalloc(len); off = 0; + gl = grid_peek_line(gd, py); for (xx = px; xx < px + nx; xx++) { - gc = grid_peek_cell(gd, xx, py); + if (gl == NULL || xx >= gl->cellsize) + break; + gc = &gl->celldata[xx]; if (gc->flags & GRID_FLAG_PADDING) continue; grid_cell_get(gc, &ud); From 2756d127507cb939582adf2bbaf7be4cf5b711cd Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:49:42 +0000 Subject: [PATCH 139/949] Handle input mouse positions <33 (we already can generate them). --- tty-keys.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 26edbf32..3b652db8 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -676,11 +676,17 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) log_debug("mouse input: %.*s", (int) *size, buf); /* Check and return the mouse input. */ - if (b < 32 || x < 33 || y < 33) + if (b < 32) return (-1); b -= 32; - x -= 33; - y -= 33; + if (x >= 33) + x -= 33; + else + x = 256 - x; + if (y >= 33) + y -= 33; + else + y = 256 - y; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; From 40811eb8d45c1b6d93da2ebae6c4bc821d2b9836 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:50:20 +0000 Subject: [PATCH 140/949] Add length limit operator for formats. --- format.c | 40 ++++++++++++++++++++++++++++++++++------ options-table.c | 2 +- tmux.1 | 6 ++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/format.c b/format.c index e870cd0a..72e42ba0 100644 --- a/format.c +++ b/format.c @@ -18,6 +18,8 @@ #include +#include +#include #include #include #include @@ -188,18 +190,40 @@ format_find(struct format_tree *ft, const char *key) * #{?blah,a,b} is replace with a if blah exists and is nonzero else b. */ int -format_replace(struct format_tree *ft, - const char *key, size_t keylen, char **buf, size_t *len, size_t *off) +format_replace(struct format_tree *ft, const char *key, size_t keylen, + char **buf, size_t *len, size_t *off) { - char *copy, *ptr; + char *copy, *copy0, *endptr, *ptr; const char *value; size_t valuelen; + u_long limit = ULONG_MAX; /* Make a copy of the key. */ - copy = xmalloc(keylen + 1); + copy0 = copy = xmalloc(keylen + 1); memcpy(copy, key, keylen); copy[keylen] = '\0'; + /* Is there a length limit or whatnot? */ + if (!islower((u_char) *copy) && *copy != '?') { + while (*copy != ':' && *copy != '\0') { + switch (*copy) { + case '=': + errno = 0; + limit = strtoul(copy + 1, &endptr, 10); + if (errno == ERANGE && limit == ULONG_MAX) + goto fail; + copy = endptr; + break; + default: + copy++; + break; + } + } + if (*copy != ':') + goto fail; + copy++; + } + /* * Is this a conditional? If so, check it exists and extract either the * first or second element. If not, look up the key directly. @@ -230,6 +254,10 @@ format_replace(struct format_tree *ft, } valuelen = strlen(value); + /* Truncate the value if needed. */ + if (valuelen > limit) + valuelen = limit; + /* Expand the buffer and copy in the value. */ while (*len - *off < valuelen + 1) { *buf = xrealloc(*buf, 2, *len); @@ -238,11 +266,11 @@ format_replace(struct format_tree *ft, memcpy(*buf + *off, value, valuelen); *off += valuelen; - free(copy); + free(copy0); return (0); fail: - free(copy); + free(copy0); return (-1); } diff --git a/options-table.c b/options-table.c index 138a307e..a62d38b2 100644 --- a/options-table.c +++ b/options-table.c @@ -387,7 +387,7 @@ const struct options_table_entry session_options_table[] = { { .name = "status-right", .type = OPTIONS_TABLE_STRING, - .default_str = "\"#22T\" %H:%M %d-%b-%y" + .default_str = "\"#{=22:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", diff --git a/tmux.1 b/tmux.1 index fe2b1765..1f886cf7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3024,6 +3024,12 @@ will include the string if the session is attached and the string .Ql not attached if it is unattached. +A limit may be placed on the length of the resultant string by prefixing it +by an +.Ql = , +a number and a colon, so +.Ql #{=10:pane_title} +will include at most the first 10 characters of the pane title. .Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" From 2bf2f5d58ee060068e915873a191a1d0d6e5c18f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:50:36 +0000 Subject: [PATCH 141/949] Allow nested format expansion. --- format.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/format.c b/format.c index 72e42ba0..1b460855 100644 --- a/format.c +++ b/format.c @@ -193,7 +193,7 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr; + char *copy, *copy0, *endptr, *ptr, *saved; const char *value; size_t valuelen; u_long limit = ULONG_MAX; @@ -247,10 +247,13 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; value = ptr + 1; } + saved = format_expand(ft, value); + value = saved; } else { value = format_find(ft, copy); if (value == NULL) value = ""; + saved = NULL; } valuelen = strlen(value); @@ -266,6 +269,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, memcpy(*buf + *off, value, valuelen); *off += valuelen; + free(saved); free(copy0); return (0); @@ -278,10 +282,10 @@ fail: char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *ptr; - const char *s; + char *buf; + const char *ptr, *s; size_t off, len, n; - int ch; + int ch, brackets; len = 64; buf = xmalloc(len); @@ -299,11 +303,16 @@ format_expand(struct format_tree *ft, const char *fmt) fmt++; ch = (u_char) *fmt++; - switch (ch) { case '{': - ptr = strchr(fmt, '}'); - if (ptr == NULL) + brackets = 1; + for (ptr = fmt; *ptr != '\0'; ptr++) { + if (*ptr == '{') + brackets++; + if (*ptr == '}' && --brackets == 0) + break; + } + if (*ptr != '}' || brackets != 0) break; n = ptr - fmt; From fd1750af490f837405eb688ed82b3f8b18d29e26 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:56:50 +0000 Subject: [PATCH 142/949] Add automatic-rename-format option allowing automatic rename to use something other than pane_current_command. --- names.c | 58 ++++++++++++++++++++----------------------------- options-table.c | 5 +++++ 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/names.c b/names.c index 76dec82c..cfdaff83 100644 --- a/names.c +++ b/names.c @@ -22,12 +22,10 @@ #include #include #include -#include #include "tmux.h" void window_name_callback(unused int, unused short, void *); -char *parse_window_name(const char *); void queue_window_name(struct window *w) @@ -47,7 +45,7 @@ void window_name_callback(unused int fd, unused short events, void *data) { struct window *w = data; - char *name, *wname; + char *name; if (w->active == NULL) return; @@ -59,49 +57,39 @@ window_name_callback(unused int fd, unused short events, void *data) } queue_window_name(w); - if (w->active->screen != &w->active->base) - name = NULL; - else - name = get_proc_name(w->active->fd, w->active->tty); - if (name == NULL) - wname = default_window_name(w); - else { - /* - * If tmux is using the default command, it will be a login - * shell and argv[0] may have a - prefix. Remove this if it is - * present. Ick. - */ - if (w->active->cmd != NULL && *w->active->cmd == '\0' && - name != NULL && name[0] == '-' && name[1] != '\0') - wname = parse_window_name(name + 1); - else - wname = parse_window_name(name); - free(name); - } - - if (w->active->fd == -1) { - xasprintf(&name, "%s[dead]", wname); - free(wname); - wname = name; - } - - if (strcmp(wname, w->name)) { - window_set_name(w, wname); + name = format_window_name(w); + if (strcmp(name, w->name) != 0) { + window_set_name(w, name); server_status_window(w); } - free(wname); + free(name); } char * default_window_name(struct window *w) { - if (w->active->screen != &w->active->base) - return (xstrdup("[tmux]")); if (w->active->cmd != NULL && *w->active->cmd != '\0') return (parse_window_name(w->active->cmd)); return (parse_window_name(w->active->shell)); } +char * +format_window_name(struct window *w) +{ + struct format_tree *ft; + char *fmt, *name; + + ft = format_create(); + format_window(ft, w); + format_window_pane(ft, w->active); + + fmt = options_get_string(&w->options, "automatic-rename-format"); + name = format_expand(ft, fmt); + + format_free(ft); + return (name); +} + char * parse_window_name(const char *in) { @@ -111,7 +99,7 @@ parse_window_name(const char *in) if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; - while (*name == ' ') + while (*name == ' ' || *name == '-') name++; if ((ptr = strchr(name, ' ')) != NULL) *ptr = '\0'; diff --git a/options-table.c b/options-table.c index a62d38b2..22913854 100644 --- a/options-table.c +++ b/options-table.c @@ -482,6 +482,11 @@ const struct options_table_entry window_options_table[] = { .default_num = 1 }, + { .name = "automatic-rename-format", + .type = OPTIONS_TABLE_STRING, + .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}#{?pane_dead,[dead],}" + }, + { .name = "c0-change-trigger", .type = OPTIONS_TABLE_NUMBER, .default_num = 250, From 81a548bcc4bf4e85935ce8ee65217b41d094d990 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:57:14 +0000 Subject: [PATCH 143/949] Accept multiple parameters to SM/RM/DECSET/DECRST, based on a diff from Hayaki Saito. --- input.c | 151 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/input.c b/input.c index 5f70f872..e27250ed 100644 --- a/input.c +++ b/input.c @@ -70,6 +70,10 @@ int input_input(struct input_ctx *); int input_c0_dispatch(struct input_ctx *); int input_esc_dispatch(struct input_ctx *); int input_csi_dispatch(struct input_ctx *); +void input_csi_dispatch_rm(struct input_ctx *); +void input_csi_dispatch_rm_private(struct input_ctx *); +void input_csi_dispatch_sm(struct input_ctx *); +void input_csi_dispatch_sm_private(struct input_ctx *); void input_csi_dispatch_sgr(struct input_ctx *); int input_dcs_dispatch(struct input_ctx *); int input_utf8_open(struct input_ctx *); @@ -1071,7 +1075,6 @@ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; @@ -1230,7 +1233,60 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_CSI_RM: - switch (input_get(ictx, 0, 0, -1)) { + input_csi_dispatch_rm(ictx); + break; + case INPUT_CSI_RM_PRIVATE: + input_csi_dispatch_rm_private(ictx); + break; + case INPUT_CSI_SCP: + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = s->cx; + ictx->old_cy = s->cy; + break; + case INPUT_CSI_SGR: + input_csi_dispatch_sgr(ictx); + break; + case INPUT_CSI_SM: + input_csi_dispatch_sm(ictx); + break; + case INPUT_CSI_SM_PRIVATE: + input_csi_dispatch_sm_private(ictx); + break; + case INPUT_CSI_TBC: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + if (s->cx < screen_size_x(s)) + bit_clear(s->tabs, s->cx); + break; + case 3: + bit_nclear(s->tabs, 0, screen_size_x(s) - 1); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_VPA: + n = input_get(ictx, 0, 1, 1); + screen_write_cursormove(sctx, s->cx, n - 1); + break; + case INPUT_CSI_DECSCUSR: + n = input_get(ictx, 0, 0, 0); + screen_set_cursor_style(s, n); + break; + } + + return (0); +} + +/* Handle CSI RM. */ +void +input_csi_dispatch_rm(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { case 4: /* IRM */ screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; @@ -1238,10 +1294,18 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_RM_PRIVATE: - switch (input_get(ictx, 0, 0, -1)) { - case 1: /* GATM */ + } +} + +/* Handle CSI private RM. */ +void +input_csi_dispatch_rm_private(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 1: /* DECCKM */ screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ @@ -1271,10 +1335,10 @@ input_csi_dispatch(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_off(wp, &ictx->cell, 0); + window_pane_alternate_off(ictx->wp, &ictx->cell, 0); break; case 1049: - window_pane_alternate_off(wp, &ictx->cell, 1); + window_pane_alternate_off(ictx->wp, &ictx->cell, 1); break; case 2004: screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); @@ -1283,17 +1347,17 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_SCP: - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = s->cx; - ictx->old_cy = s->cy; - break; - case INPUT_CSI_SGR: - input_csi_dispatch_sgr(ictx); - break; - case INPUT_CSI_SM: - switch (input_get(ictx, 0, 0, -1)) { + } +} + +/* Handle CSI SM. */ +void +input_csi_dispatch_sm(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { case 4: /* IRM */ screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; @@ -1301,10 +1365,18 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_SM_PRIVATE: - switch (input_get(ictx, 0, 0, -1)) { - case 1: /* GATM */ + } +} + +/* Handle CSI private SM. */ +void +input_csi_dispatch_sm_private(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 1: /* DECCKM */ screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ @@ -1330,10 +1402,10 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY); break; case 1004: - if (s->mode & MODE_FOCUSON) + if (ictx->ctx.s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - wp->flags |= PANE_FOCUSPUSH; /* force update */ + ictx->wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); @@ -1343,10 +1415,10 @@ input_csi_dispatch(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_on(wp, &ictx->cell, 0); + window_pane_alternate_on(ictx->wp, &ictx->cell, 0); break; case 1049: - window_pane_alternate_on(wp, &ictx->cell, 1); + window_pane_alternate_on(ictx->wp, &ictx->cell, 1); break; case 2004: screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); @@ -1355,32 +1427,7 @@ input_csi_dispatch(struct input_ctx *ictx) log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - break; - case INPUT_CSI_TBC: - switch (input_get(ictx, 0, 0, 0)) { - case 0: - if (s->cx < screen_size_x(s)) - bit_clear(s->tabs, s->cx); - break; - case 3: - bit_nclear(s->tabs, 0, screen_size_x(s) - 1); - break; - default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); - break; - } - break; - case INPUT_CSI_VPA: - n = input_get(ictx, 0, 1, 1); - screen_write_cursormove(sctx, s->cx, n - 1); - break; - case INPUT_CSI_DECSCUSR: - n = input_get(ictx, 0, 0, 0); - screen_set_cursor_style(s, n); - break; } - - return (0); } /* Handle CSI SGR. */ From 784b711393f99523482515d7e6d0114f96f2ecec Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:57:52 +0000 Subject: [PATCH 144/949] Assign mouse x/y coords before checking them. When receiving mouse inputs, we should set the x/y coordinates earlier than we currently do, so that we aren't off-by-one in the case when the statusbar is at the top of the screen. By Thomas Adam. --- tty-keys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 3b652db8..7de5ce59 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -746,6 +746,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->sgr = sgr; m->sgr_xb = sgr_b; m->sgr_rel = sgr_rel; + m->x = x; + m->y = y; if (b & 64) { /* wheel button */ b &= 3; if (b == 0) @@ -773,8 +775,6 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) } m->button = (b & 3); } - m->x = x; - m->y = y; return (0); } From 34674bb180176662d51b774cfcd9f335f329f235 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:58:24 +0000 Subject: [PATCH 145/949] Renumber windows: Lookup lastw via window not index When calling 'movew -r' on a session to reorder the winlinks, ensure when adding back in the information for the lastw stack that we look up the winlink based on the window and not its index. Using the index doesn't make sense here because when comparing it to the old set, it will never match since the winlink has been renumbered. Bug reported by Ben Boeckel. Patch by Thomas Adam. --- session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.c b/session.c index 7e144886..f967de19 100644 --- a/session.c +++ b/session.c @@ -616,7 +616,7 @@ session_renumber_windows(struct session *s) memcpy(&old_lastw, &s->lastw, sizeof old_lastw); TAILQ_INIT(&s->lastw); TAILQ_FOREACH(wl, &old_lastw, sentry) { - wl_new = winlink_find_by_index(&s->windows, wl->idx); + wl_new = winlink_find_by_window(&s->windows, wl->window); if (wl_new != NULL) TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry); } From e6af0ad23e1a43add71dd09a03c05057aab26748 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:58:52 +0000 Subject: [PATCH 146/949] choose-tree: Reset top when toggling items When choose-tree is told to expand/collapse items (especially when first rendering collapsed to just show sessions), ensure that in addition to setting the selected item, that the item itself appears on the bottom of the screen, rather than off screen. This was causing rendering glitches when a very small tmux window tried to render a list of items in choose-tree much larger than itself, and the selected item appeared off screen, and didn't show the selection until the selection had wrapped around to the top of the screen. --- window-choose.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/window-choose.c b/window-choose.c index 38773605..70c20085 100644 --- a/window-choose.c +++ b/window-choose.c @@ -81,6 +81,7 @@ int window_choose_key_index(struct window_choose_mode_data *, u_int); int window_choose_index_key(struct window_choose_mode_data *, int); void window_choose_prompt_input(enum window_choose_input_type, const char *, struct window_pane *, int); +void window_choose_reset_top(struct window_pane *, u_int); void window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) @@ -107,8 +108,17 @@ window_choose_set_current(struct window_pane *wp, u_int cur) struct screen *s = &data->screen; data->selected = cur; - if (data->selected > screen_size_y(s) - 1) - data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); + window_choose_reset_top(wp, screen_size_y(s)); +} + +void +window_choose_reset_top(struct window_pane *wp, u_int sy) +{ + struct window_choose_mode_data *data = wp->modedata; + + data->top = 0; + if (data->selected > sy - 1) + data->top = data->selected - (sy - 1); window_choose_redraw_screen(wp); } @@ -277,10 +287,7 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; - data->top = 0; - if (data->selected > sy - 1) - data->top = data->selected - (sy - 1); - + window_choose_reset_top(wp, sy); screen_resize(s, sx, sy, 0); window_choose_redraw_screen(wp); } @@ -373,6 +380,7 @@ window_choose_collapse_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; + struct screen *scr = &data->screen; struct session *s, *chosen; u_int i; @@ -391,7 +399,7 @@ window_choose_collapse_all(struct window_pane *wp) if (item->wcd->type & TREE_SESSION) data->selected = i; } - window_choose_redraw_screen(wp); + window_choose_reset_top(wp, screen_size_y(scr)); } void @@ -399,6 +407,7 @@ window_choose_expand_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; + struct screen *scr = &data->screen; struct session *s; u_int i; @@ -414,7 +423,7 @@ window_choose_expand_all(struct window_pane *wp) } } - window_choose_redraw_screen(wp); + window_choose_reset_top(wp, screen_size_y(scr)); } void From 90ae7682ed06557bd4d8deac9d9e48ecc7b38a07 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 11:59:23 +0000 Subject: [PATCH 147/949] Clear window->flags when clearing winlinks When clearing WINLINK_ALERTFLAGS for all sessions, we must also, for that window, clear the window->flags as well, otherwise sessions may well still see flags for winlinks long since cleared. This therefore introduces WINDOW_ALERTFLAGS to help with this. --- window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/window.c b/window.c index 3b6b74bd..0ea70688 100644 --- a/window.c +++ b/window.c @@ -1242,6 +1242,7 @@ winlink_clear_flags(struct winlink *wl) continue; wm->flags &= ~WINLINK_ALERTFLAGS; + wm->window->flags &= ~WINDOW_ALERTFLAGS; server_status_session(s); } } From d45c12b6c9405da549197c2852ac124fc0d5b340 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:00:18 +0000 Subject: [PATCH 148/949] Remove the barely-used and unnecessary command check() function. --- cmd-bind-key.c | 27 ++++++++++++--------------- cmd-break-pane.c | 1 - cmd-capture-pane.c | 1 - cmd-choose-buffer.c | 1 - cmd-choose-client.c | 1 - cmd-choose-list.c | 1 - cmd-choose-tree.c | 4 ---- cmd-clear-history.c | 1 - cmd-clock-mode.c | 1 - cmd-command-prompt.c | 2 -- cmd-confirm-before.c | 1 - cmd-copy-mode.c | 1 - cmd-delete-buffer.c | 1 - cmd-detach-client.c | 1 - cmd-display-panes.c | 1 - cmd-find-window.c | 1 - cmd-has-session.c | 1 - cmd-join-pane.c | 2 -- cmd-kill-pane.c | 1 - cmd-kill-server.c | 1 - cmd-kill-session.c | 1 - cmd-kill-window.c | 1 - cmd-link-window.c | 1 - cmd-list-buffers.c | 1 - cmd-list-clients.c | 1 - cmd-list-commands.c | 1 - cmd-list-keys.c | 1 - cmd-list-panes.c | 1 - cmd-list-sessions.c | 1 - cmd-list-windows.c | 1 - cmd-load-buffer.c | 1 - cmd-lock-server.c | 3 --- cmd-move-window.c | 1 - cmd-paste-buffer.c | 1 - cmd-pipe-pane.c | 1 - cmd-refresh-client.c | 1 - cmd-rename-session.c | 1 - cmd-rename-window.c | 1 - cmd-resize-pane.c | 1 - cmd-respawn-pane.c | 1 - cmd-respawn-window.c | 1 - cmd-rotate-window.c | 1 - cmd-run-shell.c | 1 - cmd-save-buffer.c | 2 -- cmd-select-layout.c | 3 --- cmd-select-pane.c | 2 -- cmd-select-window.c | 4 ---- cmd-send-keys.c | 2 -- cmd-server-info.c | 1 - cmd-set-buffer.c | 1 - cmd-set-environment.c | 1 - cmd-set-option.c | 2 -- cmd-show-environment.c | 1 - cmd-show-messages.c | 1 - cmd-show-options.c | 2 -- cmd-start-server.c | 1 - cmd-suspend-client.c | 1 - cmd-swap-pane.c | 1 - cmd-swap-window.c | 1 - cmd-switch-client.c | 1 - cmd-unbind-key.c | 24 +++++++++++------------- cmd-unlink-window.c | 1 - 62 files changed, 23 insertions(+), 105 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index d9b65bec..1ca31484 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -27,7 +27,6 @@ * Bind a key to a command, this recurses through cmd_*. */ -enum cmd_retval cmd_bind_key_check(struct args *); enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); @@ -38,23 +37,9 @@ const struct cmd_entry cmd_bind_key_entry = { "[-cnr] [-t key-table] key command [arguments]", 0, NULL, - cmd_bind_key_check, cmd_bind_key_exec }; -enum cmd_retval -cmd_bind_key_check(struct args *args) -{ - if (args_has(args, 't')) { - if (args->argc != 2 && args->argc != 3) - return (CMD_RETURN_ERROR); - } else { - if (args->argc < 2) - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); -} - enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -63,6 +48,18 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_list *cmdlist; int key; + if (args_has(args, 't')) { + if (args->argc != 2 && args->argc != 3) { + cmdq_error(cmdq, "not enough arguments"); + return (CMD_RETURN_ERROR); + } + } else { + if (args->argc < 2) { + cmdq_error(cmdq, "not enough arguments"); + return (CMD_RETURN_ERROR); + } + } + key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index bac332a2..defd22ec 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_break_pane_entry = { "[-dP] [-F format] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_break_pane_exec }; diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 779cbe08..cf9a2f49 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -42,7 +42,6 @@ const struct cmd_entry cmd_capture_pane_entry = { CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_capture_pane_exec }; diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 8713815d..359de068 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_choose_buffer_entry = { CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, - NULL, cmd_choose_buffer_exec }; diff --git a/cmd-choose-client.c b/cmd-choose-client.c index df57f9cf..47ff1976 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -37,7 +37,6 @@ const struct cmd_entry cmd_choose_client_entry = { CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, - NULL, cmd_choose_client_exec }; diff --git a/cmd-choose-list.c b/cmd-choose-list.c index 15f87294..c3caabba 100644 --- a/cmd-choose-list.c +++ b/cmd-choose-list.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_choose_list_entry = { "[-l items] " CMD_TARGET_WINDOW_USAGE "[template]", 0, NULL, - NULL, cmd_choose_list_exec }; diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 601d24f1..257908e1 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_choose_tree_entry = { "[-W format] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_choose_tree_exec }; @@ -51,7 +50,6 @@ const struct cmd_entry cmd_choose_session_entry = { CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, - NULL, cmd_choose_tree_exec }; @@ -61,7 +59,6 @@ const struct cmd_entry cmd_choose_window_entry = { CMD_TARGET_WINDOW_USAGE "[-F format] [template]", 0, NULL, - NULL, cmd_choose_tree_exec }; @@ -228,7 +225,6 @@ windows_only: free(final_win_template_last); window_choose_ready(wl->window->active, cur_win, NULL); - window_choose_collapse_all(wl->window->active); if (args_has(args, 'u')) { window_choose_expand_all(wl->window->active); diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 768ba86d..69885f08 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_clear_history_entry = { CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_clear_history_exec }; diff --git a/cmd-clock-mode.c b/cmd-clock-mode.c index b1837004..09f16e17 100644 --- a/cmd-clock-mode.c +++ b/cmd-clock-mode.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_clock_mode_entry = { CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_clock_mode_exec }; diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 3b773316..fc625f53 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -30,7 +30,6 @@ */ void cmd_command_prompt_key_binding(struct cmd *, int); -int cmd_command_prompt_check(struct args *); enum cmd_retval cmd_command_prompt_exec(struct cmd *, struct cmd_q *); int cmd_command_prompt_callback(void *, const char *); @@ -42,7 +41,6 @@ const struct cmd_entry cmd_command_prompt_entry = { "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", 0, cmd_command_prompt_key_binding, - NULL, cmd_command_prompt_exec }; diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 6282be2e..5b8151c9 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -38,7 +38,6 @@ const struct cmd_entry cmd_confirm_before_entry = { "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", 0, cmd_confirm_before_key_binding, - NULL, cmd_confirm_before_exec }; diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 883a9376..bc9cfd62 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_copy_mode_entry = { "[-u] " CMD_TARGET_PANE_USAGE, 0, cmd_copy_mode_key_binding, - NULL, cmd_copy_mode_exec }; diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c index 6e425b57..32fb243b 100644 --- a/cmd-delete-buffer.c +++ b/cmd-delete-buffer.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_delete_buffer_entry = { CMD_BUFFER_USAGE, 0, NULL, - NULL, cmd_delete_buffer_exec }; diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 17b437ab..fc80499c 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_detach_client_entry = { "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, CMD_READONLY, NULL, - NULL, cmd_detach_client_exec }; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index a97a1809..9160f4e7 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_display_panes_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_display_panes_exec }; diff --git a/cmd-find-window.c b/cmd-find-window.c index f757d10f..45dbd571 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -48,7 +48,6 @@ const struct cmd_entry cmd_find_window_entry = { "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", 0, NULL, - NULL, cmd_find_window_exec }; diff --git a/cmd-has-session.c b/cmd-has-session.c index 28e3aea3..d7ef9be6 100644 --- a/cmd-has-session.c +++ b/cmd-has-session.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_has_session_entry = { CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_has_session_exec }; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index cf17e7d9..b70f93dc 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -40,7 +40,6 @@ const struct cmd_entry cmd_join_pane_entry = { "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", 0, cmd_join_pane_key_binding, - NULL, cmd_join_pane_exec }; @@ -50,7 +49,6 @@ const struct cmd_entry cmd_move_pane_entry = { "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", 0, NULL, - NULL, cmd_join_pane_exec }; diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index ba3bfd20..64fd11e4 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_kill_pane_entry = { "[-a] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_kill_pane_exec }; diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 808dca59..ef4a3946 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_kill_server_entry = { "", 0, NULL, - NULL, cmd_kill_server_exec }; diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 095fb9bb..097189ec 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_kill_session_entry = { "[-a] " CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_kill_session_exec }; diff --git a/cmd-kill-window.c b/cmd-kill-window.c index dcb1fd28..2f924260 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_kill_window_entry = { "[-a] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_kill_window_exec }; diff --git a/cmd-link-window.c b/cmd-link-window.c index c7dfa5aa..8bd63b7c 100644 --- a/cmd-link-window.c +++ b/cmd-link-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_link_window_entry = { "[-dk] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, - NULL, cmd_link_window_exec }; diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 54284d55..e36a7cd0 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_list_buffers_entry = { "[-F format]", 0, NULL, - NULL, cmd_list_buffers_exec }; diff --git a/cmd-list-clients.c b/cmd-list-clients.c index e0f8558e..98c564ad 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_list_clients_entry = { "[-F format] " CMD_TARGET_SESSION_USAGE, CMD_READONLY, NULL, - NULL, cmd_list_clients_exec }; diff --git a/cmd-list-commands.c b/cmd-list-commands.c index 7073d5f8..287df428 100644 --- a/cmd-list-commands.c +++ b/cmd-list-commands.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_list_commands_entry = { "", 0, NULL, - NULL, cmd_list_commands_exec }; diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 1f568909..65e4469e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_list_keys_entry = { "[-t key-table]", 0, NULL, - NULL, cmd_list_keys_exec }; diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 910c19bc..07884ff2 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_list_panes_entry = { "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_list_panes_exec }; diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 14ac4808..d401608f 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_list_sessions_entry = { "[-F format]", 0, NULL, - NULL, cmd_list_sessions_exec }; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index c709e471..bc56816f 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_list_windows_entry = { "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_list_windows_exec }; diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 698210d8..8c92ca32 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_load_buffer_entry = { CMD_BUFFER_USAGE " path", 0, NULL, - NULL, cmd_load_buffer_exec }; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 8491c8b1..97c86c1e 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_lock_server_entry = { "", 0, NULL, - NULL, cmd_lock_server_exec }; @@ -46,7 +45,6 @@ const struct cmd_entry cmd_lock_session_entry = { CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_lock_server_exec }; @@ -56,7 +54,6 @@ const struct cmd_entry cmd_lock_client_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_lock_server_exec }; diff --git a/cmd-move-window.c b/cmd-move-window.c index 945e9daa..bb160e5c 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_move_window_entry = { "[-dkr] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, - NULL, cmd_move_window_exec }; diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 1cfc17c8..8f7530a2 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_paste_buffer_entry = { "[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_paste_buffer_exec }; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 5de675df..9b29e568 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -42,7 +42,6 @@ const struct cmd_entry cmd_pipe_pane_entry = { "[-o] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, - NULL, cmd_pipe_pane_exec }; diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index e95e1ea3..d3dae49d 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_refresh_client_entry = { "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_refresh_client_exec }; diff --git a/cmd-rename-session.c b/cmd-rename-session.c index c94b460b..ba8f9588 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_rename_session_entry = { CMD_TARGET_SESSION_USAGE " new-name", 0, NULL, - NULL, cmd_rename_session_exec }; diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 34b03f98..bdd3fbef 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_rename_window_entry = { CMD_TARGET_WINDOW_USAGE " new-name", 0, NULL, - NULL, cmd_rename_window_exec }; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 41c15269..e54c0760 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_resize_pane_entry = { "[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", 0, cmd_resize_pane_key_binding, - NULL, cmd_resize_pane_exec }; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 0aae0331..2315b241 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_respawn_pane_entry = { "[-k] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, - NULL, cmd_respawn_pane_exec }; diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index a446794b..e4ef6dfb 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_respawn_window_entry = { "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, - NULL, cmd_respawn_window_exec }; diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 7af592b3..6005ae5d 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_rotate_window_entry = { "[-DU] " CMD_TARGET_WINDOW_USAGE, 0, cmd_rotate_window_key_binding, - NULL, cmd_rotate_window_exec }; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index ef1dbdd4..d835e461 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_run_shell_entry = { "[-b] " CMD_TARGET_PANE_USAGE " shell-command", 0, NULL, - NULL, cmd_run_shell_exec }; diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 29f71837..57c3d65d 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -38,7 +38,6 @@ const struct cmd_entry cmd_save_buffer_entry = { "[-a] " CMD_BUFFER_USAGE " path", 0, NULL, - NULL, cmd_save_buffer_exec }; @@ -48,7 +47,6 @@ const struct cmd_entry cmd_show_buffer_entry = { CMD_BUFFER_USAGE, 0, NULL, - NULL, cmd_save_buffer_exec }; diff --git a/cmd-select-layout.c b/cmd-select-layout.c index aa73e500..26d8538e 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_select_layout_entry = { "[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]", 0, cmd_select_layout_key_binding, - NULL, cmd_select_layout_exec }; @@ -43,7 +42,6 @@ const struct cmd_entry cmd_next_layout_entry = { CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_select_layout_exec }; @@ -53,7 +51,6 @@ const struct cmd_entry cmd_previous_layout_entry = { CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_select_layout_exec }; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 77be368f..1a1072d8 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,7 +33,6 @@ const struct cmd_entry cmd_select_pane_entry = { "[-lDLRU] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_key_binding, - NULL, cmd_select_pane_exec }; @@ -43,7 +42,6 @@ const struct cmd_entry cmd_last_pane_entry = { CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_select_pane_exec }; diff --git a/cmd-select-window.c b/cmd-select-window.c index 6206ae4b..744bdf77 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_select_window_entry = { "[-lnpT] " CMD_TARGET_WINDOW_USAGE, 0, cmd_select_window_key_binding, - NULL, cmd_select_window_exec }; @@ -45,7 +44,6 @@ const struct cmd_entry cmd_next_window_entry = { "[-a] " CMD_TARGET_SESSION_USAGE, 0, cmd_select_window_key_binding, - NULL, cmd_select_window_exec }; @@ -55,7 +53,6 @@ const struct cmd_entry cmd_previous_window_entry = { "[-a] " CMD_TARGET_SESSION_USAGE, 0, cmd_select_window_key_binding, - NULL, cmd_select_window_exec }; @@ -65,7 +62,6 @@ const struct cmd_entry cmd_last_window_entry = { CMD_TARGET_SESSION_USAGE, 0, NULL, - NULL, cmd_select_window_exec }; diff --git a/cmd-send-keys.c b/cmd-send-keys.c index dcd5f288..ef61d2bb 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_send_keys_entry = { "[-lR] " CMD_TARGET_PANE_USAGE " key ...", 0, NULL, - NULL, cmd_send_keys_exec }; @@ -45,7 +44,6 @@ const struct cmd_entry cmd_send_prefix_entry = { "[-2] " CMD_TARGET_PANE_USAGE, 0, NULL, - NULL, cmd_send_keys_exec }; diff --git a/cmd-server-info.c b/cmd-server-info.c index 07b224a1..6cbabe2b 100644 --- a/cmd-server-info.c +++ b/cmd-server-info.c @@ -39,7 +39,6 @@ const struct cmd_entry cmd_server_info_entry = { "", 0, NULL, - NULL, cmd_server_info_exec }; diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 46450e21..f67f7a0c 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_set_buffer_entry = { CMD_BUFFER_USAGE " data", 0, NULL, - NULL, cmd_set_buffer_exec }; diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 6e75a294..7a446fc6 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_set_environment_entry = { "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", 0, NULL, - NULL, cmd_set_environment_exec }; diff --git a/cmd-set-option.c b/cmd-set-option.c index 3b822d8b..1b25fac0 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -67,7 +67,6 @@ const struct cmd_entry cmd_set_option_entry = { "[-agosquw] [-t target-session|target-window] option [value]", 0, NULL, - NULL, cmd_set_option_exec }; @@ -77,7 +76,6 @@ const struct cmd_entry cmd_set_window_option_entry = { "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", 0, NULL, - NULL, cmd_set_option_exec }; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index cb53e84c..4ba111b2 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_show_environment_entry = { "[-g] " CMD_TARGET_SESSION_USAGE " [name]", 0, NULL, - NULL, cmd_show_environment_exec }; diff --git a/cmd-show-messages.c b/cmd-show-messages.c index cbf083a7..f43607aa 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_show_messages_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_show_messages_exec }; diff --git a/cmd-show-options.c b/cmd-show-options.c index 57e49a33..529289ea 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -40,7 +40,6 @@ const struct cmd_entry cmd_show_options_entry = { "[-gqsvw] [-t target-session|target-window] [option]", 0, NULL, - NULL, cmd_show_options_exec }; @@ -50,7 +49,6 @@ const struct cmd_entry cmd_show_window_options_entry = { "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", 0, NULL, - NULL, cmd_show_options_exec }; diff --git a/cmd-start-server.c b/cmd-start-server.c index c926b5fe..d98f9506 100644 --- a/cmd-start-server.c +++ b/cmd-start-server.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_start_server_entry = { "", CMD_STARTSERVER, NULL, - NULL, cmd_start_server_exec }; diff --git a/cmd-suspend-client.c b/cmd-suspend-client.c index dda0f977..5d1e7fe2 100644 --- a/cmd-suspend-client.c +++ b/cmd-suspend-client.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_suspend_client_entry = { CMD_TARGET_CLIENT_USAGE, 0, NULL, - NULL, cmd_suspend_client_exec }; diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index e6b46d67..e8170bbb 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_swap_pane_entry = { "[-dDU] " CMD_SRCDST_PANE_USAGE, 0, cmd_swap_pane_key_binding, - NULL, cmd_swap_pane_exec }; diff --git a/cmd-swap-window.c b/cmd-swap-window.c index ed175480..00bf6d4d 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -34,7 +34,6 @@ const struct cmd_entry cmd_swap_window_entry = { "[-d] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, - NULL, cmd_swap_window_exec }; diff --git a/cmd-switch-client.c b/cmd-switch-client.c index ea5012ed..9db35365 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_switch_client_entry = { "[-lnpr] [-c target-client] [-t target-session]", CMD_READONLY, cmd_switch_client_key_binding, - NULL, cmd_switch_client_exec }; diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 88c81270..b89340da 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -26,7 +26,6 @@ * Unbind key from command. */ -enum cmd_retval cmd_unbind_key_check(struct args *); enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); @@ -36,20 +35,9 @@ const struct cmd_entry cmd_unbind_key_entry = { "[-acn] [-t key-table] key", 0, NULL, - cmd_unbind_key_check, cmd_unbind_key_exec }; -enum cmd_retval -cmd_unbind_key_check(struct args *args) -{ - if (args_has(args, 'a') && args->argc != 0) - return (CMD_RETURN_ERROR); - if (!args_has(args, 'a') && args->argc != 1) - return (CMD_RETURN_ERROR); - return (CMD_RETURN_NORMAL); -} - enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -58,13 +46,23 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) int key; if (!args_has(args, 'a')) { + if (args->argc != 1) { + cmdq_error(cmdq, "missing key"); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_ERROR); key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - } else + } else { + if (args->argc != 0) { + cmdq_error(cmdq, "key given with -a"); + return (CMD_RETURN_ERROR); + } key = KEYC_NONE; + } if (args_has(args, 't')) return (cmd_unbind_key_table(self, cmdq, key)); diff --git a/cmd-unlink-window.c b/cmd-unlink-window.c index 78c4b390..d4c77f2a 100644 --- a/cmd-unlink-window.c +++ b/cmd-unlink-window.c @@ -32,7 +32,6 @@ const struct cmd_entry cmd_unlink_window_entry = { "[-k] " CMD_TARGET_WINDOW_USAGE, 0, NULL, - NULL, cmd_unlink_window_exec }; From 1b7c2dd056f4e41f0cec38c1b8ed5531f7281e33 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:01:14 +0000 Subject: [PATCH 149/949] Trivial style and spacing nits. --- cmd-display-message.c | 7 +++---- grid.c | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 52d47a4c..b60d732a 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -36,7 +36,6 @@ const struct cmd_entry cmd_display_message_entry = { " [message]", 0, NULL, - NULL, cmd_display_message_exec }; @@ -71,9 +70,9 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 'c')) { - c = cmd_find_client(cmdq, args_get(args, 'c'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); + c = cmd_find_client(cmdq, args_get(args, 'c'), 0); + if (c == NULL) + return (CMD_RETURN_ERROR); } else { c = cmd_current_client(cmdq); if (c == NULL && !args_has(self->args, 'p')) { diff --git a/grid.c b/grid.c index b2ee0c69..d06e7154 100644 --- a/grid.c +++ b/grid.c @@ -268,8 +268,7 @@ grid_get_cell(struct grid *gd, u_int px, u_int py) /* Set cell at relative position. */ void -grid_set_cell( - struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { if (grid_check_y(gd, py) != 0) return; @@ -657,8 +656,8 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, * available. */ void -grid_duplicate_lines( - struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) +grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, + u_int ny) { struct grid_line *dstl, *srcl; u_int yy; From 1bd0851ee852b305c48863b7701af74a171633e9 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:02:55 +0000 Subject: [PATCH 150/949] Mark flags as optional and mutually exclusive. From Tiago Cunha. --- cmd-wait-for.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 3a8d8ea4..42c7f42d 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -33,10 +33,9 @@ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_wait_for_entry = { "wait-for", "wait", "LSU", 1, 1, - "[-LSU] channel", + "[-L|-S|-U] channel", 0, NULL, - NULL, cmd_wait_for_exec }; From e4dc1568ce49120962d4932ff8417c60778a5a33 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:03:22 +0000 Subject: [PATCH 151/949] Don't treat TMUX_TMPDIR as a potential file The point of setting TMUX_TMPDIR is to then make any labels from -L go to that directory. In the case of makesocketpath() with no TMUX_TMPDIR set, would set both the path and the default socket to a file. The checking of the permissions on the file worked fine in that case, but when TMUX_TMPDIR is set, won't work on a directory. This fixes the problem by ensuring the check on the permissions is performed on directories only. By Thomas Adam. --- tmux.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tmux.c b/tmux.c index 73843a96..901ded80 100644 --- a/tmux.c +++ b/tmux.c @@ -189,7 +189,8 @@ makesocketpath(const char *label) errno = ENOTDIR; return (NULL); } - if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { + if (sb.st_uid != uid || (!S_ISDIR(sb.st_mode) && + sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { errno = EACCES; return (NULL); } @@ -389,7 +390,8 @@ main(int argc, char **argv) /* -L or default set. */ if (label != NULL) { if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket\n"); + fprintf(stderr, "can't create socket: %s\n", + strerror(errno)); exit(1); } } From 1a49ebaa9f5e5b90ebb27eb5acd0ff5653e34a46 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:04:01 +0000 Subject: [PATCH 152/949] First period not last for host_short, from Michael Scholz. --- format.c | 55 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/format.c b/format.c index 1b460855..8f86eba3 100644 --- a/format.c +++ b/format.c @@ -34,9 +34,10 @@ * string. */ -int format_replace(struct format_tree *, const char *, size_t, char **, - size_t *, size_t *); -void format_window_pane_tabs(struct format_tree *, struct window_pane *); +int format_replace(struct format_tree *, const char *, size_t, char **, + size_t *, size_t *); +char *format_get_command(struct window_pane *); +void format_window_pane_tabs(struct format_tree *, struct window_pane *); /* Format key-value replacement entry. */ RB_GENERATE(format_tree, format_entry, entry, format_cmp); @@ -120,7 +121,7 @@ format_create(void) if (gethostname(host, sizeof host) == 0) { format_add(ft, "host", "%s", host); - if ((ptr = strrchr(host, '.')) != NULL) + if ((ptr = strchr(host, '.')) != NULL) *ptr = '\0'; format_add(ft, "host_short", "%s", host); } @@ -348,6 +349,21 @@ format_expand(struct format_tree *ft, const char *fmt) return (buf); } +/* Get command name for format. */ +char * +format_get_command(struct window_pane *wp) +{ + char *cmd; + + cmd = get_proc_name(wp->fd, wp->tty); + if (cmd == NULL || *cmd == '\0') { + cmd = wp->cmd; + if (cmd == NULL || *cmd == '\0') + cmd = wp->shell; + } + return (parse_window_name(cmd)); +} + /* Set default format keys for a session. */ void format_session(struct format_tree *ft, struct session *s) @@ -427,25 +443,38 @@ format_client(struct format_tree *ft, struct client *c) format_add(ft, "client_last_session", "%s", s->name); } +/* Set default format keys for a window. */ +void +format_window(struct format_tree *ft, struct window *w) +{ + char *layout; + + layout = layout_dump(w); + + format_add(ft, "window_id", "@%u", w->id); + 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_layout", "%s", layout); + format_add(ft, "window_panes", "%u", window_count_panes(w)); + + free(layout); +} + /* Set default format keys for a winlink. */ void format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) { struct window *w = wl->window; - char *layout, *flags; + char *flags; - layout = layout_dump(w); flags = window_printable_flags(s, wl); - format_add(ft, "window_id", "@%u", w->id); + format_window(ft, w); + format_add(ft, "window_index", "%d", wl->idx); - 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_flags", "%s", flags); - format_add(ft, "window_layout", "%s", layout); format_add(ft, "window_active", "%d", wl == s->curw); - format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_bell_flag", "%u", !!(wl->flags & WINLINK_BELL)); @@ -456,8 +485,8 @@ format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) format_add(ft, "window_silence_flag", "%u", !!(wl->flags & WINLINK_SILENCE)); + free(flags); - free(layout); } /* Add window pane tabs. */ From fc54bfe6b0b5a9053fe967e87f55fbe684abc679 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:04:38 +0000 Subject: [PATCH 153/949] Make cmdq->client_exit a tristate (-1 means "not set") so that if explicitly set it can be copied from child to parent cmdq by if-shell and source-file. This fixes using attach or new. From Chris Johnsen. --- cmd-if-shell.c | 4 +++- cmd-source-file.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index e810b0fc..4193944c 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -41,7 +41,6 @@ const struct cmd_entry cmd_if_shell_entry = { "[-b] " CMD_TARGET_PANE_USAGE " shell-command command [command]", 0, NULL, - NULL, cmd_if_shell_exec }; @@ -148,6 +147,9 @@ cmd_if_shell_done(struct cmd_q *cmdq1) struct cmd_if_shell_data *cdata = cmdq1->data; struct cmd_q *cmdq = cdata->cmdq; + if (cmdq1->client_exit >= 0) + cmdq->client_exit = cmdq1->client_exit; + if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); diff --git a/cmd-source-file.c b/cmd-source-file.c index f50efbe3..8fcfe2f3 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -37,7 +37,6 @@ const struct cmd_entry cmd_source_file_entry = { "path", 0, NULL, - NULL, cmd_source_file_exec }; @@ -96,6 +95,9 @@ cmd_source_file_done(struct cmd_q *cmdq1) { struct cmd_q *cmdq = cmdq1->data; + if (cmdq1->client_exit >= 0) + cmdq->client_exit = cmdq1->client_exit; + cmdq_free(cmdq1); cfg_references--; From b822d24b15669a0b7d325e2a2f04959b05a3d4fb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:07:36 +0000 Subject: [PATCH 154/949] Support -c for new-session, based on code from J Raynor. --- cmd-new-window.c | 3 +-- cmd-queue.c | 28 +++++++++++++++++++-- cmd-split-window.c | 3 +-- cmd.c | 61 +++++++++++++++++----------------------------- tmux.1 | 21 ++++++++++++---- 5 files changed, 67 insertions(+), 49 deletions(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index eac0df44..e72a1196 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -35,7 +35,6 @@ const struct cmd_entry cmd_new_window_entry = { CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, - NULL, cmd_new_window_exec }; @@ -103,7 +102,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); + cwd = cmdq_default_path(cmdq, args_get(args, 'c')); if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); diff --git a/cmd-queue.c b/cmd-queue.c index f3506a34..1eacb4bc 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -35,7 +35,7 @@ cmdq_new(struct client *c) cmdq->dead = 0; cmdq->client = c; - cmdq->client_exit = 0; + cmdq->client_exit = -1; TAILQ_INIT(&cmdq->queue); cmdq->item = NULL; @@ -259,7 +259,7 @@ cmdq_continue(struct cmd_q *cmdq) } while (cmdq->item != NULL); empty: - if (cmdq->client_exit) + if (cmdq->client_exit > 0) cmdq->client->flags |= CLIENT_EXIT; if (cmdq->emptyfn != NULL) cmdq->emptyfn(cmdq); /* may free cmdq */ @@ -283,3 +283,27 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } + +/* Get default path using command queue. */ +const char * +cmdq_default_path(struct cmd_q *cmdq, const char *cwd) +{ + struct client *c = cmdq->client; + struct session *s; + const char *current; + + if ((s = cmd_current_session(cmdq, 0)) == NULL) + return (NULL); + + if (cwd == NULL) + cwd = options_get_string(&s->options, "default-path"); + + if (c != NULL && c->session == NULL && c->cwd != NULL) + current = c->cwd; + else if (s->curw != NULL) + current = get_proc_cwd(s->curw->window->active->fd); + else + current = NULL; + + return (cmd_default_path(s->cwd, current, cwd)); +} diff --git a/cmd-split-window.c b/cmd-split-window.c index 98bbd423..2e984de5 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -38,7 +38,6 @@ const struct cmd_entry cmd_split_window_entry = { CMD_TARGET_PANE_USAGE " [command]", 0, cmd_split_window_key_binding, - NULL, cmd_split_window_exec }; @@ -84,7 +83,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); + cwd = cmdq_default_path(cmdq, args_get(args, 'c')); type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) diff --git a/cmd.c b/cmd.c index e7290f26..2aa5f514 100644 --- a/cmd.c +++ b/cmd.c @@ -255,8 +255,6 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) goto usage; if (entry->args_upper != -1 && args->argc > entry->args_upper) goto usage; - if (entry->check != NULL && entry->check(args) != 0) - goto usage; cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; @@ -1281,68 +1279,55 @@ cmd_template_replace(const char *template, const char *s, int idx) } /* - * Return the default path for a new pane, using the given path or the - * default-path option if it is NULL. Several special values are accepted: the - * empty string or relative path for the current pane's working directory, ~ - * for the user's home, - for the session working directory, . for the tmux - * server's working directory. The default on failure is the session's working - * directory. + * Return the default path for a new pane. Several special values are accepted: + * the empty string or relative path for the current working directory, + * ~ for the user's home, - for the base working directory, . for the server + * working directory. */ const char * -cmd_get_default_path(struct cmd_q *cmdq, const char *cwd) +cmd_default_path(const char *base, const char *current, const char *in) { - struct client *c = cmdq->client; - struct session *s; - struct environ_entry *envent; const char *root; + struct environ_entry *envent; char tmp[MAXPATHLEN]; struct passwd *pw; int n; size_t skip; static char path[MAXPATHLEN]; - if ((s = cmd_current_session(cmdq, 0)) == NULL) - return (NULL); - - if (cwd == NULL) - cwd = options_get_string(&s->options, "default-path"); - skip = 1; - if (strcmp(cwd, "$HOME") == 0 || strncmp(cwd, "$HOME/", 6) == 0) { + if (strcmp(in, "$HOME") == 0 || strncmp(in, "$HOME/", 6) == 0) { /* User's home directory - $HOME. */ skip = 5; goto find_home; - } else if (cwd[0] == '~' && (cwd[1] == '\0' || cwd[1] == '/')) { + } else if (in[0] == '~' && (in[1] == '\0' || in[1] == '/')) { /* User's home directory - ~. */ goto find_home; - } else if (cwd[0] == '-' && (cwd[1] == '\0' || cwd[1] == '/')) { - /* Session working directory. */ - root = s->cwd; + } else if (in[0] == '-' && (in[1] == '\0' || in[1] == '/')) { + /* Base working directory. */ + root = base; goto complete_path; - } else if (cwd[0] == '.' && (cwd[1] == '\0' || cwd[1] == '/')) { + } else if (in[0] == '.' && (in[1] == '\0' || in[1] == '/')) { /* Server working directory. */ if (getcwd(tmp, sizeof tmp) != NULL) { root = tmp; goto complete_path; } - return (s->cwd); - } else if (*cwd == '/') { + return ("/"); + } else if (*in == '/') { /* Absolute path. */ - return (cwd); + return (in); } else { /* Empty or relative path. */ - if (c != NULL && c->session == NULL && c->cwd != NULL) - root = c->cwd; - else if (s->curw != NULL) - root = get_proc_cwd(s->curw->window->active->fd); + if (current != NULL) + root = current; else - return (s->cwd); + return (base); skip = 0; - if (root != NULL) - goto complete_path; + goto complete_path; } - return (s->cwd); + return (base); find_home: envent = environ_find(&global_environ, "HOME"); @@ -1351,15 +1336,15 @@ find_home: else if ((pw = getpwuid(getuid())) != NULL) root = pw->pw_dir; else - return (s->cwd); + return (base); complete_path: if (root[skip] == '\0') { strlcpy(path, root, sizeof path); return (path); } - n = snprintf(path, sizeof path, "%s/%s", root, cwd + skip); + n = snprintf(path, sizeof path, "%s/%s", root, in + skip); if (n > 0 && (size_t)n < sizeof path) return (path); - return (s->cwd); + return (base); } diff --git a/tmux.1 b/tmux.1 index 1f886cf7..07d6b3fe 100644 --- a/tmux.1 +++ b/tmux.1 @@ -99,7 +99,9 @@ Force .Nm to assume the terminal supports 256 colours. .It Fl C -Start in control mode. +Start in control mode (see the +.Sx CONTROL MODE +section). Given twice .Xo ( Fl CC ) Xc disables echo. @@ -622,9 +624,10 @@ If it does exist, exit with 0. Kill the .Nm server and clients and destroy all sessions. -.It Ic kill-session +.It Xo Ic kill-session .Op Fl a .Op Fl t Ar target-session +.Xc Destroy the given session, closing any windows linked to it and no other sessions, and detaching all clients attached to it. If @@ -669,6 +672,7 @@ Lock all clients attached to .Ar target-session . .It Xo Ic new-session .Op Fl AdDP +.Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name @@ -2708,8 +2712,8 @@ The default is on. Control automatic window renaming. When this setting is enabled, .Nm -will attempt - on supported platforms - to rename the window to reflect the -command currently running in it. +will rename the window automatically using the format specified by +.Ic automatic-rename-format . This flag is automatically disabled for an individual window when a name is specified at creation with .Ic new-window @@ -2723,6 +2727,13 @@ It may be switched off globally with: set-window-option -g automatic-rename off .Ed .Pp +.It Ic automatic-rename-format Ar format +The format (see +.Sx FORMATS ) +used when the +.Ic automatic-rename +option is enabled. +.Pp .It Ic c0-change-interval Ar interval .It Ic c0-change-trigger Ar trigger These two options configure a simple form of rate limiting for a pane. @@ -3570,7 +3581,7 @@ If the command doesn't return success, the exit status is also displayed. .D1 (alias: Ic info ) Show server information and terminal details. .It Xo Ic wait-for -.Fl LSU +.Op Fl L | S | U .Ar channel .Xc .D1 (alias: Ic wait ) From d2160e3f838bc2f5c08dffc0f05bc27502668ed7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:08:14 +0000 Subject: [PATCH 155/949] mouse-resize-pane: Only resize on border select The current behaviour of mouse-resize-pane is such that if the mouse button is held down and a selection takes place within a pane, that if the mouse pointer then hits a border edge, that pane-resize would initiate. This seems counter-intuitive; instead, check for a resize condition if the border of a pane is selected, and in the case of mouse selection within a pane, no longer resize the pane if edge of the border is hit. By Thomas Adam. --- layout.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/layout.c b/layout.c index 4dd3756c..b91b86cd 100644 --- a/layout.c +++ b/layout.c @@ -533,6 +533,9 @@ layout_resize_pane_mouse(struct client *c) pane_border = 0; if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) { TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + if (wp->xoff + wp->sx == m->lx && wp->yoff <= 1 + m->ly && wp->yoff + wp->sy >= m->ly) { @@ -550,7 +553,7 @@ layout_resize_pane_mouse(struct client *c) } if (pane_border) server_redraw_window(w); - } else if (~m->event & MOUSE_EVENT_UP) { + } else if (m->event & MOUSE_EVENT_DOWN) { TAILQ_FOREACH(wp, &w->panes, entry) { if ((wp->xoff + wp->sx == m->x && wp->yoff <= 1 + m->y && From 6c093010e0c181433d263be54b65def01a8a3513 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:09:34 +0000 Subject: [PATCH 156/949] Remove CMD_SENDENVIRON. --- client.c | 7 ++----- cmd-attach-session.c | 3 +-- cmd-new-session.c | 39 ++++++++++++++++++++------------------- tmux.h | 31 ++++++++++++++++++------------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/client.c b/client.c index 0c8657eb..0101fc0b 100644 --- a/client.c +++ b/client.c @@ -179,7 +179,7 @@ client_main(int argc, char **argv, int flags) cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; - cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST; + cmdflags = CMD_STARTSERVER|CMD_CANTNEST; } else { msg = MSG_COMMAND; @@ -197,8 +197,6 @@ client_main(int argc, char **argv, int flags) TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; - if (cmd->entry->flags & CMD_SENDENVIRON) - cmdflags |= CMD_SENDENVIRON; if (cmd->entry->flags & CMD_CANTNEST) cmdflags |= CMD_CANTNEST; } @@ -258,8 +256,7 @@ client_main(int argc, char **argv, int flags) set_signals(client_signal); /* Send initial environment. */ - if (cmdflags & CMD_SENDENVIRON) - client_send_environ(); + client_send_environ(); client_send_identify(flags); /* Send first command. */ diff --git a/cmd-attach-session.c b/cmd-attach-session.c index f5f2778d..70fea988 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -32,8 +32,7 @@ const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", "drt:", 0, 0, "[-dr] " CMD_TARGET_SESSION_USAGE, - CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON, - NULL, + CMD_CANTNEST|CMD_STARTSERVER, NULL, cmd_attach_session_exec }; diff --git a/cmd-new-session.c b/cmd-new-session.c index 653db876..7c6ede62 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -30,28 +30,19 @@ * Create a new session and attach to the current terminal unless -d is given. */ -enum cmd_retval cmd_new_session_check(struct args *); enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", - "AdDF:n:Ps:t:x:y:", 0, 1, - "[-AdDP] [-F format] [-n window-name] [-s session-name] " - CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", - CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, + "Ac:dDF:n:Ps:t:x:y:", 0, 1, + "[-AdDP] [-c start-directory] [-F format] [-n window-name] " + "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] " + "[command]", + CMD_STARTSERVER|CMD_CANTNEST, NULL, - cmd_new_session_check, cmd_new_session_exec }; -enum cmd_retval -cmd_new_session_check(struct args *args) -{ - if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) - return (CMD_RETURN_ERROR); - return (CMD_RETURN_NORMAL); -} - enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -62,14 +53,19 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ env; struct termios tio, *tiop; struct passwd *pw; - const char *newname, *target, *update, *cwd, *errstr; - const char *template; + const char *newname, *target, *update, *base, *cwd; + const char *errstr, *template; char *cmd, *cause, *cp; int detached, idx; u_int sx, sy; int already_attached; struct format_tree *ft; + if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { + cmdq_error(cmdq, "command or window name given with target"); + return (CMD_RETURN_ERROR); + } + newname = args_get(args, 's'); if (newname != NULL) { if (!session_check_name(newname)) { @@ -131,14 +127,19 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Get the new session working directory. */ if (c != NULL && c->cwd != NULL) - cwd = c->cwd; + base = c->cwd; else { pw = getpwuid(getuid()); if (pw->pw_dir != NULL && *pw->pw_dir != '\0') - cwd = pw->pw_dir; + base = pw->pw_dir; else - cwd = "/"; + base = "/"; } + if (args_has(args, 'c')) + cwd = args_get(args, 'c'); + else + cwd = options_get_string(&global_s_options, "default-path"); + cwd = cmd_default_path(base, base, cwd); /* Find new session size. */ if (c != NULL) { diff --git a/tmux.h b/tmux.h index 21be2bd6..891f6233 100644 --- a/tmux.h +++ b/tmux.h @@ -151,7 +151,7 @@ extern char **environ; "[layout #{window_layout}] #{window_id}" \ "#{?window_active, (active),}"; #define LIST_WINDOWS_WITH_SESSION_TEMPLATE \ - "#{session_name}: " \ + "#{session_name}:" \ "#{window_index}: #{window_name}#{window_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " @@ -1008,6 +1008,7 @@ struct window { #define WINDOW_REDRAW 0x4 #define WINDOW_SILENCE 0x8 #define WINDOW_ZOOMED 0x10 +#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) struct options options; @@ -1449,12 +1450,10 @@ struct cmd_entry { #define CMD_STARTSERVER 0x1 #define CMD_CANTNEST 0x2 -#define CMD_SENDENVIRON 0x4 -#define CMD_READONLY 0x8 +#define CMD_READONLY 0x4 int flags; void (*key_binding)(struct cmd *, int); - int (*check)(struct args *); enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; @@ -1552,16 +1551,19 @@ int format_cmp(struct format_entry *, struct format_entry *); RB_PROTOTYPE(format_tree, format_entry, entry, format_cmp); struct format_tree *format_create(void); void format_free(struct format_tree *); -void printflike3 format_add( - struct format_tree *, const char *, const char *, ...); +void printflike3 format_add(struct format_tree *, const char *, const char *, + ...); const char *format_find(struct format_tree *, const char *); char *format_expand(struct format_tree *, const char *); void format_session(struct format_tree *, struct session *); void format_client(struct format_tree *, struct client *); -void format_winlink( - struct format_tree *, struct session *, struct winlink *); -void format_window_pane(struct format_tree *, struct window_pane *); -void format_paste_buffer(struct format_tree *, struct paste_buffer *); +void format_window(struct format_tree *, struct window *); +void format_winlink(struct format_tree *, struct session *, + struct winlink *); +void format_window_pane(struct format_tree *, + struct window_pane *); +void format_paste_buffer(struct format_tree *, + struct paste_buffer *); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -1767,7 +1769,7 @@ int cmd_find_index(struct cmd_q *, const char *, struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); char *cmd_template_replace(const char *, const char *, int); -const char *cmd_get_default_path(struct cmd_q *, const char *); +const char *cmd_default_path(const char *, const char *, const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; @@ -1876,6 +1878,7 @@ void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); void cmdq_flush(struct cmd_q *); +const char *cmdq_default_path(struct cmd_q *, const char *); /* cmd-string.c */ int cmd_string_parse(const char *, struct cmd_list **, const char *, @@ -2275,8 +2278,10 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void queue_window_name(struct window *); -char *default_window_name(struct window *); +void queue_window_name(struct window *); +char *default_window_name(struct window *); +char *format_window_name(struct window *); +char *parse_window_name(const char *); /* signal.c */ void set_signals(void(*)(int, short, void *)); From eb26dbd0724075e3cc316f722ff934de46458eb4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:12:08 +0000 Subject: [PATCH 157/949] Merge IDENTIFY_* flags with CLIENT_* flags. --- tmux.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tmux.c b/tmux.c index 901ded80..140946b9 100644 --- a/tmux.c +++ b/tmux.c @@ -261,17 +261,17 @@ main(int argc, char **argv) while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { switch (opt) { case '2': - flags |= IDENTIFY_256COLOURS; + flags |= CLIENT_256COLOURS; break; case 'c': free(shell_cmd); shell_cmd = xstrdup(optarg); break; case 'C': - if (flags & IDENTIFY_CONTROL) - flags |= IDENTIFY_TERMIOS; + if (flags & CLIENT_CONTROL) + flags |= CLIENT_CONTROLCONTROL; else - flags |= IDENTIFY_CONTROL; + flags |= CLIENT_CONTROL; break; case 'f': free(cfg_file); @@ -292,7 +292,7 @@ main(int argc, char **argv) path = xstrdup(optarg); break; case 'u': - flags |= IDENTIFY_UTF8; + flags |= CLIENT_UTF8; break; case 'v': debug_level++; @@ -307,7 +307,7 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (!(flags & IDENTIFY_UTF8)) { + if (!(flags & CLIENT_UTF8)) { /* * If the user has set whichever of LC_ALL, LC_CTYPE or LANG * exist (in that order) to contain UTF-8, it is a safe @@ -321,7 +321,7 @@ main(int argc, char **argv) } if (s != NULL && (strcasestr(s, "UTF-8") != NULL || strcasestr(s, "UTF8") != NULL)) - flags |= IDENTIFY_UTF8; + flags |= CLIENT_UTF8; } environ_init(&global_environ); @@ -340,7 +340,7 @@ main(int argc, char **argv) options_table_populate_tree(window_options_table, &global_w_options); /* Enable UTF-8 if the first client is on UTF-8 terminal. */ - if (flags & IDENTIFY_UTF8) { + if (flags & CLIENT_UTF8) { options_set_number(&global_s_options, "status-utf8", 1); options_set_number(&global_s_options, "mouse-utf8", 1); options_set_number(&global_w_options, "utf8", 1); From a0404b69026f420c8a1c35b6d017047f208cd322 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:12:54 +0000 Subject: [PATCH 158/949] retcode -> retval for exit message. --- cmd-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-queue.c b/cmd-queue.c index 1eacb4bc..b2030e69 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -143,7 +143,7 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) evbuffer_add(c->stderr_data, "\n", 1); server_push_stderr(c); - c->retcode = 1; + c->retval = 1; } else { *msg = toupper((u_char) *msg); status_message_set(c, "%s", msg); From 10c38436aae90c61e1b43ffdbd4d10d3eb95fd6a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:13:56 +0000 Subject: [PATCH 159/949] Similarly for MSG_COMMAND - allow full imsg limit not arbitrary 2048. --- client.c | 148 ++++++++++++++++++++++++++++-------------------- server-client.c | 67 +++++++++++----------- server-fn.c | 9 +-- tmux.h | 82 +++++++++++++-------------- 4 files changed, 163 insertions(+), 143 deletions(-) diff --git a/client.c b/client.c index 0101fc0b..5be747dd 100644 --- a/client.c +++ b/client.c @@ -54,7 +54,8 @@ int client_get_lock(char *); int client_connect(char *, int); void client_send_identify(int); void client_send_environ(void); -void client_write_server(enum msgtype, void *, size_t); +int client_write_one(enum msgtype, int, const void *, size_t); +int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); void client_signal(int, short, void *); void client_stdin_callback(int, short, void *); @@ -165,12 +166,13 @@ client_main(int argc, char **argv, int flags) { struct cmd *cmd; struct cmd_list *cmdlist; - struct msg_command_data cmddata; - int cmdflags, fd; + struct msg_command_data *data; + int cmdflags, fd, i; pid_t ppid; enum msgtype msg; char *cause; struct termios tio, saved_tio; + size_t size; /* Set up the initial command. */ cmdflags = 0; @@ -234,7 +236,7 @@ client_main(int argc, char **argv, int flags) setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); - if (flags & IDENTIFY_TERMIOS) { + if (flags & CLIENT_CONTROLCONTROL) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); @@ -261,19 +263,32 @@ client_main(int argc, char **argv, int flags) /* Send first command. */ if (msg == MSG_COMMAND) { + /* How big is the command? */ + size = 0; + for (i = 0; i < argc; i++) + size += strlen(argv[i]) + 1; + data = xmalloc((sizeof *data) + size); + /* Fill in command line arguments. */ - cmddata.pid = environ_pid; - cmddata.session_id = environ_session_id; + data->pid = environ_pid; + data->session_id = environ_session_id; /* Prepare command for server. */ - cmddata.argc = argc; - if (cmd_pack_argv( - argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) { + data->argc = argc; + if (cmd_pack_argv(argc, argv, (char*)(data + 1), size) != 0) { fprintf(stderr, "command too long\n"); + free(data); return (1); } + size += sizeof *data; - client_write_server(msg, &cmddata, sizeof cmddata); + /* Send the command. */ + if (client_write_server(msg, data, size) != 0) { + fprintf(stderr, "failed to send command\n"); + free(data); + return (1); + } + free(data); } else if (msg == MSG_SHELL) client_write_server(msg, NULL, 0); @@ -289,14 +304,12 @@ client_main(int argc, char **argv, int flags) ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } else if (flags & IDENTIFY_TERMIOS) { - if (flags & IDENTIFY_CONTROL) { - if (client_exitreason != CLIENT_EXIT_NONE) - printf("%%exit %s\n", client_exit_message()); - else - printf("%%exit\n"); - printf("\033\\"); - } + } else if (flags & CLIENT_CONTROLCONTROL) { + if (client_exitreason != CLIENT_EXIT_NONE) + printf("%%exit %s\n", client_exit_message()); + else + printf("%%exit\n"); + printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); } setblocking(STDIN_FILENO, 1); @@ -342,12 +355,29 @@ client_send_environ(void) } } -/* Write a message to the server without a file descriptor. */ -void -client_write_server(enum msgtype type, void *buf, size_t len) +/* Helper to send one message. */ +int +client_write_one(enum msgtype type, int fd, const void *buf, size_t len) { - imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); - client_update_event(); + int retval; + + retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd, + (void*)buf, len); + if (retval != 1) + return (-1); + return (0); +} + +/* Write a message to the server without a file descriptor. */ +int +client_write_server(enum msgtype type, const void *buf, size_t len) +{ + int retval; + + retval = client_write_one(type, -1, buf, len); + if (retval == 0) + client_update_event(); + return (retval); } /* Update client event based on whether it needs to read or read and write. */ @@ -481,33 +511,33 @@ client_write(int fd, const char *data, size_t size) /* Dispatch imsgs when in wait state (before MSG_READY). */ int -client_dispatch_wait(void *data) +client_dispatch_wait(void *data0) { - struct imsg imsg; - ssize_t n, datalen; - struct msg_shell_data shelldata; - struct msg_exit_data exitdata; - struct msg_stdout_data stdoutdata; - struct msg_stderr_data stderrdata; - const char *shellcmd = data; + struct imsg imsg; + char *data; + ssize_t n, datalen; + struct msg_stdout_data stdoutdata; + struct msg_stderr_data stderrdata; + int retval; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) fatalx("imsg_get failed"); if (n == 0) return (0); + + data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; log_debug("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: - if (datalen != sizeof exitdata) { - if (datalen != 0) - fatalx("bad MSG_EXIT size"); - } else { - memcpy(&exitdata, imsg.data, sizeof exitdata); - client_exitval = exitdata.retcode; + if (datalen != sizeof retval && datalen != 0) + fatalx("bad MSG_EXIT size"); + if (datalen == sizeof retval) { + memcpy(&retval, data, sizeof retval); + client_exitval = retval; } imsg_free(&imsg); return (-1); @@ -527,17 +557,19 @@ client_dispatch_wait(void *data) break; case MSG_STDOUT: if (datalen != sizeof stdoutdata) - fatalx("bad MSG_STDOUT"); - memcpy(&stdoutdata, imsg.data, sizeof stdoutdata); + fatalx("bad MSG_STDOUT size"); + memcpy(&stdoutdata, data, sizeof stdoutdata); - client_write(STDOUT_FILENO, stdoutdata.data, stdoutdata.size); + client_write(STDOUT_FILENO, stdoutdata.data, + stdoutdata.size); break; case MSG_STDERR: if (datalen != sizeof stderrdata) - fatalx("bad MSG_STDERR"); - memcpy(&stderrdata, imsg.data, sizeof stderrdata); + fatalx("bad MSG_STDERR size"); + memcpy(&stderrdata, data, sizeof stderrdata); - client_write(STDERR_FILENO, stderrdata.data, stderrdata.size); + client_write(STDERR_FILENO, stderrdata.data, + stderrdata.size); break; case MSG_VERSION: if (datalen != 0) @@ -551,14 +583,11 @@ client_dispatch_wait(void *data) imsg_free(&imsg); return (-1); case MSG_SHELL: - if (datalen != sizeof shelldata) - fatalx("bad MSG_SHELL size"); - memcpy(&shelldata, imsg.data, sizeof shelldata); - shelldata.shell[(sizeof shelldata.shell) - 1] = '\0'; + if (data[datalen - 1] != '\0') + fatalx("bad MSG_SHELL string"); clear_signals(0); - - shell_exec(shelldata.shell, shellcmd); + shell_exec(data, data0); /* NOTREACHED */ case MSG_DETACH: client_write_server(MSG_EXITING, NULL, 0); @@ -578,16 +607,18 @@ client_dispatch_wait(void *data) int client_dispatch_attached(void) { - struct imsg imsg; - struct msg_lock_data lockdata; - struct sigaction sigact; - ssize_t n, datalen; + struct imsg imsg; + struct sigaction sigact; + char *data; + ssize_t n, datalen; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) fatalx("imsg_get failed"); if (n == 0) return (0); + + data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; log_debug("got %d from server", imsg.hdr.type); @@ -605,8 +636,7 @@ client_dispatch_attached(void) client_write_server(MSG_EXITING, NULL, 0); break; case MSG_EXIT: - if (datalen != 0 && - datalen != sizeof (struct msg_exit_data)) + if (datalen != 0 && datalen != sizeof (int)) fatalx("bad MSG_EXIT size"); client_write_server(MSG_EXITING, NULL, 0); @@ -639,12 +669,10 @@ client_dispatch_attached(void) kill(getpid(), SIGTSTP); break; case MSG_LOCK: - if (datalen != sizeof lockdata) - fatalx("bad MSG_LOCK size"); - memcpy(&lockdata, imsg.data, sizeof lockdata); + if (data[datalen - 1] != '\0') + fatalx("bad MSG_LOCK string"); - lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; - system(lockdata.cmd); + system(data); client_write_server(MSG_UNLOCK, NULL, 0); break; default: diff --git a/server-client.c b/server-client.c index 44119237..4bba5f7d 100644 --- a/server-client.c +++ b/server-client.c @@ -40,7 +40,7 @@ void server_client_reset_state(struct client *); int server_client_assume_paste(struct session *); int server_client_msg_dispatch(struct client *); -void server_client_msg_command(struct client *, struct msg_command_data *); +void server_client_msg_command(struct client *, struct imsg *); void server_client_msg_identify( struct client *, struct msg_identify_data *, int); void server_client_msg_shell(struct client *); @@ -695,8 +695,6 @@ server_client_repeat_timer(unused int fd, unused short events, void *data) void server_client_check_exit(struct client *c) { - struct msg_exit_data exitdata; - if (!(c->flags & CLIENT_EXIT)) return; @@ -707,9 +705,7 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; - exitdata.retcode = c->retcode; - server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); - + server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval); c->flags &= ~CLIENT_EXIT; } @@ -790,10 +786,10 @@ int server_client_msg_dispatch(struct client *c) { struct imsg imsg; - struct msg_command_data commanddata; struct msg_identify_data identifydata; struct msg_environ_data environdata; struct msg_stdin_data stdindata; + const char *data; ssize_t n, datalen; if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) @@ -804,6 +800,8 @@ server_client_msg_dispatch(struct client *c) return (-1); if (n == 0) return (0); + + data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; if (imsg.hdr.peerid != PROTOCOL_VERSION) { @@ -815,13 +813,6 @@ server_client_msg_dispatch(struct client *c) log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); switch (imsg.hdr.type) { - case MSG_COMMAND: - if (datalen != sizeof commanddata) - fatalx("bad MSG_COMMAND size"); - memcpy(&commanddata, imsg.data, sizeof commanddata); - - server_client_msg_command(c, &commanddata); - break; case MSG_IDENTIFY: if (datalen != sizeof identifydata) fatalx("bad MSG_IDENTIFY size"); @@ -829,10 +820,13 @@ server_client_msg_dispatch(struct client *c) server_client_msg_identify(c, &identifydata, imsg.fd); break; + case MSG_COMMAND: + server_client_msg_command(c, &imsg); + break; case MSG_STDIN: if (datalen != sizeof stdindata) fatalx("bad MSG_STDIN size"); - memcpy(&stdindata, imsg.data, sizeof stdindata); + memcpy(&stdindata, data, sizeof stdindata); if (c->stdin_callback == NULL) break; @@ -907,15 +901,26 @@ server_client_msg_dispatch(struct client *c) /* Handle command message. */ void -server_client_msg_command(struct client *c, struct msg_command_data *data) +server_client_msg_command(struct client *c, struct imsg *imsg) { - struct cmd_list *cmdlist = NULL; - int argc; - char **argv, *cause; + struct msg_command_data data; + char *buf; + size_t len; + struct cmd_list *cmdlist = NULL; + int argc; + char **argv, *cause; - argc = data->argc; - data->argv[(sizeof data->argv) - 1] = '\0'; - if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { + if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data) + fatalx("bad MSG_COMMAND size"); + memcpy(&data, imsg->data, sizeof data); + + buf = (char*)imsg->data + sizeof data; + len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data; + if (len > 0 && buf[len - 1] != '\0') + fatalx("bad MSG_COMMAND string"); + + argc = data.argc; + if (cmd_unpack_argv(buf, len, argc, &argv) != 0) { cmdq_error(c->cmdq, "command too long"); goto error; } @@ -954,12 +959,12 @@ server_client_msg_identify( if (*data->cwd != '\0') c->cwd = xstrdup(data->cwd); - if (data->flags & IDENTIFY_CONTROL) { + if (data->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; c->flags |= CLIENT_CONTROL; - if (data->flags & IDENTIFY_TERMIOS) + if (data->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); server_write_client(c, MSG_STDIN, NULL, 0); @@ -978,14 +983,14 @@ server_client_msg_identify( } data->term[(sizeof data->term) - 1] = '\0'; tty_init(&c->tty, c, fd, data->term); - if (data->flags & IDENTIFY_UTF8) + if (data->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; - if (data->flags & IDENTIFY_256COLOURS) + if (data->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); - if (!(data->flags & IDENTIFY_CONTROL)) + if (!(data->flags & CLIENT_CONTROL)) c->flags |= CLIENT_TERMINAL; } @@ -993,16 +998,12 @@ server_client_msg_identify( void server_client_msg_shell(struct client *c) { - struct msg_shell_data data; - const char *shell; + const char *shell; shell = options_get_string(&global_s_options, "default-shell"); - if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) - strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); + server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1); - server_write_client(c, MSG_SHELL, &data, sizeof data); c->flags |= CLIENT_BAD; /* it will die after exec */ } diff --git a/server-fn.c b/server-fn.c index fb0eadd6..f6485b49 100644 --- a/server-fn.c +++ b/server-fn.c @@ -235,9 +235,7 @@ server_lock_session(struct session *s) void server_lock_client(struct client *c) { - const char *cmd; - size_t cmdlen; - struct msg_lock_data lockdata; + const char *cmd; if (c->flags & CLIENT_CONTROL) return; @@ -246,8 +244,7 @@ server_lock_client(struct client *c) return; cmd = options_get_string(&c->session->options, "lock-command"); - cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd); - if (cmdlen >= sizeof lockdata.cmd) + if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) return; tty_stop_tty(&c->tty); @@ -256,7 +253,7 @@ server_lock_client(struct client *c) tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata); + server_write_client(c, MSG_LOCK, cmd, strlen(cmd) + 1); } void diff --git a/tmux.h b/tmux.h index 891f6233..2d631a17 100644 --- a/tmux.h +++ b/tmux.h @@ -19,7 +19,7 @@ #ifndef TMUX_H #define TMUX_H -#define PROTOCOL_VERSION 7 +#define PROTOCOL_VERSION 8 #include #include @@ -62,7 +62,6 @@ extern char **environ; * Maximum sizes of strings in message data. Don't forget to bump * PROTOCOL_VERSION if any of these change! */ -#define COMMAND_LENGTH 2048 /* packed argv size */ #define TERMINAL_LENGTH 128 /* length of TERM environment variable */ #define ENVIRON_LENGTH 1024 /* environment variable length */ @@ -437,27 +436,36 @@ ARRAY_DECL(causelist, char *); /* Message codes. */ enum msgtype { - MSG_COMMAND, + MSG_VERSION = 12, + + MSG_IDENTIFY_FLAGS = 100, + MSG_IDENTIFY_TERM, + MSG_IDENTIFY_TTYNAME, + MSG_IDENTIFY_CWD, + MSG_IDENTIFY_STDIN, + MSG_IDENTIFY_ENVIRON, + MSG_IDENTIFY_DONE, + + MSG_COMMAND = 200, MSG_DETACH, - MSG_ERROR, + MSG_DETACHKILL, MSG_EXIT, MSG_EXITED, MSG_EXITING, - MSG_IDENTIFY, - MSG_STDIN, + MSG_LOCK, MSG_READY, MSG_RESIZE, - MSG_SHUTDOWN, - MSG_SUSPEND, - MSG_VERSION, - MSG_WAKEUP, - MSG_ENVIRON, - MSG_UNLOCK, - MSG_LOCK, MSG_SHELL, + MSG_SHUTDOWN, MSG_STDERR, + MSG_STDIN, MSG_STDOUT, - MSG_DETACHKILL + MSG_SUSPEND, + MSG_UNLOCK, + MSG_WAKEUP, + + MSG_IDENTIFY = 300, + MSG_ENVIRON }; /* @@ -466,42 +474,23 @@ enum msgtype { * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { - pid_t pid; /* from $TMUX or -1 */ - int session_id; /* from $TMUX or -1 */ + pid_t pid; /* from $TMUX or -1 */ + int session_id; /* from $TMUX or -1 */ - int argc; - char argv[COMMAND_LENGTH]; -}; + int argc; +}; /* followed by packed argv */ struct msg_identify_data { char cwd[MAXPATHLEN]; - char term[TERMINAL_LENGTH]; -#define IDENTIFY_UTF8 0x1 -#define IDENTIFY_256COLOURS 0x2 -/* 0x4 unused */ -#define IDENTIFY_CONTROL 0x8 -#define IDENTIFY_TERMIOS 0x10 int flags; }; -struct msg_lock_data { - char cmd[COMMAND_LENGTH]; -}; - struct msg_environ_data { char var[ENVIRON_LENGTH]; }; -struct msg_shell_data { - char shell[MAXPATHLEN]; -}; - -struct msg_exit_data { - int retcode; -}; - struct msg_stdin_data { ssize_t size; char data[BUFSIZ]; @@ -1294,8 +1283,9 @@ RB_HEAD(status_out_tree, status_out); /* Client connection. */ struct client { struct imsgbuf ibuf; + struct event event; - int retcode; + int retval; struct timeval creation_time; struct timeval activity_time; @@ -1326,7 +1316,7 @@ struct client { #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 -#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ +#define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 #define CLIENT_BAD 0x80 #define CLIENT_IDENTIFY 0x100 @@ -1335,7 +1325,11 @@ struct client { #define CLIENT_READONLY 0x800 #define CLIENT_REDRAWWINDOW 0x1000 #define CLIENT_CONTROL 0x2000 -#define CLIENT_FOCUSED 0x4000 +#define CLIENT_CONTROLCONTROL 0x4000 +#define CLIENT_FOCUSED 0x8000 +#define CLIENT_UTF8 0x10000 +#define CLIENT_256COLOURS 0x20000 +#define CLIENT_IDENTIFIED 0x40000 int flags; struct event identify_timer; @@ -1925,10 +1919,10 @@ void server_window_loop(void); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); void server_write_ready(struct client *); -int server_write_client( - struct client *, enum msgtype, const void *, size_t); -void server_write_session( - struct session *, enum msgtype, const void *, size_t); +int server_write_client(struct client *, enum msgtype, const void *, + size_t); +void server_write_session(struct session *, enum msgtype, const void *, + size_t); void server_redraw_client(struct client *); void server_status_client(struct client *); void server_redraw_session(struct session *); From 165aa597600ae5bb2b22f06a2633fbae5a77d917 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:14:09 +0000 Subject: [PATCH 160/949] Make tilde expansion in command strings work even if it isn't terminated by /. --- cmd-string.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/cmd-string.c b/cmd-string.c index f785b842..e19b8856 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -318,10 +318,13 @@ cmd_string_expand_tilde(const char *s, size_t *p) { struct passwd *pw; struct environ_entry *envent; - char *home, *path, *username; + char *home, *path, *user, *cp; + int last; home = NULL; - if (cmd_string_getc(s, p) == '/') { + + last = cmd_string_getc(s, p); + if (last == EOF || last == '/' || last == ' '|| last == '\t') { envent = environ_find(&global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') home = envent->value; @@ -329,15 +332,27 @@ cmd_string_expand_tilde(const char *s, size_t *p) home = pw->pw_dir; } else { cmd_string_ungetc(p); - if ((username = cmd_string_string(s, p, '/', 0)) == NULL) - return (NULL); - if ((pw = getpwnam(username)) != NULL) + + cp = user = xmalloc(strlen(s)); + for (;;) { + last = cmd_string_getc(s, p); + if (last == EOF || last == '/' || last == ' '|| last == '\t') + break; + *cp++ = last; + } + *cp = '\0'; + + if ((pw = getpwnam(user)) != NULL) home = pw->pw_dir; - free(username); + free(user); } + if (home == NULL) return (NULL); - xasprintf(&path, "%s/", home); + if (last != EOF) + xasprintf(&path, "%s%c", home, last); + else + xasprintf(&path, "%s", home); return (path); } From 282c5f9644ed262ee15efbd3d072f7acc577da15 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:26:34 +0000 Subject: [PATCH 161/949] Alter how tmux handles the working directory to internally use file descriptors rather than strings. - Each session still has a current working directory. - New sessions still get their working directory from the client that created them or its attached session if any. - New windows are created by default in the session working directory. - The -c flag to new, neww, splitw allows the working directory to be overridden. - The -c flag to attach let's the session working directory be changed. - The default-path option has been removed. To get the equivalent to default-path '.', do: bind c neww -c $PWD To get the equivalent of default-path '~', do: bind c neww -c ~ This also changes the client identify protocol to be a set of messages rather than one as well as some other changes that should make it easier to make backwards-compatible protocol changes in future. --- client.c | 56 ++++++++------------ cmd-attach-session.c | 68 ++++++++++++++++++++---- cmd-load-buffer.c | 30 +++++------ cmd-new-session.c | 74 ++++++++++++++++---------- cmd-new-window.c | 41 +++++++++++++-- cmd-queue.c | 24 --------- cmd-save-buffer.c | 54 +++++++++---------- cmd-split-window.c | 40 +++++++++++--- format.c | 3 -- options-table.c | 5 -- server-client.c | 123 +++++++++++++++++++++++++++++-------------- server-fn.c | 8 +-- session.c | 17 +++--- tmux.1 | 24 ++------- tmux.c | 28 ++-------- tmux.h | 52 ++++++------------ window.c | 16 +++--- 17 files changed, 363 insertions(+), 300 deletions(-) diff --git a/client.c b/client.c index 5be747dd..82e43992 100644 --- a/client.c +++ b/client.c @@ -53,7 +53,6 @@ int client_attached; int client_get_lock(char *); int client_connect(char *, int); void client_send_identify(int); -void client_send_environ(void); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -257,8 +256,7 @@ client_main(int argc, char **argv, int flags) /* Establish signal handlers. */ set_signals(client_signal); - /* Send initial environment. */ - client_send_environ(); + /* Send identify messages. */ client_send_identify(flags); /* Send first command. */ @@ -316,45 +314,39 @@ client_main(int argc, char **argv, int flags) return (client_exitval); } -/* Send identify message to server with the file descriptors. */ +/* Send identify messages to server. */ void client_send_identify(int flags) { - struct msg_identify_data data; - char *term; - int fd; + const char *s; + char **ss; + int fd; - data.flags = flags; + client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); - if (getcwd(data.cwd, sizeof data.cwd) == NULL) - *data.cwd = '\0'; + if ((s = getenv("TERM")) == NULL) + s = ""; + client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - term = getenv("TERM"); - if (term == NULL || - strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) - *data.term = '\0'; + if ((s = ttyname(STDIN_FILENO)) == NULL) + s = ""; + client_write_one(MSG_IDENTIFY_TTYNAME, -1, s, strlen(s) + 1); + + if ((fd = open(".", O_RDONLY)) == -1) + fd = open("/", O_RDONLY); + client_write_one(MSG_IDENTIFY_CWD, fd, NULL, 0); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); - imsg_compose(&client_ibuf, - MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); + client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + + for (ss = environ; *ss != NULL; ss++) + client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); + client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); + client_update_event(); } -/* Forward entire environment to server. */ -void -client_send_environ(void) -{ - struct msg_environ_data data; - char **var; - - for (var = environ; *var != NULL; var++) { - if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var) - continue; - client_write_server(MSG_ENVIRON, &data, sizeof data); - } -} - /* Helper to send one message. */ int client_write_one(enum msgtype type, int fd, const void *buf, size_t len) @@ -595,8 +587,6 @@ client_dispatch_wait(void *data0) case MSG_EXITED: imsg_free(&imsg); return (-1); - default: - fatalx("unexpected message"); } imsg_free(&imsg); @@ -675,8 +665,6 @@ client_dispatch_attached(void) system(data); client_write_server(MSG_UNLOCK, NULL, 0); break; - default: - fatalx("unexpected message"); } imsg_free(&imsg); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 70fea988..8dcc5997 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -18,7 +18,11 @@ #include +#include +#include #include +#include +#include #include "tmux.h" @@ -30,21 +34,25 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", - "drt:", 0, 0, - "[-dr] " CMD_TARGET_SESSION_USAGE, + "c:drt:", 0, 0, + "[-dr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, CMD_CANTNEST|CMD_STARTSERVER, NULL, cmd_attach_session_exec }; enum cmd_retval -cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) +cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, + const char *cflag) { - struct session *s; - struct client *c; - const char *update; - char *cause; - u_int i; + struct session *s; + struct client *c; + const char *update; + char *cause; + u_int i; + int fd; + struct format_tree *ft; + char *cp; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); @@ -73,6 +81,27 @@ cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) } } + if (cflag != NULL) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, cflag); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + close(s->cwd); + s->cwd = fd; + } + cmdq->client->session = s; notify_attached_session_changed(cmdq->client); session_update_activity(s); @@ -85,6 +114,27 @@ cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) return (CMD_RETURN_ERROR); } + if (cflag != NULL) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, cflag); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + close(s->cwd); + s->cwd = fd; + } + if (rflag) cmdq->client->flags |= CLIENT_READONLY; @@ -115,5 +165,5 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; return (cmd_attach_session(cmdq, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'))); + args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'))); } diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 8c92ca32..85595285 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -49,11 +50,11 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path, *newpath, *wd; + const char *path; char *pdata, *new_pdata, *cause; size_t psize; u_int limit; - int ch, error, buffer, *buffer_ptr; + int ch, error, buffer, *buffer_ptr, cwd, fd; if (!args_has(args, 'b')) buffer = -1; @@ -81,20 +82,17 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_WAIT); } - if (c != NULL) - wd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) { - wd = options_get_string(&s->options, "default-path"); - if (*wd == '\0') - wd = s->cwd; - } else - wd = NULL; - if (wd != NULL && *wd != '\0') { - newpath = get_full_path(wd, path); - if (newpath != NULL) - path = newpath; - } - if ((f = fopen(path, "rb")) == NULL) { + if (c != NULL && c->session == NULL) + cwd = c->cwd; + else if ((s = cmd_current_session(cmdq, 0)) != NULL) + cwd = s->cwd; + else + cwd = AT_FDCWD; + + if ((fd = openat(cwd, path, O_RDONLY)) == -1 || + (f = fdopen(fd, "rb")) == NULL) { + if (fd != -1) + close(fd); cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 7c6ede62..5e69a77c 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -18,6 +18,8 @@ #include +#include +#include #include #include #include @@ -47,18 +49,15 @@ enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c = cmdq->client; + struct client *c = cmdq->client, *c0; struct session *s, *groupwith; struct window *w; struct environ env; struct termios tio, *tiop; - struct passwd *pw; - const char *newname, *target, *update, *base, *cwd; - const char *errstr, *template; + const char *newname, *target, *update, *errstr, *template; char *cmd, *cause, *cp; - int detached, idx; + int detached, already_attached, idx, cwd, fd = -1; u_int sx, sy; - int already_attached; struct format_tree *ft; if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { @@ -75,7 +74,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (session_find(newname) != NULL) { if (args_has(args, 'A')) { return (cmd_attach_session(cmdq, newname, - args_has(args, 'D'), 0)); + args_has(args, 'D'), 0, NULL)); } cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); @@ -100,6 +99,31 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session != NULL) already_attached = 1; + /* Get the new session working directory. */ + if (args_has(args, 'c')) { + ft = format_create(); + if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c0); + cp = format_expand(ft, args_get(args, 'c')); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + cwd = fd; + } else if (c->session == NULL) + cwd = c->cwd; + else if ((c0 = cmd_current_client(cmdq)) != NULL) + cwd = c0->session->cwd; + else { + fd = open(".", O_RDONLY); + cwd = fd; + } + /* * Save the termios settings, part of which is used for new windows in * this session. @@ -121,26 +145,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (server_client_open(c, NULL, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } } - /* Get the new session working directory. */ - if (c != NULL && c->cwd != NULL) - base = c->cwd; - else { - pw = getpwuid(getuid()); - if (pw->pw_dir != NULL && *pw->pw_dir != '\0') - base = pw->pw_dir; - else - base = "/"; - } - if (args_has(args, 'c')) - cwd = args_get(args, 'c'); - else - cwd = options_get_string(&global_s_options, "default-path"); - cwd = cmd_default_path(base, base, cwd); - /* Find new session size. */ if (c != NULL) { sx = c->tty.sx; @@ -153,14 +161,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "width %s", errstr); - return (CMD_RETURN_ERROR); + goto error; } } if (detached && args_has(args, 'y')) { sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "height %s", errstr); - return (CMD_RETURN_ERROR); + goto error; } } if (sy > 0 && options_get_number(&global_s_options, "status")) @@ -190,7 +198,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } environ_free(&env); @@ -241,8 +249,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) template = NEW_SESSION_TEMPLATE; ft = format_create(); - if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) - format_client(ft, c); + if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c0); format_session(ft, s); cp = format_expand(ft, template); @@ -254,5 +262,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (!detached) cmdq->client_exit = 0; + + if (fd != -1) + close(fd); return (CMD_RETURN_NORMAL); + +error: + if (fd != -1) + close(fd); + return (CMD_RETURN_ERROR); } diff --git a/cmd-new-window.c b/cmd-new-window.c index e72a1196..445f0a33 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -18,7 +18,11 @@ #include +#include +#include #include +#include +#include #include "tmux.h" @@ -45,9 +49,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct session *s; struct winlink *wl; struct client *c; - const char *cmd, *cwd, *template; + const char *cmd, *template; char *cause, *cp; - int idx, last, detached; + int idx, last, detached, cwd, fd = -1; struct format_tree *ft; if (args_has(args, 'a')) { @@ -102,7 +106,29 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmdq_default_path(cmdq, args_get(args, 'c')); + + if (args_has(args, 'c')) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, args_get(args, 'c')); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + cwd = fd; + } else if (cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else + cwd = s->cwd; if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); @@ -110,7 +136,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } if (!detached) { session_select(s, wl->idx); @@ -136,5 +162,12 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } + if (fd != -1) + close(fd); return (CMD_RETURN_NORMAL); + +error: + if (fd != -1) + close(fd); + return (CMD_RETURN_ERROR); } diff --git a/cmd-queue.c b/cmd-queue.c index b2030e69..bb3a09ec 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -283,27 +283,3 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } - -/* Get default path using command queue. */ -const char * -cmdq_default_path(struct cmd_q *cmdq, const char *cwd) -{ - struct client *c = cmdq->client; - struct session *s; - const char *current; - - if ((s = cmd_current_session(cmdq, 0)) == NULL) - return (NULL); - - if (cwd == NULL) - cwd = options_get_string(&s->options, "default-path"); - - if (c != NULL && c->session == NULL && c->cwd != NULL) - current = c->cwd; - else if (s->curw != NULL) - current = get_proc_cwd(s->curw->window->active->fd); - else - current = NULL; - - return (cmd_default_path(s->cwd, current, cwd)); -} diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 57c3d65d..1b0a4e7b 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -20,8 +20,10 @@ #include #include +#include #include #include +#include #include #include "tmux.h" @@ -54,17 +56,14 @@ enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *newpath, *wd; - char *cause, *start, *end; - size_t size, used; - int buffer; - mode_t mask; + const char *path; + char *cause, *start, *end, *msg; + size_t size, used, msglen; + int cwd, fd, buffer; FILE *f; - char *msg; - size_t msglen; if (!args_has(args, 'b')) { if ((pb = paste_get_top(&global_buffers)) == NULL) { @@ -91,7 +90,6 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else path = args->argv[0]; if (strcmp(path, "-") == 0) { - c = cmdq->client; if (c == NULL) { cmdq_error(cmdq, "can't write to stdout"); return (CMD_RETURN_ERROR); @@ -101,28 +99,26 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) goto do_print; } - c = cmdq->client; - if (c != NULL) - wd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) { - wd = options_get_string(&s->options, "default-path"); - if (*wd == '\0') - wd = s->cwd; - } else - wd = NULL; - if (wd != NULL && *wd != '\0') { - newpath = get_full_path(wd, path); - if (newpath != NULL) - path = newpath; - } - - mask = umask(S_IRWXG | S_IRWXO); - if (args_has(self->args, 'a')) - f = fopen(path, "ab"); + if (c != NULL && c->session == NULL) + cwd = c->cwd; + else if ((s = cmd_current_session(cmdq, 0)) != NULL) + cwd = s->cwd; else - f = fopen(path, "wb"); - umask(mask); + cwd = AT_FDCWD; + + f = NULL; + if (args_has(self->args, 'a')) { + fd = openat(cwd, path, O_CREAT|O_RDWR|O_APPEND, 0600); + if (fd != -1) + f = fdopen(fd, "ab"); + } else { + fd = openat(cwd, path, O_CREAT|O_RDWR, 0600); + if (fd != -1) + f = fdopen(fd, "wb"); + } if (f == NULL) { + if (fd != -1) + close(fd); cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 2e984de5..43d47c47 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -18,8 +18,11 @@ #include +#include +#include #include #include +#include #include #include "tmux.h" @@ -58,16 +61,14 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp, *new_wp = NULL; struct environ env; - const char *cmd, *cwd, *shell; - char *cause, *new_cause; + const char *cmd, *shell, *template; + char *cause, *new_cause, *cp; u_int hlimit; - int size, percentage; + int size, percentage, cwd, fd = -1; enum layout_type type; struct layout_cell *lc; - const char *template; struct client *c; struct format_tree *ft; - char *cp; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); @@ -83,7 +84,29 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmdq_default_path(cmdq, args_get(args, 'c')); + + if (args_has(args, 'c')) { + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, s->curw); + format_window_pane(ft, s->curw->window->active); + cp = format_expand(ft, args_get(args, 'c')); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + cwd = fd; + } else if (cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else + cwd = s->cwd; type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) @@ -156,6 +179,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } notify_window_layout_changed(w); + + if (fd != -1) + close(fd); return (CMD_RETURN_NORMAL); error: @@ -164,5 +190,7 @@ error: window_remove_pane(w, new_wp); cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); + if (fd != -1) + close(fd); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 8f86eba3..19c4dc15 100644 --- a/format.c +++ b/format.c @@ -403,7 +403,6 @@ format_client(struct format_tree *ft, struct client *c) time_t t; struct session *s; - format_add(ft, "client_cwd", "%s", c->cwd); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); if (c->tty.path != NULL) @@ -552,8 +551,6 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_pid", "%ld", (long) wp->pid); if (wp->cmd != NULL) format_add(ft, "pane_start_command", "%s", wp->cmd); - if (wp->cwd != NULL) - format_add(ft, "pane_start_path", "%s", wp->cwd); if ((cwd = get_proc_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); if ((cmd = get_proc_name(wp->fd, wp->tty)) != NULL) { diff --git a/options-table.c b/options-table.c index 22913854..97ca90be 100644 --- a/options-table.c +++ b/options-table.c @@ -126,11 +126,6 @@ const struct options_table_entry session_options_table[] = { .default_str = "" }, - { .name = "default-path", - .type = OPTIONS_TABLE_STRING, - .default_str = "~" - }, - { .name = "default-shell", .type = OPTIONS_TABLE_STRING, .default_str = _PATH_BSHELL diff --git a/server-client.c b/server-client.c index 4bba5f7d..a39f56d8 100644 --- a/server-client.c +++ b/server-client.c @@ -41,8 +41,7 @@ int server_client_assume_paste(struct session *); int server_client_msg_dispatch(struct client *); void server_client_msg_command(struct client *, struct imsg *); -void server_client_msg_identify( - struct client *, struct msg_identify_data *, int); +void server_client_msg_identify(struct client *, struct imsg *); void server_client_msg_shell(struct client *); /* Create a new client. */ @@ -151,6 +150,8 @@ server_client_lost(struct client *c) */ if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); + free(c->ttyname); + free(c->term); evbuffer_free (c->stdin_data); evbuffer_free (c->stdout_data); @@ -162,6 +163,7 @@ server_client_lost(struct client *c) screen_free(&c->status); free(c->title); + close(c->cwd); evtimer_del(&c->repeat_timer); @@ -179,7 +181,6 @@ server_client_lost(struct client *c) free(c->prompt_string); free(c->prompt_buffer); - free(c->cwd); c->cmdq->dead = 1; cmdq_free(c->cmdq); @@ -786,8 +787,6 @@ int server_client_msg_dispatch(struct client *c) { struct imsg imsg; - struct msg_identify_data identifydata; - struct msg_environ_data environdata; struct msg_stdin_data stdindata; const char *data; ssize_t n, datalen; @@ -813,12 +812,14 @@ server_client_msg_dispatch(struct client *c) log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); switch (imsg.hdr.type) { - case MSG_IDENTIFY: - if (datalen != sizeof identifydata) - fatalx("bad MSG_IDENTIFY size"); - memcpy(&identifydata, imsg.data, sizeof identifydata); - - server_client_msg_identify(c, &identifydata, imsg.fd); + case MSG_IDENTIFY_FLAGS: + case MSG_IDENTIFY_TERM: + case MSG_IDENTIFY_TTYNAME: + case MSG_IDENTIFY_CWD: + case MSG_IDENTIFY_STDIN: + case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_DONE: + server_client_msg_identify(c, &imsg); break; case MSG_COMMAND: server_client_msg_command(c, &imsg); @@ -876,23 +877,12 @@ server_client_msg_dispatch(struct client *c) server_redraw_client(c); recalculate_sizes(); break; - case MSG_ENVIRON: - if (datalen != sizeof environdata) - fatalx("bad MSG_ENVIRON size"); - memcpy(&environdata, imsg.data, sizeof environdata); - - environdata.var[(sizeof environdata.var) - 1] = '\0'; - if (strchr(environdata.var, '=') != NULL) - environ_put(&c->environ, environdata.var); - break; case MSG_SHELL: if (datalen != 0) fatalx("bad MSG_SHELL size"); server_client_msg_shell(c); break; - default: - fatalx("unexpected message"); } imsg_free(&imsg); @@ -951,46 +941,99 @@ error: /* Handle identify message. */ void -server_client_msg_identify( - struct client *c, struct msg_identify_data *data, int fd) +server_client_msg_identify(struct client *c, struct imsg *imsg) { - c->cwd = NULL; - data->cwd[(sizeof data->cwd) - 1] = '\0'; - if (*data->cwd != '\0') - c->cwd = xstrdup(data->cwd); + const char *data; + size_t datalen; + int flags; - if (data->flags & CLIENT_CONTROL) { + if (c->flags & CLIENT_IDENTIFIED) + fatalx("out-of-order identify message"); + + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_IDENTIFY_FLAGS: + if (datalen != sizeof flags) + fatalx("bad MSG_IDENTIFY_FLAGS size"); + memcpy(&flags, data, sizeof flags); + c->flags |= flags; + break; + case MSG_IDENTIFY_TERM: + if (data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_TERM string"); + c->term = xstrdup(data); + break; + case MSG_IDENTIFY_TTYNAME: + if (data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_TTYNAME string"); + c->ttyname = xstrdup(data); + break; + case MSG_IDENTIFY_CWD: + if (datalen != 0) + fatalx("bad MSG_IDENTIFY_CWD size"); + c->cwd = imsg->fd; + break; + case MSG_IDENTIFY_STDIN: + if (datalen != 0) + fatalx("bad MSG_IDENTIFY_STDIN size"); + c->fd = imsg->fd; + break; + case MSG_IDENTIFY_ENVIRON: + if (data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_ENVIRON string"); + if (strchr(data, '=') != NULL) + environ_put(&c->environ, data); + break; + default: + break; + } + + if (imsg->hdr.type != MSG_IDENTIFY_DONE) + return; + c->flags |= CLIENT_IDENTIFIED; + +#ifdef __CYGWIN__ + c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); + c->cwd = open(".", O_RDONLY); +#endif + + if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; + evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; - c->flags |= CLIENT_CONTROL; - if (data->flags & CLIENT_CONTROLCONTROL) + + if (c->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); server_write_client(c, MSG_STDIN, NULL, 0); c->tty.fd = -1; c->tty.log_fd = -1; - close(fd); + close(c->fd); + c->fd = -1; + return; } - if (fd == -1) + if (c->fd == -1) return; - if (!isatty(fd)) { - close(fd); + if (!isatty(c->fd)) { + close(c->fd); + c->fd = -1; return; } - data->term[(sizeof data->term) - 1] = '\0'; - tty_init(&c->tty, c, fd, data->term); - if (data->flags & CLIENT_UTF8) + tty_init(&c->tty, c, c->fd, c->term); + if (c->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; - if (data->flags & CLIENT_256COLOURS) + if (c->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); - if (!(data->flags & CLIENT_CONTROL)) + if (!(c->flags & CLIENT_CONTROL)) c->flags |= CLIENT_TERMINAL; } diff --git a/server-fn.c b/server-fn.c index f6485b49..d0747628 100644 --- a/server-fn.c +++ b/server-fn.c @@ -56,8 +56,8 @@ server_write_ready(struct client *c) } int -server_write_client( - struct client *c, enum msgtype type, const void *buf, size_t len) +server_write_client(struct client *c, enum msgtype type, const void *buf, + size_t len) { struct imsgbuf *ibuf = &c->ibuf; int error; @@ -73,8 +73,8 @@ server_write_client( } void -server_write_session( - struct session *s, enum msgtype type, const void *buf, size_t len) +server_write_session(struct session *s, enum msgtype type, const void *buf, + size_t len) { struct client *c; u_int i; diff --git a/session.c b/session.c index f967de19..4f6ebed6 100644 --- a/session.c +++ b/session.c @@ -85,9 +85,8 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *name, const char *cmd, const char *cwd, - struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, - char **cause) +session_create(const char *name, const char *cmd, int cwd, struct environ *env, + struct termios *tio, int idx, u_int sx, u_int sy, char **cause) { struct session *s; @@ -99,7 +98,7 @@ session_create(const char *name, const char *cmd, const char *cwd, fatal("gettimeofday failed"); session_update_activity(s); - s->cwd = xstrdup(cwd); + s->cwd = dup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); @@ -171,7 +170,7 @@ session_destroy(struct session *s) winlink_remove(&s->windows, wl); } - free(s->cwd); + close(s->cwd); RB_INSERT(sessions, &dead_sessions, s); } @@ -227,8 +226,8 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * -session_new(struct session *s, - const char *name, const char *cmd, const char *cwd, int idx, char **cause) +session_new(struct session *s, const char *name, const char *cmd, int cwd, + int idx, char **cause) { struct window *w; struct winlink *wl; @@ -251,8 +250,8 @@ session_new(struct session *s, shell = _PATH_BSHELL; hlimit = options_get_number(&s->options, "history-limit"); - w = window_create( - name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause); + w = window_create(name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, + hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(&env); diff --git a/tmux.1 b/tmux.1 index 07d6b3fe..eb417da0 100644 --- a/tmux.1 +++ b/tmux.1 @@ -564,6 +564,7 @@ The following commands are available to manage clients and sessions: .Bl -tag -width Ds .It Xo Ic attach-session .Op Fl dr +.Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) @@ -597,6 +598,10 @@ needs to select the most recently used session, it will prefer the most recently used .Em unattached session. +.Pp +.Fl c +will set the session working directory (used for new windows) to +.Ar working-directory . .It Xo Ic detach-client .Op Fl P .Op Fl a @@ -1509,13 +1514,6 @@ is not specified, the value of the option is used. .Fl c specifies the working directory in which the new window is created. -It may have an absolute path or one of the following values (or a subdirectory): -.Bl -column "XXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXX" -offset indent -.It Li "Empty string" Ta "Current pane's directory" -.It Li "~" Ta "User's home directory" -.It Li "-" Ta "Where session was started" -.It Li "." Ta "Where server was started" -.El .Pp When the shell command completes, the window closes. See the @@ -2175,17 +2173,6 @@ The default is an empty string, which instructs to create a login shell using the value of the .Ic default-shell option. -.It Ic default-path Ar path -Set the default working directory for new panes. -If empty, the working directory is determined from the process -running in the active pane, from the command line environment or from the -working directory where the session was created. -Otherwise the same options are available as for the -.Fl c -flag to -.Ic new-window . -The default is -.Ql ~ . .It Ic default-shell Ar path Specify the default shell. This is used as the login shell for new windows when the @@ -3054,7 +3041,6 @@ The following variables are available, where appropriate: .It Li "client_activity_string" Ta "" Ta "String time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" .It Li "client_created_string" Ta "" Ta "String time client created" -.It Li "client_cwd" Ta "" Ta "Working directory of client" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" diff --git a/tmux.c b/tmux.c index 140946b9..a78746ae 100644 --- a/tmux.c +++ b/tmux.c @@ -124,30 +124,6 @@ areshell(const char *shell) return (0); } -const char* -get_full_path(const char *wd, const char *path) -{ - int fd; - static char newpath[MAXPATHLEN]; - const char *retval; - - fd = open(".", O_RDONLY); - if (fd == -1) - return (NULL); - - retval = NULL; - if (chdir(wd) == 0) { - if (realpath(path, newpath) == 0) - retval = newpath; - } - - if (fchdir(fd) != 0) - chdir("/"); - close(fd); - - return (retval); -} - void parseenvironment(void) { @@ -246,7 +222,7 @@ int main(int argc, char **argv) { struct passwd *pw; - char *s, *path, *label, *home, **var; + char *s, *path, *label, *home, **var, tmp[MAXPATHLEN]; int opt, flags, quiet, keys; #ifdef DEBUG @@ -327,6 +303,8 @@ main(int argc, char **argv) environ_init(&global_environ); for (var = environ; *var != NULL; var++) environ_put(&global_environ, *var); + if (getcwd(tmp, sizeof tmp) != NULL) + environ_set(&global_environ, "PWD", tmp); options_init(&global_options, NULL); options_table_populate_tree(server_options_table, &global_options); diff --git a/tmux.h b/tmux.h index 2d631a17..e281790d 100644 --- a/tmux.h +++ b/tmux.h @@ -58,13 +58,6 @@ extern char **environ; /* Automatic name refresh interval, in milliseconds. */ #define NAME_INTERVAL 500 -/* - * Maximum sizes of strings in message data. Don't forget to bump - * PROTOCOL_VERSION if any of these change! - */ -#define TERMINAL_LENGTH 128 /* length of TERM environment variable */ -#define ENVIRON_LENGTH 1024 /* environment variable length */ - /* * UTF-8 data size. This must be big enough to hold combined characters as well * as single. @@ -463,9 +456,6 @@ enum msgtype { MSG_SUSPEND, MSG_UNLOCK, MSG_WAKEUP, - - MSG_IDENTIFY = 300, - MSG_ENVIRON }; /* @@ -480,17 +470,6 @@ struct msg_command_data { int argc; }; /* followed by packed argv */ -struct msg_identify_data { - char cwd[MAXPATHLEN]; - char term[TERMINAL_LENGTH]; - - int flags; -}; - -struct msg_environ_data { - char var[ENVIRON_LENGTH]; -}; - struct msg_stdin_data { ssize_t size; char data[BUFSIZ]; @@ -937,7 +916,7 @@ struct window_pane { char *cmd; char *shell; - char *cwd; + int cwd; pid_t pid; char tty[TTY_NAME_MAX]; @@ -1084,7 +1063,7 @@ struct session { u_int id; char *name; - char *cwd; + int cwd; struct timeval creation_time; struct timeval activity_time; @@ -1284,6 +1263,7 @@ RB_HEAD(status_out_tree, status_out); struct client { struct imsgbuf ibuf; + int fd; struct event event; int retval; @@ -1293,8 +1273,10 @@ struct client { struct environ environ; char *title; - char *cwd; + int cwd; + char *term; + char *ttyname; struct tty tty; void (*stdin_callback)(struct client *, int, void *); @@ -1527,7 +1509,6 @@ void logfile(const char *); const char *getshell(void); int checkshell(const char *); int areshell(const char *); -const char* get_full_path(const char *, const char *); void setblocking(int, int); __dead void shell_exec(const char *, const char *); @@ -1763,7 +1744,6 @@ int cmd_find_index(struct cmd_q *, const char *, struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); char *cmd_template_replace(const char *, const char *, int); -const char *cmd_default_path(const char *, const char *, const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; @@ -1854,7 +1834,8 @@ extern const struct cmd_entry cmd_up_pane_entry; extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ -enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int); +enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, + const char *); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); @@ -1872,7 +1853,6 @@ void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); void cmdq_flush(struct cmd_q *); -const char *cmdq_default_path(struct cmd_q *, const char *); /* cmd-string.c */ int cmd_string_parse(const char *, struct cmd_list **, const char *, @@ -2144,9 +2124,9 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *); int window_index(struct window *, u_int *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); -struct window *window_create(const char *, const char *, const char *, - const char *, struct environ *, struct termios *, - u_int, u_int, u_int, char **); +struct window *window_create(const char *, const char *, const char *, int, + struct environ *, struct termios *, u_int, u_int, u_int, + char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); void window_set_active_at(struct window *, u_int, u_int); @@ -2170,8 +2150,8 @@ struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); void window_pane_timer_start(struct window_pane *); int window_pane_spawn(struct window_pane *, const char *, - const char *, const char *, struct environ *, - struct termios *, char **); + const char *, int, struct environ *, struct termios *, + char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); @@ -2307,7 +2287,7 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, const char *, +struct session *session_create(const char *, const char *, int, struct environ *, struct termios *, int, u_int, u_int, char **); void session_destroy(struct session *); @@ -2315,8 +2295,8 @@ int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); -struct winlink *session_new(struct session *, - const char *, const char *, const char *, int, char **); +struct winlink *session_new(struct session *, const char *, const char *, int, + int, char **); struct winlink *session_attach( struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); diff --git a/window.c b/window.c index 0ea70688..83e14db6 100644 --- a/window.c +++ b/window.c @@ -309,7 +309,7 @@ window_create1(u_int sx, u_int sy) struct window * window_create(const char *name, const char *cmd, const char *shell, - const char *cwd, struct environ *env, struct termios *tio, + int cwd, struct environ *env, struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; @@ -675,7 +675,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->cmd = NULL; wp->shell = NULL; - wp->cwd = NULL; + wp->cwd = -1; wp->fd = -1; wp->event = NULL; @@ -730,7 +730,7 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); - free(wp->cwd); + close(wp->cwd); free(wp->shell); free(wp->cmd); free(wp); @@ -738,7 +738,7 @@ window_pane_destroy(struct window_pane *wp) int window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, - const char *cwd, struct environ *env, struct termios *tio, char **cause) + int cwd, struct environ *env, struct termios *tio, char **cause) { struct winsize ws; char *argv0, paneid[16]; @@ -757,9 +757,9 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, free(wp->shell); wp->shell = xstrdup(shell); } - if (cwd != NULL) { - free(wp->cwd); - wp->cwd = xstrdup(cwd); + if (cwd != -1) { + close(wp->cwd); + wp->cwd = dup(cwd); } log_debug("spawn: %s -- %s", wp->shell, wp->cmd); @@ -774,7 +774,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, xasprintf(cause, "%s: %s", cmd, strerror(errno)); return (-1); case 0: - if (chdir(wp->cwd) != 0) + if (fchdir(wp->cwd) != 0) chdir("/"); if (tcgetattr(STDIN_FILENO, &tio2) != 0) From b8b85fbb0c6cf4e9a3fa650ec7dc5036a1b0b01a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:27:38 +0000 Subject: [PATCH 162/949] Don't look at string[length - 1] if length == 0. --- client.c | 4 ++-- server-client.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client.c b/client.c index 82e43992..e1bd47c2 100644 --- a/client.c +++ b/client.c @@ -575,7 +575,7 @@ client_dispatch_wait(void *data0) imsg_free(&imsg); return (-1); case MSG_SHELL: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_SHELL string"); clear_signals(0); @@ -659,7 +659,7 @@ client_dispatch_attached(void) kill(getpid(), SIGTSTP); break; case MSG_LOCK: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_LOCK string"); system(data); diff --git a/server-client.c b/server-client.c index a39f56d8..e202902e 100644 --- a/server-client.c +++ b/server-client.c @@ -961,12 +961,12 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) c->flags |= flags; break; case MSG_IDENTIFY_TERM: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); c->term = xstrdup(data); break; case MSG_IDENTIFY_TTYNAME: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TTYNAME string"); c->ttyname = xstrdup(data); break; @@ -981,7 +981,7 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) c->fd = imsg->fd; break; case MSG_IDENTIFY_ENVIRON: - if (data[datalen - 1] != '\0') + if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) environ_put(&c->environ, data); From 7936ce38749a4751120c856a112ee85122df612c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:28:08 +0000 Subject: [PATCH 163/949] Show session name in detached message. Requested by somebody a few months ago who didn't bother testing it. But it works for me anyway. --- client.c | 21 ++++++++++++++++++--- cmd-attach-session.c | 10 +++++++--- cmd-detach-client.c | 22 +++++++++++++++------- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/client.c b/client.c index e1bd47c2..0d57b793 100644 --- a/client.c +++ b/client.c @@ -48,6 +48,7 @@ enum { } client_exitreason = CLIENT_EXIT_NONE; int client_exitval; enum msgtype client_exittype; +const char *client_exitsession; int client_attached; int client_get_lock(char *); @@ -138,12 +139,24 @@ failed: const char * client_exit_message(void) { + static char msg[256]; + switch (client_exitreason) { case CLIENT_EXIT_NONE: break; case CLIENT_EXIT_DETACHED: + if (client_exitsession != NULL) { + xsnprintf(msg, sizeof msg, "detached " + "(from session %s)", client_exitsession); + return (msg); + } return ("detached"); case CLIENT_EXIT_DETACHED_HUP: + if (client_exitsession != NULL) { + xsnprintf(msg, sizeof msg, "detached and SIGHUP " + "(from session %s)", client_exitsession); + return (msg); + } return ("detached and SIGHUP"); case CLIENT_EXIT_LOST_TTY: return ("lost tty"); @@ -582,6 +595,7 @@ client_dispatch_wait(void *data0) shell_exec(data, data0); /* NOTREACHED */ case MSG_DETACH: + case MSG_DETACHKILL: client_write_server(MSG_EXITING, NULL, 0); break; case MSG_EXITED: @@ -613,11 +627,12 @@ client_dispatch_attached(void) log_debug("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { - case MSG_DETACHKILL: case MSG_DETACH: - if (datalen != 0) - fatalx("bad MSG_DETACH size"); + case MSG_DETACHKILL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_DETACH string"); + client_exitsession = xstrdup(data); client_exittype = imsg.hdr.type; if (imsg.hdr.type == MSG_DETACHKILL) client_exitreason = CLIENT_EXIT_DETACHED_HUP; diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 8dcc5997..c71e6f1f 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -77,7 +77,9 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, continue; if (c == cmdq->client) continue; - server_write_client(c, MSG_DETACH, NULL, 0); + server_write_client(c, MSG_DETACH, + c->session->name, + strlen(c->session->name) + 1); } } @@ -138,8 +140,10 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (rflag) cmdq->client->flags |= CLIENT_READONLY; - if (dflag) - server_write_session(s, MSG_DETACH, NULL, 0); + if (dflag) { + server_write_session(s, MSG_DETACH, s->name, + strlen(s->name) + 1); + } update = options_get_string(&s->options, "update-environment"); environ_update(update, &cmdq->client->environ, &s->environ); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index fc80499c..82001bee 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -18,6 +18,8 @@ #include +#include + #include "tmux.h" /* @@ -40,8 +42,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c, *c2; - struct session *s; - enum msgtype msgtype; + struct session *s; + enum msgtype msgtype; u_int i; if (args_has(args, 'P')) @@ -56,8 +58,10 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session == s) - server_write_client(c, msgtype, NULL, 0); + if (c == NULL || c->session != s) + continue; + server_write_client(c, msgtype, c->session->name, + strlen(c->session->name) + 1); } } else { c = cmd_find_client(cmdq, args_get(args, 't'), 0); @@ -69,10 +73,14 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) c2 = ARRAY_ITEM(&clients, i); if (c2 == NULL || c == c2) continue; - server_write_client(c2, msgtype, NULL, 0); + server_write_client(c2, msgtype, + c2->session->name, + strlen(c2->session->name) + 1); } - } else - server_write_client(c, msgtype, NULL, 0); + } else { + server_write_client(c, msgtype, c->session->name, + strlen(c->session->name) + 1); + } } return (CMD_RETURN_STOP); From 909e1c1a86667bf71eab83ac064f038995bf5d20 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:28:38 +0000 Subject: [PATCH 164/949] Don't boke when figuring out working directory from configuration file. --- cmd-new-session.c | 2 +- cmd-new-window.c | 2 +- cmd-split-window.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 5e69a77c..49ece8b4 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -115,7 +115,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } cwd = fd; - } else if (c->session == NULL) + } else if (c != NULL && c->session == NULL) cwd = c->cwd; else if ((c0 = cmd_current_client(cmdq)) != NULL) cwd = c0->session->cwd; diff --git a/cmd-new-window.c b/cmd-new-window.c index 445f0a33..e2a01190 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -125,7 +125,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } cwd = fd; - } else if (cmdq->client->session == NULL) + } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else cwd = s->cwd; diff --git a/cmd-split-window.c b/cmd-split-window.c index 43d47c47..92e6121f 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -103,7 +103,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } cwd = fd; - } else if (cmdq->client->session == NULL) + } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else cwd = s->cwd; From 6ac7abe8f038c21c1cf33d50a02e970f0de81c09 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:28:56 +0000 Subject: [PATCH 165/949] Remove now unused cmd_get_default_path. --- cmd.c | 71 ----------------------------------------------------------- 1 file changed, 71 deletions(-) diff --git a/cmd.c b/cmd.c index 2aa5f514..b349da77 100644 --- a/cmd.c +++ b/cmd.c @@ -1277,74 +1277,3 @@ cmd_template_replace(const char *template, const char *s, int idx) return (buf); } - -/* - * Return the default path for a new pane. Several special values are accepted: - * the empty string or relative path for the current working directory, - * ~ for the user's home, - for the base working directory, . for the server - * working directory. - */ -const char * -cmd_default_path(const char *base, const char *current, const char *in) -{ - const char *root; - struct environ_entry *envent; - char tmp[MAXPATHLEN]; - struct passwd *pw; - int n; - size_t skip; - static char path[MAXPATHLEN]; - - skip = 1; - if (strcmp(in, "$HOME") == 0 || strncmp(in, "$HOME/", 6) == 0) { - /* User's home directory - $HOME. */ - skip = 5; - goto find_home; - } else if (in[0] == '~' && (in[1] == '\0' || in[1] == '/')) { - /* User's home directory - ~. */ - goto find_home; - } else if (in[0] == '-' && (in[1] == '\0' || in[1] == '/')) { - /* Base working directory. */ - root = base; - goto complete_path; - } else if (in[0] == '.' && (in[1] == '\0' || in[1] == '/')) { - /* Server working directory. */ - if (getcwd(tmp, sizeof tmp) != NULL) { - root = tmp; - goto complete_path; - } - return ("/"); - } else if (*in == '/') { - /* Absolute path. */ - return (in); - } else { - /* Empty or relative path. */ - if (current != NULL) - root = current; - else - return (base); - skip = 0; - goto complete_path; - } - - return (base); - -find_home: - envent = environ_find(&global_environ, "HOME"); - if (envent != NULL && *envent->value != '\0') - root = envent->value; - else if ((pw = getpwuid(getuid())) != NULL) - root = pw->pw_dir; - else - return (base); - -complete_path: - if (root[skip] == '\0') { - strlcpy(path, root, sizeof path); - return (path); - } - n = snprintf(path, sizeof path, "%s/%s", root, in + skip); - if (n > 0 && (size_t)n < sizeof path) - return (path); - return (base); -} From c1ccefc62d2a70c91ac31cff141031d61cc0a12c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:29:35 +0000 Subject: [PATCH 166/949] We accidentally haven't been using $TMUX to work out the session for a while and in fact it is less useful that using the client ttyname. So don't bother and don't pass it from the client. If we need it in future it is in c->environ. --- client.c | 4 ---- cmd.c | 8 -------- server-client.c | 2 ++ tmux.c | 30 ++++++++---------------------- tmux.h | 7 ------- 5 files changed, 10 insertions(+), 41 deletions(-) diff --git a/client.c b/client.c index 0d57b793..3b929b71 100644 --- a/client.c +++ b/client.c @@ -280,10 +280,6 @@ client_main(int argc, char **argv, int flags) size += strlen(argv[i]) + 1; data = xmalloc((sizeof *data) + size); - /* Fill in command line arguments. */ - data->pid = environ_pid; - data->session_id = environ_session_id; - /* Prepare command for server. */ data->argc = argc; if (cmd_pack_argv(argc, argv, (char*)(data + 1), size) != 0) { diff --git a/cmd.c b/cmd.c index b349da77..2edda633 100644 --- a/cmd.c +++ b/cmd.c @@ -314,7 +314,6 @@ cmd_print(struct cmd *cmd, char *buf, size_t len) struct session * cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) { - struct msg_command_data *data = cmdq->msgdata; struct client *c = cmdq->client; struct session *s; struct sessionslist ss; @@ -356,13 +355,6 @@ cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) return (s); } - /* Use the session from the TMUX environment variable. */ - if (data != NULL && data->pid == getpid() && data->session_id != -1) { - s = session_find_by_id(data->session_id); - if (s != NULL) - return (s); - } - return (cmd_choose_session(prefer_unattached)); } diff --git a/server-client.c b/server-client.c index e202902e..6aa2a0fa 100644 --- a/server-client.c +++ b/server-client.c @@ -62,6 +62,8 @@ server_client_create(int fd) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); + environ_init(&c->environ); + c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; diff --git a/tmux.c b/tmux.c index a78746ae..74d827a5 100644 --- a/tmux.c +++ b/tmux.c @@ -49,11 +49,8 @@ time_t start_time; char socket_path[MAXPATHLEN]; int login_shell; char *environ_path; -pid_t environ_pid = -1; -int environ_session_id = -1; __dead void usage(void); -void parseenvironment(void); char *makesocketpath(const char *); __dead void @@ -124,23 +121,6 @@ areshell(const char *shell) return (0); } -void -parseenvironment(void) -{ - char *env, path[256]; - long pid; - int id; - - if ((env = getenv("TMUX")) == NULL) - return; - - if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3) - return; - environ_path = xstrdup(path); - environ_pid = pid; - environ_session_id = id; -} - char * makesocketpath(const char *label) { @@ -223,7 +203,9 @@ main(int argc, char **argv) { struct passwd *pw; char *s, *path, *label, *home, **var, tmp[MAXPATHLEN]; - int opt, flags, quiet, keys; + char in[256]; + long long pid; + int opt, flags, quiet, keys, session; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -351,11 +333,15 @@ main(int argc, char **argv) } } + /* Get path from environment. */ + s = getenv("TMUX"); + if (s != NULL && sscanf(s, "%255[^,],%lld,%d", in, &pid, &session) == 3) + environ_path = xstrdup(in); + /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. */ - parseenvironment(); if (path == NULL) { /* If no -L, use the environment. */ if (label == NULL) { diff --git a/tmux.h b/tmux.h index e281790d..e18176e8 100644 --- a/tmux.h +++ b/tmux.h @@ -464,9 +464,6 @@ enum msgtype { * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { - pid_t pid; /* from $TMUX or -1 */ - int session_id; /* from $TMUX or -1 */ - int argc; }; /* followed by packed argv */ @@ -1408,8 +1405,6 @@ struct cmd_q { void (*emptyfn)(struct cmd_q *); void *data; - struct msg_command_data *msgdata; - TAILQ_ENTRY(cmd_q) waitentry; }; @@ -1503,8 +1498,6 @@ extern time_t start_time; extern char socket_path[MAXPATHLEN]; extern int login_shell; extern char *environ_path; -extern pid_t environ_pid; -extern int environ_session_id; void logfile(const char *); const char *getshell(void); int checkshell(const char *); From b85de1ddb34a1b22567e9bf89c9a21a81bf12aa1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:29:53 +0000 Subject: [PATCH 167/949] Pass -1 for cwd now not NULL. --- cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 2315b241..d7d88c27 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -77,7 +77,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[0]; else cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index e4ef6dfb..b681f2f4 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -79,7 +79,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[0]; else cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(&env); From 99e3cbc526cff605f32d61c0b4be77a8b2dbaa9f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:35:30 +0000 Subject: [PATCH 168/949] Use format_get_command() and some spacing tweaks. --- client.c | 1 + job.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client.c b/client.c index 3b929b71..0f2d3684 100644 --- a/client.c +++ b/client.c @@ -351,6 +351,7 @@ client_send_identify(int flags) for (ss = environ; *ss != NULL; ss++) client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); + client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); client_update_event(); diff --git a/job.c b/job.c index 0ae03b31..e8006308 100644 --- a/job.c +++ b/job.c @@ -145,7 +145,7 @@ job_write_callback(unused struct bufferevent *bufev, void *data) size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, - (long) job->pid, len); + (long) job->pid, len); if (len == 0) { shutdown(job->fd, SHUT_WR); From d0566a474aa43cb27c6a8fc62ff80ef32c7fe86e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 12:39:24 +0000 Subject: [PATCH 169/949] Remove the KERN_PROC_CWD the proc_current_path format (which is the only thing that uses it now). --- format.c | 5 +---- procname.c | 15 --------------- tmux.1 | 1 - tmux.h | 1 - 4 files changed, 1 insertion(+), 21 deletions(-) diff --git a/format.c b/format.c index 19c4dc15..40957a59 100644 --- a/format.c +++ b/format.c @@ -518,7 +518,6 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) struct grid_line *gl; unsigned long long size; u_int i, idx; - const char *cwd; char *cmd; size = 0; @@ -551,9 +550,7 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_pid", "%ld", (long) wp->pid); if (wp->cmd != NULL) format_add(ft, "pane_start_command", "%s", wp->cmd); - if ((cwd = get_proc_cwd(wp->fd)) != NULL) - format_add(ft, "pane_current_path", "%s", cwd); - if ((cmd = get_proc_name(wp->fd, wp->tty)) != NULL) { + if ((cmd = format_get_command(wp)) != NULL) { format_add(ft, "pane_current_command", "%s", cmd); free(cmd); } diff --git a/procname.c b/procname.c index 1518c94b..ee9b99dc 100644 --- a/procname.c +++ b/procname.c @@ -37,7 +37,6 @@ struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *get_proc_name(int, char *); -char *get_proc_cwd(int); struct kinfo_proc * cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2) @@ -132,17 +131,3 @@ error: free(buf); return (NULL); } - -char* -get_proc_cwd(int fd) -{ - int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; - static char path[MAXPATHLEN]; - size_t pathlen = sizeof path; - - if ((name[2] = tcgetpgrp(fd)) == -1) - return (NULL); - if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) - return (NULL); - return (path); -} diff --git a/tmux.1 b/tmux.1 index eb417da0..4fb588e3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3068,7 +3068,6 @@ The following variables are available, where appropriate: .It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" -.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_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" diff --git a/tmux.h b/tmux.h index e18176e8..67192740 100644 --- a/tmux.h +++ b/tmux.h @@ -2317,7 +2317,6 @@ u_int utf8_split2(u_int, u_char *); /* procname.c */ char *get_proc_name(int, char *); -char *get_proc_cwd(int); /* log.c */ void log_open(int, const char *); From 0b77d17b35fad797b26632f3305dcd2c9b994a3f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 23:31:03 +0000 Subject: [PATCH 170/949] Fix leak in format_get_command. --- format.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 40957a59..14dcd000 100644 --- a/format.c +++ b/format.c @@ -353,7 +353,7 @@ format_expand(struct format_tree *ft, const char *fmt) char * format_get_command(struct window_pane *wp) { - char *cmd; + char *cmd, *out; cmd = get_proc_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { @@ -361,7 +361,9 @@ format_get_command(struct window_pane *wp) if (cmd == NULL || *cmd == '\0') cmd = wp->shell; } - return (parse_window_name(cmd)); + out = parse_window_name(cmd); + free(cmd); + return (out); } /* Set default format keys for a session. */ From 17ec688ced8c1d6104cab700f9713cfa0fa1f858 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Oct 2013 23:31:28 +0000 Subject: [PATCH 171/949] Bracket in the wrong place in description of c0-change-trigger. --- tmux.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 4fb588e3..65849212 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2734,8 +2734,8 @@ instead redraw it entirely every .Ar interval milliseconds. This helps to prevent fast output (such as -.Xr yes 1 -overwhelming the terminal). +.Xr yes 1 ) +overwhelming the terminal. The default is a trigger of 250 and an interval of 100. A trigger of zero disables the rate limiting. .Pp From 98b81e983428c7770022c698a0c4ef3c7fc4ea36 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Oct 2013 08:03:43 +0000 Subject: [PATCH 172/949] And get it right this time... don't leak if it is an empty string either. --- format.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/format.c b/format.c index 14dcd000..bfc40906 100644 --- a/format.c +++ b/format.c @@ -357,9 +357,12 @@ format_get_command(struct window_pane *wp) cmd = get_proc_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { - cmd = wp->cmd; - if (cmd == NULL || *cmd == '\0') - cmd = wp->shell; + free(cmd); + cmd = xstrdup(wp->cmd); + if (cmd == NULL || *cmd == '\0') { + free(cmd); + cmd = xstrdup(wp->shell); + } } out = parse_window_name(cmd); free(cmd); From ffba21a60c26eefe5e4cb67dd290514fa3b5af17 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Oct 2013 08:06:03 +0000 Subject: [PATCH 173/949] Remove stray return, from Chris Johnsen. --- cmd-unbind-key.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index b89340da..5d86665b 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -50,7 +50,6 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "missing key"); return (CMD_RETURN_ERROR); } - return (CMD_RETURN_ERROR); key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); From 4901d9ddc8d8c33ecdca363dcb67e66482745fa5 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Oct 2013 08:07:12 +0000 Subject: [PATCH 174/949] Don't leak file descriptors in the rare MSG_VERSION case. From Chris Johnsen. --- server-client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server-client.c b/server-client.c index 6aa2a0fa..b6d4870d 100644 --- a/server-client.c +++ b/server-client.c @@ -808,6 +808,8 @@ server_client_msg_dispatch(struct client *c) if (imsg.hdr.peerid != PROTOCOL_VERSION) { server_write_client(c, MSG_VERSION, NULL, 0); c->flags |= CLIENT_BAD; + if (imsg.fd != -1) + close(imsg.fd); imsg_free(&imsg); continue; } From b8b31ad53e693675dbe1e2f7a66c69701f119754 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Oct 2013 10:27:23 +0100 Subject: [PATCH 175/949] Add openat() to compat. --- Makefile.am | 3 +++ compat.h | 8 ++++++- compat/openat.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 7 ++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 compat/openat.c diff --git a/Makefile.am b/Makefile.am index fb707df0..690e466d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -238,6 +238,9 @@ endif if NO_CFMAKERAW nodist_tmux_SOURCES += compat/cfmakeraw.c endif +if NO_OPENAT +nodist_tmux_SOURCES += compat/openat.c +endif # Install tmux.1 in the right format. install-exec-hook: diff --git a/compat.h b/compat.h index b84ff400..ab3224b1 100644 --- a/compat.h +++ b/compat.h @@ -243,7 +243,13 @@ int unsetenv(const char *); #ifndef HAVE_CFMAKERAW /* cfmakeraw.c */ -void cfmakeraw(struct termios *tio); +void cfmakeraw(struct termios *); +#endif + +#ifndef HAVE_OPENAT +/* openat.c */ +#define AT_FDCWD -100 +int openat(int, const char *, int, ...); #endif #ifdef HAVE_GETOPT diff --git a/compat/openat.c b/compat/openat.c new file mode 100644 index 00000000..005235b4 --- /dev/null +++ b/compat/openat.c @@ -0,0 +1,63 @@ +/* $Id$ */ + +/* + * Copyright (c) 2013 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "tmux.h" + +int +openat(int fd, const char *path, int flags, ...) +{ + mode_t mode; + va_list ap; + int dotfd, retval, saved_errno; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } else + mode = 0; + + dotfd = -1; + if (fd != AT_FDCWD) { + dotfd = open(".", O_RDONLY); + if (dotfd == -1) + return (-1); + if (fchdir(fd) != 0) + return (-1); + } + + retval = open(path, flags, mode); + + if (dotfd != -1) { + if (fchdir(dotfd) != 0) { + saved_errno = errno; + close(retval); + close(dotfd); + errno = saved_errno; + return (-1); + } + close(dotfd); + } + + return (retval); +} diff --git a/configure.ac b/configure.ac index 590b9db0..b047eef2 100644 --- a/configure.ac +++ b/configure.ac @@ -323,6 +323,13 @@ if test "x$found_cfmakeraw" = xyes; then fi AM_CONDITIONAL(NO_CFMAKERAW, [test "x$found_cfmakeraw" = xno]) +# Look for openat, compat/openat.c used if missing. +AC_CHECK_FUNC(openat, found_openat=yes, found_openat=no) +if test "x$found_openat" = xyes; then + AC_DEFINE(HAVE_OPENAT) +fi +AM_CONDITIONAL(NO_OPENAT, [test "x$found_openat" = xno]) + # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) From d3f37566e2f111f2019edb8c07e39dcae15c3574 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 5 Oct 2013 12:45:24 +0100 Subject: [PATCH 176/949] Ignore .dirstamp files GNU automake 1.14+ uses these files for subdir-option detection. We don't want to accidentally commit these. They're not useful to us. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 20a3bbf4..ba580eda 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ core tags .deps/ +compat/.dirstamp aclocal.m4 autom4te.cache/ config.log From 5b065e93b35cb5785b7a370a0ac4478b5e7ea698 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 3 Oct 2013 16:56:12 +0100 Subject: [PATCH 177/949] Check setupterm() in libtinfo also Some ncurses packages have build time configuration options to separate its different parts into separate libraries. Some Linux distributions in particular separate out the terminfo routines in to libtinfo. This change teaches configure that setupterm() can also be found there. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b047eef2..10588335 100644 --- a/configure.ac +++ b/configure.ac @@ -135,7 +135,7 @@ fi # Look for curses. AC_SEARCH_LIBS( setupterm, - [terminfo curses ncurses], + [terminfo curses ncurses tinfo], found_curses=yes, found_curses=no ) From 7c3e7d65355db41ab848c97383b9e996910acd75 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 3 Oct 2013 22:31:26 +0100 Subject: [PATCH 178/949] Add subdir-objects to shut automake up automake 1.14 onwards has started emitting lots of warnings about this option: automake: warning: possible forward-incompatibility. automake: At least a source file is in a subdirectory, but the 'subdir-objects' automake: automake option hasn't been enabled. For now, the corresponding output automake: object file(s) will be placed in the top-level directory. However, automake: this behaviour will change in future Automake versions: they will automake: unconditionally cause object files to be placed in the same subdirectory automake: of the corresponding sources. automake: You are advised to start using 'subdir-objects' option throughout your automake: project, to avoid future incompatibilities. So enable this in AM_INIT_AUTOMAKE. This doesn't seem to break older automake versions. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 10588335..ceb37db8 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_INIT(tmux, 1.9) AC_CONFIG_AUX_DIR(etc) -AM_INIT_AUTOMAKE([foreign]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) AC_CANONICAL_HOST From 570028e9c011340a9cb5adbebba8bf51686c4d36 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 31 Aug 2013 10:42:09 +0100 Subject: [PATCH 179/949] Add entry about smaller clients based on text from Thomas Adam. --- FAQ | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/FAQ b/FAQ index 81421bc0..da72d433 100644 --- a/FAQ +++ b/FAQ @@ -421,4 +421,19 @@ On OS X, reattach-to-usernamespace lets pbcopy/pbpaste work: https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard +* Why do I see dots around a session when I attach to it? + +tmux limits the size of the window to the smallest attached session. If +it didn't do this then it would be impossible to see the entire window. +The dots mark the size of the window tmux can display. + +To avoid this, detach all other clients when attaching: + + $ tmux attach -d + +Or from inside tmux by detaching individual clients with C-b D or all +using: + + C-b : attach -d + $Id$ From d8d746b4b830c2b36a7d1667ee620535b89b57f4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 23 Aug 2013 15:25:05 +0100 Subject: [PATCH 180/949] Set EVENT_NOEPOLL on Linux again. --- osdep-linux.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/osdep-linux.c b/osdep-linux.c index b65acffc..ccac2670 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -84,14 +84,7 @@ osdep_get_cwd(int fd) struct event_base * osdep_event_init(void) { - /* - * On Linux, epoll doesn't work on /dev/null (yes, really). - * - * This has been commented because libevent versions up until the very - * latest (1.4 git or 2.0.10) do not handle signals properly when using - * poll or select, causing hangs. - * - */ - /* setenv("EVENT_NOEPOLL", "1", 1); */ + /* On Linux, epoll doesn't work on /dev/null (yes, really). */ + setenv("EVENT_NOEPOLL", "1", 1); return (event_init()); } From 558e5639d0f82775cd70932f6eb46a45413387c2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:33:34 +0100 Subject: [PATCH 181/949] Remove from TODO. --- TODO | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO b/TODO index 92b60a9a..a43ff189 100644 --- a/TODO +++ b/TODO @@ -89,8 +89,6 @@ - mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the flag on w/o checking the others before calling tty_update_mode) -- panes should have names like windows - - way to tag a layout as a number/name - optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx From 85df41872836a6c2a6343b1eb6c924056978a208 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:30:27 +0100 Subject: [PATCH 182/949] ++ to TODO. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index a43ff189..a41a8e43 100644 --- a/TODO +++ b/TODO @@ -16,6 +16,8 @@ - options bits and pieces: * set-remain-on-exit is a complete hack * way to set socket path from config file + * -fg/-bg/-attr is crap - better just foo-style options which accept + fg=,bg=,bright and so on like #[] - format improvements: * option to quote format (#{session_name:quoted}) From 6126fa09955a9871c9848654aaf6cb5c6911d682 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Aug 2013 18:28:31 +0100 Subject: [PATCH 183/949] + to TODO. --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index a41a8e43..1a8cf375 100644 --- a/TODO +++ b/TODO @@ -30,6 +30,7 @@ * choose-mode and copy-mode are very similar, make choose-mode a subset? * flag to choose-* for sort order * choose mode would be better per client than per window + * two choices (first one then second, for swap-pane and join-pane) - improve monitor-*: * straighten out rules for multiple clients From b347a994fd568c961d629d576c8f3bdf0cdf5185 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 2 Aug 2013 13:53:17 +0100 Subject: [PATCH 184/949] + to TODO. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 1a8cf375..c647c7a9 100644 --- a/TODO +++ b/TODO @@ -8,6 +8,8 @@ * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions + * exact match operator for targets (or break the substring match + and require eg x* instead of just x) - make command sequences more usable * don't require space after ; From 1a0951959fc65934ed51ce69dd66a67619d95cec Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 27 Jul 2013 19:57:21 +0100 Subject: [PATCH 185/949] Add destroy entry to TODO. --- TODO | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TODO b/TODO index c647c7a9..f4cf7b6d 100644 --- a/TODO +++ b/TODO @@ -118,3 +118,12 @@ - we need a tmux terminfo entry to document the extensions we are using in upstream terminfo + +- the way pane, window, session destroy is handled is too complicated and the + distinction between session.c, window.c and server-fn.c functions is not + clear. could we just have kill_pane(), kill_window(), unlink_window(), + kill_session() that fix up all data structures (flagging sessions as dead) + and return a value to say whether clients need to be checked for dead + sessions? sort of like session_detach now but more so. or some other scheme + to make it simpler and clearer? also would be nice to remove/rename + server-fn.c. From f703a30dfe2f3178202ccd5121128d4d3c4bbec8 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 11 Oct 2013 14:39:22 +0100 Subject: [PATCH 186/949] Fixup osdep-* specific code get_proc_name() is osdep_get_name() outside of OpenBSD. --- client.c | 4 ---- cmd-save-buffer.c | 1 - format.c | 2 +- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/client.c b/client.c index e7fa4b71..d1a3b177 100644 --- a/client.c +++ b/client.c @@ -349,10 +349,6 @@ client_send_identify(int flags) fd = open("/", O_RDONLY); client_write_one(MSG_IDENTIFY_CWD, fd, NULL, 0); -#ifdef __CYGWIN__ - snprintf(&data.ttyname, sizeof data.ttyname, "%s", - ttyname(STDIN_FILENO)); -#else if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index fc56dd8f..3788fc22 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "tmux.h" diff --git a/format.c b/format.c index d8d34fe7..450e15f3 100644 --- a/format.c +++ b/format.c @@ -355,7 +355,7 @@ format_get_command(struct window_pane *wp) { char *cmd, *out; - cmd = get_proc_name(wp->fd, wp->tty); + cmd = osdep_get_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = xstrdup(wp->cmd); From d518067be6220757a9101ca27fff14d5f599c410 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 11 Oct 2013 14:55:57 +0100 Subject: [PATCH 187/949] Forward-declarations for osdep-linux --- osdep-linux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osdep-linux.c b/osdep-linux.c index ccac2670..20a76611 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -26,6 +26,10 @@ #include "tmux.h" +char *osdep_get_name(int, char *); +char *osdep_get_cwd(int); +struct event_base *osdep_event_init(void); + char * osdep_get_name(int fd, unused char *tty) { From 5944230c507b8cdc10688f0196b10126daee3aad Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Fri, 11 Oct 2013 19:38:40 +0100 Subject: [PATCH 188/949] Fix up missing cwd definition This went walkies during the merge. --- format.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/format.c b/format.c index 1e89b2d2..b3f7fc3a 100644 --- a/format.c +++ b/format.c @@ -522,8 +522,7 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) struct grid *gd = wp->base.grid; struct grid_line *gl; unsigned long long size; - u_int i, idx; - const char *cmd; + u_int i, idx, cwd; char *cmd; size = 0; From 334c28afe71eabb236bf8cfdfc18f56d99719711 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Fri, 11 Oct 2013 19:41:43 +0100 Subject: [PATCH 189/949] Fix previous cwd is a char*, not a u_int. --- format.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index b3f7fc3a..de6d8c84 100644 --- a/format.c +++ b/format.c @@ -522,8 +522,8 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) struct grid *gd = wp->base.grid; struct grid_line *gl; unsigned long long size; - u_int i, idx, cwd; - char *cmd; + u_int i, idx; + char *cmd, *cwd; size = 0; for (i = 0; i < gd->hsize; i++) { From 2eb6d6e31bdd2270e242d234006e7c83e0d12255 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Oct 2013 00:15:11 +0000 Subject: [PATCH 190/949] Fix detach -a by skipping clients where the session is NULL. --- cmd-detach-client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 82001bee..d40ef5a0 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -71,7 +71,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'a')) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c2 = ARRAY_ITEM(&clients, i); - if (c2 == NULL || c == c2) + if (c2 == NULL || c2->session == NULL || + c2 == c) continue; server_write_client(c2, msgtype, c2->session->name, From f52eac62259c431daac84ddb6c2a5b7ebd528c2c Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 20 Oct 2013 09:37:50 +0000 Subject: [PATCH 191/949] Don't turn on modifyOtherKeys by default, it is annoying if tmux is killed and it's left on and we can't turn it on and off like we do for attributes. It's not hard to enable in .Xresources or .Xdefaults anyway. --- tty.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tty.c b/tty.c index c989aaaa..53b415ff 100644 --- a/tty.c +++ b/tty.c @@ -224,7 +224,7 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - tty_puts(tty, "\033[c\033[>4;1m\033[m"); + tty_puts(tty, "\033[c"); } tty->cx = UINT_MAX; @@ -292,7 +292,6 @@ tty_stop_tty(struct tty *tty) tty->flags &= ~TTY_FOCUS; tty_puts(tty, "\033[?1004l"); } - tty_raw(tty, "\033[>4m\033[m"); } tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); From bf35441608b05d52627215aa70bd52d9460812d4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 20 Oct 2013 17:28:43 +0000 Subject: [PATCH 192/949] Do not run any command line command from the client which starts the server until after the configuration file completes. This prevents it racing against run-shell or if-shell in .tmux.conf that run in the background. --- cfg.c | 15 +++++++++++++++ server-client.c | 9 ++++++--- server.c | 3 +++ tmux.h | 1 + 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/cfg.c b/cfg.c index c625a2fb..35192dc0 100644 --- a/cfg.c +++ b/cfg.c @@ -31,6 +31,7 @@ struct cmd_q *cfg_cmd_q; int cfg_finished; int cfg_references; struct causelist cfg_causes; +struct client *cfg_client; int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) @@ -127,6 +128,20 @@ cfg_default_done(unused struct cmd_q *cmdq) cmdq_free(cfg_cmd_q); cfg_cmd_q = NULL; + + if (cfg_client != NULL) { + /* + * The client command queue starts with client_exit set to 1 so + * only continue if not empty (that is, we have been delayed + * during configuration parsing for long enough that the + * MSG_COMMAND has arrived), else the client will exit before + * the MSG_COMMAND which might tell it not to. + */ + if (!TAILQ_EMPTY(&cfg_client->cmdq->queue)) + cmdq_continue(cfg_client->cmdq); + cfg_client->references--; + cfg_client = NULL; + } } void diff --git a/server-client.c b/server-client.c index b6d4870d..82d840a0 100644 --- a/server-client.c +++ b/server-client.c @@ -155,8 +155,8 @@ server_client_lost(struct client *c) free(c->ttyname); free(c->term); - evbuffer_free (c->stdin_data); - evbuffer_free (c->stdout_data); + evbuffer_free(c->stdin_data); + evbuffer_free(c->stdout_data); if (c->stderr_data != c->stdout_data) evbuffer_free (c->stderr_data); @@ -932,7 +932,10 @@ server_client_msg_command(struct client *c, struct imsg *imsg) } cmd_free_argv(argc, argv); - cmdq_run(c->cmdq, cmdlist); + if (c != cfg_client || cfg_finished) + cmdq_run(c->cmdq, cmdlist); + else + cmdq_append(c->cmdq, cmdlist); cmd_list_free(cmdlist); return; diff --git a/server.c b/server.c index ffc25db0..8ac9321e 100644 --- a/server.c +++ b/server.c @@ -168,6 +168,9 @@ server_start(int lockfd, char *lockfile) cfg_finished = 0; cfg_references = 1; ARRAY_INIT(&cfg_causes); + cfg_client = ARRAY_FIRST(&clients); + if (cfg_client != NULL) + cfg_client->references++; if (access(TMUX_CONF, R_OK) == 0) { if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) { diff --git a/tmux.h b/tmux.h index 67192740..ddee0730 100644 --- a/tmux.h +++ b/tmux.h @@ -1510,6 +1510,7 @@ extern struct cmd_q *cfg_cmd_q; extern int cfg_finished; extern int cfg_references; extern struct causelist cfg_causes; +extern struct client *cfg_client; int load_cfg(const char *, struct cmd_q *, char **); void cfg_default_done(struct cmd_q *); void cfg_show_causes(struct session *); From 7fa55b0419b88b5568bb2fd99dd0de9fba80ac61 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 23 Oct 2013 11:31:03 +0000 Subject: [PATCH 193/949] Key to swap to other end of selection (bound to o with vi keys), from J Raynor. --- mode-key.c | 1 + tmux.1 | 1 + tmux.h | 1 + window-copy.c | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/mode-key.c b/mode-key.c index 78a8bdf3..995ccdfa 100644 --- a/mode-key.c +++ b/mode-key.c @@ -292,6 +292,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { 'k', 0, MODEKEYCOPY_UP }, { 'l', 0, MODEKEYCOPY_RIGHT }, { 'n', 0, MODEKEYCOPY_SEARCHAGAIN }, + { 'o', 0, MODEKEYCOPY_OTHEREND }, { 't', 0, MODEKEYCOPY_JUMPTO }, { 'q', 0, MODEKEYCOPY_CANCEL }, { 'v', 0, MODEKEYCOPY_RECTANGLETOGGLE }, diff --git a/tmux.1 b/tmux.1 index 65849212..e815fc4e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -870,6 +870,7 @@ The following keys are supported as appropriate for the mode: .It Li "Next space, end of word" Ta "E" Ta "" .It Li "Next word" Ta "w" Ta "" .It Li "Next word end" Ta "e" Ta "M-f" +.It Li "Other end of selection" Ta "o" Ta "" .It Li "Paste buffer" Ta "p" Ta "C-y" .It Li "Previous page" Ta "C-b" Ta "Page up" .It Li "Previous word" Ta "b" Ta "M-b" diff --git a/tmux.h b/tmux.h index ddee0730..81c99f9b 100644 --- a/tmux.h +++ b/tmux.h @@ -564,6 +564,7 @@ enum mode_key_cmd { MODEKEYCOPY_NEXTSPACEEND, MODEKEYCOPY_NEXTWORD, MODEKEYCOPY_NEXTWORDEND, + MODEKEYCOPY_OTHEREND, MODEKEYCOPY_PREVIOUSPAGE, MODEKEYCOPY_PREVIOUSSPACE, MODEKEYCOPY_PREVIOUSWORD, diff --git a/window-copy.c b/window-copy.c index 7a647c51..275909cd 100644 --- a/window-copy.c +++ b/window-copy.c @@ -65,6 +65,7 @@ u_int window_copy_find_length(struct window_pane *, u_int); void window_copy_cursor_start_of_line(struct window_pane *); void window_copy_cursor_back_to_indentation(struct window_pane *); void window_copy_cursor_end_of_line(struct window_pane *); +void window_copy_other_end(struct window_pane *); void window_copy_cursor_left(struct window_pane *); void window_copy_cursor_right(struct window_pane *); void window_copy_cursor_up(struct window_pane *, int); @@ -415,6 +416,10 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); return; + case MODEKEYCOPY_OTHEREND: + for (; np != 0; np--) + window_copy_other_end(wp); + break; case MODEKEYCOPY_LEFT: for (; np != 0; np--) window_copy_cursor_left(wp); @@ -1618,6 +1623,39 @@ window_copy_cursor_end_of_line(struct window_pane *wp) window_copy_redraw_lines(wp, data->cy, 1); } +void +window_copy_other_end(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int selx, sely, cx, cy, yy; + + if (!s->sel.flag) + return; + + selx = data->selx; + sely = data->sely; + cx = data->cx; + cy = data->cy; + yy = screen_hsize(data->backing) + data->cy - data->oy; + + data->selx = cx; + data->sely = yy; + data->cx = selx; + + if (sely < screen_hsize(data->backing) - data->oy) { + data->oy = screen_hsize(data->backing) - sely; + data->cy = 0; + } else if (sely > screen_hsize(data->backing) - data->oy + screen_size_y(s)) { + data->oy = screen_hsize(data->backing) - sely + screen_size_y(s) - 1; + data->cy = screen_size_y(s) - 1; + + } else + data->cy = cy + sely - yy; + + window_copy_redraw_screen(wp); +} + void window_copy_cursor_left(struct window_pane *wp) { From a6cd84869ee1543af0c44b63cae5a7908e74ba7d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 Nov 2013 12:39:20 +0000 Subject: [PATCH 194/949] Correctly redraw the top two lines in copy mode when they are selected - the selection was being updated before the redraw so the markings were lost. Based on a fix from J Raynor. --- window-copy.c | 77 +++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/window-copy.c b/window-copy.c index 275909cd..d24f08e6 100644 --- a/window-copy.c +++ b/window-copy.c @@ -51,7 +51,7 @@ void window_copy_search_down(struct window_pane *, const char *); void window_copy_goto_line(struct window_pane *, const char *); void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); -int window_copy_update_selection(struct window_pane *); +int window_copy_update_selection(struct window_pane *, int); void *window_copy_get_selection(struct window_pane *, size_t *); void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); void window_copy_copy_pipe( @@ -329,7 +329,7 @@ window_copy_pageup(struct window_pane *wp) data->oy = screen_hsize(data->backing); else data->oy += n; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } @@ -458,7 +458,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) else data->oy -= n; } - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HALFPAGEUP: @@ -469,7 +469,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) else data->oy += n; } - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HALFPAGEDOWN: @@ -480,39 +480,39 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) else data->oy -= n; } - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_TOPLINE: data->cx = 0; data->cy = 0; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_MIDDLELINE: data->cx = 0; data->cy = (screen_size_y(s) - 1) / 2; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_BOTTOMLINE: data->cx = 0; data->cy = screen_size_y(s) - 1; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HISTORYTOP: data->cx = 0; data->cy = 0; data->oy = screen_hsize(data->backing); - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HISTORYBOTTOM: data->cx = 0; data->cy = screen_size_y(s) - 1; data->oy = 0; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_STARTSELECTION: @@ -865,7 +865,7 @@ window_copy_mouse( if (s->mode & MODE_MOUSE_BUTTON) { if (~m->event & MOUSE_EVENT_UP) { window_copy_update_cursor(wp, m->x, m->y); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_screen(wp); return; } @@ -915,7 +915,7 @@ window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) } data->oy = gd->hsize - offset; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } @@ -1108,7 +1108,7 @@ window_copy_goto_line(struct window_pane *wp, const char *linestr) return; data->oy = lineno; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } @@ -1221,11 +1221,11 @@ window_copy_start_selection(struct window_pane *wp) data->sely = screen_hsize(data->backing) + data->cy - data->oy; s->sel.flag = 1; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); } int -window_copy_update_selection(struct window_pane *wp) +window_copy_update_selection(struct window_pane *wp, int may_redraw) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -1260,7 +1260,7 @@ window_copy_update_selection(struct window_pane *wp) screen_set_selection(s, sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc); - if (data->rectflag) { + if (data->rectflag && may_redraw) { /* * Can't rely on the caller to redraw the right lines for * rectangle selection - find the highest line and the number @@ -1564,7 +1564,7 @@ window_copy_cursor_start_of_line(struct window_pane *wp) } } window_copy_update_cursor(wp, 0, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -1589,7 +1589,7 @@ window_copy_cursor_back_to_indentation(struct window_pane *wp) } window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -1619,7 +1619,7 @@ window_copy_cursor_end_of_line(struct window_pane *wp) } window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -1666,7 +1666,7 @@ window_copy_cursor_left(struct window_pane *wp) window_copy_cursor_end_of_line(wp); } else { window_copy_update_cursor(wp, data->cx - 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } } @@ -1689,7 +1689,7 @@ window_copy_cursor_right(struct window_pane *wp) window_copy_cursor_down(wp, 0); } else { window_copy_update_cursor(wp, data->cx + 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } } @@ -1719,7 +1719,7 @@ window_copy_cursor_up(struct window_pane *wp, int scroll_only) } } else { window_copy_update_cursor(wp, data->cx, data->cy - 1); - if (window_copy_update_selection(wp)) { + if (window_copy_update_selection(wp, 1)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wp, data->cy, 1); else @@ -1757,7 +1757,7 @@ window_copy_cursor_down(struct window_pane *wp, int scroll_only) window_copy_redraw_lines(wp, data->cy - 1, 2); } else { window_copy_update_cursor(wp, data->cx, data->cy + 1); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy - 1, 2); } @@ -1789,7 +1789,7 @@ window_copy_cursor_jump(struct window_pane *wp) if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1818,7 +1818,7 @@ window_copy_cursor_jump_back(struct window_pane *wp) if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1847,7 +1847,7 @@ window_copy_cursor_jump_to(struct window_pane *wp) if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px - 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1876,7 +1876,7 @@ window_copy_cursor_jump_to_back(struct window_pane *wp) if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px + 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1924,7 +1924,7 @@ window_copy_cursor_next_word(struct window_pane *wp, const char *separators) } while (expected == 1); window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -1974,7 +1974,7 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) px--; window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -2012,7 +2012,7 @@ window_copy_cursor_previous_word(struct window_pane *wp, const char *separators) out: window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -2029,6 +2029,8 @@ window_copy_scroll_up(struct window_pane *wp, u_int ny) return; data->oy -= ny; + window_copy_update_selection(wp, 0); + screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_deleteline(&ctx, ny); @@ -2038,12 +2040,9 @@ window_copy_scroll_up(struct window_pane *wp, u_int ny) window_copy_write_line(wp, &ctx, 1); if (screen_size_y(s) > 3) window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); - if (s->sel.flag && screen_size_y(s) > ny) { - window_copy_update_selection(wp); + if (s->sel.flag && screen_size_y(s) > ny) window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); - } screen_write_cursormove(&ctx, data->cx, data->cy); - window_copy_update_selection(wp); screen_write_stop(&ctx); } @@ -2063,17 +2062,17 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny) return; data->oy += ny; + window_copy_update_selection(wp, 0); + screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_insertline(&ctx, ny); window_copy_write_lines(wp, &ctx, 0, ny); - if (s->sel.flag && screen_size_y(s) > ny) { - window_copy_update_selection(wp); + if (s->sel.flag && screen_size_y(s) > ny) window_copy_write_line(wp, &ctx, ny); - } else if (ny == 1) /* nuke position */ + else if (ny == 1) /* nuke position */ window_copy_write_line(wp, &ctx, 1); screen_write_cursormove(&ctx, data->cx, data->cy); - window_copy_update_selection(wp); screen_write_stop(&ctx); } @@ -2090,6 +2089,6 @@ window_copy_rectangle_toggle(struct window_pane *wp) if (data->cx > px) window_copy_update_cursor(wp, px, data->cy); - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } From f0ed61f53cec00ce728fabb5fb615a75671043ea Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 9 Nov 2013 00:48:57 +0000 Subject: [PATCH 195/949] Support case insensitive searching in the same manner as emacs - all lowercase means case insensitive, any uppercase means case sensitive. From J Raynor. --- window-copy.c | 54 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/window-copy.c b/window-copy.c index d24f08e6..0c122cda 100644 --- a/window-copy.c +++ b/window-copy.c @@ -18,6 +18,7 @@ #include +#include #include #include @@ -41,11 +42,11 @@ void window_copy_write_lines( void window_copy_scroll_to(struct window_pane *, u_int, u_int); int window_copy_search_compare( - struct grid *, u_int, u_int, struct grid *, u_int); + struct grid *, u_int, u_int, struct grid *, u_int, int); int window_copy_search_lr( - struct grid *, struct grid *, u_int *, u_int, u_int, u_int); + struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); int window_copy_search_rl( - struct grid *, struct grid *, u_int *, u_int, u_int, u_int); + struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); void window_copy_search_up(struct window_pane *, const char *); void window_copy_search_down(struct window_pane *, const char *); void window_copy_goto_line(struct window_pane *, const char *); @@ -921,7 +922,7 @@ window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) int window_copy_search_compare( - struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx) + struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx, int cis) { const struct grid_cell *gc, *sgc; struct utf8_data ud, sud; @@ -933,21 +934,28 @@ window_copy_search_compare( if (ud.size != sud.size || ud.width != sud.width) return (0); + + if (cis && ud.size == 1) + return (tolower(ud.data[0]) == sud.data[0]); + return (memcmp(ud.data, sud.data, ud.size) == 0); } int window_copy_search_lr(struct grid *gd, - struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) + struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; + int matched; for (ax = first; ax < last; ax++) { if (ax + sgd->sx >= gd->sx) break; for (bx = 0; bx < sgd->sx; bx++) { px = ax + bx; - if (!window_copy_search_compare(gd, px, py, sgd, bx)) + matched = window_copy_search_compare(gd, px, py, sgd, + bx, cis); + if (!matched) break; } if (bx == sgd->sx) { @@ -960,16 +968,19 @@ window_copy_search_lr(struct grid *gd, int window_copy_search_rl(struct grid *gd, - struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) + struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; + int matched; for (ax = last + 1; ax > first; ax--) { if (gd->sx - (ax - 1) < sgd->sx) continue; for (bx = 0; bx < sgd->sx; bx++) { px = ax - 1 + bx; - if (!window_copy_search_compare(gd, px, py, sgd, bx)) + matched = window_copy_search_compare(gd, px, py, sgd, + bx, cis); + if (!matched) break; } if (bx == sgd->sx) { @@ -990,7 +1001,8 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, last, fx, fy, px; - int utf8flag, n, wrapped, wrapflag; + int utf8flag, n, wrapped, wrapflag, cis; + const char *ptr; if (*searchstr == '\0') return; @@ -1016,13 +1028,21 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) fx--; n = wrapped = 0; + cis = 1; + for (ptr = searchstr; *ptr != '\0'; ptr++) { + if (*ptr != tolower(*ptr)) { + cis = 0; + break; + } + } + retry: sgd = ss.grid; for (i = fy + 1; i > 0; i--) { last = screen_size_x(s); if (i == fy + 1) last = fx; - n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last); + n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last, cis); if (n) { window_copy_scroll_to(wp, px, i - 1); break; @@ -1048,7 +1068,8 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, first, fx, fy, px; - int utf8flag, n, wrapped, wrapflag; + int utf8flag, n, wrapped, wrapflag, cis; + const char *ptr; if (*searchstr == '\0') return; @@ -1074,13 +1095,22 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) fx++; n = wrapped = 0; + cis = 1; + for (ptr = searchstr; *ptr != '\0'; ptr++) { + if (*ptr != tolower(*ptr)) { + cis = 0; + break; + } + } + retry: sgd = ss.grid; for (i = fy + 1; i < gd->hsize + gd->sy + 1; i++) { first = 0; if (i == fy + 1) first = fx; - n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx); + n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx, + cis); if (n) { window_copy_scroll_to(wp, px, i - 1); break; From 7624800ddc8fa8e51164dc7c615894c086deabef Mon Sep 17 00:00:00 2001 From: benno Date: Wed, 13 Nov 2013 20:43:36 +0000 Subject: [PATCH 196/949] from nicm: : handle msgbuf_write() returning EAGAIN --- client.c | 2 +- server-client.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 0f2d3684..82ef9cf0 100644 --- a/client.c +++ b/client.c @@ -464,7 +464,7 @@ client_callback(unused int fd, short events, void *data) } if (events & EV_WRITE) { - if (msgbuf_write(&client_ibuf.w) < 0) + if (msgbuf_write(&client_ibuf.w) < 0 && errno != EAGAIN) goto lost_server; } diff --git a/server-client.c b/server-client.c index 82d840a0..c6257edb 100644 --- a/server-client.c +++ b/server-client.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -222,7 +223,8 @@ server_client_callback(int fd, short events, void *data) return; if (fd == c->ibuf.fd) { - if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0) + if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0 && + errno != EAGAIN) goto client_lost; if (c->flags & CLIENT_BAD) { From 30275bc61081023b4e0a56452e90fac23c6f6b60 Mon Sep 17 00:00:00 2001 From: okan Date: Sun, 17 Nov 2013 20:19:36 +0000 Subject: [PATCH 197/949] Include unistd.h as it is the standard location for getopt(). OK millert@ --- arguments.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arguments.c b/arguments.c index ca828f00..e699834c 100644 --- a/arguments.c +++ b/arguments.c @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" From 72a4602b880357776d3a2ea7657cf88f3393c288 Mon Sep 17 00:00:00 2001 From: deraadt Date: Wed, 20 Nov 2013 17:01:23 +0000 Subject: [PATCH 198/949] missing unsigned char casts areound tolower() ok nicm --- window-copy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index 0c122cda..dd4f23fa 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1030,7 +1030,7 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) cis = 1; for (ptr = searchstr; *ptr != '\0'; ptr++) { - if (*ptr != tolower(*ptr)) { + if (*ptr != tolower((u_char)*ptr)) { cis = 0; break; } @@ -1097,7 +1097,7 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) cis = 1; for (ptr = searchstr; *ptr != '\0'; ptr++) { - if (*ptr != tolower(*ptr)) { + if (*ptr != tolower((u_char)*ptr)) { cis = 0; break; } From 7aeb4473add76e11dd595919f81ff5d9cad28718 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 Nov 2013 20:58:36 +0000 Subject: [PATCH 199/949] Handle empty current directory more gracefully. --- cmd-new-session.c | 17 ++++++++++------- cmd-new-window.c | 17 ++++++++++------- cmd-split-window.c | 17 ++++++++++------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 49ece8b4..bf843cad 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -107,13 +107,16 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) cp = format_expand(ft, args_get(args, 'c')); format_free(ft); - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } + if (cp != NULL && *cp != '\0') { + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + } else if (cp != NULL) + free(cp); cwd = fd; } else if (c != NULL && c->session == NULL) cwd = c->cwd; diff --git a/cmd-new-window.c b/cmd-new-window.c index e2a01190..354d0ae5 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -117,13 +117,16 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) cp = format_expand(ft, args_get(args, 'c')); format_free(ft); - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } + if (cp != NULL && *cp != '\0') { + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + } else if (cp != NULL) + free(cp); cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; diff --git a/cmd-split-window.c b/cmd-split-window.c index 92e6121f..a431e000 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -95,13 +95,16 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) cp = format_expand(ft, args_get(args, 'c')); format_free(ft); - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } + if (cp != NULL && *cp != '\0') { + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + } else if (cp != NULL) + free(cp); cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; From 40982a01fb39b7be749bc807a06ba352dd6c2e5e Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 23 Nov 2013 09:18:29 +0000 Subject: [PATCH 200/949] With -k, kill window after using it to work out -c path. Reported by jmacristovao at gmail dot com. --- cmd-new-window.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index 354d0ae5..e205e6bc 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -82,26 +82,6 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } detached = args_has(args, 'd'); - wl = NULL; - if (idx != -1) - wl = winlink_find_by_index(&s->windows, idx); - if (wl != NULL && args_has(args, 'k')) { - /* - * Can't use session_detach as it will destroy session if this - * makes it empty. - */ - notify_window_unlinked(s, wl->window); - wl->flags &= ~WINLINK_ALERTFLAGS; - winlink_stack_remove(&s->lastw, wl); - winlink_remove(&s->windows, wl); - - /* Force select/redraw if current. */ - if (wl == s->curw) { - detached = 0; - s->curw = NULL; - } - } - if (args->argc == 0) cmd = options_get_string(&s->options, "default-command"); else @@ -133,6 +113,26 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) else cwd = s->cwd; + wl = NULL; + if (idx != -1) + wl = winlink_find_by_index(&s->windows, idx); + if (wl != NULL && args_has(args, 'k')) { + /* + * Can't use session_detach as it will destroy session if this + * makes it empty. + */ + notify_window_unlinked(s, wl->window); + wl->flags &= ~WINLINK_ALERTFLAGS; + winlink_stack_remove(&s->lastw, wl); + winlink_remove(&s->windows, wl); + + /* Force select/redraw if current. */ + if (wl == s->curw) { + detached = 0; + s->curw = NULL; + } + } + if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); wl = session_new(s, args_get(args, 'n'), cmd, cwd, idx, &cause); From 1286c56188502e217c49f712faba66ee4178f964 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 24 Nov 2013 11:29:09 +0000 Subject: [PATCH 201/949] Replace ## by # in format. --- format.c | 7 +++++++ tmux.1 | 3 +++ 2 files changed, 10 insertions(+) diff --git a/format.c b/format.c index bfc40906..bcb897d6 100644 --- a/format.c +++ b/format.c @@ -321,6 +321,13 @@ format_expand(struct format_tree *ft, const char *fmt) break; fmt += n + 1; continue; + case '#': + while (len - off < 2) { + buf = xrealloc(buf, 2, len); + len *= 2; + } + buf[off++] = '#'; + continue; default: s = NULL; if (ch >= 'A' && ch <= 'Z') diff --git a/tmux.1 b/tmux.1 index e815fc4e..a2404b4f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3011,6 +3011,9 @@ for example .Ql #{session_name} . Some variables also have an shorter alias such as .Ql #S . +.Ql ## +is replaced by a single +.Ql # . Conditionals are also accepted by prefixing with .Ql \&? and separating two alternatives with a comma; From d459314517151dd5a7ffcb3eb93d44f481ca8626 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 24 Nov 2013 19:38:32 +0000 Subject: [PATCH 202/949] Add comments to ACS table matching terminfo(5). --- tty-acs.c | 64 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/tty-acs.c b/tty-acs.c index 297c51ac..13dbbc72 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -30,38 +30,38 @@ struct tty_acs_entry { const char *string; }; const struct tty_acs_entry tty_acs_table[] = { - { '+', "\342\206\222" }, - { ',', "\342\206\220" }, - { '-', "\342\206\221" }, - { '.', "\342\206\223" }, - { '0', "\342\226\256" }, - { '`', "\342\227\206" }, - { 'a', "\342\226\222" }, - { 'f', "\302\260" }, - { 'g', "\302\261" }, - { 'h', "\342\226\222" }, - { 'i', "\342\230\203" }, - { 'j', "\342\224\230" }, - { 'k', "\342\224\220" }, - { 'l', "\342\224\214" }, - { 'm', "\342\224\224" }, - { 'n', "\342\224\274" }, - { 'o', "\342\216\272" }, - { 'p', "\342\216\273" }, - { 'q', "\342\224\200" }, - { 'r', "\342\216\274" }, - { 's', "\342\216\275" }, - { 't', "\342\224\234" }, - { 'u', "\342\224\244" }, - { 'v', "\342\224\264" }, - { 'w', "\342\224\254" }, - { 'x', "\342\224\202" }, - { 'y', "\342\211\244" }, - { 'z', "\342\211\245" }, - { '{', "\317\200" }, - { '|', "\342\211\240" }, - { '}', "\302\243" }, - { '~', "\302\267" } + { '+', "\342\206\222" }, /* arrow pointing right */ + { ',', "\342\206\220" }, /* arrow pointing left */ + { '-', "\342\206\221" }, /* arrow pointing up */ + { '.', "\342\206\223" }, /* arrow pointing down */ + { '0', "\342\226\256" }, /* solid square block */ + { '`', "\342\227\206" }, /* diamond */ + { 'a', "\342\226\222" }, /* checker board (stipple) */ + { 'f', "\302\260" }, /* degree symbol */ + { 'g', "\302\261" }, /* plus/minus */ + { 'h', "\342\226\222" }, /* board of squares */ + { 'i', "\342\230\203" }, /* lantern symbol */ + { 'j', "\342\224\230" }, /* lower right corner */ + { 'k', "\342\224\220" }, /* upper right corner */ + { 'l', "\342\224\214" }, /* upper left corner */ + { 'm', "\342\224\224" }, /* lower left corner */ + { 'n', "\342\224\274" }, /* large plus or crossover */ + { 'o', "\342\216\272" }, /* scan line 1 */ + { 'p', "\342\216\273" }, /* scan line 3 */ + { 'q', "\342\224\200" }, /* horizontal line */ + { 'r', "\342\216\274" }, /* scan line 7 */ + { 's', "\342\216\275" }, /* scan line 9 */ + { 't', "\342\224\234" }, /* tee pointing right */ + { 'u', "\342\224\244" }, /* tee pointing left */ + { 'v', "\342\224\264" }, /* tee pointing up */ + { 'w', "\342\224\254" }, /* tee pointing down */ + { 'x', "\342\224\202" }, /* vertical line */ + { 'y', "\342\211\244" }, /* less-than-or-equal-to */ + { 'z', "\342\211\245" }, /* greater-than-or-equal-to */ + { '{', "\317\200" }, /* greek pi */ + { '|', "\342\211\240" }, /* not-equal */ + { '}', "\302\243" }, /* UK pound sign */ + { '~', "\302\267" } /* bullet */ }; int From a352570e9fee02ad50ce627dc1e9b0b5a82bc8f1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 19 Nov 2013 14:24:18 +0000 Subject: [PATCH 203/949] +. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index f4cf7b6d..a65688f9 100644 --- a/TODO +++ b/TODO @@ -119,6 +119,8 @@ - we need a tmux terminfo entry to document the extensions we are using in upstream terminfo +- send arrow key sequences for mouse scroll wheel when in alternate screen + - the way pane, window, session destroy is handled is too complicated and the distinction between session.c, window.c and server-fn.c functions is not clear. could we just have kill_pane(), kill_window(), unlink_window(), From b091790622dfe923cfc45a79eb4089e917634510 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 30 Nov 2013 17:56:49 +0000 Subject: [PATCH 204/949] +. --- TODO | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index a65688f9..21d8d7d8 100644 --- a/TODO +++ b/TODO @@ -128,4 +128,14 @@ and return a value to say whether clients need to be checked for dead sessions? sort of like session_detach now but more so. or some other scheme to make it simpler and clearer? also would be nice to remove/rename - server-fn.c. + server-fn.c + +- more readable way to work out the various things commands need to know about + the client, notably: + - is this the config file? (cmdq->c == NULL) + - is this a command client? (cmdq->c != NULL && cmdq->c->session == NULL) + - is this a control client? + - can i do stdin or stdout to this client? + or even guarantee that cmdq->c != NULL and provide a better way to tell when + in the config file - then we use cmdq->c if we need a client w/o a session + else cmd_current_client From ee65bde13085591f84810acd217f539c14e7f817 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 9 Dec 2013 22:53:17 +0000 Subject: [PATCH 205/949] Note libevent version. --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 77c6058d..fad67b71 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ simple, modern, BSD-licensed alternative to programs such as GNU screen. This release runs on OpenBSD, FreeBSD, NetBSD, Linux and OS X and may still run on Solaris and AIX (although they haven't been tested in a while). -Since the 1.2 release tmux depends on libevent. Download it from: +tmux depends on libevent 2.x. Download it from: http://www.monkey.org/~provos/libevent/ From ccf39fcdc1208d4ae293ef8a78d6f7db9b712cdc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 9 Dec 2013 23:27:42 +0000 Subject: [PATCH 206/949] +. --- TODO | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO b/TODO index 21d8d7d8..d0bbaa5d 100644 --- a/TODO +++ b/TODO @@ -139,3 +139,6 @@ or even guarantee that cmdq->c != NULL and provide a better way to tell when in the config file - then we use cmdq->c if we need a client w/o a session else cmd_current_client + +- buffer_sample and the choose-* could let UTF-8 through and display it + properly. From 886c282679678fded4fdce122a3da4d646f6670c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 6 Jan 2014 15:12:05 +0000 Subject: [PATCH 207/949] Use 0 if O_DIRECTORY is missing, reported by Dagobert Michelsen. --- compat.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compat.h b/compat.h index ab3224b1..65d6ec7a 100644 --- a/compat.h +++ b/compat.h @@ -125,6 +125,10 @@ typedef uint64_t u_int64_t; #define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) #endif +#ifndef O_DIRECTORY +#define O_DIRECTORY 0 +#endif + #ifndef INFTIM #define INFTIM -1 #endif From ba014c1a605f78b301b47ef922dd24a3c5c2c4c1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 6 Jan 2014 15:16:57 +0000 Subject: [PATCH 208/949] NetBSD broke strnvis when they added it, check for that. --- configure.ac | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/configure.ac b/configure.ac index ceb37db8..644b283d 100644 --- a/configure.ac +++ b/configure.ac @@ -311,6 +311,16 @@ AM_CONDITIONAL(NO_STRTONUM, [test "x$found_strtonum" = xno]) # Look for strnvis, compat/{vis,unvis}.c used if missing. AC_CHECK_FUNC(strnvis, found_strnvis=yes, found_strnvis=no) +if test "x$found_strnvis" = xyes; then + AC_MSG_CHECKING(if strnvis is broken) + AC_EGREP_HEADER([strnvis\(char \*, const char \*, size_t, int\)], + vis.h, + AC_MSG_RESULT(no), + [found_strnvis=no]) + if test "x$found_strnvis" = xno; then + AC_MSG_RESULT(yes) + fi +fi if test "x$found_strnvis" = xyes; then AC_DEFINE(HAVE_VIS) fi From 1751da76d5fed929636633905be5f5cfa204844b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jan 2014 13:46:12 +0000 Subject: [PATCH 209/949] Remove unnecessary calls to va_start/va_end, from Tiago Cunha. --- cmd-queue.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index bb3a09ec..caa80afe 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -69,9 +69,7 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { - va_start(ap, fmt); evbuffer_add_vprintf(c->stdout_data, fmt, ap); - va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); @@ -104,9 +102,7 @@ cmdq_info(struct cmd_q *cmdq, const char *fmt, ...) if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { - va_start(ap, fmt); evbuffer_add_vprintf(c->stdout_data, fmt, ap); - va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); From 66829ee12e45656fccfe8879b7ac2f983d138b42 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jan 2014 13:51:57 +0000 Subject: [PATCH 210/949] Simplify args_set, from Tiago Cunha. --- arguments.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/arguments.c b/arguments.c index e699834c..4a3f4579 100644 --- a/arguments.c +++ b/arguments.c @@ -205,19 +205,15 @@ args_set(struct args *args, u_char ch, const char *value) /* Replace existing argument. */ if ((entry = args_find(args, ch)) != NULL) { free(entry->value); - if (value != NULL) - entry->value = xstrdup(value); - else - entry->value = NULL; - return; + entry->value = NULL; + } else { + entry = xcalloc(1, sizeof *entry); + entry->flag = ch; + RB_INSERT(args_tree, &args->tree, entry); } - entry = xcalloc(1, sizeof *entry); - entry->flag = ch; if (value != NULL) entry->value = xstrdup(value); - - RB_INSERT(args_tree, &args->tree, entry); } /* Get argument value. Will be NULL if it isn't present. */ From 994cb872cfb5ef49a08a714d093d92bfee79b0e8 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jan 2014 13:58:06 +0000 Subject: [PATCH 211/949] Style and comment fixes from Tiago Cunha. --- grid.c | 2 +- options.c | 2 +- window-choose.c | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/grid.c b/grid.c index d06e7154..d9b3c716 100644 --- a/grid.c +++ b/grid.c @@ -644,7 +644,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, if (trim) { while (off > 0 && buf[off - 1] == ' ') off--; - } + } buf[off] = '\0'; return (buf); diff --git a/options.c b/options.c index ec036741..51a6608e 100644 --- a/options.c +++ b/options.c @@ -26,7 +26,7 @@ /* * Option handling; each option has a name, type and value and is stored in - * a splay tree. + * a red-black tree. */ RB_GENERATE(options_tree, options_entry, entry, options_cmp); diff --git a/window-choose.c b/window-choose.c index 70c20085..77add5e4 100644 --- a/window-choose.c +++ b/window-choose.c @@ -345,8 +345,7 @@ window_choose_collapse(struct window_pane *wp, struct session *s) * assign the actual result we want to render and copy the new one over * the top of it. */ - for (i = 0; i < ARRAY_LENGTH(&data->list); i++) - { + for (i = 0; i < ARRAY_LENGTH(&data->list); i++) { item = &ARRAY_ITEM(&data->list, i); wcd = item->wcd; From adc1f21eaee899b8ecfdb6ef3676d9a25019e4fa Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jan 2014 14:05:55 +0000 Subject: [PATCH 212/949] Three small changes from Tiago Cunha: - Check for truncation when copying path. - Don't need to use a temporary buffer in screen_set_title. - Include strerror in output when connecting to server fails. --- client.c | 3 ++- screen.c | 6 +----- tmux.c | 6 +++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client.c b/client.c index 82ef9cf0..1458963c 100644 --- a/client.c +++ b/client.c @@ -232,7 +232,8 @@ client_main(int argc, char **argv, int flags) /* Initialise the client socket and start the server. */ fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { - fprintf(stderr, "failed to connect to server\n"); + fprintf(stderr, "failed to connect to server: %s\n", + strerror(errno)); return (1); } diff --git a/screen.c b/screen.c index 76aa91c6..2b0e9fba 100644 --- a/screen.c +++ b/screen.c @@ -110,12 +110,8 @@ screen_set_cursor_colour(struct screen *s, const char *colour_string) void screen_set_title(struct screen *s, const char *title) { - char tmp[BUFSIZ]; - - strlcpy(tmp, title, sizeof tmp); - free(s->title); - s->title = xstrdup(tmp); + s->title = xstrdup(title); } /* Resize screen. */ diff --git a/tmux.c b/tmux.c index 74d827a5..c8f9e059 100644 --- a/tmux.c +++ b/tmux.c @@ -361,7 +361,11 @@ main(int argc, char **argv) } } free(label); - strlcpy(socket_path, path, sizeof socket_path); + + if (strlcpy(socket_path, path, sizeof socket_path) >= sizeof socket_path) { + fprintf(stderr, "socket path too long: %s\n", path); + exit(1); + } free(path); /* Set process title. */ From 1a0d3cd5d3bfcf15be80b5c9739b039fc7e0f575 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jan 2014 14:20:55 +0000 Subject: [PATCH 213/949] Allow attach-session -t to accept a window and pane to select them on attach. Based on a diff from J Raynor. --- cmd-attach-session.c | 26 ++++++++++++++++++++++++-- cmd.c | 2 -- tmux.h | 2 ++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index c71e6f1f..6fb83d20 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -47,6 +47,9 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, { struct session *s; struct client *c; + struct winlink *wl = NULL; + struct window *w = NULL; + struct window_pane *wp = NULL; const char *update; char *cause; u_int i; @@ -59,12 +62,31 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, return (CMD_RETURN_ERROR); } - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); + if (tflag == NULL) { + if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) + return (CMD_RETURN_ERROR); + } else if (tflag[strcspn(tflag, ":.")] != '\0') { + if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) + return (CMD_RETURN_ERROR); + } else { + if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) + return (CMD_RETURN_ERROR); + w = cmd_lookup_windowid(tflag); + if (w == NULL && (wp = cmd_lookup_paneid(tflag)) != NULL) + w = wp->window; + if (w != NULL) + wl = winlink_find_by_window(&s->windows, w); + } if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); + if (wl != NULL) { + if (wp != NULL) + window_set_active_pane(wp->window, wp); + session_set_current(s, wl); + } + if (cmdq->client->session != NULL) { if (dflag) { /* diff --git a/cmd.c b/cmd.c index 2edda633..fe55109b 100644 --- a/cmd.c +++ b/cmd.c @@ -126,9 +126,7 @@ struct session *cmd_lookup_session(const char *, int *); struct session *cmd_lookup_session_id(const char *); struct winlink *cmd_lookup_window(struct session *, const char *, int *); int cmd_lookup_index(struct session *, const char *, int *); -struct window_pane *cmd_lookup_paneid(const char *); struct winlink *cmd_lookup_winlink_windowid(struct session *, const char *); -struct window *cmd_lookup_windowid(const char *); struct session *cmd_window_session(struct cmd_q *, struct window *, struct winlink **); struct winlink *cmd_find_window_offset(const char *, struct session *, int *); diff --git a/tmux.h b/tmux.h index 81c99f9b..0a4f8fb5 100644 --- a/tmux.h +++ b/tmux.h @@ -1739,6 +1739,8 @@ int cmd_find_index(struct cmd_q *, const char *, struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); char *cmd_template_replace(const char *, const char *, int); +struct window *cmd_lookup_windowid(const char *); +struct window_pane *cmd_lookup_paneid(const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; From c2cac69a22f70cb690ccc97425623f8405013dcf Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jan 2014 14:28:14 +0000 Subject: [PATCH 214/949] Similar to attach-session, make switch-client -t accept a window and pane. From Johannes Jakobsson. --- cmd-switch-client.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 9db35365..d0544013 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -59,9 +59,13 @@ cmd_switch_client_key_binding(struct cmd *self, int key) enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - struct session *s; + struct args *args = self->args; + struct client *c; + struct session *s; + struct winlink *wl = NULL; + struct window *w = NULL; + struct window_pane *wp = NULL; + const char *tflag; if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); @@ -76,7 +80,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } } - s = NULL; + tflag = args_get(args, 't'); if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(cmdq, "can't find next session"); @@ -94,10 +98,33 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } - } else - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + } else { + if (tflag == NULL) { + if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) + return (CMD_RETURN_ERROR); + } else if (tflag[strcspn(tflag, ":.")] != '\0') { + if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) + return (CMD_RETURN_ERROR); + } else { + if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) + return (CMD_RETURN_ERROR); + w = cmd_lookup_windowid(tflag); + if (w == NULL && + (wp = cmd_lookup_paneid(tflag)) != NULL) + w = wp->window; + if (w != NULL) + wl = winlink_find_by_window(&s->windows, w); + } + + if (cmdq->client == NULL) + return (CMD_RETURN_NORMAL); + + if (wl != NULL) { + if (wp != NULL) + window_set_active_pane(wp->window, wp); + session_set_current(s, wl); + } + } if (c->session != NULL) c->last_session = c->session; From b185449d073b376eb305ff2a68d87fd82ed56377 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jan 2014 21:20:45 +0000 Subject: [PATCH 215/949] Fix a memory/fd leak reported by Tiago Cunha. --- client.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 1458963c..4cf73fb9 100644 --- a/client.c +++ b/client.c @@ -118,10 +118,15 @@ retry: close(fd); xasprintf(&lockfile, "%s.lock", path); - if ((lockfd = client_get_lock(lockfile)) == -1) + if ((lockfd = client_get_lock(lockfile)) == -1) { + free(lockfile); goto retry; - if (unlink(path) != 0 && errno != ENOENT) + } + if (unlink(path) != 0 && errno != ENOENT) { + free(lockfile); + close(lockfd); return (-1); + } fd = server_start(lockfd, lockfile); free(lockfile); close(lockfd); From 3368b602a868eb6570c4ee92f45c2e0ee78631cb Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Jan 2014 11:44:18 +0000 Subject: [PATCH 216/949] Couple of fixes from cppcheck via Tiago Cunha. --- arguments.c | 3 +-- grid.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arguments.c b/arguments.c index 4a3f4579..5ff7ed2c 100644 --- a/arguments.c +++ b/arguments.c @@ -78,7 +78,6 @@ struct args * args_parse(const char *template, int argc, char **argv) { struct args *args; - char *ptr; int opt; args = xcalloc(1, sizeof *args); @@ -89,7 +88,7 @@ args_parse(const char *template, int argc, char **argv) while ((opt = getopt(argc, argv, template)) != -1) { if (opt < 0) continue; - if (opt == '?' || (ptr = strchr(template, opt)) == NULL) { + if (opt == '?' || strchr(template, opt) == NULL) { args_free(args); return (NULL); } diff --git a/grid.c b/grid.c index d9b3c716..852423b5 100644 --- a/grid.c +++ b/grid.c @@ -124,7 +124,7 @@ grid_compare(struct grid *ga, struct grid *gb) struct grid_cell *gca, *gcb; u_int xx, yy; - if (ga->sx != gb->sx || ga->sy != ga->sy) + if (ga->sx != gb->sx || ga->sy != gb->sy) return (1); for (yy = 0; yy < ga->sy; yy++) { From 938768ed3de3e38cb96344b8ec7b794b5e828acf Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Jan 2014 11:46:28 +0000 Subject: [PATCH 217/949] Do not attempt to read .tmux.conf if we can't figure out a home directory, from Tiago Cunha. --- tmux.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tmux.c b/tmux.c index c8f9e059..d4848396 100644 --- a/tmux.c +++ b/tmux.c @@ -202,8 +202,9 @@ int main(int argc, char **argv) { struct passwd *pw; - char *s, *path, *label, *home, **var, tmp[MAXPATHLEN]; + char *s, *path, *label, **var, tmp[MAXPATHLEN]; char in[256]; + const char *home; long long pid; int opt, flags, quiet, keys, session; @@ -325,11 +326,15 @@ main(int argc, char **argv) pw = getpwuid(getuid()); if (pw != NULL) home = pw->pw_dir; + else + home = NULL; } - xasprintf(&cfg_file, "%s/.tmux.conf", home); - if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { - free(cfg_file); - cfg_file = NULL; + if (home != NULL) { + xasprintf(&cfg_file, "%s/.tmux.conf", home); + if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { + free(cfg_file); + cfg_file = NULL; + } } } From df680d725751e0fe9eca4403c732ec1332d91d97 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Jan 2014 13:57:49 +0000 Subject: [PATCH 218/949] Only exit copy mode at the bottom if no selection in progress, from Benoit Pierre. --- window-copy.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index dd4f23fa..ddd5bf41 100644 --- a/window-copy.c +++ b/window-copy.c @@ -853,8 +853,12 @@ window_copy_mouse( } else if (m->wheel == MOUSE_WHEEL_DOWN) { for (i = 0; i < 5; i++) window_copy_cursor_down(wp, 1); - if (data->oy == 0) - goto reset_mode; + /* + * We reached the bottom, leave copy mode, + * but only if no selection is in progress. + */ + if (data->oy == 0 && !s->sel.flag) + goto reset_mode; } return; } From 9ee93b3ea30cfa8e67a62b3c6cf522a9e677ca84 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Jan 2014 14:00:08 +0000 Subject: [PATCH 219/949] Do not permit periods in session names (colons are already banned). From J Raynor. --- session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/session.c b/session.c index 4f6ebed6..11ae2d35 100644 --- a/session.c +++ b/session.c @@ -175,11 +175,11 @@ session_destroy(struct session *s) RB_INSERT(sessions, &dead_sessions, s); } -/* Check a session name is valid: not empty and no colons. */ +/* Check a session name is valid: not empty and no colons or periods. */ int session_check_name(const char *name) { - return (*name != '\0' && strchr(name, ':') == NULL); + return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } /* Update session active time. */ From c9658705854599d89bd71bbd985d388fee01e113 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 22 Jan 2014 14:02:32 +0000 Subject: [PATCH 220/949] + to TODO. --- TODO | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO b/TODO index d0bbaa5d..529b5e8c 100644 --- a/TODO +++ b/TODO @@ -142,3 +142,6 @@ - buffer_sample and the choose-* could let UTF-8 through and display it properly. + +- formats to show if a window is linked into multiple sessions, into multiple + attached sessions, and is the active window in multiple attached sessions? From cbf9224c5f7bcf07352f6ea128c90c12cb0af22e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 22 Jan 2014 14:03:16 +0000 Subject: [PATCH 221/949] Add AC_PROG_MKDIR_P. --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 644b283d..f646a0d0 100644 --- a/configure.ac +++ b/configure.ac @@ -16,6 +16,7 @@ AC_CANONICAL_HOST # Set up the compiler in two different ways and say yes we may want to install. AC_PROG_CC AM_PROG_CC_C_O +AC_PROG_MKDIR_P AC_PROG_INSTALL # Default tmux.conf goes in /etc not ${prefix}/etc. From d23561f38172d1fd3766bf55390ba750a5bd1b64 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Jan 2014 14:43:42 +0000 Subject: [PATCH 222/949] Merge server-info into show-messages and remove some not useful output. --- Makefile | 1 - cmd-server-info.c | 173 -------------------------------------------- cmd-show-messages.c | 112 +++++++++++++++++++++++++++- tmux.1 | 14 +++- 4 files changed, 120 insertions(+), 180 deletions(-) delete mode 100644 cmd-server-info.c diff --git a/Makefile b/Makefile index 9fa55d9b..96972944 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,6 @@ SRCS= arguments.c \ cmd-select-pane.c \ cmd-select-window.c \ cmd-send-keys.c \ - cmd-server-info.c \ cmd-set-buffer.c \ cmd-set-environment.c \ cmd-set-option.c \ diff --git a/cmd-server-info.c b/cmd-server-info.c deleted file mode 100644 index 6cbabe2b..00000000 --- a/cmd-server-info.c +++ /dev/null @@ -1,173 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2008 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include "tmux.h" - -/* - * Show various information about server. - */ - -enum cmd_retval cmd_server_info_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_server_info_entry = { - "server-info", "info", - "", 0, 0, - "", - 0, - NULL, - cmd_server_info_exec -}; - -enum cmd_retval -cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq) -{ - struct tty_term *term; - struct client *c; - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - struct tty_code *code; - const struct tty_term_code_entry *ent; - struct utsname un; - struct job *job; - struct grid *gd; - struct grid_line *gl; - u_int i, j, k, lines; - size_t size; - char out[80]; - char *tim; - time_t t; - - tim = ctime(&start_time); - *strchr(tim, '\n') = '\0'; - cmdq_print(cmdq, "pid %ld, started %s", (long) getpid(), tim); - cmdq_print(cmdq, "socket path %s, debug level %d", socket_path, - debug_level); - if (uname(&un) >= 0) { - cmdq_print(cmdq, "system is %s %s %s %s", - un.sysname, un.release, un.version, un.machine); - } - if (cfg_file != NULL) - cmdq_print(cmdq, "configuration file is %s", cfg_file); - else - cmdq_print(cmdq, "configuration file not specified"); - cmdq_print(cmdq, "protocol version is %d", PROTOCOL_VERSION); - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Clients:"); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - - cmdq_print(cmdq,"%2d: %s (%d, %d): %s [%ux%u %s bs=%hho " - "class=%u] [flags=0x%x/0x%x, references=%u]", i, - c->tty.path, c->ibuf.fd, c->tty.fd, c->session->name, - c->tty.sx, c->tty.sy, c->tty.termname, - c->tty.tio.c_cc[VERASE], c->tty.class, - c->flags, c->tty.flags, c->references); - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Sessions: [%zu]", sizeof (struct grid_cell)); - RB_FOREACH(s, sessions, &sessions) { - t = s->creation_time.tv_sec; - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - - cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] " - "[flags=0x%x]", s->id, s->name, - winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); - RB_FOREACH(wl, winlinks, &s->windows) { - w = wl->window; - cmdq_print(cmdq, "%4u: %s [%ux%u] [flags=0x%x, " - "references=%u, last layout=%d]", wl->idx, w->name, - w->sx, w->sy, w->flags, w->references, - w->lastlayout); - j = 0; - TAILQ_FOREACH(wp, &w->panes, entry) { - lines = size = 0; - gd = wp->base.grid; - for (k = 0; k < gd->hsize + gd->sy; k++) { - gl = &gd->linedata[k]; - if (gl->celldata == NULL) - continue; - lines++; - size += gl->cellsize * - sizeof *gl->celldata; - } - cmdq_print(cmdq, - "%6u: %s %lu %d %u/%u, %zu bytes", j, - wp->tty, (u_long) wp->pid, wp->fd, lines, - gd->hsize + gd->sy, size); - j++; - } - } - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Terminals:"); - LIST_FOREACH(term, &tty_terms, entry) { - cmdq_print(cmdq, "%s [references=%u, flags=0x%x]:", - term->name, term->references, term->flags); - for (i = 0; i < NTTYCODE; i++) { - ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; - switch (code->type) { - case TTYCODE_NONE: - cmdq_print(cmdq, "%2u: %s: [missing]", - ent->code, ent->name); - break; - case TTYCODE_STRING: - strnvis(out, code->value.string, sizeof out, - VIS_OCTAL|VIS_TAB|VIS_NL); - cmdq_print(cmdq, "%2u: %s: (string) %s", - ent->code, ent->name, out); - break; - case TTYCODE_NUMBER: - cmdq_print(cmdq, "%2u: %s: (number) %d", - ent->code, ent->name, code->value.number); - break; - case TTYCODE_FLAG: - cmdq_print(cmdq, "%2u: %s: (flag) %s", - ent->code, ent->name, - code->value.flag ? "true" : "false"); - break; - } - } - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Jobs:"); - LIST_FOREACH(job, &all_jobs, lentry) { - cmdq_print(cmdq, "%s [fd=%d, pid=%d, status=%d]", - job->cmd, job->fd, job->pid, job->status); - } - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-show-messages.c b/cmd-show-messages.c index f43607aa..a3938e0a 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -20,6 +20,8 @@ #include #include +#include +#include #include "tmux.h" @@ -31,13 +33,98 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { "show-messages", "showmsgs", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, + "IJTt:", 0, 0, + "[-IJT] " CMD_TARGET_CLIENT_USAGE, 0, NULL, cmd_show_messages_exec }; +const struct cmd_entry cmd_server_info_entry = { + "server-info", "info", + "", 0, 0, + "", + 0, + NULL, + cmd_show_messages_exec +}; + +void cmd_show_messages_server (struct cmd_q *); +void cmd_show_messages_terminals (struct cmd_q *); +void cmd_show_messages_jobs (struct cmd_q *); + +void +cmd_show_messages_server (struct cmd_q *cmdq) +{ + char *tim; + + tim = ctime(&start_time); + *strchr(tim, '\n') = '\0'; + + cmdq_print(cmdq, "started %s", tim); + cmdq_print(cmdq, "socket path %s", socket_path); + cmdq_print(cmdq, "debug level %d", debug_level); + cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); +} + +void +cmd_show_messages_terminals (struct cmd_q *cmdq) +{ + struct tty_term *term; + const struct tty_term_code_entry *ent; + struct tty_code *code; + u_int i, n; + char out[80]; + + n = 0; + LIST_FOREACH(term, &tty_terms, entry) { + cmdq_print(cmdq, + "Terminal %u: %s [references=%u, flags=0x%x]:", + n, term->name, term->references, term->flags); + n++; + for (i = 0; i < NTTYCODE; i++) { + ent = &tty_term_codes[i]; + code = &term->codes[ent->code]; + switch (code->type) { + case TTYCODE_NONE: + cmdq_print(cmdq, "%4u: %s: [missing]", + ent->code, ent->name); + break; + case TTYCODE_STRING: + strnvis(out, code->value.string, sizeof out, + VIS_OCTAL|VIS_TAB|VIS_NL); + cmdq_print(cmdq, "%4u: %s: (string) %s", + ent->code, ent->name, out); + break; + case TTYCODE_NUMBER: + cmdq_print(cmdq, "%4u: %s: (number) %d", + ent->code, ent->name, code->value.number); + break; + case TTYCODE_FLAG: + cmdq_print(cmdq, "%4u: %s: (flag) %s", + ent->code, ent->name, + code->value.flag ? "true" : "false"); + break; + } + } + } +} + +void +cmd_show_messages_jobs (struct cmd_q *cmdq) +{ + struct job *job; + u_int n; + + n = 0; + LIST_FOREACH(job, &all_jobs, lentry) { + cmdq_print(cmdq, + "Job %u: %s [fd=%d, pid=%d, status=%d]", + n, job->cmd, job->fd, job->pid, job->status); + n++; + } +} + enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -46,6 +133,27 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) struct message_entry *msg; char *tim; u_int i; + int done; + + done = 0; + if (args_has (args, 'I') || self->entry == &cmd_server_info_entry) { + cmd_show_messages_server (cmdq); + done = 1; + } + if (args_has (args, 'T') || self->entry == &cmd_server_info_entry) { + if (done) + cmdq_print (cmdq, "%s", ""); + cmd_show_messages_terminals (cmdq); + done = 1; + } + if (args_has (args, 'J') || self->entry == &cmd_server_info_entry) { + if (done) + cmdq_print (cmdq, "%s", ""); + cmd_show_messages_jobs (cmdq); + done = 1; + } + if (done) + return (CMD_RETURN_NORMAL); if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); diff --git a/tmux.1 b/tmux.1 index a2404b4f..e44a44a6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -770,15 +770,24 @@ is specified, only update the client's status bar. Rename the session to .Ar new-name . .It Xo Ic show-messages +.Op Fl IJT .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) +Show client messages or server information. Any messages displayed on the status line are saved in a per-client message log, up to a maximum of the limit set by the .Ar message-limit session option for the session attached to that client. -This command displays the log for +With +.Fl t , +display the log for .Ar target-client . +.Fl I, +.Fl J +and +.Fl T +show debugging information about the running server, jobs and terminals. .It Ic source-file Ar path .D1 (alias: Ic source ) Execute commands from @@ -3566,9 +3575,6 @@ specified by .Fl t or the current pane if omitted). If the command doesn't return success, the exit status is also displayed. -.It Ic server-info -.D1 (alias: Ic info ) -Show server information and terminal details. .It Xo Ic wait-for .Op Fl L | S | U .Ar channel From 7d3d9963839e92e56cfd484918c16ef58c0be687 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Jan 2014 22:32:15 +0000 Subject: [PATCH 223/949] Support paste key in copy mode input (for search etc). Also clamp length to screen width. --- window-copy.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index ddd5bf41..48cd4b33 100644 --- a/window-copy.c +++ b/window-copy.c @@ -750,8 +750,10 @@ window_copy_key_input(struct window_pane *wp, int key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - size_t inputlen; + size_t inputlen, n; int np; + struct paste_buffer *pb; + u_char ch; switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYEDIT_CANCEL: @@ -765,6 +767,20 @@ window_copy_key_input(struct window_pane *wp, int key) case MODEKEYEDIT_DELETELINE: *data->inputstr = '\0'; break; + case MODEKEYEDIT_PASTE: + if ((pb = paste_get_top(&global_buffers)) == NULL) + break; + for (n = 0; n < pb->size; n++) { + ch = (u_char) pb->data[n]; + if (ch < 32 || ch == 127) + break; + } + inputlen = strlen(data->inputstr); + + data->inputstr = xrealloc(data->inputstr, 1, inputlen + n + 1); + memcpy(data->inputstr + inputlen, pb->data, n); + data->inputstr[inputlen + n] = '\0'; + break; case MODEKEYEDIT_ENTER: np = data->numprefix; if (np <= 0) @@ -1154,8 +1170,8 @@ window_copy_write_line( struct screen *s = &data->screen; struct options *oo = &wp->window->options; struct grid_cell gc; - char hdr[32]; - size_t last, xoff = 0, size = 0; + char hdr[512]; + size_t last, xoff = 0, size = 0, limit; window_mode_attrs(&gc, oo); @@ -1168,11 +1184,14 @@ window_copy_write_line( screen_write_cursormove(ctx, screen_size_x(s) - size, 0); screen_write_puts(ctx, &gc, "%s", hdr); } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { + limit = sizeof hdr; + if (limit > screen_size_x(s)) + limit = screen_size_x(s); if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { - xoff = size = xsnprintf(hdr, sizeof hdr, + xoff = size = xsnprintf(hdr, limit, "Repeat: %u", data->numprefix); } else { - xoff = size = xsnprintf(hdr, sizeof hdr, + xoff = size = xsnprintf(hdr, limit, "%s: %s", data->inputprompt, data->inputstr); } screen_write_cursormove(ctx, 0, last); From dda70d4ef1ce155906c123c6880352c4979e4b57 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jan 2014 23:57:35 +0000 Subject: [PATCH 224/949] Merge start-server into kill-server. --- Makefile | 1 - cmd-kill-server.c | 14 ++++++++++++-- cmd-start-server.c | 42 ------------------------------------------ 3 files changed, 12 insertions(+), 45 deletions(-) delete mode 100644 cmd-start-server.c diff --git a/Makefile b/Makefile index 96972944..a0736bdf 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,6 @@ SRCS= arguments.c \ cmd-show-options.c \ cmd-source-file.c \ cmd-split-window.c \ - cmd-start-server.c \ cmd-string.c \ cmd-suspend-client.c \ cmd-swap-pane.c \ diff --git a/cmd-kill-server.c b/cmd-kill-server.c index ef4a3946..6f0b7494 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -38,10 +38,20 @@ const struct cmd_entry cmd_kill_server_entry = { cmd_kill_server_exec }; +const struct cmd_entry cmd_start_server_entry = { + "start-server", "start", + "", 0, 0, + "", + CMD_STARTSERVER, + NULL, + cmd_kill_server_exec +}; + enum cmd_retval -cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) +cmd_kill_server_exec(struct cmd *self, unused struct cmd_q *cmdq) { - kill(getpid(), SIGTERM); + if (self->entry == &cmd_kill_server_entry) + kill(getpid(), SIGTERM); return (CMD_RETURN_NORMAL); } diff --git a/cmd-start-server.c b/cmd-start-server.c deleted file mode 100644 index d98f9506..00000000 --- a/cmd-start-server.c +++ /dev/null @@ -1,42 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include "tmux.h" - -/* - * Start the server and do nothing else. - */ - -enum cmd_retval cmd_start_server_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_start_server_entry = { - "start-server", "start", - "", 0, 0, - "", - CMD_STARTSERVER, - NULL, - cmd_start_server_exec -}; - -enum cmd_retval -cmd_start_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) -{ - return (CMD_RETURN_NORMAL); -} From c930fd5ff696f5a60e93ed503f0ff57e0bbf6e4d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2014 22:19:17 +0000 Subject: [PATCH 225/949] Remember the last active pane in the top-bottom or left-right cell so that it can be restored when moving back to that cell with selectp -L/-R/etc. From Suraj N Kurapati. --- tmux.h | 2 ++ window.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/tmux.h b/tmux.h index 0a4f8fb5..b28f10a0 100644 --- a/tmux.h +++ b/tmux.h @@ -1028,6 +1028,8 @@ struct layout_cell { u_int yoff; struct window_pane *wp; + struct window_pane *lastwp; + struct layout_cells cells; TAILQ_ENTRY(layout_cell) entry; diff --git a/window.c b/window.c index 83e14db6..dcb2a5f1 100644 --- a/window.c +++ b/window.c @@ -61,6 +61,10 @@ struct window_pane_tree all_window_panes; u_int next_window_pane_id; u_int next_window_id; +struct window_pane *window_pane_active_set(struct window_pane *, + struct window_pane *); +void window_pane_active_lost(struct window_pane *, struct window_pane *); + void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); @@ -386,6 +390,59 @@ window_resize(struct window *w, u_int sx, u_int sy) w->sy = sy; } +/* + * Restore previously active pane when changing from wp to nextwp. The intended + * pane is in nextwp and it returns the previously focused pane. + */ +struct window_pane * +window_pane_active_set(struct window_pane *wp, struct window_pane *nextwp) +{ + struct layout_cell *lc; + struct window_pane *lastwp; + + /* Target pane's parent must not be an ancestor of source pane. */ + for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { + if (lc == nextwp->layout_cell->parent) + return (nextwp); + } + + /* + * Previously active pane, if any, must not be the same as the source + * pane. + */ + if (nextwp->layout_cell->parent != NULL) { + lastwp = nextwp->layout_cell->parent->lastwp; + if (lastwp != wp && window_pane_visible(lastwp)) + return (lastwp); + } + return (nextwp); +} + +/* Remember previously active pane when changing from wp to nextwp. */ +void +window_pane_active_lost(struct window_pane *wp, struct window_pane *nextwp) +{ + struct layout_cell *lc, *lc2; + + /* Save the target pane in its parent. */ + nextwp->layout_cell->parent->lastwp = nextwp; + + /* + * Save the source pane in all of its parents up to, but not including, + * the common ancestor of itself and the target panes. + */ + if (wp == NULL) + return; + for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { + lc2 = nextwp->layout_cell->parent; + for (; lc2 != NULL; lc2 = lc2->parent) { + if (lc == lc2) + return; + } + lc->lastwp = wp; + } +} + void window_set_active_pane(struct window *w, struct window_pane *wp) { @@ -393,6 +450,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return; w->last = w->active; w->active = wp; + window_pane_active_lost(w->last, wp); while (!window_pane_visible(w->active)) { w->active = TAILQ_PREV(w->active, window_panes, entry); if (w->active == NULL) @@ -707,6 +765,16 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) void window_pane_destroy(struct window_pane *wp) { + struct window_pane *wp2; + + /* Forget removed pane in all layout cells that remember it. */ + RB_FOREACH(wp2, window_pane_tree, &all_window_panes) { + if (wp2->layout_cell != NULL && + wp2->layout_cell->parent != NULL && + wp2->layout_cell->parent->lastwp == wp) + wp2->layout_cell->parent->lastwp = NULL; + } + window_pane_reset_mode(wp); if (event_initialized(&wp->changes_timer)) @@ -1130,7 +1198,7 @@ window_pane_find_up(struct window_pane *wp) if (wp2->yoff + wp2->sy + 1 != top) continue; if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (wp2); + return (window_pane_active_set(wp, wp2)); } return (NULL); } @@ -1156,7 +1224,7 @@ window_pane_find_down(struct window_pane *wp) if (wp2->yoff != bottom) continue; if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (wp2); + return (window_pane_active_set(wp, wp2)); } return (NULL); } @@ -1185,7 +1253,7 @@ window_pane_find_left(struct window_pane *wp) if (wp2->xoff + wp2->sx + 1 != left) continue; if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (wp2); + return (window_pane_active_set(wp, wp2)); } return (NULL); } @@ -1214,7 +1282,7 @@ window_pane_find_right(struct window_pane *wp) if (wp2->xoff != right) continue; if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (wp2); + return (window_pane_active_set(wp, wp2)); } return (NULL); } From 945339b443affdaaca260605e15b5a3b9a3c6e16 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2014 23:07:09 +0000 Subject: [PATCH 226/949] Allow replacing each of the many sets of separate foo-{fg,bg,attr} options with a single foo-style option. For example: set -g status-fg yellow set -g status-bg red set -g status-attr blink Becomes: set -g status-style fg=yellow,bg=red,blink The -a flag to set can be used to add to rather than replace a style. So: set -g status-bg red Becomes: set -ag status-style bg=red Currently this is fully backwards compatible (all *-{fg,bg,attr} options remain) but the plan is to deprecate them over time. From Tiago Cunha. --- Makefile | 1 + cmd-set-option.c | 28 +++++ grid.c | 1 - options-table.c | 269 +++++++++++++++++++++++++++++----------- options.c | 36 ++++++ screen-redraw.c | 20 +-- screen-write.c | 85 +------------ status.c | 118 +++--------------- style.c | 224 ++++++++++++++++++++++++++++++++++ tmux.1 | 310 ++++++++++++++++++++++++++--------------------- tmux.h | 36 ++++-- tty.c | 3 +- window-choose.c | 4 +- window-copy.c | 4 +- window.c | 10 -- 15 files changed, 710 insertions(+), 439 deletions(-) create mode 100644 style.c diff --git a/Makefile b/Makefile index a0736bdf..e566bb2a 100644 --- a/Makefile +++ b/Makefile @@ -113,6 +113,7 @@ SRCS= arguments.c \ session.c \ signal.c \ status.c \ + style.c \ tmux.c \ tty-acs.c \ tty-keys.c \ diff --git a/cmd-set-option.c b/cmd-set-option.c index 1b25fac0..b661913f 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -60,6 +60,9 @@ struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_q *, struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); +struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, + const struct options_table_entry *, struct options *, + const char *); const struct cmd_entry cmd_set_option_entry = { "set-option", "set", @@ -304,9 +307,11 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, break; case OPTIONS_TABLE_COLOUR: o = cmd_set_option_colour(self, cmdq, oe, oo, value); + style_update_new(oo, o->name, oe->style); break; case OPTIONS_TABLE_ATTRIBUTES: o = cmd_set_option_attributes(self, cmdq, oe, oo, value); + style_update_new(oo, o->name, oe->style); break; case OPTIONS_TABLE_FLAG: o = cmd_set_option_flag(self, cmdq, oe, oo, value); @@ -314,6 +319,9 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, case OPTIONS_TABLE_CHOICE: o = cmd_set_option_choice(self, cmdq, oe, oo, value); break; + case OPTIONS_TABLE_STYLE: + o = cmd_set_option_style(self, cmdq, oe, oo, value); + break; } if (o == NULL) return (-1); @@ -462,3 +470,23 @@ cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq, return (options_set_number(oo, oe->name, choice)); } + +/* Set a style option. */ +struct options_entry * +cmd_set_option_style(struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) +{ + struct args *args = self->args; + struct options_entry *o; + int append; + + append = args_has(args, 'a'); + if ((o = options_set_style(oo, oe->name, value, append)) == NULL) { + cmdq_error(cmdq, "bad style: %s", value); + return (NULL); + } + + style_update_old(oo, oe->name, &o->style); + return (o); +} diff --git a/grid.c b/grid.c index 852423b5..fb838adf 100644 --- a/grid.c +++ b/grid.c @@ -37,7 +37,6 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; -const struct grid_cell grid_marker_cell = { 0, 0, 8, 8, (1 << 4) | 1, "_" }; #define grid_put_cell(gd, px, py, gc) do { \ memcpy(&gd->linedata[py].celldata[px], \ diff --git a/options-table.c b/options-table.c index 97ca90be..4480b344 100644 --- a/options-table.c +++ b/options-table.c @@ -196,32 +196,43 @@ const struct options_table_entry session_options_table[] = { { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "message-style" }, { .name = "message-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 3 + .default_num = 3, + .style = "message-style" }, { .name = "message-command-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "message-command-style" }, { .name = "message-command-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 + .default_num = 0, + .style = "message-command-style" }, { .name = "message-command-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 3 + .default_num = 3, + .style = "message-command-style" + }, + + { .name = "message-command-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "bg=black,fg=yellow" }, { .name = "message-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 + .default_num = 0, + .style = "message-style" }, { .name = "message-limit", @@ -231,6 +242,11 @@ const struct options_table_entry session_options_table[] = { .default_num = 20 }, + { .name = "message-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "bg=yellow,fg=black" + }, + { .name = "mouse-resize-pane", .type = OPTIONS_TABLE_FLAG, .default_num = 0 @@ -253,22 +269,36 @@ const struct options_table_entry session_options_table[] = { { .name = "pane-active-border-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "pane-active-border-style" }, { .name = "pane-active-border-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 2 + .default_num = 2, + .style = "pane-active-border-style" + }, + + { .name = "pane-active-border-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "fg=green" }, { .name = "pane-border-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "pane-border-style" }, { .name = "pane-border-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "pane-border-style" + }, + + { .name = "pane-border-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" }, { .name = "prefix", @@ -315,17 +345,20 @@ const struct options_table_entry session_options_table[] = { { .name = "status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "status-style" }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 2 + .default_num = 2, + .style = "status-style" }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 + .default_num = 0, + .style = "status-style" }, { .name = "status-interval", @@ -354,17 +387,20 @@ const struct options_table_entry session_options_table[] = { { .name = "status-left-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "status-left-style" }, { .name = "status-left-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "status-left-style" }, { .name = "status-left-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "status-left-style" }, { .name = "status-left-length", @@ -374,6 +410,11 @@ const struct options_table_entry session_options_table[] = { .default_num = 10 }, + { .name = "status-left-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_status_position_list, @@ -387,17 +428,20 @@ const struct options_table_entry session_options_table[] = { { .name = "status-right-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "status-right-style" }, { .name = "status-right-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "status-right-style" }, { .name = "status-right-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "status-right-style" }, { .name = "status-right-length", @@ -407,6 +451,16 @@ const struct options_table_entry session_options_table[] = { .default_num = 40 }, + { .name = "status-right-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + + { .name = "status-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "bg=green,fg=black" + }, + { .name = "status-utf8", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ @@ -537,17 +591,20 @@ const struct options_table_entry window_options_table[] = { { .name = "mode-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "mode-style" }, { .name = "mode-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 3 + .default_num = 3, + .style = "mode-style" }, { .name = "mode-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 + .default_num = 0, + .style = "mode-style" }, { .name = "mode-keys", @@ -562,6 +619,11 @@ const struct options_table_entry window_options_table[] = { .default_num = 0 }, + { .name = "mode-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "bg=yellow,fg=black" + }, + { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, .default_num = 0 @@ -617,72 +679,101 @@ const struct options_table_entry window_options_table[] = { { .name = "window-status-activity-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = GRID_ATTR_REVERSE + .default_num = GRID_ATTR_REVERSE, + .style = "window-status-activity-style" }, { .name = "window-status-activity-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "window-status-activity-style" }, { .name = "window-status-activity-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "window-status-activity-style" }, - { .name = "window-status-bell-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = GRID_ATTR_REVERSE - }, - - { .name = "window-status-bell-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-bell-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-content-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = GRID_ATTR_REVERSE - }, - - { .name = "window-status-content-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-content-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + { .name = "window-status-activity-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "reverse" }, { .name = "window-status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "window-status-style" + }, + + { .name = "window-status-bell-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = GRID_ATTR_REVERSE, + .style = "window-status-bell-style" + }, + + { .name = "window-status-bell-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8, + .style = "window-status-bell-style" + }, + + { .name = "window-status-bell-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8, + .style = "window-status-bell-style" + }, + + { .name = "window-status-bell-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "reverse" }, { .name = "window-status-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "window-status-style" + }, + + { .name = "window-status-content-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = GRID_ATTR_REVERSE, + .style = "window-status-content-style" + }, + + { .name = "window-status-content-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8, + .style = "window-status-content-style" + }, + + { .name = "window-status-content-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8, + .style = "window-status-content-style" + }, + + { .name = "window-status-content-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "reverse" }, { .name = "window-status-current-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .default_num = 0, + .style = "window-status-current-style" }, { .name = "window-status-current-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "window-status-current-style" }, { .name = "window-status-current-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "window-status-current-style" }, { .name = "window-status-current-format", @@ -690,24 +781,15 @@ const struct options_table_entry window_options_table[] = { .default_str = "#I:#W#F" }, - { .name = "window-status-last-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 - }, - - { .name = "window-status-last-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-last-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + { .name = "window-status-current-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" }, { .name = "window-status-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .default_num = 8, + .style = "window-status-style" }, { .name = "window-status-format", @@ -715,11 +797,39 @@ const struct options_table_entry window_options_table[] = { .default_str = "#I:#W#F" }, + { .name = "window-status-last-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0, + .style = "window-status-last-style" + }, + + { .name = "window-status-last-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8, + .style = "window-status-last-style" + }, + + { .name = "window-status-last-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8, + .style = "window-status-last-style" + }, + + { .name = "window-status-last-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, .default_str = " " }, + { .name = "window-status-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, .default_num = 1 @@ -741,10 +851,17 @@ options_table_populate_tree( const struct options_table_entry *oe; for (oe = table; oe->name != NULL; oe++) { - if (oe->default_str != NULL) + switch (oe->type) { + case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); - else + break; + case OPTIONS_TABLE_STYLE: + options_set_style(oo, oe->name, oe->default_str); + break; + default: options_set_number(oo, oe->name, oe->default_num); + break; + } } } @@ -789,6 +906,10 @@ options_table_print_entry(const struct options_table_entry *oe, s = oe->choices[o->num]; xsnprintf(out, sizeof out, "%s", s); break; + case OPTIONS_TABLE_STYLE: + s = style_tostring(&o->style); + xsnprintf(out, sizeof out, "%s", s); + break; } return (out); } diff --git a/options.c b/options.c index 51a6608e..699dd9b0 100644 --- a/options.c +++ b/options.c @@ -109,6 +109,7 @@ options_set_string(struct options *oo, const char *name, const char *fmt, ...) o = xmalloc(sizeof *o); o->name = xstrdup(name); RB_INSERT(options_tree, &oo->tree, o); + memcpy(&o->style, &grid_default_cell, sizeof o->style); } else if (o->type == OPTIONS_STRING) free(o->str); @@ -140,6 +141,7 @@ options_set_number(struct options *oo, const char *name, long long value) o = xmalloc(sizeof *o); o->name = xstrdup(name); RB_INSERT(options_tree, &oo->tree, o); + memcpy(&o->style, &grid_default_cell, sizeof o->style); } else if (o->type == OPTIONS_STRING) free(o->str); @@ -159,3 +161,37 @@ options_get_number(struct options *oo, const char *name) fatalx("option not a number"); return (o->num); } + +struct options_entry * +options_set_style(struct options *oo, const char *name, const char *value, + int append) +{ + struct options_entry *o; + + if ((o = options_find1(oo, name)) == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + RB_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + free(o->str); + + if (!append) + memcpy(&o->style, &grid_default_cell, sizeof o->style); + + o->type = OPTIONS_STYLE; + if (style_parse(&grid_default_cell, &o->style, value) == -1) + return (NULL); + return (o); +} + +struct grid_cell * +options_get_style(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option"); + if (o->type != OPTIONS_STYLE) + fatalx("option not a style"); + return (&o->style); +} diff --git a/screen-redraw.c b/screen-redraw.c index 4601c6f3..a67cb952 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -224,7 +224,7 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only) struct window_pane *wp; struct grid_cell active_gc, other_gc; u_int i, j, type, top; - int status, spos, fg, bg; + int status, spos; /* Suspended clients should not be updated. */ if (c->flags & CLIENT_SUSPENDED) @@ -251,17 +251,9 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only) } /* Set up pane border attributes. */ - memcpy(&other_gc, &grid_marker_cell, sizeof other_gc); - memcpy(&active_gc, &grid_marker_cell, sizeof active_gc); - active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; - fg = options_get_number(oo, "pane-border-fg"); - colour_set_fg(&other_gc, fg); - bg = options_get_number(oo, "pane-border-bg"); - colour_set_bg(&other_gc, bg); - fg = options_get_number(oo, "pane-active-border-fg"); - colour_set_fg(&active_gc, fg); - bg = options_get_number(oo, "pane-active-border-bg"); - colour_set_bg(&active_gc, bg); + style_apply(&other_gc, oo, "pane-border-style"); + style_apply(&active_gc, oo, "pane-active-border-style"); + active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; /* nuke existing */ /* Draw background and borders. */ for (j = 0; j < tty->sy - status; j++) { @@ -368,7 +360,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) px -= len * 3; py -= 2; - memcpy(&gc, &grid_marker_cell, sizeof gc); + memcpy(&gc, &grid_default_cell, sizeof gc); if (w->active == wp) colour_set_bg(&gc, active_colour); else @@ -395,7 +387,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) tty_cursor(tty, xoff + wp->sx - len, yoff); draw_text: - memcpy(&gc, &grid_marker_cell, sizeof gc); + memcpy(&gc, &grid_default_cell, sizeof gc); if (w->active == wp) colour_set_fg(&gc, active_colour); else diff --git a/screen-write.c b/screen-write.c index 7fcfc5ee..3da145e8 100644 --- a/screen-write.c +++ b/screen-write.c @@ -248,7 +248,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, } *last = '\0'; - screen_write_parsestyle(gc, &lgc, ptr); + style_parse(gc, &lgc, ptr); ptr = last + 1; continue; } @@ -288,89 +288,6 @@ screen_write_cnputs(struct screen_write_ctx *ctx, free(msg); } -/* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */ -void -screen_write_parsestyle( - struct grid_cell *defgc, struct grid_cell *gc, const char *in) -{ - const char delimiters[] = " ,"; - char tmp[32]; - int val; - size_t end; - u_char fg, bg, attr, flags; - - if (*in == '\0') - return; - if (strchr(delimiters, in[strlen(in) - 1]) != NULL) - return; - - fg = gc->fg; - bg = gc->bg; - attr = gc->attr; - flags = gc->flags; - do { - end = strcspn(in, delimiters); - if (end > (sizeof tmp) - 1) - return; - memcpy(tmp, in, end); - tmp[end] = '\0'; - - if (strcasecmp(tmp, "default") == 0) { - fg = defgc->fg; - bg = defgc->bg; - attr = defgc->attr; - flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); - flags |= - defgc->flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); - } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { - if ((val = colour_fromstring(tmp + 3)) == -1) - return; - if (*in == 'f' || *in == 'F') { - if (val != 8) { - if (val & 0x100) { - flags |= GRID_FLAG_FG256; - val &= ~0x100; - } else - flags &= ~GRID_FLAG_FG256; - fg = val; - } else { - fg = defgc->fg; - flags &= ~GRID_FLAG_FG256; - flags |= defgc->flags & GRID_FLAG_FG256; - } - } else if (*in == 'b' || *in == 'B') { - if (val != 8) { - if (val & 0x100) { - flags |= GRID_FLAG_BG256; - val &= ~0x100; - } else - flags &= ~GRID_FLAG_BG256; - bg = val; - } else { - bg = defgc->bg; - flags &= ~GRID_FLAG_BG256; - flags |= defgc->flags & GRID_FLAG_BG256; - } - } else - return; - } else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { - if ((val = attributes_fromstring(tmp + 2)) == -1) - return; - attr &= ~val; - } else { - if ((val = attributes_fromstring(tmp)) == -1) - return; - attr |= val; - } - - in += end + strspn(in + end, delimiters); - } while (*in != '\0'); - gc->fg = fg; - gc->bg = bg; - gc->attr = attr; - gc->flags = flags; -} - /* Copy from another screen. */ void screen_write_copy(struct screen_write_ctx *ctx, diff --git a/status.c b/status.c index c5572b74..e14c1a81 100644 --- a/status.c +++ b/status.c @@ -80,18 +80,9 @@ status_redraw_get_left(struct client *c, { struct session *s = c->session; char *left; - int fg, bg, attr; size_t leftlen; - fg = options_get_number(&s->options, "status-left-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(&s->options, "status-left-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(&s->options, "status-left-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, &s->options, "status-left-style"); left = status_replace(c, NULL, NULL, NULL, options_get_string(&s->options, "status-left"), t, 1); @@ -110,18 +101,9 @@ status_redraw_get_right(struct client *c, { struct session *s = c->session; char *right; - int fg, bg, attr; size_t rightlen; - fg = options_get_number(&s->options, "status-right-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(&s->options, "status-right-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(&s->options, "status-right-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, &s->options, "status-right-style"); right = status_replace(c, NULL, NULL, NULL, options_get_string(&s->options, "status-right"), t, 1); @@ -177,10 +159,7 @@ status_redraw(struct client *c) t = c->status_timer.tv_sec; /* Set up default colour. */ - memcpy(&stdgc, &grid_default_cell, sizeof gc); - colour_set_fg(&stdgc, options_get_number(&s->options, "status-fg")); - colour_set_bg(&stdgc, options_get_number(&s->options, "status-bg")); - stdgc.attr |= options_get_number(&s->options, "status-attr"); + style_apply(&stdgc, &s->options, "status-style"); /* Create the target screen. */ memcpy(&old_status, &c->status, sizeof old_status); @@ -646,73 +625,22 @@ status_print( struct session *s = c->session; const char *fmt; char *text; - int fg, bg, attr; - fg = options_get_number(oo, "window-status-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, oo, "window-status-style"); fmt = options_get_string(oo, "window-status-format"); if (wl == s->curw) { - fg = options_get_number(oo, "window-status-current-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-current-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-current-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, oo, "window-status-current-style"); fmt = options_get_string(oo, "window-status-current-format"); } - if (wl == TAILQ_FIRST(&s->lastw)) { - fg = options_get_number(oo, "window-status-last-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-last-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-last-attr"); - if (attr != 0) - gc->attr = attr; - } + if (wl == TAILQ_FIRST(&s->lastw)) + style_apply_update(gc, oo, "window-status-last-style"); - if (wl->flags & WINLINK_BELL) { - fg = options_get_number(oo, "window-status-bell-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-bell-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-bell-attr"); - if (attr != 0) - gc->attr = attr; - } else if (wl->flags & WINLINK_CONTENT) { - fg = options_get_number(oo, "window-status-content-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-content-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-content-attr"); - if (attr != 0) - gc->attr = attr; - } else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) { - fg = options_get_number(oo, "window-status-activity-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-activity-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-activity-attr"); - if (attr != 0) - gc->attr = attr; - } + if (wl->flags & WINLINK_BELL) + style_apply_update(gc, oo, "window-status-bell-style"); + else if (wl->flags & WINLINK_CONTENT) + style_apply_update(gc, oo, "window-status-content-style"); + else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) + style_apply_update(gc, oo, "window-status-activity-style"); text = status_replace(c, NULL, wl, NULL, fmt, t, 1); return (text); @@ -814,10 +742,7 @@ status_message_redraw(struct client *c) if (len > c->tty.sx) len = c->tty.sx; - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); - colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); - gc.attr |= options_get_number(&s->options, "message-attr"); + style_apply(&gc, &s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); @@ -935,18 +860,11 @@ status_prompt_redraw(struct client *c) len = c->tty.sx; off = 0; - memcpy(&gc, &grid_default_cell, sizeof gc); - /* Change colours for command mode. */ - if (c->prompt_mdata.mode == 1) { - colour_set_fg(&gc, options_get_number(&s->options, "message-command-fg")); - colour_set_bg(&gc, options_get_number(&s->options, "message-command-bg")); - gc.attr |= options_get_number(&s->options, "message-command-attr"); - } else { - colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); - colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); - gc.attr |= options_get_number(&s->options, "message-attr"); - } + if (c->prompt_mdata.mode == 1) + style_apply(&gc, &s->options, "message-command-style"); + else + style_apply(&gc, &s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); diff --git a/style.c b/style.c new file mode 100644 index 00000000..97d5576e --- /dev/null +++ b/style.c @@ -0,0 +1,224 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2014 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */ +int +style_parse(const struct grid_cell *defgc, struct grid_cell *gc, + const char *in) +{ + const char delimiters[] = " ,"; + char tmp[32]; + int val; + size_t end; + u_char fg, bg, attr, flags; + + if (*in == '\0') + return (0); + if (strchr(delimiters, in[strlen(in) - 1]) != NULL) + return (-1); + + fg = gc->fg; + bg = gc->bg; + attr = gc->attr; + flags = gc->flags; + do { + end = strcspn(in, delimiters); + if (end > (sizeof tmp) - 1) + return (-1); + memcpy(tmp, in, end); + tmp[end] = '\0'; + + if (strcasecmp(tmp, "default") == 0) { + fg = defgc->fg; + bg = defgc->bg; + attr = defgc->attr; + flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + flags |= + defgc->flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); + } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { + if ((val = colour_fromstring(tmp + 3)) == -1) + return (-1); + if (*in == 'f' || *in == 'F') { + if (val != 8) { + if (val & 0x100) { + flags |= GRID_FLAG_FG256; + val &= ~0x100; + } else + flags &= ~GRID_FLAG_FG256; + fg = val; + } else { + fg = defgc->fg; + flags &= ~GRID_FLAG_FG256; + flags |= defgc->flags & GRID_FLAG_FG256; + } + } else if (*in == 'b' || *in == 'B') { + if (val != 8) { + if (val & 0x100) { + flags |= GRID_FLAG_BG256; + val &= ~0x100; + } else + flags &= ~GRID_FLAG_BG256; + bg = val; + } else { + bg = defgc->bg; + flags &= ~GRID_FLAG_BG256; + flags |= defgc->flags & GRID_FLAG_BG256; + } + } else + return (-1); + } else if (strcasecmp(tmp, "none") == 0) + attr = 0; + else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { + if ((val = attributes_fromstring(tmp + 2)) == -1) + return (-1); + attr &= ~val; + } else { + if ((val = attributes_fromstring(tmp)) == -1) + return (-1); + attr |= val; + } + + in += end + strspn(in + end, delimiters); + } while (*in != '\0'); + gc->fg = fg; + gc->bg = bg; + gc->attr = attr; + gc->flags = flags; + + return (0); +} + +/* Convert style to a string. */ +const char * +style_tostring(struct grid_cell *gc) +{ + int c, off = 0, comma = 0; + static char s[256]; + + *s = '\0'; + + if (gc->fg != 8) { + if (gc->flags & GRID_FLAG_FG256) + c = gc->fg | 0x100; + else + c = gc->fg; + off += xsnprintf(s, sizeof s, "fg=%s", colour_tostring(c)); + comma = 1; + } + + if (gc->bg != 8) { + if (gc->flags & GRID_FLAG_BG256) + c = gc->bg | 0x100; + else + c = gc->bg; + off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", + comma ? "," : "", colour_tostring(c)); + comma = 1; + } + + if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) { + xsnprintf(s + off, sizeof s - off, "%s%s", + comma ? "," : "", attributes_tostring(gc->attr)); + } + + if (*s == '\0') + return ("default"); + return (s); +} + +/* Synchronize new -style option with the old one. */ +void +style_update_new(struct options *oo, const char *name, const char *newname) +{ + int value; + struct grid_cell *gc; + + /* It's a colour or attribute, but with no -style equivalent. */ + if (newname == NULL) + return; + + gc = options_get_style(oo, newname); + value = options_get_number(oo, name); + + if (strstr(name, "-bg") != NULL) + colour_set_bg(gc, value); + else if (strstr(name, "-fg") != NULL) + colour_set_fg(gc, value); + else if (strstr(name, "-attr") != NULL) + gc->attr = value; +} + +/* Synchronize all the old options with the new -style one. */ +void +style_update_old(struct options *oo, const char *name, struct grid_cell *gc) +{ + char newname[128]; + int c, size; + + size = strrchr(name, '-') - name; + + if (gc->flags & GRID_FLAG_BG256) + c = gc->bg | 0x100; + else + c = gc->bg; + xsnprintf(newname, sizeof newname, "%.*s-bg", size, name); + options_set_number(oo, newname, c); + + if (gc->flags & GRID_FLAG_FG256) + c = gc->fg | 0x100; + else + c = gc->fg; + xsnprintf(newname, sizeof newname, "%.*s-fg", size, name); + options_set_number(oo, newname, c); + + xsnprintf(newname, sizeof newname, "%.*s-attr", size, name); + options_set_number(oo, newname, gc->attr); +} + +/* Apply a style. */ +void +style_apply(struct grid_cell *gc, struct options *oo, const char *name) +{ + struct grid_cell *gcp; + + memcpy(gc, &grid_default_cell, sizeof *gc); + gcp = options_get_style(oo, name); + colour_set_fg(gc, gcp->fg); + colour_set_bg(gc, gcp->bg); + gc->attr |= gcp->attr; +} + +/* Apply a style, updating if default. */ +void +style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) +{ + struct grid_cell *gcp; + + gcp = options_get_style(oo, name); + if (gcp->fg != 8) + colour_set_fg(gc, gcp->fg); + if (gcp->bg != 8) + colour_set_bg(gc, gcp->bg); + if (gcp->attr != 0) + gc->attr |= gcp->attr; +} diff --git a/tmux.1 b/tmux.1 index e44a44a6..e0b27b6d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2060,11 +2060,6 @@ otherwise a session option. If .Fl g is specified, the global session or window option is set. -With -.Fl a , -and if the option expects a string, -.Ar value -is appended to the existing setting. The .Fl u flag unsets an option, so a session inherits the option from the global @@ -2081,6 +2076,32 @@ flag suppresses the informational message (as if the .Ic quiet server option was set). .Pp +With +.Fl a , +and if the option expects a string or a style, +.Ar value +is appended to the existing setting. +For example: +.Bd -literal -offset indent +set -g status-left "foo" +set -ag status-left "bar" +.Ed +.Pp +Will result in +.Ql foobar . +And: +.Bd -literal -offset indent +set -g status-style "bg=red" +set -ag status-style "fg=blue" +.Ed +.Pp +Will result in a red background +.Em and +blue foreground. +Without +.Fl a , +the result would be the default background and a blue foreground. +.Pp Available window options are listed under .Ic set-window-option . .Pp @@ -2272,26 +2293,18 @@ the entire server will lock after .Em all sessions would have locked. This has no effect as a session option; it must be set as a global option. -.It Ic message-attr Ar attributes -Set status line message attributes, where -.Ar attributes -is either -.Ic none -or a comma-delimited list of one or more of: -.Ic bright -(or -.Ic bold ) , -.Ic dim , -.Ic underscore , -.Ic blink , -.Ic reverse , -.Ic hidden , -or -.Ic italics . -.It Ic message-bg Ar colour -Set status line message background colour, where -.Ar colour -is one of: +.It Ic message-command-style Ar style +Set status line message command style, where +.Ar style +is a comma-separated list of characteristics to be specified. +.Pp +These may be +.Ql bg=colour +to set the background colour, +.Ql fg=colour +to set the foreground colour, and a list of attributes as specified below. +.Pp +The colour is one of: .Ic black , .Ic red , .Ic green , @@ -2312,18 +2325,46 @@ from the 256-colour set, or a hexadecimal RGB string such as .Ql #ffffff , which chooses the closest match from the default 256-colour set. -.It Ic message-command-attr Ar attributes -Set status line message attributes when in command mode. -.It Ic message-command-bg Ar colour -Set status line message background colour when in command mode. -.It Ic message-command-fg Ar colour -Set status line message foreground colour when in command mode. -.It Ic message-fg Ar colour -Set status line message foreground colour. +.Pp +The attributes is either +.Ic none +or a comma-delimited list of one or more of: +.Ic bright +(or +.Ic bold ) , +.Ic dim , +.Ic underscore , +.Ic blink , +.Ic reverse , +.Ic hidden , +or +.Ic italics , +to turn an attribute on, or an attribute prefixed with +.Ql no +to turn one off. +.Pp +Examples are: +.Bd -literal -offset indent +fg=yellow,bold,underscore,blink +bg=black,fg=default,noreverse +.Ed +.Pp +With the +.Fl a +flag to the +.Ic set-option +command the new style is added otherwise the existing style is replaced. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. The default is 20. +.It Ic message-style Ar style +Set status line message style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .It Xo Ic mouse-resize-pane .Op Ic on | off .Xc @@ -2347,12 +2388,22 @@ window. .Op Ic on | off .Xc If enabled, request mouse input as UTF-8 on UTF-8 terminals. -.It Ic pane-active-border-bg Ar colour -.It Ic pane-active-border-fg Ar colour -Set the pane border colour for the currently active pane. -.It Ic pane-border-bg Ar colour -.It Ic pane-border-fg Ar colour -Set the pane border colour for panes aside from the active pane. +.It Ic pane-active-border-style Ar style +Set the pane border style for the currently active pane. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +Attributes are ignored. +.It Ic pane-border-style Ar style +Set the pane border style for paneas aside from the active pane. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +Attributes are ignored. .It Ic prefix Ar key Set the key accepted as a prefix key. .It Ic prefix2 Ar key @@ -2418,12 +2469,6 @@ option. .Op Ic on | off .Xc Show or hide the status line. -.It Ic status-attr Ar attributes -Set status line attributes. -.It Ic status-bg Ar colour -Set status line background colour. -.It Ic status-fg Ar colour -Set status line foreground colour. .It Ic status-interval Ar interval Update the status bar every .Ar interval @@ -2481,19 +2526,10 @@ section). For details on how the names and titles can be set see the .Sx "NAMES AND TITLES" section. +For a list of allowed attributes see the +.Ic message-command-style +option. .Pp -#[attributes] allows a comma-separated list of attributes to be specified, -these may be -.Ql fg=colour -to set the foreground colour, -.Ql bg=colour -to set the background colour, the name of one of the attributes (listed under -the -.Ic message-attr -option) to turn an attribute on, or an attribute prefixed with -.Ql no -to turn one off, for example -.Ic nobright . Examples are: .Bd -literal -offset indent #(sysctl vm.loadavg) @@ -2509,17 +2545,18 @@ By default, UTF-8 in is not interpreted, to enable UTF-8, use the .Ic status-utf8 option. -.It Ic status-left-attr Ar attributes -Set the attribute of the left part of the status line. -.It Ic status-left-bg Ar colour -Set the background colour of the left part of the status line. -.It Ic status-left-fg Ar colour -Set the foreground colour of the left part of the status line. .It Ic status-left-length Ar length Set the maximum .Ar length of the left component of the status bar. The default is 10. +.It Ic status-left-style Ar style +Set the style of the left part of the status line. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .It Xo Ic status-position .Op Ic top | bottom .Xc @@ -2538,17 +2575,25 @@ will be passed to character pairs are replaced, and UTF-8 is dependent on the .Ic status-utf8 option. -.It Ic status-right-attr Ar attributes -Set the attribute of the right part of the status line. -.It Ic status-right-bg Ar colour -Set the background colour of the right part of the status line. -.It Ic status-right-fg Ar colour -Set the foreground colour of the right part of the status line. .It Ic status-right-length Ar length Set the maximum .Ar length of the right component of the status bar. The default is 40. +.It Ic status-right-style Ar style +Set the style of the right part of the status line. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.It Ic status-style Ar style +Set status line style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .It Xo Ic status-utf8 .Op Ic on | off .Xc @@ -2775,15 +2820,6 @@ or .Ic main-vertical layouts. .Pp -.It Ic mode-attr Ar attributes -Set window modes attributes. -.Pp -.It Ic mode-bg Ar colour -Set window modes background colour. -.Pp -.It Ic mode-fg Ar colour -Set window modes foreground colour. -.Pp .It Xo Ic mode-keys .Op Ic vi | emacs .Xc @@ -2809,6 +2845,14 @@ If set to the mouse behaves as set to on, but cannot be used to enter copy mode. .Pp +.It Ic mode-style Ar style +Set window modes style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Xo Ic monitor-activity .Op Ic on | off .Xc @@ -2879,64 +2923,42 @@ Instructs .Nm to expect UTF-8 sequences to appear in this window. .Pp -.It Ic window-status-bell-attr Ar attributes -Set status line attributes for windows which have a bell alert. +.It Ic window-status-activity-style Ar style +Set status line style for windows with an activity alert. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp -.It Ic window-status-bell-bg Ar colour -Set status line background colour for windows with a bell alert. +.It Ic window-status-bell-style Ar style +Set status line style for windows with a bell alert. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp -.It Ic window-status-bell-fg Ar colour -Set status line foreground colour for windows with a bell alert. -.Pp -.It Ic window-status-content-attr Ar attributes -Set status line attributes for windows which have a content alert. -.Pp -.It Ic window-status-content-bg Ar colour -Set status line background colour for windows with a content alert. -.Pp -.It Ic window-status-content-fg Ar colour -Set status line foreground colour for windows with a content alert. -.Pp -.It Ic window-status-activity-attr Ar attributes -Set status line attributes for windows which have an activity (or silence) alert. -.Pp -.It Ic window-status-activity-bg Ar colour -Set status line background colour for windows with an activity alert. -.Pp -.It Ic window-status-activity-fg Ar colour -Set status line foreground colour for windows with an activity alert. -.Pp -.It Ic window-status-attr Ar attributes -Set status line attributes for a single window. -.Pp -.It Ic window-status-bg Ar colour -Set status line background colour for a single window. -.Pp -.It Ic window-status-current-attr Ar attributes -Set status line attributes for the currently active window. -.Pp -.It Ic window-status-current-bg Ar colour -Set status line background colour for the currently active window. -.Pp -.It Ic window-status-current-fg Ar colour -Set status line foreground colour for the currently active window. +.It Ic window-status-content-style Ar style +Set status line style for windows with a content alert. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp .It Ic window-status-current-format Ar string Like .Ar window-status-format , but is the format used when the window is the current window. .Pp -.It Ic window-status-last-attr Ar attributes -Set status line attributes for the last active window. -.Pp -.It Ic window-status-last-bg Ar colour -Set status line background colour for the last active window. -.Pp -.It Ic window-status-last-fg Ar colour -Set status line foreground colour for the last active window. -.Pp -.It Ic window-status-fg Ar colour -Set status line foreground colour for a single window. +.It Ic window-status-current-style Ar style +Set status line style for the currently active window. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp .It Ic window-status-format Ar string Set the format in which the window is displayed in the status line window list. @@ -2946,10 +2968,26 @@ option for details of special character sequences available. The default is .Ql #I:#W#F . .Pp +.It Ic window-status-last-style Ar style +Set status line style for the last active window. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Ic window-status-separator Ar string Sets the separator drawn between windows in the status line. The default is a single space character. .Pp +.It Ic window-status-style Ar style +Set status line style for a single window. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Xo Ic xterm-keys .Op Ic on | off .Xc @@ -3288,16 +3326,10 @@ content) is present. .Pp The colour and attributes of the status line may be configured, the entire status line using the -.Ic status-attr , -.Ic status-fg -and -.Ic status-bg -session options and individual windows using the -.Ic window-status-attr , -.Ic window-status-fg -and -.Ic window-status-bg -window options. +.Ic status-style +session option and individual windows using the +.Ic window-status-style +window option. .Pp The status line is automatically refreshed at interval if it has changed, the interval may be controlled with the @@ -3798,7 +3830,7 @@ bind-key C-a send-prefix Turning the status line off, or changing its colour: .Bd -literal -offset indent set-option -g status off -set-option -g status-bg blue +set-option -g status-style bg=blue .Ed .Pp Setting other options, such as the default command, diff --git a/tmux.h b/tmux.h index b28f10a0..b8fd445c 100644 --- a/tmux.h +++ b/tmux.h @@ -726,11 +726,12 @@ struct options_entry { enum { OPTIONS_STRING, OPTIONS_NUMBER, - OPTIONS_DATA, + OPTIONS_STYLE } type; - char *str; - long long num; + char *str; + long long num; + struct grid_cell style; RB_ENTRY(options_entry) entry; }; @@ -1453,7 +1454,8 @@ enum options_table_type { OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_ATTRIBUTES, OPTIONS_TABLE_FLAG, - OPTIONS_TABLE_CHOICE + OPTIONS_TABLE_CHOICE, + OPTIONS_TABLE_STYLE }; struct options_table_entry { @@ -1466,6 +1468,8 @@ struct options_table_entry { const char *default_str; long long default_num; + + const char *style; }; /* Tree of format entries. */ @@ -1577,12 +1581,15 @@ void options_free(struct options *); struct options_entry *options_find1(struct options *, const char *); struct options_entry *options_find(struct options *, const char *); void options_remove(struct options *, const char *); -struct options_entry *printflike3 options_set_string( - struct options *, const char *, const char *, ...); +struct options_entry *printflike3 options_set_string(struct options *, + const char *, const char *, ...); char *options_get_string(struct options *, const char *); -struct options_entry *options_set_number( - struct options *, const char *, long long); +struct options_entry *options_set_number(struct options *, const char *, + long long); long long options_get_number(struct options *, const char *); +struct options_entry *options_set_style(struct options *, const char *, + const char *, int); +struct grid_cell *options_get_style(struct options *, const char *); /* options-table.c */ extern const struct options_table_entry server_options_table[]; @@ -2042,8 +2049,6 @@ void printflike5 screen_write_nputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, int, const char *, ...); void screen_write_vnputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, int, const char *, va_list); -void screen_write_parsestyle( - struct grid_cell *, struct grid_cell *, const char *); void screen_write_putc( struct screen_write_ctx *, struct grid_cell *, u_char); void screen_write_copy(struct screen_write_ctx *, @@ -2173,7 +2178,6 @@ struct window_pane *window_pane_find_right(struct window_pane *); void window_set_name(struct window *, const char *); void window_remove_ref(struct window *); void winlink_clear_flags(struct winlink *); -void window_mode_attrs(struct grid_cell *, struct options *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); @@ -2345,4 +2349,14 @@ int xvasprintf(char **, const char *, va_list); int printflike3 xsnprintf(char *, size_t, const char *, ...); int xvsnprintf(char *, size_t, const char *, va_list); +/* style.c */ +int style_parse(const struct grid_cell *, + struct grid_cell *, const char *); +const char *style_tostring(struct grid_cell *); +void style_update_new(struct options *, const char *, const char *); +void style_update_old(struct options *, const char *, + struct grid_cell *); +void style_apply(struct grid_cell *, struct options *, const char *); +void style_apply_update(struct grid_cell *, struct options *, const char *); + #endif /* TMUX_H */ diff --git a/tty.c b/tty.c index 53b415ff..f5a85992 100644 --- a/tty.c +++ b/tty.c @@ -1354,8 +1354,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc) tty_putcode(tty, TTYC_BOLD); if (changed & GRID_ATTR_DIM) tty_putcode(tty, TTYC_DIM); - if (changed & GRID_ATTR_ITALICS) - { + if (changed & GRID_ATTR_ITALICS) { if (tty_term_has(tty->term, TTYC_SITM)) tty_putcode(tty, TTYC_SITM); else diff --git a/window-choose.c b/window-choose.c index 77add5e4..0cf7f66b 100644 --- a/window-choose.c +++ b/window-choose.c @@ -736,7 +736,7 @@ window_choose_write_line( utf8flag = options_get_number(&wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); screen_write_cursormove(ctx, 0, py); if (data->top + py < ARRAY_LENGTH(&data->list)) { @@ -763,7 +763,7 @@ window_choose_write_line( screen_write_putc(ctx, &gc, ' '); if (data->input_type != WINDOW_CHOOSE_NORMAL) { - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); xoff = xsnprintf(hdr, sizeof hdr, "%s: %s", data->input_prompt, data->input_str); diff --git a/window-copy.c b/window-copy.c index 48cd4b33..df4ca55a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1173,7 +1173,7 @@ window_copy_write_line( char hdr[512]; size_t last, xoff = 0, size = 0, limit; - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); last = screen_size_y(s) - 1; if (py == 0) { @@ -1290,7 +1290,7 @@ window_copy_update_selection(struct window_pane *wp, int may_redraw) return (0); /* Set colours. */ - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); /* Find top of screen. */ ty = screen_hsize(data->backing) - data->oy; diff --git a/window.c b/window.c index dcb2a5f1..1e11cace 100644 --- a/window.c +++ b/window.c @@ -1315,13 +1315,3 @@ winlink_clear_flags(struct winlink *wl) } } } - -/* Set the grid_cell with fg/bg/attr information when window is in a mode. */ -void -window_mode_attrs(struct grid_cell *gc, struct options *oo) -{ - memcpy(gc, &grid_default_cell, sizeof *gc); - colour_set_fg(gc, options_get_number(oo, "mode-fg")); - colour_set_bg(gc, options_get_number(oo, "mode-bg")); - gc->attr |= options_get_number(oo, "mode-attr"); -} From 62e0ed5d7eac5470e4ad2fbde5034d3fa72343c0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2014 23:11:44 +0000 Subject: [PATCH 227/949] Fix missing argument, stupid last minute changes... --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 4480b344..ec1d3680 100644 --- a/options-table.c +++ b/options-table.c @@ -856,7 +856,7 @@ options_table_populate_tree( options_set_string(oo, oe->name, "%s", oe->default_str); break; case OPTIONS_TABLE_STYLE: - options_set_style(oo, oe->name, oe->default_str); + options_set_style(oo, oe->name, oe->default_str, 0); break; default: options_set_number(oo, oe->name, oe->default_num); From 1935eb5c1ea697762a142ca5353ade82d9a372a1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 31 Jan 2014 11:17:20 +0000 Subject: [PATCH 228/949] Add \033[18t window operations from J Raynor. --- input.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index e27250ed..58064ddd 100644 --- a/input.c +++ b/input.c @@ -74,6 +74,7 @@ void input_csi_dispatch_rm(struct input_ctx *); void input_csi_dispatch_rm_private(struct input_ctx *); void input_csi_dispatch_sm(struct input_ctx *); void input_csi_dispatch_sm_private(struct input_ctx *); +void input_csi_dispatch_winops(struct input_ctx *); void input_csi_dispatch_sgr(struct input_ctx *); int input_dcs_dispatch(struct input_ctx *); int input_utf8_open(struct input_ctx *); @@ -154,6 +155,7 @@ enum input_csi_type { INPUT_CSI_SM_PRIVATE, INPUT_CSI_TBC, INPUT_CSI_VPA, + INPUT_CSI_WINOPS, }; /* Control (CSI) command table. */ @@ -188,6 +190,7 @@ const struct input_table_entry input_csi_table[] = { { 'q', " ", INPUT_CSI_DECSCUSR }, { 'r', "", INPUT_CSI_DECSTBM }, { 's', "", INPUT_CSI_SCP }, + { 't', "", INPUT_CSI_WINOPS }, { 'u', "", INPUT_CSI_RCP }, }; @@ -1077,7 +1080,7 @@ input_csi_dispatch(struct input_ctx *ictx) struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; struct input_table_entry *entry; - int n, m; + int n, m; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1117,6 +1120,9 @@ input_csi_dispatch(struct input_ctx *ictx) m = input_get(ictx, 1, 1, 1); screen_write_cursormove(sctx, m - 1, n - 1); break; + case INPUT_CSI_WINOPS: + input_csi_dispatch_winops(ictx); + break; case INPUT_CSI_CUU: screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; @@ -1430,6 +1436,55 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) } } +/* Handle CSI window operations. */ +void +input_csi_dispatch_winops(struct input_ctx *ictx) +{ + struct window_pane *wp = ictx->wp; + int n, m; + + m = 0; + while ((n = input_get(ictx, m, 0, -1)) != -1) { + switch (n) { + case 1: + case 2: + case 5: + case 6: + case 7: + case 11: + case 13: + case 14: + case 19: + case 20: + case 21: + case 24: + break; + case 3: + case 4: + case 8: + m++; + if (input_get(ictx, m, 0, -1) == -1) + return; + /* FALLTHROUGH */ + case 9: + case 10: + case 22: + case 23: + m++; + if (input_get(ictx, m, 0, -1) == -1) + return; + break; + case 18: + input_reply(ictx, "\033[8;%u;%u", wp->sy, wp->sx); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + m++; + } +} + /* Handle CSI SGR. */ void input_csi_dispatch_sgr(struct input_ctx *ictx) From 72d1be5ddd0dbc87f99d5430bb4ee4a295ea193b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 31 Jan 2014 11:20:28 +0000 Subject: [PATCH 229/949] Fix partial matches with xterm-keys on, from m0viefreak dot cm at googlemail dot com. --- xterm-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xterm-keys.c b/xterm-keys.c index 75eb6751..9b5a0a21 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -133,7 +133,7 @@ xterm_keys_match(const char *template, const char *buf, size_t len) do { if (*template != '_' && buf[pos] != *template) return (-1); - } while (pos++ != len && *++template != '\0'); + } while (*++template != '\0' && ++pos != len); if (*template != '\0') /* partial */ return (1); From 9f02feb9d089b1a4639afb52ab0e8212eeb55a7c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 31 Jan 2014 14:19:24 +0000 Subject: [PATCH 230/949] Break up and simplify screen_redraw_screen. --- screen-redraw.c | 153 +++++++++++++++++++++++++----------------------- server-client.c | 6 +- tmux.h | 2 +- 3 files changed, 84 insertions(+), 77 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index a67cb952..3a082cbf 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -29,6 +29,9 @@ int screen_redraw_check_cell(struct client *, u_int, u_int, int screen_redraw_check_active(u_int, u_int, int, struct window *, struct window_pane *); +void screen_redraw_draw_borders(struct client *, int, u_int); +void screen_redraw_draw_panes(struct client *, u_int); +void screen_redraw_draw_status(struct client *, u_int); void screen_redraw_draw_number(struct client *, struct window_pane *); #define CELL_INSIDE 0 @@ -216,15 +219,13 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, /* Redraw entire screen. */ void -screen_redraw_screen(struct client *c, int status_only, int borders_only) +screen_redraw_screen(struct client *c, int draw_panes, int draw_status, + int draw_borders) { - struct window *w = c->session->curw->window; - struct options *oo = &c->session->options; - struct tty *tty = &c->tty; - struct window_pane *wp; - struct grid_cell active_gc, other_gc; - u_int i, j, type, top; - int status, spos; + struct options *oo = &c->session->options; + struct tty *tty = &c->tty; + u_int top; + int status, spos; /* Suspended clients should not be updated. */ if (c->flags & CLIENT_SUSPENDED) @@ -239,72 +240,15 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only) top = 0; if (status && spos == 0) top = 1; + if (!status) + draw_status = 0; - /* If only drawing status and it is present, don't need the rest. */ - if (status_only && status) { - if (top) - tty_draw_line(tty, &c->status, 0, 0, 0); - else - tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); - tty_reset(tty); - return; - } - - /* Set up pane border attributes. */ - style_apply(&other_gc, oo, "pane-border-style"); - style_apply(&active_gc, oo, "pane-active-border-style"); - active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; /* nuke existing */ - - /* Draw background and borders. */ - for (j = 0; j < tty->sy - status; j++) { - if (status_only) { - if (spos == 1 && j != tty->sy - 1) - continue; - else if (spos == 0 && j != 0) - break; - } - for (i = 0; i < tty->sx; i++) { - type = screen_redraw_check_cell(c, i, j, &wp); - if (type == CELL_INSIDE) - continue; - if (screen_redraw_check_active(i, j, type, w, wp)) - tty_attributes(tty, &active_gc); - else - tty_attributes(tty, &other_gc); - tty_cursor(tty, i, top + j); - tty_putc(tty, CELL_BORDERS[type]); - } - } - - /* If only drawing borders, that's it. */ - if (borders_only) - return; - - /* Draw the panes, if necessary. */ - TAILQ_FOREACH(wp, &w->panes, entry) { - if (!window_pane_visible(wp)) - continue; - for (i = 0; i < wp->sy; i++) { - if (status_only) { - if (spos == 1 && wp->yoff + i != tty->sy - 1) - continue; - else if (spos == 0 && wp->yoff + i != 0) - break; - } - tty_draw_line( - tty, wp->screen, i, wp->xoff, top + wp->yoff); - } - if (c->flags & CLIENT_IDENTIFY) - screen_redraw_draw_number(c, wp); - } - - /* Draw the status line. */ - if (status) { - if (top) - tty_draw_line(tty, &c->status, 0, 0, 0); - else - tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); - } + if (draw_borders) + screen_redraw_draw_borders(c, status, top); + if (draw_panes) + screen_redraw_draw_panes(c, top); + if (draw_status) + screen_redraw_draw_status(c, top); tty_reset(tty); } @@ -326,6 +270,69 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) tty_reset(&c->tty); } +/* Draw the borders. */ +void +screen_redraw_draw_borders(struct client *c, int status, u_int top) +{ + struct window *w = c->session->curw->window; + struct options *oo = &c->session->options; + struct tty *tty = &c->tty; + struct window_pane *wp; + struct grid_cell active_gc, other_gc; + u_int i, j, type; + + style_apply(&other_gc, oo, "pane-border-style"); + style_apply(&active_gc, oo, "pane-active-border-style"); + active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + + for (j = 0; j < tty->sy - status; j++) { + for (i = 0; i < tty->sx; i++) { + type = screen_redraw_check_cell(c, i, j, &wp); + if (type == CELL_INSIDE) + continue; + if (screen_redraw_check_active(i, j, type, w, wp)) + tty_attributes(tty, &active_gc); + else + tty_attributes(tty, &other_gc); + tty_cursor(tty, i, top + j); + tty_putc(tty, CELL_BORDERS[type]); + } + } +} + +/* Draw the panes. */ +void +screen_redraw_draw_panes(struct client *c, u_int top) +{ + struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; + struct window_pane *wp; + struct screen *s; + u_int i; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + s = wp->screen; + for (i = 0; i < wp->sy; i++) + tty_draw_line(tty, s, i, wp->xoff, top + wp->yoff); + if (c->flags & CLIENT_IDENTIFY) + screen_redraw_draw_number(c, wp); + } +} + +/* Draw the status line. */ +void +screen_redraw_draw_status(struct client *c, u_int top) +{ + struct tty *tty = &c->tty; + + if (top) + tty_draw_line(tty, &c->status, 0, 0, 0); + else + tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); +} + /* Draw number on a pane. */ void screen_redraw_draw_number(struct client *c, struct window_pane *wp) diff --git a/server-client.c b/server-client.c index c6257edb..5a6cb7d9 100644 --- a/server-client.c +++ b/server-client.c @@ -743,7 +743,7 @@ server_client_check_redraw(struct client *c) } if (c->flags & CLIENT_REDRAW) { - screen_redraw_screen(c, 0, 0); + screen_redraw_screen(c, 1, 1, 1); c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); } else if (c->flags & CLIENT_REDRAWWINDOW) { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) @@ -757,10 +757,10 @@ server_client_check_redraw(struct client *c) } if (c->flags & CLIENT_BORDERS) - screen_redraw_screen(c, 0, 1); + screen_redraw_screen(c, 0, 0, 1); if (c->flags & CLIENT_STATUS) - screen_redraw_screen(c, 1, 0); + screen_redraw_screen(c, 0, 1, 0); c->tty.flags |= flags; diff --git a/tmux.h b/tmux.h index b8fd445c..c6919622 100644 --- a/tmux.h +++ b/tmux.h @@ -2084,7 +2084,7 @@ void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); /* screen-redraw.c */ -void screen_redraw_screen(struct client *, int, int); +void screen_redraw_screen(struct client *, int, int, int); void screen_redraw_pane(struct client *, struct window_pane *); /* screen.c */ From 44737b06db6113528fe684a5ba5dcec854a93a6a Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Fri, 31 Jan 2014 21:47:54 +0000 Subject: [PATCH 231/949] Fixup BSD specific things from last merge There's entries for header files we don't use, and the cvsimport doesn't like removing files automatically, etc., and it won't have known to have done this from autoconf's POV, so define that in the correct place, hence the removal of the previously committed Makefile. --- Makefile | 138 ----------------------------------- Makefile.am | 3 +- cmd-server-info.c | 173 -------------------------------------------- cmd-show-messages.c | 1 - 4 files changed, 1 insertion(+), 314 deletions(-) delete mode 100644 Makefile delete mode 100644 cmd-server-info.c diff --git a/Makefile b/Makefile deleted file mode 100644 index e566bb2a..00000000 --- a/Makefile +++ /dev/null @@ -1,138 +0,0 @@ -# $OpenBSD$ - -PROG= tmux -SRCS= arguments.c \ - attributes.c \ - cfg.c \ - client.c \ - clock.c \ - cmd-attach-session.c \ - cmd-bind-key.c \ - cmd-break-pane.c \ - cmd-capture-pane.c \ - cmd-choose-buffer.c \ - cmd-choose-client.c \ - cmd-choose-list.c \ - cmd-choose-tree.c \ - cmd-clear-history.c \ - cmd-clock-mode.c \ - cmd-command-prompt.c \ - cmd-confirm-before.c \ - cmd-copy-mode.c \ - cmd-delete-buffer.c \ - cmd-detach-client.c \ - cmd-display-message.c \ - cmd-display-panes.c \ - cmd-find-window.c \ - cmd-has-session.c \ - cmd-if-shell.c \ - cmd-join-pane.c \ - cmd-kill-pane.c \ - cmd-kill-server.c \ - cmd-kill-session.c \ - cmd-kill-window.c \ - cmd-link-window.c \ - cmd-list-buffers.c \ - cmd-list-clients.c \ - cmd-list-commands.c \ - cmd-list-keys.c \ - cmd-list-panes.c \ - cmd-list-sessions.c \ - cmd-list-windows.c \ - cmd-list.c \ - cmd-load-buffer.c \ - cmd-lock-server.c \ - cmd-move-window.c \ - cmd-new-session.c \ - cmd-new-window.c \ - cmd-paste-buffer.c \ - cmd-pipe-pane.c \ - cmd-refresh-client.c \ - cmd-rename-session.c \ - cmd-rename-window.c \ - cmd-resize-pane.c \ - cmd-respawn-pane.c \ - cmd-respawn-window.c \ - cmd-rotate-window.c \ - cmd-run-shell.c \ - cmd-save-buffer.c \ - cmd-select-layout.c \ - cmd-select-pane.c \ - cmd-select-window.c \ - cmd-send-keys.c \ - cmd-set-buffer.c \ - cmd-set-environment.c \ - cmd-set-option.c \ - cmd-show-environment.c \ - cmd-show-messages.c \ - cmd-show-options.c \ - cmd-source-file.c \ - cmd-split-window.c \ - cmd-string.c \ - cmd-suspend-client.c \ - cmd-swap-pane.c \ - cmd-swap-window.c \ - cmd-switch-client.c \ - cmd-unbind-key.c \ - cmd-unlink-window.c \ - cmd-wait-for.c \ - cmd.c \ - cmd-queue.c \ - colour.c \ - control.c \ - control-notify.c \ - environ.c \ - format.c \ - grid-cell.c \ - grid-view.c \ - grid.c \ - input-keys.c \ - input.c \ - job.c \ - key-bindings.c \ - key-string.c \ - layout-custom.c \ - layout-set.c \ - layout.c \ - log.c \ - mode-key.c \ - names.c \ - notify.c \ - options-table.c \ - options.c \ - paste.c \ - procname.c \ - resize.c \ - screen-redraw.c \ - screen-write.c \ - screen.c \ - server-client.c \ - server-fn.c \ - server-window.c \ - server.c \ - session.c \ - signal.c \ - status.c \ - style.c \ - tmux.c \ - tty-acs.c \ - tty-keys.c \ - tty-term.c \ - tty.c \ - utf8.c \ - window-choose.c \ - window-clock.c \ - window-copy.c \ - window.c \ - xmalloc.c \ - xterm-keys.c - -CDIAGFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 -CDIAGFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare -CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align - -LDADD= -lutil -lcurses -levent -DPADD= ${LIBUTIL} ${LIBCURSES} ${LIBEVENT} - -.include diff --git a/Makefile.am b/Makefile.am index 690e466d..e5a7286e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -122,7 +122,6 @@ dist_tmux_SOURCES = \ cmd-select-pane.c \ cmd-select-window.c \ cmd-send-keys.c \ - cmd-server-info.c \ cmd-set-buffer.c \ cmd-set-environment.c \ cmd-set-option.c \ @@ -131,7 +130,6 @@ dist_tmux_SOURCES = \ cmd-show-options.c \ cmd-source-file.c \ cmd-split-window.c \ - cmd-start-server.c \ cmd-string.c \ cmd-suspend-client.c \ cmd-swap-pane.c \ @@ -175,6 +173,7 @@ dist_tmux_SOURCES = \ session.c \ signal.c \ status.c \ + style.c \ tmux.c \ tty-acs.c \ tty-keys.c \ diff --git a/cmd-server-info.c b/cmd-server-info.c deleted file mode 100644 index 3aa5df8a..00000000 --- a/cmd-server-info.c +++ /dev/null @@ -1,173 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2008 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include - -#include -#include -#include -#include - -#include "tmux.h" - -/* - * Show various information about server. - */ - -enum cmd_retval cmd_server_info_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_server_info_entry = { - "server-info", "info", - "", 0, 0, - "", - 0, - NULL, - cmd_server_info_exec -}; - -enum cmd_retval -cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq) -{ - struct tty_term *term; - struct client *c; - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - struct tty_code *code; - const struct tty_term_code_entry *ent; - struct utsname un; - struct job *job; - struct grid *gd; - struct grid_line *gl; - u_int i, j, k, lines; - size_t size; - char out[80]; - char *tim; - time_t t; - - tim = ctime(&start_time); - *strchr(tim, '\n') = '\0'; - cmdq_print(cmdq, - "tmux " VERSION ", pid %ld, started %s", (long) getpid(), tim); - cmdq_print(cmdq, "socket path %s, debug level %d", socket_path, - debug_level); - if (uname(&un) >= 0) { - cmdq_print(cmdq, "system is %s %s %s %s", - un.sysname, un.release, un.version, un.machine); - } - if (cfg_file != NULL) - cmdq_print(cmdq, "configuration file is %s", cfg_file); - else - cmdq_print(cmdq, "configuration file not specified"); - cmdq_print(cmdq, "protocol version is %d", PROTOCOL_VERSION); - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Clients:"); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - - cmdq_print(cmdq,"%2d: %s (%d, %d): %s [%ux%u %s bs=%hho " - "class=%u] [flags=0x%x/0x%x, references=%u]", i, - c->tty.path, c->ibuf.fd, c->tty.fd, c->session->name, - c->tty.sx, c->tty.sy, c->tty.termname, - c->tty.tio.c_cc[VERASE], c->tty.class, - c->flags, c->tty.flags, c->references); - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Sessions: [%zu]", sizeof (struct grid_cell)); - RB_FOREACH(s, sessions, &sessions) { - t = s->creation_time.tv_sec; - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - - cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] " - "[flags=0x%x]", s->id, s->name, - winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); - RB_FOREACH(wl, winlinks, &s->windows) { - w = wl->window; - cmdq_print(cmdq, "%4u: %s [%ux%u] [flags=0x%x, " - "references=%u, last layout=%d]", wl->idx, w->name, - w->sx, w->sy, w->flags, w->references, - w->lastlayout); - j = 0; - TAILQ_FOREACH(wp, &w->panes, entry) { - lines = size = 0; - gd = wp->base.grid; - for (k = 0; k < gd->hsize + gd->sy; k++) { - gl = &gd->linedata[k]; - if (gl->celldata == NULL) - continue; - lines++; - size += gl->cellsize * - sizeof *gl->celldata; - } - cmdq_print(cmdq, - "%6u: %s %lu %d %u/%u, %zu bytes", j, - wp->tty, (u_long) wp->pid, wp->fd, lines, - gd->hsize + gd->sy, size); - j++; - } - } - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Terminals:"); - LIST_FOREACH(term, &tty_terms, entry) { - cmdq_print(cmdq, "%s [references=%u, flags=0x%x]:", - term->name, term->references, term->flags); - for (i = 0; i < NTTYCODE; i++) { - ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; - switch (code->type) { - case TTYCODE_NONE: - cmdq_print(cmdq, "%2u: %s: [missing]", - ent->code, ent->name); - break; - case TTYCODE_STRING: - strnvis(out, code->value.string, sizeof out, - VIS_OCTAL|VIS_TAB|VIS_NL); - cmdq_print(cmdq, "%2u: %s: (string) %s", - ent->code, ent->name, out); - break; - case TTYCODE_NUMBER: - cmdq_print(cmdq, "%2u: %s: (number) %d", - ent->code, ent->name, code->value.number); - break; - case TTYCODE_FLAG: - cmdq_print(cmdq, "%2u: %s: (flag) %s", - ent->code, ent->name, - code->value.flag ? "true" : "false"); - break; - } - } - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Jobs:"); - LIST_FOREACH(job, &all_jobs, lentry) { - cmdq_print(cmdq, "%s [fd=%d, pid=%d, status=%d]", - job->cmd, job->fd, job->pid, job->status); - } - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 393ed789..0905c32e 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "tmux.h" From 57332be8da86f0e40a91d7acd857564e789027a7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 1 Feb 2014 00:47:04 +0000 Subject: [PATCH 232/949] Tidy up TODO list. --- TODO | 138 +++++++++++++++++++++++++++-------------------------------- 1 file changed, 62 insertions(+), 76 deletions(-) diff --git a/TODO b/TODO index 529b5e8c..b9a83fb4 100644 --- a/TODO +++ b/TODO @@ -18,14 +18,16 @@ - options bits and pieces: * set-remain-on-exit is a complete hack * way to set socket path from config file - * -fg/-bg/-attr is crap - better just foo-style options which accept - fg=,bg=,bright and so on like #[] - format improvements: * option to quote format (#{session_name:quoted}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces, #!2T maybe * status stuff is redundant with formats + * last window update time and format for it + * formats to show if a window is linked into multiple sessions, into + multiple attached sessions, and is the active window in multiple + attached sessions? - choose mode improvements: * choose-pane command (augment choose-tree to do this?) @@ -40,108 +42,92 @@ * monitor changes within a region * perhaps monitor /all/ panes in the window not just one -- panning over window (window larger than visible) - -- link panes into multiple windows - - improve mouse support: * bind commands to mouse in different areas? * more fine-grained options * commands executed when clicking on a pattern (URL) + * send arrow key sequences for mouse scroll wheel in alternate screen + * mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets + the flag on w/o checking the others before calling tty_update_mode) - hooks! - warts on current naming: * display-time but message-fg/bg/attr * list-* vs show-* - * server-info * split-window -> split-pane?? -- way to keep a job running just read its last line of output for #() - - better UTF-8 support: * #22T can split in the middle of UTF-8 characters! * window names and titles * message display * prompt input * multibyte key input - -- live update: server started with -U connects to server, requests sessions and - windows, receives file descriptors - -- there are inconsistencies in what we get from old shell and what comes from - config for new sessions and windows - -- multiline status line? - -- support title stack, both internally and externally (restore on detach) - http://docs.freebsd.org/cgi/getmsg.cgi?fetch=1149299+0+archive/2010/freebsd-questions/20100207.freebsd-questions - -- last window update time and format for it - -- bind commands to key sequences -- make it so ALL keys go through a table, - first an implicit table in which C-b is the only default binding to a command - that says "next key from $othertable" and so on. means -n can go away as well + * buffer_sample and the choose-* could show UTF-8 properly - copy/paste improvements: - * case insensitive searching * incremental searching * append to buffer * paste w/o trailing whitespace * named buffers and allow gaps in the stack * command to toggle selection not to move it in copy-mode -- mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the flag - on w/o checking the others before calling tty_update_mode) +- layout stuff + * way to tag a layout as a number/name + * maybe keep last layout + size around and if size reverts just put it + back + * revamp layouts: they are too complicated, should be more closely + integrated, should support hints, layout sets should just be a + special case of custom layouts, and we should support panes that are + not attached to a cell at all. this could be the time to introduce + panelink to replace layout_cell + * way to set hints/limits about pane size for resizing + * panning over window (window larger than visible) -- way to tag a layout as a number/name +- terminfo bits + * use a better termcap internally instead of screen, perhaps xterm + * use screen-256color when started on 256 colour terminal? + * need a tmux terminfo entry to document the extensions we are using in + upstream terminfo + * support title stack, both internally and externally (restore on + detach) http://docs.freebsd.org/cgi/getmsg.cgi?fetch=1149299+0+archive/2010/freebsd-questions/20100207.freebsd-questions -- optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx +- code cleanup + * instead of separate window and session options, just one master + options list with each option having a type (window or session), then + options on window, on session, and global. for window options we look + window->session->global, and for session we look session->global + * the way pane, window, session destroy is handled is too complicated + and the distinction between session.c, window.c and server-fn.c + functions is not clear. could we just have kill_pane(), + kill_window(), unlink_window(), kill_session() that fix up all data + structures (flagging sessions as dead) and return a value to say + whether clients need to be checked for dead sessions? sort of like + session_detach now but more so. or some other scheme to make it + simpler and clearer? also would be nice to remove/rename server-fn.c + * more readable way to work out the various things commands need to + know about the client, notably: + - is this the config file? (cmdq->c == NULL) + - is this a command client? (cmdq->c != NULL && + cmdq->c->session == NULL) + - is this a control client? + - can i do stdin or stdout to this client? + or even guarantee that cmdq->c != NULL and provide a better way to + tell when in the config file - then we use cmdq->c if we need a + client w/o a session else cmd_current_client + * optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx -- instead of separate window and session options, just one master options list - with each option having a type (window or session), then options on window, - on session, and global. for window options we look window->session->global, - and for session we look session->global +- miscellaneous + * way to keep a job running just read its last line of output for #() + link panes into multiple windows + * live update: server started with -U connects to server, requests + sessions and windows, receives file descriptors + * there are inconsistencies in what we get from old shell and what + comes from config for new sessions and windows + * multiline status line? + * bind commands to key sequences -- make it so ALL keys go through a + table, first an implicit table in which C-b is the only default + binding to a command that says "next key from $othertable" and so + on. means -n can go away as well -- maybe keep last layout + size around and if size reverts just put it back -- way to set hints/limits about pane size for resizing - -- revamp layouts: they are too complicated, should be more closely integrated, - should support hints, layout sets should just be a special case of custom - layouts, and we should support panes that are not attached to a cell at - all. this could be the time to introduce panelink to replace layout_cell - -- use a better termcap internally instead of screen, perhaps xterm - -- use screen-256color when started on 256 colour terminal? - -- we need a tmux terminfo entry to document the extensions we are using in - upstream terminfo - -- send arrow key sequences for mouse scroll wheel when in alternate screen - -- the way pane, window, session destroy is handled is too complicated and the - distinction between session.c, window.c and server-fn.c functions is not - clear. could we just have kill_pane(), kill_window(), unlink_window(), - kill_session() that fix up all data structures (flagging sessions as dead) - and return a value to say whether clients need to be checked for dead - sessions? sort of like session_detach now but more so. or some other scheme - to make it simpler and clearer? also would be nice to remove/rename - server-fn.c - -- more readable way to work out the various things commands need to know about - the client, notably: - - is this the config file? (cmdq->c == NULL) - - is this a command client? (cmdq->c != NULL && cmdq->c->session == NULL) - - is this a control client? - - can i do stdin or stdout to this client? - or even guarantee that cmdq->c != NULL and provide a better way to tell when - in the config file - then we use cmdq->c if we need a client w/o a session - else cmd_current_client - -- buffer_sample and the choose-* could let UTF-8 through and display it - properly. - -- formats to show if a window is linked into multiple sessions, into multiple - attached sessions, and is the active window in multiple attached sessions? From 973de5a704d4858b7626fc7a07865c1ea8f71eac Mon Sep 17 00:00:00 2001 From: jmc Date: Sun, 2 Feb 2014 08:48:48 +0000 Subject: [PATCH 233/949] fix some minor formatting glitches; --- tmux.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index e0b27b6d..d32a16d2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -783,7 +783,7 @@ With .Fl t , display the log for .Ar target-client . -.Fl I, +.Fl I , .Fl J and .Fl T @@ -2499,7 +2499,7 @@ Display will be passed through .Xr strftime 3 and formats (see -.Sx FORMATS Ns ) +.Sx FORMATS ) will be expanded. It may also contain any of the following special character sequences: .Bl -column "Character pair" "Replaced with" -offset indent From 1721056f351114cf03e4da284fdfc6b80f3aae38 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Wed, 5 Feb 2014 10:51:25 +0000 Subject: [PATCH 234/949] Remove references to 88colour support Tmux doesn't directly support terminals with 88 colours directly anymore. --- tmux.1 | 2 +- tmux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index cf1919c6..cff5204e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 28lCquvV +.Op Fl 2lCquvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name diff --git a/tmux.c b/tmux.c index f5be1de4..a804e6f7 100644 --- a/tmux.c +++ b/tmux.c @@ -60,7 +60,7 @@ __dead void usage(void) { fprintf(stderr, - "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2lquvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", __progname); exit(1); From 73c125f248ddb43cedf8dffec73b574d4a890cce Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 5 Feb 2014 19:13:11 +0000 Subject: [PATCH 235/949] Sort and organize commands in tmux.vim Signed-off-by: Ben Boeckel --- examples/tmux.vim | 49 ++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index e85f8ff6..bff9cd1e 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -31,32 +31,29 @@ syn keyword tmuxAction any current none syn keyword tmuxBoolean off on syn keyword tmuxCmds - \ attach[-session] detach[-client] has[-session] kill-server - \ kill-session lsc list-clients lscm list-commands ls list-sessions - \ lockc lock-client locks lock-session new[-session] refresh[-client] - \ rename[-session] showmsgs show-messages source[-file] start[-server] - \ suspendc suspend-client switchc switch-client - \ copy-mode - \ breakp break-pane capturep capture-pane choose-client choose-session - \ choose-tree choose-window displayp display-panes findw find-window - \ joinp join-pane killp kill-pane killw kill-window lastp last-pane - \ last[-window] linkw link-window lsp list-panes lsw list-windows movep - \ move-pane movew move-window neww new-window nextl next-layout - \ next[-window] pipep pipe-pane prevl previous-layout prev[ious-window] - \ renamew rename-window resizep resize-pane respawnp respawn-pane - \ respawnw respawn-window rotatew rotate-window selectl select-layout - \ selectp select-pane selectw select-window splitw split-window swapp - \ swap-pane swapw swap-window unlinkw unlink-window - \ bind[-key] lsk list-keys send[-keys] send-prefix unbind[-key] - \ set[-option] setw set-window-option show[-options] showw - \ show-window-options - \ setenv set-environment showenv show-environment - \ command-prompt confirm[-before] display[-message] - \ choose-buffer clearhist clear-history deleteb delete-buffer lsb - \ list-buffers loadb load-buffer pasteb paste-buffer saveb save-buffer - \ setb set-buffer showb show-buffer - \ clock-mode if[-shell] lock[-server] run[-shell] server-info info - \ choose-list wait-for + \ attach[-session] bind[-key] break-pane breakp capture-pane capturep + \ choose-buffer choose-client choose-list choose-session choose-tree + \ choose-window clear-history clearhist clock-mode command-prompt + \ confirm[-before] copy-mode delete-buffer deleteb detach[-client] + \ display[-message] display-panes displayp find-window findw + \ has[-session] if[-shell] join-pane joinp kill-pane killp kill-server + \ kill-session kill-window killw last-pane lastp last[-window] + \ link-window linkw list-buffers lsb list-clients lsc + \ list-commands lscm list-keys lsk list-panes lsp list-sessions ls + \ list-windows lsw load-buffer loadb lock-client lockc lock[-server] + \ lock-session locks move-pane movep move-window movew new[-session] + \ next-layout nextl next[-window] paste-buffer pasteb pipe-pane pipep + \ previous-layout prevl prev[ious-window] refresh[-client] + \ rename[-session] rename-window renamew resize-pane resizep + \ respawn-pane respawnp respawn-window respawnw rotate-window rotatew + \ run[-shell] save-buffer saveb select-layout selectl + \ select-pane selectp select-window selectw send[-keys] send-prefix + \ server-info info set-buffer setb set-environment setenv set[-option] + \ set-window-option setw show-buffer showb show-environment showenv + \ show-messages showmsgs show[-options] show-window-options showw + \ source[-file] split-window splitw start[-server] + \ suspend-client suspendc swap-pane swapp swap-window swapw + \ switch-client switchc unbind[-key] unlink-window unlinkw wait[-for] syn keyword tmuxOptsSet \ buffer-limit escape-time exit-unattached exit-unattached quiet From 8edbbb98656a76f0a25b5243b526709a67ae3531 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 5 Feb 2014 19:14:12 +0000 Subject: [PATCH 236/949] Sort and organize option names in tmux.vim Also update with the latest options and remove the to-be-deprecated {-attr,-bg,-fg} options. Signed-off-by: Ben Boeckel --- examples/tmux.vim | 51 +++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index bff9cd1e..037ddf7b 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -56,43 +56,34 @@ syn keyword tmuxCmds \ switch-client switchc unbind[-key] unlink-window unlinkw wait[-for] syn keyword tmuxOptsSet - \ buffer-limit escape-time exit-unattached exit-unattached quiet - \ set-clipboard - \ base-index bell-action bell-on-alert default-command default-path + \ buffer-limit escape-time exit-unattached exit-unattached focus-events + \ quiet set-clipboard + \ assume-paste-time base-index bell-action bell-on-alert default-command \ default-shell default-terminal destroy-unattached detach-on-destroy \ display-panes-active-colour display-panes-colour display-panes-time - \ display-time history-limit - \ lock-after-time lock-command lock-server - \ message-command-attr message-attr message-command-bg message-bg - \ message-command-fg message-fg message-limit - \ mouse-resize-pane mouse-select-pane mouse-select-window mouse-utf8 - \ pane-active-border-bg pane-border-bg pane-active-border-fg - \ pane-border-fg prefix prefix2 + \ display-time history-limit lock-after-time lock-command lock-server + \ message-command-style message-limit message-style mouse-resize-pane + \ mouse-select-pane mouse-select-window mouse-utf8 + \ pane-active-border-style pane-border-style prefix prefix2 \ renumber-windows repeat-time set-remain-on-exit set-titles - \ set-titles-string status status-attr status-bg status-fg - \ status-interval status-justify status-keys status-left - \ status-left-attr status-left-bg status-left-fg status-left-length - \ status-position status-right status-right-attr status-right-bg - \ status-right-fg status-right-length status-utf8 terminal-overrides - \ update-environment visual-activity visual-bell visual-content - \ visual-silence word-separators + \ set-titles-string status status-interval status-justify status-keys + \ status-left status-left-length status-left-style status-position + \ status-right status-right-length staus-right-style status-utf8 + \ terminal-overrides update-environment visual-activity visual-bell + \ visual-content visual-silence word-separators syn keyword tmuxOptsSetw - \ aggressive-resize alternate-screen automatic-rename + \ aggressive-resize allow-rename alternate-screen automatic-rename \ c0-change-interval c0-change-trigger clock-mode-colour - \ clock-mode-style force-height force-width layout-history-limit - \ main-pane-height main-pane-width mode-attr mode-bg mode-fg move-keys - \ mode-mouse monitor-activity monitor-content monitor-silence + \ clock-mode-style force-height force-width force-height + \ force-width main-pane-height main-pane-width mode-keys + \ mode-mouse mode-style monitor-activity monitor-content monitor-silence \ other-pane-height other-pane-width pane-base-index remain-on-exit - \ synchronize-panes utf8 window-status-bell-attr window-status-bell-bg - \ window-status-bell-fg window-status-content-attr - \ window-status-content-bg window-status-content-fg - \ window-status-activity-attr window-status-activity-bg - \ window-status-activity-fg window-status-attr - \ window-status-current-attr window-status-attr window-status-current-bg - \ window-status-bg window-status-current-fg window-status-fg - \ window-status-current-format window-status-format - \ window-status-separator xterm-keys wrap-search + \ synchronize-panes utf8 window-status-activity-style + \ window-status-bell-style window-status-content-style + \ window-status-current-format window-status-current-style + \ window-status-format window-status-last-style window-status-separator + \ window-status-style wrap-search xterm-keys syn keyword tmuxTodo FIXME NOTE TODO XXX contained From c52548f6fd311e4df3076ba4cc6f6ab8849557ac Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 10 Feb 2014 11:20:41 +0000 Subject: [PATCH 237/949] The last fix to xterm keys meant that some keys such as \033OA were being wrongly treated as partial matches. So both check xterm keys after standard keys and only wildcard the minimum required ('1' to '8'). Problems reported by Ralf Horstmann and Tim van der Molen. --- tty-keys.c | 18 +++++++++--------- xterm-keys.c | 4 +++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 7de5ce59..4492df1e 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -479,6 +479,15 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Look for matching key string and return if found. */ + tk = tty_keys_find(tty, buf, len, &size); + if (tk != NULL) { + if (tk->next != NULL) + goto partial_key; + key = tk->key; + goto complete_key; + } + /* Try to parse a key with an xterm-style modifier. */ switch (xterm_keys_find(buf, len, &size, &key)) { case 0: /* found */ @@ -489,15 +498,6 @@ tty_keys_next(struct tty *tty) goto partial_key; } - /* Look for matching key string and return if found. */ - tk = tty_keys_find(tty, buf, len, &size); - if (tk != NULL) { - if (tk->next != NULL) - goto partial_key; - key = tk->key; - goto complete_key; - } - first_key: /* Is this a meta key? */ if (len >= 2 && buf[0] == '\033') { diff --git a/xterm-keys.c b/xterm-keys.c index 9b5a0a21..0e20165b 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -131,7 +131,9 @@ xterm_keys_match(const char *template, const char *buf, size_t len) pos = 0; do { - if (*template != '_' && buf[pos] != *template) + if (*template == '_' && buf[pos] >= '1' && buf[pos] <= '8') + continue; + if (buf[pos] != *template) return (-1); } while (*++template != '\0' && ++pos != len); From 325396046a3906de02b8697377575caaa80546b5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 12 Feb 2014 20:26:13 +0000 Subject: [PATCH 238/949] Avoid use of uninitialized variable, from Thomas Adam. --- cmd-switch-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index d0544013..99a1ae64 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -61,7 +61,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; - struct session *s; + struct session *s = NULL; struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; From f58721a9e8831f5e47e05fceb7fa5558ddb612ff Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Feb 2014 12:35:58 +0000 Subject: [PATCH 239/949] Make C-j the same as C-m, from Simon Nicolussi. --- mode-key.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mode-key.c b/mode-key.c index 995ccdfa..80a943e4 100644 --- a/mode-key.c +++ b/mode-key.c @@ -153,6 +153,7 @@ const struct mode_key_entry mode_key_vi_edit[] = { { '\025' /* C-u */, 0, MODEKEYEDIT_DELETELINE }, { '\027' /* C-w */, 0, MODEKEYEDIT_DELETEWORD }, { '\033' /* Escape */, 0, MODEKEYEDIT_SWITCHMODE }, + { '\n', 0, MODEKEYEDIT_ENTER }, { '\r', 0, MODEKEYEDIT_ENTER }, { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE }, { KEYC_DC, 0, MODEKEYEDIT_DELETE }, @@ -176,6 +177,7 @@ const struct mode_key_entry mode_key_vi_edit[] = { { 'X', 1, MODEKEYEDIT_BACKSPACE }, { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL }, { '\010' /* C-h */, 1, MODEKEYEDIT_BACKSPACE }, + { '\n', 1, MODEKEYEDIT_ENTER }, { '\r', 1, MODEKEYEDIT_ENTER }, { '^', 1, MODEKEYEDIT_STARTOFLINE }, { 'a', 1, MODEKEYEDIT_SWITCHMODEAPPEND }, @@ -219,6 +221,7 @@ const struct mode_key_entry mode_key_vi_choice[] = { { '\005' /* C-e */, 0, MODEKEYCHOICE_SCROLLDOWN }, { '\006' /* C-f */, 0, MODEKEYCHOICE_PAGEDOWN }, { '\031' /* C-y */, 0, MODEKEYCHOICE_SCROLLUP }, + { '\n', 0, MODEKEYCHOICE_CHOOSE }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'j', 0, MODEKEYCHOICE_DOWN }, { 'k', 0, MODEKEYCHOICE_UP }, @@ -281,6 +284,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { '\025' /* C-u */, 0, MODEKEYCOPY_HALFPAGEUP }, { '\031' /* C-y */, 0, MODEKEYCOPY_SCROLLUP }, { '\033' /* Escape */, 0, MODEKEYCOPY_CLEARSELECTION }, + { '\n', 0, MODEKEYCOPY_COPYSELECTION }, { '\r', 0, MODEKEYCOPY_COPYSELECTION }, { '^', 0, MODEKEYCOPY_BACKTOINDENTATION }, { 'b', 0, MODEKEYCOPY_PREVIOUSWORD }, @@ -329,6 +333,7 @@ const struct mode_key_entry mode_key_emacs_edit[] = { { '\027' /* C-w */, 0, MODEKEYEDIT_DELETEWORD }, { '\031' /* C-y */, 0, MODEKEYEDIT_PASTE }, { '\033' /* Escape */, 0, MODEKEYEDIT_CANCEL }, + { '\n', 0, MODEKEYEDIT_ENTER }, { '\r', 0, MODEKEYEDIT_ENTER }, { 'b' | KEYC_ESCAPE, 0, MODEKEYEDIT_PREVIOUSWORD }, { 'f' | KEYC_ESCAPE, 0, MODEKEYEDIT_NEXTWORDEND }, @@ -363,6 +368,7 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { '\020' /* C-p */, 0, MODEKEYCHOICE_UP }, { '\026' /* C-v */, 0, MODEKEYCHOICE_PAGEDOWN }, { '\033' /* Escape */, 0, MODEKEYCHOICE_CANCEL }, + { '\n', 0, MODEKEYCHOICE_CHOOSE }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCHOICE_PAGEUP }, From d0accdba884517c55a842dec59ef2c8db6c208b5 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Feb 2014 12:37:54 +0000 Subject: [PATCH 240/949] Check for NULL session and whatnot in status_replace, from Thomas Adam. --- status.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/status.c b/status.c index e14c1a81..6966aadf 100644 --- a/status.c +++ b/status.c @@ -445,11 +445,11 @@ status_replace(struct client *c, struct session *s, struct winlink *wl, if (fmt == NULL) return (xstrdup("")); - if (s == NULL) + if (s == NULL && c != NULL) s = c->session; - if (wl == NULL) + if (wl == NULL && s != NULL) wl = s->curw; - if (wp == NULL) + if (wp == NULL && wl != NULL) wp = wl->window->active; len = strftime(in, sizeof in, fmt, localtime(&t)); @@ -472,10 +472,14 @@ status_replace(struct client *c, struct session *s, struct winlink *wl, *optr = '\0'; ft = format_create(); - format_client(ft, c); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wp); + if (c != NULL) + format_client(ft, c); + if (s != NULL) + format_session(ft, s); + if (s != NULL && wl != NULL) + format_winlink(ft, s, wl); + if (wp != NULL) + format_window_pane(ft, wp); expanded = format_expand(ft, out); format_free(ft); return (expanded); From bfb700cf4159c0d11c3137f1429eb1947f35cb77 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Feb 2014 12:44:45 +0000 Subject: [PATCH 241/949] Do not need to call winlink_find_by_window, from Filip Moc. --- server-window.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/server-window.c b/server-window.c index e82f0e36..39699c37 100644 --- a/server-window.c +++ b/server-window.c @@ -76,7 +76,7 @@ server_window_check_bell(struct session *s, struct winlink *wl) wl->flags |= WINLINK_BELL; if (s->flags & SESSION_UNATTACHED) return (0); - if (s->curw->window == wl->window) + if (s->curw->window == w) w->flags &= ~WINDOW_BELL; visual = options_get_number(&s->options, "visual-bell"); @@ -93,10 +93,8 @@ server_window_check_bell(struct session *s, struct winlink *wl) } if (c->session->curw->window == w) status_message_set(c, "Bell in current window"); - else if (action == BELL_ANY) { - status_message_set(c, "Bell in window %u", - winlink_find_by_window(&s->windows, w)->idx); - } + else if (action == BELL_ANY) + status_message_set(c, "Bell in window %u", wl->idx); } return (1); @@ -110,7 +108,7 @@ server_window_check_activity(struct session *s, struct winlink *wl) struct window *w = wl->window; u_int i; - if (s->curw->window == wl->window) + if (s->curw->window == w) w->flags &= ~WINDOW_ACTIVITY; if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) @@ -130,8 +128,7 @@ server_window_check_activity(struct session *s, struct winlink *wl) c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; - status_message_set(c, "Activity in window %u", - winlink_find_by_window(&s->windows, w)->idx); + status_message_set(c, "Activity in window %u", wl->idx); } } @@ -182,8 +179,7 @@ server_window_check_silence(struct session *s, struct winlink *wl) c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; - status_message_set(c, "Silence in window %u", - winlink_find_by_window(&s->windows, w)->idx); + status_message_set(c, "Silence in window %u", wl->idx); } } @@ -225,8 +221,7 @@ server_window_check_content( c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; - status_message_set(c, "Content in window %u", - winlink_find_by_window(&s->windows, w)->idx); + status_message_set(c, "Content in window %u", wl->idx); } } From 3aadc9d665fd3ec52181c5e1d8bd2d6481a97fef Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 14 Feb 2014 13:56:39 +0000 Subject: [PATCH 242/949] Missing *. --- TODO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO b/TODO index b9a83fb4..6c418949 100644 --- a/TODO +++ b/TODO @@ -119,7 +119,7 @@ - miscellaneous * way to keep a job running just read its last line of output for #() - link panes into multiple windows + * link panes into multiple windows * live update: server started with -U connects to server, requests sessions and windows, receives file descriptors * there are inconsistencies in what we get from old shell and what From f835be4bb25f629613bbb7e0195a5c4fb12abf3e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Feb 2014 13:59:01 +0000 Subject: [PATCH 243/949] Style nit - no space between function name and bracket. --- cmd-load-buffer.c | 2 +- cmd-set-option.c | 2 +- cmd-show-messages.c | 28 ++++++++++++++-------------- grid.c | 2 +- server-client.c | 10 +++++----- server-fn.c | 6 +++--- status.c | 2 +- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 85595285..eb6e0d0c 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -72,7 +72,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) buffer_ptr = xmalloc(sizeof *buffer_ptr); *buffer_ptr = buffer; - error = server_set_stdin_callback (c, cmd_load_buffer_callback, + error = server_set_stdin_callback(c, cmd_load_buffer_callback, buffer_ptr, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); diff --git a/cmd-set-option.c b/cmd-set-option.c index b661913f..046bebd6 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -172,7 +172,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) } /* Start or stop timers when automatic-rename changed. */ - if (strcmp (oe->name, "automatic-rename") == 0) { + if (strcmp(oe->name, "automatic-rename") == 0) { for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if ((w = ARRAY_ITEM(&windows, i)) == NULL) continue; diff --git a/cmd-show-messages.c b/cmd-show-messages.c index a3938e0a..45358fde 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -49,12 +49,12 @@ const struct cmd_entry cmd_server_info_entry = { cmd_show_messages_exec }; -void cmd_show_messages_server (struct cmd_q *); -void cmd_show_messages_terminals (struct cmd_q *); -void cmd_show_messages_jobs (struct cmd_q *); +void cmd_show_messages_server(struct cmd_q *); +void cmd_show_messages_terminals(struct cmd_q *); +void cmd_show_messages_jobs(struct cmd_q *); void -cmd_show_messages_server (struct cmd_q *cmdq) +cmd_show_messages_server(struct cmd_q *cmdq) { char *tim; @@ -68,7 +68,7 @@ cmd_show_messages_server (struct cmd_q *cmdq) } void -cmd_show_messages_terminals (struct cmd_q *cmdq) +cmd_show_messages_terminals(struct cmd_q *cmdq) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -111,7 +111,7 @@ cmd_show_messages_terminals (struct cmd_q *cmdq) } void -cmd_show_messages_jobs (struct cmd_q *cmdq) +cmd_show_messages_jobs(struct cmd_q *cmdq) { struct job *job; u_int n; @@ -136,20 +136,20 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) int done; done = 0; - if (args_has (args, 'I') || self->entry == &cmd_server_info_entry) { - cmd_show_messages_server (cmdq); + if (args_has(args, 'I') || self->entry == &cmd_server_info_entry) { + cmd_show_messages_server(cmdq); done = 1; } - if (args_has (args, 'T') || self->entry == &cmd_server_info_entry) { + if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { if (done) - cmdq_print (cmdq, "%s", ""); - cmd_show_messages_terminals (cmdq); + cmdq_print(cmdq, "%s", ""); + cmd_show_messages_terminals(cmdq); done = 1; } - if (args_has (args, 'J') || self->entry == &cmd_server_info_entry) { + if (args_has(args, 'J') || self->entry == &cmd_server_info_entry) { if (done) - cmdq_print (cmdq, "%s", ""); - cmd_show_messages_jobs (cmdq); + cmdq_print(cmdq, "%s", ""); + cmd_show_messages_jobs(cmdq); done = 1; } if (done) diff --git a/grid.c b/grid.c index fb838adf..7fb15dc3 100644 --- a/grid.c +++ b/grid.c @@ -749,7 +749,7 @@ grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, dst_gl->flags |= GRID_LINE_WRAPPED; /* Copy the data. */ - memcpy (&dst_gl->celldata[0], &src_gl->celldata[offset], + memcpy(&dst_gl->celldata[0], &src_gl->celldata[offset], to_copy * sizeof dst_gl->celldata[0]); /* Move offset and reduce old line size. */ diff --git a/server-client.c b/server-client.c index 5a6cb7d9..5326f256 100644 --- a/server-client.c +++ b/server-client.c @@ -68,9 +68,9 @@ server_client_create(int fd) c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; - c->stdin_data = evbuffer_new (); - c->stdout_data = evbuffer_new (); - c->stderr_data = evbuffer_new (); + c->stdin_data = evbuffer_new(); + c->stdout_data = evbuffer_new(); + c->stderr_data = evbuffer_new(); c->tty.fd = -1; c->title = NULL; @@ -123,7 +123,7 @@ server_client_open(struct client *c, struct session *s, char **cause) return (0); if (!(c->flags & CLIENT_TERMINAL)) { - *cause = xstrdup ("not a terminal"); + *cause = xstrdup("not a terminal"); return (-1); } @@ -174,7 +174,7 @@ server_client_lost(struct client *c) evtimer_del(&c->identify_timer); free(c->message_string); - if (event_initialized (&c->message_timer)) + if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { msg = &ARRAY_ITEM(&c->message_log, i); diff --git a/server-fn.c b/server-fn.c index d0747628..f585cb71 100644 --- a/server-fn.c +++ b/server-fn.c @@ -454,7 +454,7 @@ server_destroy_session(struct session *s) } void -server_check_unattached (void) +server_check_unattached(void) { struct session *s; @@ -480,7 +480,7 @@ server_set_identify(struct client *c) tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; - if (event_initialized (&c->identify_timer)) + if (event_initialized(&c->identify_timer)) evtimer_del(&c->identify_timer); evtimer_set(&c->identify_timer, server_callback_identify, c); evtimer_add(&c->identify_timer, &tv); @@ -592,7 +592,7 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, c->references++; if (c->stdin_closed) - c->stdin_callback (c, 1, c->stdin_callback_data); + c->stdin_callback(c, 1, c->stdin_callback_data); server_write_client(c, MSG_STDIN, NULL, 0); diff --git a/status.c b/status.c index 6966aadf..84589427 100644 --- a/status.c +++ b/status.c @@ -690,7 +690,7 @@ status_message_set(struct client *c, const char *fmt, ...) tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; - if (event_initialized (&c->message_timer)) + if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); evtimer_set(&c->message_timer, status_message_callback, c); evtimer_add(&c->message_timer, &tv); From e9d32f901ac86986943cac3b391442ae5bce883f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Feb 2014 14:00:18 +0000 Subject: [PATCH 244/949] Make status-interval of zero work as indented. --- server-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 5326f256..c2903a0d 100644 --- a/server-client.c +++ b/server-client.c @@ -280,7 +280,7 @@ server_client_status_timer(void) interval = options_get_number(&s->options, "status-interval"); difference = tv.tv_sec - c->status_timer.tv_sec; - if (difference >= interval) { + if (interval != 0 && difference >= interval) { status_update_jobs(c); c->flags |= CLIENT_STATUS; } From f2e54e1e2ff5a1902cdab4db50a503541186638e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Feb 2014 14:37:08 +0000 Subject: [PATCH 245/949] If the terminfo entry has colors#256, assume that setaf and setab work and use them for the 256 colour set. If the terminfo entry doesn't have colors#256 and the user gives -2 to the client, use a \033[38;5;Xm sequence as before. Should allow fbterm to work with it's weird setaf and setab. --- tty.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tty.c b/tty.c index f5a85992..20b7070a 100644 --- a/tty.c +++ b/tty.c @@ -1581,13 +1581,29 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) { char s[32]; - if (!(tty->term->flags & TERM_256COLOURS) && - !(tty->term_flags & TERM_256COLOURS)) - return (-1); + /* + * If the terminfo entry has 256 colours, assume that setaf and setab + * work correctly. + */ + if (tty->term->flags & TERM_256COLOURS) { + if (*type == '3') + tty_putcode1(tty, TTYC_SETAF, colour); + else + tty_putcode1(tty, TTYC_SETAB, colour); + return (0); + } - xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); - tty_puts(tty, s); - return (0); + /* + * If the user has specified -2 to the client, setaf and setab may not + * work, so send the usual sequence. + */ + if (tty->term_flags & TERM_256COLOURS) { + xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); + tty_puts(tty, s); + return (0); + } + + return (-1); } void From 81db6bab91309e7cb42628048408c10504522a48 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 16 Feb 2014 12:45:17 +0000 Subject: [PATCH 246/949] Leftovers from removing 88 colour support, from Theo Buehler. --- tmux.1 | 6 +++--- tmux.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index d32a16d2..c0f04094 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 28lCquv +.Op Fl 2lCquv .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -2637,9 +2637,9 @@ The terminal entry value is passed through before interpretation. The default value forcibly corrects the .Ql colors -entry for terminals which support 88 or 256 colours: +entry for terminals which support 256 colours: .Bd -literal -offset indent -"*88col*:colors=88,*256col*:colors=256,xterm*:XT" +"*256col*:colors=256,xterm*:XT" .Ed .It Ic update-environment Ar variables Set a space-separated string containing a list of environment variables to be diff --git a/tmux.c b/tmux.c index d4848396..6de96ce1 100644 --- a/tmux.c +++ b/tmux.c @@ -57,7 +57,7 @@ __dead void usage(void) { fprintf(stderr, - "usage: %s [-28lquv] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2lquv] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", __progname); exit(1); From 1e981f4c6d429f5c51cb0d7a5c40fcd78ad236a2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 17 Feb 2014 18:12:47 +0000 Subject: [PATCH 247/949] Don't crash when given a invalid colour, reported by Felix Rosencrantz, fix from Thomas Adam. --- cmd-set-option.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 046bebd6..9882e449 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -307,11 +307,13 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, break; case OPTIONS_TABLE_COLOUR: o = cmd_set_option_colour(self, cmdq, oe, oo, value); - style_update_new(oo, o->name, oe->style); + if (o != NULL) + style_update_new(oo, o->name, oe->style); break; case OPTIONS_TABLE_ATTRIBUTES: o = cmd_set_option_attributes(self, cmdq, oe, oo, value); - style_update_new(oo, o->name, oe->style); + if (o != NULL) + style_update_new(oo, o->name, oe->style); break; case OPTIONS_TABLE_FLAG: o = cmd_set_option_flag(self, cmdq, oe, oo, value); From 69b7c496accc2a646e5e2dee7870bea1194a6641 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 17 Feb 2014 22:42:20 +0000 Subject: [PATCH 248/949] Be consistent and allow only mouse down and mouse wheel for any pane with mouse-select-pane rather than just in copy mode, reported by Balazs Kezes. --- server-client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server-client.c b/server-client.c index c2903a0d..ed8159f5 100644 --- a/server-client.c +++ b/server-client.c @@ -324,9 +324,9 @@ server_client_check_mouse(struct client *c, struct window_pane *wp) else if (statusat > 0 && m->y >= (u_int)statusat) m->y = statusat - 1; - /* Is this a pane selection? Allow down only in copy mode. */ + /* Is this a pane selection? */ if (options_get_number(oo, "mouse-select-pane") && - (m->event == MOUSE_EVENT_DOWN || wp->mode != &window_copy_mode)) { + (m->event == MOUSE_EVENT_DOWN || m->event == MOUSE_EVENT_WHEEL)) { window_set_active_at(wp->window, m->x, m->y); server_redraw_window_borders(wp->window); wp = wp->window->active; /* may have changed */ From 6daf06b1ad61f67e9f7780d787451b9b5f82dd43 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 17 Feb 2014 23:07:03 +0000 Subject: [PATCH 249/949] Fix memory leaks with paste_replace, based on changes from J Raynor. --- cmd-load-buffer.c | 1 + paste.c | 4 +++- window-copy.c | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index eb6e0d0c..636f0fcd 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -169,6 +169,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) /* No context so can't use server_client_msg_error. */ evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); server_push_stderr(c); + free(pdata); } free(data); diff --git a/paste.c b/paste.c index 2e89a5ef..946935a3 100644 --- a/paste.c +++ b/paste.c @@ -131,8 +131,10 @@ paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) { struct paste_buffer *pb; - if (size == 0) + if (size == 0) { + free(data); return (0); + } if (idx >= ARRAY_LENGTH(ps)) return (-1); diff --git a/window-copy.c b/window-copy.c index df4ca55a..76c9c3ce 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1455,8 +1455,8 @@ window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) if (idx == -1) { limit = options_get_number(&global_options, "buffer-limit"); paste_add(&global_buffers, buf, len, limit); - } else - paste_replace(&global_buffers, idx, buf, len); + } else if (paste_replace(&global_buffers, idx, buf, len) != 0) + free(buf); } void From c5d2de7ec0935b77dc620dfb95a1cf03d53d1aaa Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 20 Feb 2014 21:32:42 +0000 Subject: [PATCH 250/949] Update CHANGES and configure.ac for 1.9 release --- CHANGES | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index b9ad4e69..17d4d2a8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,58 @@ +CHANGES FROM 1.8 to 1.9, 20 February 2014 + +NOTE: This release has bumped the tmux protocol version. It is therefore +advised that the prior tmux server is restarted when this version of tmux is +installed, to avoid protocol mismatch errors for newer clients trying to +talk to an older running tmux server. + +Incompatible Changes +==================== + +* 88 colour support has been removed. +* 'default-path' has been removed. The new-window command accepts '-c' to + cater for this. The previous value of "." can be replaced with: 'neww -c + $PWD', the previous value of '' which meant current path of the pane can + be specified as: 'neww -c "#{pane_current_path}"' + +Deprecated Changes +================== + +* The single format specifiers: #A -> #Z (where defined) have been + deprecated and replaced with longer-named equivalents, as listed in the + FORMATS section of the tmux manpage. +* The various foo-{fg,bg,attr} commands have been deprecated and replaced + with equivalent foo-style option instead. Currently this is still + backwards-compatible, but will be removed over time. + +Normal Changes +============== + +* A new environment variable TMUX_TMPDIR is now honoured, allowing the + socket directory to be set outside of TMPDIR (/tmp/ if not set). +* If -s not given to swap-pane the current pane is assumed. +* A #{pane_syncronized} format specifier has been added to be a conditional + format if a pane is in a syncronised mode (c.f. syncronize-panes) +* Tmux now runs under Cygwin natively. +* Formats can now be nested within each other and expanded accordingly. +* Added 'automatic-rename-format' option to allow the automatic rename + mechanism to use something other than the default of + #{pane_current_command}. +* new-session learnt '-c' to specify the starting directory for that session + and all subsequent windows therein. +* The session name is now shown in the message printed to the terminal when + a session is detached. +* Lots more format specifiers have been added. +* Server race conditions have been fixed; in particular commands are not run + until after the configuration file is read completely. +* Case insensitive searching in tmux's copy-mode is now possible. +* attach-session and switch-client learnt the '-t' option to accept a window + and/or a pane to use. +* Copy-mode is only exited if no selection is in progress. +* Paste key in copy-mode is now possible to enter text from the clipboard. +* status-interval set to '0' now works as intended. +* tmux now supports 256 colours running under fbterm. +* Many bug fixes! + CHANGES FROM 1.7 to 1.8, 26 March 2013 Incompatible Changes diff --git a/configure.ac b/configure.ac index f646a0d0..4d6f2d1c 100644 --- a/configure.ac +++ b/configure.ac @@ -45,7 +45,7 @@ AC_CHECK_HEADERS( ) # Is this a debug build? -found_debug=yes +#found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, create a debug build), From 1ab0745f8e6896a815ce7a696d883d4d07c18677 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 20 Feb 2014 21:44:33 +0000 Subject: [PATCH 251/949] Working on 1.10 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4d6f2d1c..fec79043 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 1.9) +AC_INIT(tmux, 1.10) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -45,7 +45,7 @@ AC_CHECK_HEADERS( ) # Is this a debug build? -#found_debug=yes +found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, create a debug build), From 9120df33efdbaac82c152ed3d5995e2be7a70411 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 20 Feb 2014 23:20:17 +0000 Subject: [PATCH 252/949] dist: Call clean target before any other action In case 'make dist' is invoked from a dirty tree which hasn't had its object files cleaned up, clean out the tree first before tarring up the files for a release. --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index e5a7286e..a9ad5b97 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,7 @@ EXTRA_DIST = \ CHANGES FAQ README TODO COPYING examples compat \ array.h compat.h tmux.h osdep-*.c mdoc2man.awk tmux.1 dist-hook: + make clean grep "^#found_debug=" configure find $(distdir) -name .svn -type d|xargs rm -Rf From 94d99d54629b182a4266d918ccf80e4daee4567b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 21 Feb 2014 08:02:48 +0000 Subject: [PATCH 253/949] Put each command on its own line and remove duplicates, from Daniel Hahler. --- examples/tmux.vim | 276 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 227 insertions(+), 49 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 037ddf7b..4d64514a 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -31,59 +31,237 @@ syn keyword tmuxAction any current none syn keyword tmuxBoolean off on syn keyword tmuxCmds - \ attach[-session] bind[-key] break-pane breakp capture-pane capturep - \ choose-buffer choose-client choose-list choose-session choose-tree - \ choose-window clear-history clearhist clock-mode command-prompt - \ confirm[-before] copy-mode delete-buffer deleteb detach[-client] - \ display[-message] display-panes displayp find-window findw - \ has[-session] if[-shell] join-pane joinp kill-pane killp kill-server - \ kill-session kill-window killw last-pane lastp last[-window] - \ link-window linkw list-buffers lsb list-clients lsc - \ list-commands lscm list-keys lsk list-panes lsp list-sessions ls - \ list-windows lsw load-buffer loadb lock-client lockc lock[-server] - \ lock-session locks move-pane movep move-window movew new[-session] - \ next-layout nextl next[-window] paste-buffer pasteb pipe-pane pipep - \ previous-layout prevl prev[ious-window] refresh[-client] - \ rename[-session] rename-window renamew resize-pane resizep - \ respawn-pane respawnp respawn-window respawnw rotate-window rotatew - \ run[-shell] save-buffer saveb select-layout selectl - \ select-pane selectp select-window selectw send[-keys] send-prefix - \ server-info info set-buffer setb set-environment setenv set[-option] - \ set-window-option setw show-buffer showb show-environment showenv - \ show-messages showmsgs show[-options] show-window-options showw - \ source[-file] split-window splitw start[-server] - \ suspend-client suspendc swap-pane swapp swap-window swapw - \ switch-client switchc unbind[-key] unlink-window unlinkw wait[-for] + \ attach[-session] + \ bind[-key] + \ break-pane + \ breakp + \ capture-pane + \ capturep + \ choose-buffer + \ choose-client + \ choose-list + \ choose-session + \ choose-tree + \ choose-window + \ clear-history + \ clearhist + \ clock-mode + \ command-prompt + \ confirm[-before] + \ copy-mode + \ delete-buffer + \ deleteb + \ detach[-client] + \ display[-message] + \ display-panes + \ displayp + \ find-window + \ findw + \ has[-session] + \ if[-shell] + \ join-pane + \ joinp + \ kill-pane + \ killp + \ kill-server + \ kill-session + \ kill-window + \ killw + \ last-pane + \ lastp + \ last[-window] + \ link-window + \ linkw + \ list-buffers + \ lsb + \ list-clients + \ lsc + \ list-commands + \ lscm + \ list-keys + \ lsk + \ list-panes + \ lsp + \ list-sessions + \ ls + \ list-windows + \ lsw + \ load-buffer + \ loadb + \ lock-client + \ lockc + \ lock[-server] + \ lock-session + \ locks + \ move-pane + \ movep + \ move-window + \ movew + \ new[-session] + \ next-layout + \ nextl + \ next[-window] + \ paste-buffer + \ pasteb + \ pipe-pane + \ pipep + \ previous-layout + \ prevl + \ prev[ious-window] + \ refresh[-client] + \ rename[-session] + \ rename-window + \ renamew + \ resize-pane + \ resizep + \ respawn-pane + \ respawnp + \ respawn-window + \ respawnw + \ rotate-window + \ rotatew + \ run[-shell] + \ save-buffer + \ saveb + \ select-layout + \ selectl + \ select-pane + \ selectp + \ select-window + \ selectw + \ send[-keys] + \ send-prefix + \ server-info + \ info + \ set-buffer + \ setb + \ set-environment + \ setenv + \ set[-option] + \ set-window-option + \ setw + \ show-buffer + \ showb + \ show-environment + \ showenv + \ show-messages + \ showmsgs + \ show[-options] + \ show-window-options + \ showw + \ source[-file] + \ split-window + \ splitw + \ start[-server] + \ suspend-client + \ suspendc + \ swap-pane + \ swapp + \ swap-window + \ swapw + \ switch-client + \ switchc + \ unbind[-key] + \ unlink-window + \ unlinkw + \ wait[-for] syn keyword tmuxOptsSet - \ buffer-limit escape-time exit-unattached exit-unattached focus-events - \ quiet set-clipboard - \ assume-paste-time base-index bell-action bell-on-alert default-command - \ default-shell default-terminal destroy-unattached detach-on-destroy - \ display-panes-active-colour display-panes-colour display-panes-time - \ display-time history-limit lock-after-time lock-command lock-server - \ message-command-style message-limit message-style mouse-resize-pane - \ mouse-select-pane mouse-select-window mouse-utf8 - \ pane-active-border-style pane-border-style prefix prefix2 - \ renumber-windows repeat-time set-remain-on-exit set-titles - \ set-titles-string status status-interval status-justify status-keys - \ status-left status-left-length status-left-style status-position - \ status-right status-right-length staus-right-style status-utf8 - \ terminal-overrides update-environment visual-activity visual-bell - \ visual-content visual-silence word-separators + \ assume-paste-time + \ base-index + \ bell-action + \ bell-on-alert + \ buffer-limit + \ default-command + \ default-shell + \ default-terminal + \ destroy-unattached + \ detach-on-destroy + \ display-panes-active-colour + \ display-panes-colour + \ display-panes-time + \ display-time + \ escape-time + \ exit-unattached + \ focus-events + \ history-limit + \ lock-after-time + \ lock-command + \ lock-server + \ message-command-style + \ message-limit + \ message-style + \ mouse-resize-pane + \ mouse-select-pane + \ mouse-select-window + \ mouse-utf8 + \ pane-active-border-style + \ pane-border-style + \ prefix + \ prefix2 + \ quiet + \ renumber-windows + \ repeat-time + \ set-clipboard + \ set-remain-on-exit + \ set-titles + \ set-titles-string + \ status + \ status-interval + \ status-justify + \ status-keys + \ status-left + \ status-left-length + \ status-left-style + \ status-position + \ status-right + \ status-right-length + \ status-utf8 + \ staus-right-style + \ terminal-overrides + \ update-environment + \ visual-activity + \ visual-bell + \ visual-content + \ visual-silence + \ word-separators syn keyword tmuxOptsSetw - \ aggressive-resize allow-rename alternate-screen automatic-rename - \ c0-change-interval c0-change-trigger clock-mode-colour - \ clock-mode-style force-height force-width force-height - \ force-width main-pane-height main-pane-width mode-keys - \ mode-mouse mode-style monitor-activity monitor-content monitor-silence - \ other-pane-height other-pane-width pane-base-index remain-on-exit - \ synchronize-panes utf8 window-status-activity-style - \ window-status-bell-style window-status-content-style - \ window-status-current-format window-status-current-style - \ window-status-format window-status-last-style window-status-separator - \ window-status-style wrap-search xterm-keys + \ aggressive-resize + \ allow-rename + \ alternate-screen + \ automatic-rename + \ c0-change-interval + \ c0-change-trigger + \ clock-mode-colour + \ clock-mode-style + \ force-height + \ force-width + \ main-pane-height + \ main-pane-width + \ mode-keys + \ mode-mouse + \ mode-style + \ monitor-activity + \ monitor-content + \ monitor-silence + \ other-pane-height + \ other-pane-width + \ pane-base-index + \ remain-on-exit + \ synchronize-panes + \ utf8 + \ window-status-activity-style + \ window-status-bell-style + \ window-status-content-style + \ window-status-current-format + \ window-status-current-style + \ window-status-format + \ window-status-last-style + \ window-status-separator + \ window-status-style + \ wrap-search + \ xterm-keys syn keyword tmuxTodo FIXME NOTE TODO XXX contained From b7589750a1df0f34c73aadfa138e0fe5baa57233 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Fri, 21 Feb 2014 09:27:28 +0000 Subject: [PATCH 254/949] Correct Linux-specific manpage sections Some of the man page locations on Linux differ to those on *BSD. Noticed by Christopher Meng. --- tmux.1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tmux.1 b/tmux.1 index aa3e11ce..51d927a5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -61,7 +61,7 @@ A window occupies the entire screen and may be split into rectangular panes, each of which is a separate pseudo terminal (the -.Xr pty 4 +.Xr pty 7 manual page documents the technical details of pseudo terminals). Any number of .Nm @@ -359,7 +359,7 @@ or These specify the client, session, window or pane which a command should affect. .Ar target-client is the name of the -.Xr pty 4 +.Xr pty 7 file to which the client is connected, for example either of .Pa /dev/ttyp1 or @@ -710,7 +710,7 @@ and specify the size of the initial window (80 by 24 if not given). .Pp If run from a terminal, any -.Xr termios 4 +.Xr termios 3 special characters are saved and used for new windows in the new session. .Pp The @@ -1144,7 +1144,7 @@ interactively from a list. After a client is chosen, .Ql %% is replaced by the client -.Xr pty 4 +.Xr pty 7 path in .Ar template and the result executed as a command. @@ -3852,6 +3852,6 @@ bind-key / command-prompt "split-window 'exec man %%'" bind-key S command-prompt "new-window -n %1 'ssh %1'" .Ed .Sh SEE ALSO -.Xr pty 4 +.Xr pty 7 .Sh AUTHORS .An Nicholas Marriott Aq Mt nicm@users.sourceforge.net From c7f3599ebca82fcd1c2a00de234f90ac1f5f0ede Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 22 Feb 2014 01:38:47 +0000 Subject: [PATCH 255/949] Fix -fg/-bg/-style with 256 colour terminals. --- style.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/style.c b/style.c index 97d5576e..99744086 100644 --- a/style.c +++ b/style.c @@ -203,8 +203,14 @@ style_apply(struct grid_cell *gc, struct options *oo, const char *name) memcpy(gc, &grid_default_cell, sizeof *gc); gcp = options_get_style(oo, name); - colour_set_fg(gc, gcp->fg); - colour_set_bg(gc, gcp->bg); + if (gcp->flags & GRID_FLAG_FG256) + colour_set_fg(gc, gcp->fg | 0x100); + else + colour_set_fg(gc, gcp->fg); + if (gcp->flags & GRID_FLAG_BG256) + colour_set_bg(gc, gcp->bg | 0x100); + else + colour_set_bg(gc, gcp->bg); gc->attr |= gcp->attr; } @@ -215,10 +221,18 @@ style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) struct grid_cell *gcp; gcp = options_get_style(oo, name); - if (gcp->fg != 8) - colour_set_fg(gc, gcp->fg); - if (gcp->bg != 8) - colour_set_bg(gc, gcp->bg); + if (gcp->fg != 8) { + if (gcp->flags & GRID_FLAG_FG256) + colour_set_fg(gc, gcp->fg | 0x100); + else + colour_set_fg(gc, gcp->fg); + } + if (gcp->bg != 8) { + if (gcp->flags & GRID_FLAG_BG256) + colour_set_bg(gc, gcp->bg | 0x100); + else + colour_set_bg(gc, gcp->bg); + } if (gcp->attr != 0) gc->attr |= gcp->attr; } From fe6f520054cc324911505a6c370a7b5cbe2f6d23 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 22 Feb 2014 17:35:41 +0000 Subject: [PATCH 256/949] FAQ about xterm-keys in emacs and vim, from Mark Oteiza. --- FAQ | 25 ++++++++++++++++++++ examples/xterm-keys.vim | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 examples/xterm-keys.vim diff --git a/FAQ b/FAQ index da72d433..6181ad89 100644 --- a/FAQ +++ b/FAQ @@ -238,6 +238,31 @@ would be welcome. vim users may also want to set the "ttyfast" option inside tmux. +* How do I make ctrl and shift arrow keys work in emacs? + +The terminal-init-screen function in term/screen.el is called for new frames, +but it doesn't configure any function keys. + +If the tmux xterm-keys option is on, it is enough to define the same keys as +xterm. Add the following to init.el or .emacs to do this: + +(defadvice terminal-init-screen + ;; The advice is named `tmux', and is run before `terminal-init-screen' runs. + (before tmux activate) + ;; Docstring. This describes the advice and is made available inside emacs; + ;; for example when doing C-h f terminal-init-screen RET + "Apply xterm keymap, allowing use of keys passed through tmux." + ;; This is the elisp code that is run before `terminal-init-screen'. + (if (getenv "TMUX") + (let ((map (copy-keymap xterm-function-map))) + (set-keymap-parent map (keymap-parent input-decode-map)) + (set-keymap-parent input-decode-map map)))) + +And ensure .tmux.conf contains "set -g xterm-keys on". + +Alternatively, the screen.el file can be copied to the load path and +customized. + * Why doesn't elinks set the window title inside tmux? There isn't a way to detect if a terminal supports setting the window title, so diff --git a/examples/xterm-keys.vim b/examples/xterm-keys.vim new file mode 100644 index 00000000..907eb110 --- /dev/null +++ b/examples/xterm-keys.vim @@ -0,0 +1,51 @@ +" tmux.vim - Set xterm input codes passed by tmux +" Author: Mark Oteiza +" License: Public domain +" Description: Simple plugin that assigns some xterm(1)-style keys to escape +" sequences passed by tmux when "xterm-keys" is set to "on". Inspired by an +" example given by Chris Johnsen at: +" https://stackoverflow.com/a/15471820 +" +" Documentation: help:xterm-modifier-keys man:tmux(1) + +if exists("g:loaded_tmux") || &cp + finish +endif +let g:loaded_tmux = 1 + +function! s:SetXtermCapabilities() + set ttymouse=sgr + + execute "set =\e[1;*A" + execute "set =\e[1;*B" + execute "set =\e[1;*C" + execute "set =\e[1;*D" + + execute "set =\e[1;*H" + execute "set =\e[1;*F" + + execute "set =\e[2;*~" + execute "set =\e[3;*~" + execute "set =\e[5;*~" + execute "set =\e[6;*~" + + execute "set =\e[1;*P" + execute "set =\e[1;*Q" + execute "set =\e[1;*R" + execute "set =\e[1;*S" + + execute "set =\e[15;*~" + execute "set =\e[17;*~" + execute "set =\e[18;*~" + execute "set =\e[19;*~" + execute "set =\e[20;*~" + execute "set =\e[21;*~" + execute "set =\e[23;*~" + execute "set =\e[24;*~" + + execute "set t_kP=^[[5;*~" + execute "set t_kN=^[[6;*~" +endfunction + +if exists('$TMUX') + call s:SetXtermCapabilities() From 315d45a0eb596048f2513dab98e4bb47ec1852a4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 22 Feb 2014 18:01:10 +0000 Subject: [PATCH 257/949] Fix crash due to uninitialized lastwp member of layout_cell, reported by Balazs Kezes. --- layout.c | 1 + window.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/layout.c b/layout.c index b91b86cd..1c76f986 100644 --- a/layout.c +++ b/layout.c @@ -53,6 +53,7 @@ layout_create_cell(struct layout_cell *lcparent) lc->yoff = UINT_MAX; lc->wp = NULL; + lc->lastwp = NULL; return (lc); } diff --git a/window.c b/window.c index 1e11cace..842a5c63 100644 --- a/window.c +++ b/window.c @@ -410,8 +410,9 @@ window_pane_active_set(struct window_pane *wp, struct window_pane *nextwp) * Previously active pane, if any, must not be the same as the source * pane. */ - if (nextwp->layout_cell->parent != NULL) { - lastwp = nextwp->layout_cell->parent->lastwp; + lc = nextwp->layout_cell->parent; + if (lc != NULL && lc->lastwp != NULL) { + lastwp = lc->lastwp; if (lastwp != wp && window_pane_visible(lastwp)) return (lastwp); } From 80d9964a309e6a8f9ec9fb9375574f1d24350abf Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 22 Feb 2014 20:53:39 +0000 Subject: [PATCH 258/949] Update CHANGES and configure.ac for 1.9a release --- CHANGES | 11 +++++++++++ configure.ac | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 17d4d2a8..92402721 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,14 @@ +CHANGES FROM 1.9 to 1.9a 22 February 2014 + +NOTE: This is a bug-fix release to address some important bugs which just +missed the 1.9 deadline, but were found afterwards. + +Normal Changes +============== + +* Fix crash due to uninitialized lastwp member of layout_cell +* Fix -fg/-bg/-style with 256 colour terminals. + CHANGES FROM 1.8 to 1.9, 20 February 2014 NOTE: This release has bumped the tmux protocol version. It is therefore diff --git a/configure.ac b/configure.ac index fec79043..994b2287 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 1.10) +AC_INIT(tmux, 1.9a) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -45,7 +45,7 @@ AC_CHECK_HEADERS( ) # Is this a debug build? -found_debug=yes +#found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, create a debug build), From 7eed5ad97ba54b9837856fa525f436e57cd3fa73 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 22 Feb 2014 21:09:09 +0000 Subject: [PATCH 259/949] Go back to working on 1.10 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 994b2287..fec79043 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 1.9a) +AC_INIT(tmux, 1.10) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -45,7 +45,7 @@ AC_CHECK_HEADERS( ) # Is this a debug build? -#found_debug=yes +found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, create a debug build), From f1828921dfed4b7f2ba38b8c4a370b42965c404c Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 23 Feb 2014 00:53:06 +0000 Subject: [PATCH 260/949] Change terminal-overrides to a server option (now that we have them), it doesn't make much sense as a session option. --- cmd-attach-session.c | 2 +- cmd-new-session.c | 2 +- options-table.c | 16 +++++----- server-client.c | 8 ++--- tmux.1 | 72 ++++++++++++++++++++++---------------------- tmux.h | 6 ++-- tty-term.c | 7 +++-- tty.c | 4 +-- 8 files changed, 58 insertions(+), 59 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 6fb83d20..94f01e4c 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -132,7 +132,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, server_redraw_client(cmdq->client); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { - if (server_client_open(cmdq->client, s, &cause) != 0) { + if (server_client_open(cmdq->client, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); diff --git a/cmd-new-session.c b/cmd-new-session.c index bf843cad..db8416e0 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -145,7 +145,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Open the terminal if necessary. */ if (!detached && !already_attached) { - if (server_client_open(c, NULL, &cause) != 0) { + if (server_client_open(c, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); goto error; diff --git a/options-table.c b/options-table.c index ec1d3680..ea848259 100644 --- a/options-table.c +++ b/options-table.c @@ -91,6 +91,14 @@ const struct options_table_entry server_options_table[] = { .default_num = 1 }, + { .name = "terminal-overrides", + .type = OPTIONS_TABLE_STRING, + .default_str = "*256col*:colors=256" + ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" + ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" + ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" + }, + { .name = NULL } }; @@ -466,14 +474,6 @@ const struct options_table_entry session_options_table[] = { .default_num = 0 /* overridden in main() */ }, - { .name = "terminal-overrides", - .type = OPTIONS_TABLE_STRING, - .default_str = "*256col*:colors=256" - ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" - ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" - ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" - }, - { .name = "update-environment", .type = OPTIONS_TABLE_STRING, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " diff --git a/server-client.c b/server-client.c index ed8159f5..35df7531 100644 --- a/server-client.c +++ b/server-client.c @@ -114,11 +114,8 @@ server_client_create(int fd) /* Open client terminal if needed. */ int -server_client_open(struct client *c, struct session *s, char **cause) +server_client_open(struct client *c, char **cause) { - struct options *oo = s != NULL ? &s->options : &global_s_options; - char *overrides; - if (c->flags & CLIENT_CONTROL) return (0); @@ -127,8 +124,7 @@ server_client_open(struct client *c, struct session *s, char **cause) return (-1); } - overrides = options_get_string(oo, "terminal-overrides"); - if (tty_open(&c->tty, overrides, cause) != 0) + if (tty_open(&c->tty, cause) != 0) return (-1); return (0); diff --git a/tmux.1 b/tmux.1 index c0f04094..ba4c8537 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2161,6 +2161,42 @@ disallowedWindowOps: 20,21,SetXprop Or changing this property from the .Xr xterm 1 interactive menu when required. +.It Ic terminal-overrides Ar string +Contains a list of entries which override terminal descriptions read using +.Xr terminfo 5 . +.Ar string +is a comma-separated list of items each a colon-separated string made up of a +terminal type pattern (matched using +.Xr fnmatch 3 ) +and a set of +.Em name=value +entries. +.Pp +For example, to set the +.Ql clear +.Xr terminfo 5 +entry to +.Ql \ee[H\ee[2J +for all terminal types and the +.Ql dch1 +entry to +.Ql \ee[P +for the +.Ql rxvt +terminal type, the option could be set to the string: +.Bd -literal -offset indent +"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" +.Ed +.Pp +The terminal entry value is passed through +.Xr strunvis 3 +before interpretation. +The default value forcibly corrects the +.Ql colors +entry for terminals which support 256 colours: +.Bd -literal -offset indent +"*256col*:colors=256,xterm*:XT" +.Ed .El .Pp Available session options are: @@ -2605,42 +2641,6 @@ and .Ic status-right strings as UTF-8; notably, this is important for wide characters. This option defaults to off. -.It Ic terminal-overrides Ar string -Contains a list of entries which override terminal descriptions read using -.Xr terminfo 5 . -.Ar string -is a comma-separated list of items each a colon-separated string made up of a -terminal type pattern (matched using -.Xr fnmatch 3 ) -and a set of -.Em name=value -entries. -.Pp -For example, to set the -.Ql clear -.Xr terminfo 5 -entry to -.Ql \ee[H\ee[2J -for all terminal types and the -.Ql dch1 -entry to -.Ql \ee[P -for the -.Ql rxvt -terminal type, the option could be set to the string: -.Bd -literal -offset indent -"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" -.Ed -.Pp -The terminal entry value is passed through -.Xr strunvis 3 -before interpretation. -The default value forcibly corrects the -.Ql colors -entry for terminals which support 256 colours: -.Bd -literal -offset indent -"*256col*:colors=256,xterm*:XT" -.Ed .It Ic update-environment Ar variables Set a space-separated string containing a list of environment variables to be copied into the session environment when a new session is created or an diff --git a/tmux.h b/tmux.h index c6919622..5801b35e 100644 --- a/tmux.h +++ b/tmux.h @@ -1650,7 +1650,7 @@ void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_force_cursor_colour(struct tty *, const char *); void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int); -int tty_open(struct tty *, const char *, char **); +int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_write( @@ -1679,7 +1679,7 @@ void tty_bell(struct tty *); /* tty-term.c */ extern struct tty_terms tty_terms; extern const struct tty_term_code_entry tty_term_codes[NTTYCODE]; -struct tty_term *tty_term_find(char *, int, const char *, char **); +struct tty_term *tty_term_find(char *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); @@ -1893,7 +1893,7 @@ void server_add_accept(int); /* server-client.c */ void server_client_handle_key(struct client *, int); void server_client_create(int); -int server_client_open(struct client *, struct session *, char **); +int server_client_open(struct client *, char **); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); diff --git a/tty-term.c b/tty-term.c index 8644a845..a66aad83 100644 --- a/tty-term.c +++ b/tty-term.c @@ -305,7 +305,7 @@ tty_term_override(struct tty_term *term, const char *overrides) } struct tty_term * -tty_term_find(char *name, int fd, const char *overrides, char **cause) +tty_term_find(char *name, int fd, char **cause) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -383,7 +383,10 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) break; } } - tty_term_override(term, overrides); + + /* Apply terminal overrides. */ + s = options_get_string(&global_options, "terminal-overrides"); + tty_term_override(term, s); /* Delete curses data. */ del_curterm(cur_term); diff --git a/tty.c b/tty.c index 20b7070a..b545dc33 100644 --- a/tty.c +++ b/tty.c @@ -128,7 +128,7 @@ tty_set_size(struct tty *tty, u_int sx, u_int sy) { } int -tty_open(struct tty *tty, const char *overrides, char **cause) +tty_open(struct tty *tty, char **cause) { char out[64]; int fd; @@ -141,7 +141,7 @@ tty_open(struct tty *tty, const char *overrides, char **cause) tty->log_fd = fd; } - tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause); + tty->term = tty_term_find(tty->termname, tty->fd, cause); if (tty->term == NULL) { tty_close(tty); return (-1); From 488583dc8def345b32dafe5b4f9bdb0a280062d3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Feb 2014 10:19:04 +0000 Subject: [PATCH 261/949] Add to TODO. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 6c418949..3f45b62d 100644 --- a/TODO +++ b/TODO @@ -83,6 +83,8 @@ panelink to replace layout_cell * way to set hints/limits about pane size for resizing * panning over window (window larger than visible) + * a mode where one application can cross two panes (ie x|y, width = + COLUMNS/2 but height = ROWS * 2) - terminfo bits * use a better termcap internally instead of screen, perhaps xterm From 4273c1b80e2eb321d44154fe1e82e0f11aabcbd5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 24 Feb 2014 23:07:22 +0000 Subject: [PATCH 262/949] Use utempter to update utmp if it's around for configure, from madmaverick9 at roxxmail dot eu. --- configure.ac | 7 +++++++ server-fn.c | 3 +++ tmux.h | 4 ++++ window.c | 11 +++++++++++ 4 files changed, 25 insertions(+) diff --git a/configure.ac b/configure.ac index fec79043..05286d3d 100644 --- a/configure.ac +++ b/configure.ac @@ -144,6 +144,13 @@ if test "x$found_curses" = xno; then AC_MSG_ERROR("curses not found") fi +# Look for utempter +AC_CHECK_HEADER(utempter.h, have_utempter=yes, have_utempter=no) +if test "x$have_utempter" = xyes; then + AC_DEFINE(HAVE_UTEMPTER) + LIBS="$LIBS -lutempter" +fi + # Check for b64_ntop. AC_MSG_CHECKING(for b64_ntop) AC_TRY_LINK( diff --git a/server-fn.c b/server-fn.c index a00cadbd..30626984 100644 --- a/server-fn.c +++ b/server-fn.c @@ -361,6 +361,9 @@ server_destroy_pane(struct window_pane *wp) old_fd = wp->fd; if (wp->fd != -1) { +#ifdef HAVE_UTEMPTER + utempter_remove_record(wp->fd); +#endif bufferevent_free(wp->event); close(wp->fd); wp->fd = -1; diff --git a/tmux.h b/tmux.h index 7d5f230b..ec5cf55f 100644 --- a/tmux.h +++ b/tmux.h @@ -32,6 +32,10 @@ #include #include +#ifdef HAVE_UTEMPTER +#include +#endif + #include "array.h" #include "compat.h" diff --git a/window.c b/window.c index f41b58d0..70530450 100644 --- a/window.c +++ b/window.c @@ -779,6 +779,9 @@ window_pane_destroy(struct window_pane *wp) evtimer_del(&wp->changes_timer); if (wp->fd != -1) { +#ifdef HAVE_UTEMPTER + utempter_remove_record(wp->fd); +#endif bufferevent_free(wp->event); close(wp->fd); } @@ -810,6 +813,9 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, char *argv0, paneid[16]; const char *ptr; struct termios tio2; +#ifdef HAVE_UTEMPTER + char s[32]; +#endif if (wp->fd != -1) { bufferevent_free(wp->event); @@ -886,6 +892,11 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, fatal("execl failed"); } +#ifdef HAVE_UTEMPTER + xsnprintf(s, sizeof s, "tmux(%lu):%%%u", (long) getpid(), wp->id); + utempter_add_record(wp->fd, s); +#endif + setblocking(wp->fd, 0); wp->event = bufferevent_new(wp->fd, From bf82f15bbad02a8416608e9998cb8e8127806350 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 24 Feb 2014 23:09:19 +0000 Subject: [PATCH 263/949] Missing period. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 05286d3d..c171940f 100644 --- a/configure.ac +++ b/configure.ac @@ -144,7 +144,7 @@ if test "x$found_curses" = xno; then AC_MSG_ERROR("curses not found") fi -# Look for utempter +# Look for utempter. AC_CHECK_HEADER(utempter.h, have_utempter=yes, have_utempter=no) if test "x$have_utempter" = xyes; then AC_DEFINE(HAVE_UTEMPTER) From f3152079e1661897d514a1b2d7ee9ae6a786b821 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 24 Feb 2014 23:11:25 +0000 Subject: [PATCH 264/949] I prefer . here not :. --- window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window.c b/window.c index 70530450..1dd70c44 100644 --- a/window.c +++ b/window.c @@ -893,7 +893,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, } #ifdef HAVE_UTEMPTER - xsnprintf(s, sizeof s, "tmux(%lu):%%%u", (long) getpid(), wp->id); + xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id); utempter_add_record(wp->fd, s); #endif From 225164a9d66bf4dd234faf7cbfc05ded077dd12d Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Tue, 25 Feb 2014 20:23:08 +0000 Subject: [PATCH 265/949] Fix xterm-keys.vim syntax Fix the typo in the xterm-keys.vim file, from Mark Oteiza. --- examples/xterm-keys.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/xterm-keys.vim b/examples/xterm-keys.vim index 907eb110..af44968d 100644 --- a/examples/xterm-keys.vim +++ b/examples/xterm-keys.vim @@ -49,3 +49,4 @@ endfunction if exists('$TMUX') call s:SetXtermCapabilities() +endif From 091cd0109fafe28e13f78690ed7a929a4ef0983b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 26 Feb 2014 13:48:25 +0000 Subject: [PATCH 266/949] Add. --- TODO | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 3f45b62d..04f192b6 100644 --- a/TODO +++ b/TODO @@ -131,5 +131,6 @@ table, first an implicit table in which C-b is the only default binding to a command that says "next key from $othertable" and so on. means -n can go away as well - + * customizable command aliases + * get_cwd for cgywin From 6d9f936ff22e9488bec90e22338a5543fbeb25fc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 26 Feb 2014 21:42:14 +0000 Subject: [PATCH 267/949] Change secondary device attributes response to \033[>84;0;0c which is unique for tmux. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index ce103bd2..ee46c982 100644 --- a/input.c +++ b/input.c @@ -1147,7 +1147,7 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { case 0: - input_reply(ictx, "\033[>0;95;0c"); + input_reply(ictx, "\033[>84;0;0c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); From 0bb9d51965f1920f303f8c9abd8133ce99d5818f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 26 Feb 2014 21:42:59 +0000 Subject: [PATCH 268/949] Don't write into buffer if no arguments, reported by Filipe Rosset. --- cmd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd.c b/cmd.c index 5e6b93aa..a7a71a62 100644 --- a/cmd.c +++ b/cmd.c @@ -138,6 +138,9 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len) size_t arglen; int i; + if (argc == 0) + return (0); + *buf = '\0'; for (i = 0; i < argc; i++) { if (strlcpy(buf, argv[i], len) >= len) From 582c2671ddce5a8cdd6d022c7c67fe683ce71bf1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 26 Feb 2014 21:59:33 +0000 Subject: [PATCH 269/949] Having three *clock* files is ridiculous, remove clock.c. --- Makefile.am | 1 - clock.c | 166 ------------------------------------------------ screen-redraw.c | 2 +- tmux.h | 5 +- window-clock.c | 143 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 143 insertions(+), 174 deletions(-) delete mode 100644 clock.c diff --git a/Makefile.am b/Makefile.am index a9ad5b97..d305db9c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,7 +67,6 @@ dist_tmux_SOURCES = \ attributes.c \ cfg.c \ client.c \ - clock.c \ cmd-attach-session.c \ cmd-bind-key.c \ cmd-break-pane.c \ diff --git a/clock.c b/clock.c deleted file mode 100644 index ec742884..00000000 --- a/clock.c +++ /dev/null @@ -1,166 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include - -#include "tmux.h" - -const char clock_table[14][5][5] = { - { { 1,1,1,1,1 }, /* 0 */ - { 1,0,0,0,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 0,0,0,0,1 }, /* 1 */ - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 2 */ - { 0,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,0 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 3 */ - { 0,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,0,0,0,1 }, /* 4 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 5 */ - { 1,0,0,0,0 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 6 */ - { 1,0,0,0,0 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 7 */ - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 8 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 9 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 0,0,0,0,0 }, /* : */ - { 0,0,1,0,0 }, - { 0,0,0,0,0 }, - { 0,0,1,0,0 }, - { 0,0,0,0,0 } }, - { { 1,1,1,1,1 }, /* A */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* P */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,0 }, - { 1,0,0,0,0 } }, - { { 1,0,0,0,1 }, /* M */ - { 1,1,0,1,1 }, - { 1,0,1,0,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 } }, -}; - -void -clock_draw(struct screen_write_ctx *ctx, int colour, int style) -{ - struct screen *s = ctx->s; - struct grid_cell gc; - char tim[64], *ptr; - time_t t; - struct tm *tm; - u_int i, j, x, y, idx; - - t = time(NULL); - tm = localtime(&t); - if (style == 0) { - strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); - if (tm->tm_hour >= 12) - strlcat(tim, "PM", sizeof tim); - else - strlcat(tim, "AM", sizeof tim); - } else - strftime(tim, sizeof tim, "%H:%M", tm); - - - screen_write_clearscreen(ctx); - - if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { - if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { - x = (screen_size_x(s) / 2) - (strlen(tim) / 2); - y = screen_size_y(s) / 2; - screen_write_cursormove(ctx, x, y); - - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_fg(&gc, colour); - screen_write_puts(ctx, &gc, "%s", tim); - } - return; - } - - x = (screen_size_x(s) / 2) - 3 * strlen(tim); - y = (screen_size_y(s) / 2) - 3; - - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_bg(&gc, colour); - for (ptr = tim; *ptr != '\0'; ptr++) { - if (*ptr >= '0' && *ptr <= '9') - idx = *ptr - '0'; - else if (*ptr == ':') - idx = 10; - else if (*ptr == 'A') - idx = 11; - else if (*ptr == 'P') - idx = 12; - else if (*ptr == 'M') - idx = 13; - else { - x += 6; - continue; - } - - for (j = 0; j < 5; j++) { - for (i = 0; i < 5; i++) { - screen_write_cursormove(ctx, x + i, y + j); - if (clock_table[idx][j][i]) - screen_write_putc(ctx, &gc, ' '); - } - } - x += 6; - } -} diff --git a/screen-redraw.c b/screen-redraw.c index 6db1064b..6438034a 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -381,7 +381,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) for (j = 0; j < 5; j++) { for (i = px; i < px + 5; i++) { tty_cursor(tty, xoff + i, yoff + py + j); - if (clock_table[idx][j][i - px]) + if (window_clock_table[idx][j][i - px]) tty_putc(tty, ' '); } } diff --git a/tmux.h b/tmux.h index ec5cf55f..23b1b468 100644 --- a/tmux.h +++ b/tmux.h @@ -1710,10 +1710,6 @@ char *paste_print(struct paste_buffer *, size_t); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); -/* clock.c */ -extern const char clock_table[14][5][5]; -void clock_draw(struct screen_write_ctx *, int, int); - /* arguments.c */ int args_cmp(struct args_entry *, struct args_entry *); RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); @@ -2219,6 +2215,7 @@ void layout_set_active_changed(struct window *); /* window-clock.c */ extern const struct window_mode window_clock_mode; +extern const char window_clock_table[14][5][5]; /* window-copy.c */ extern const struct window_mode window_copy_mode; diff --git a/window-clock.c b/window-clock.c index 61cf1502..9765b127 100644 --- a/window-clock.c +++ b/window-clock.c @@ -46,6 +46,79 @@ struct window_clock_mode_data { time_t tim; }; +const char window_clock_table[14][5][5] = { + { { 1,1,1,1,1 }, /* 0 */ + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,1 }, /* 1 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 2 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 3 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,0,0,0,1 }, /* 4 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 5 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 6 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 7 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 8 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 9 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,0 }, /* : */ + { 0,0,1,0,0 }, + { 0,0,0,0,0 }, + { 0,0,1,0,0 }, + { 0,0,0,0,0 } }, + { { 1,1,1,1,1 }, /* A */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* P */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,0,0,0,0 } }, + { { 1,0,0,0,1 }, /* M */ + { 1,1,0,1,1 }, + { 1,0,1,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, +}; + struct screen * window_clock_init(struct window_pane *wp) { @@ -114,11 +187,77 @@ window_clock_draw_screen(struct window_pane *wp) struct window_clock_mode_data *data = wp->modedata; struct screen_write_ctx ctx; int colour, style; + struct screen *s = &data->screen; + struct grid_cell gc; + char tim[64], *ptr; + time_t t; + struct tm *tm; + u_int i, j, x, y, idx; colour = options_get_number(&wp->window->options, "clock-mode-colour"); style = options_get_number(&wp->window->options, "clock-mode-style"); - screen_write_start(&ctx, NULL, &data->screen); - clock_draw(&ctx, colour, style); + screen_write_start(&ctx, NULL, s); + + t = time(NULL); + tm = localtime(&t); + if (style == 0) { + strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); + if (tm->tm_hour >= 12) + strlcat(tim, "PM", sizeof tim); + else + strlcat(tim, "AM", sizeof tim); + } else + strftime(tim, sizeof tim, "%H:%M", tm); + + screen_write_clearscreen(&ctx); + + if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { + if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { + x = (screen_size_x(s) / 2) - (strlen(tim) / 2); + y = screen_size_y(s) / 2; + screen_write_cursormove(&ctx, x, y); + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, colour); + screen_write_puts(&ctx, &gc, "%s", tim); + } + + + screen_write_stop(&ctx); + return; + } + + x = (screen_size_x(s) / 2) - 3 * strlen(tim); + y = (screen_size_y(s) / 2) - 3; + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_bg(&gc, colour); + for (ptr = tim; *ptr != '\0'; ptr++) { + if (*ptr >= '0' && *ptr <= '9') + idx = *ptr - '0'; + else if (*ptr == ':') + idx = 10; + else if (*ptr == 'A') + idx = 11; + else if (*ptr == 'P') + idx = 12; + else if (*ptr == 'M') + idx = 13; + else { + x += 6; + continue; + } + + for (j = 0; j < 5; j++) { + for (i = 0; i < 5; i++) { + screen_write_cursormove(&ctx, x + i, y + j); + if (window_clock_table[idx][j][i]) + screen_write_putc(&ctx, &gc, ' '); + } + } + x += 6; + } + screen_write_stop(&ctx); } From 9134e4de1e0782ae4466bf1deb77a3098521b646 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 26 Feb 2014 22:22:07 +0000 Subject: [PATCH 270/949] Add start-of-list, end-of-list, top-line and bottom-line in choice mode, from madmaverick9 at roxxmail dot eu, similar diff a few days earlier from Marcel Partap. --- mode-key.c | 15 +++++++++++++++ tmux.h | 4 ++++ window-choose.c | 23 +++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/mode-key.c b/mode-key.c index 95fad285..5a68bc72 100644 --- a/mode-key.c +++ b/mode-key.c @@ -76,14 +76,18 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { /* Choice keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { { MODEKEYCHOICE_BACKSPACE, "backspace" }, + { MODEKEYCHOICE_BOTTOMLINE, "bottom-line"}, { MODEKEYCHOICE_CANCEL, "cancel" }, { MODEKEYCHOICE_CHOOSE, "choose" }, { MODEKEYCHOICE_DOWN, "down" }, + { MODEKEYCHOICE_ENDOFLIST, "end-of-list"}, { MODEKEYCHOICE_PAGEDOWN, "page-down" }, { MODEKEYCHOICE_PAGEUP, "page-up" }, { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" }, { MODEKEYCHOICE_SCROLLUP, "scroll-up" }, { MODEKEYCHOICE_STARTNUMBERPREFIX, "start-number-prefix" }, + { MODEKEYCHOICE_STARTOFLIST, "start-of-list"}, + { MODEKEYCHOICE_TOPLINE, "top-line"}, { MODEKEYCHOICE_TREE_COLLAPSE, "tree-collapse" }, { MODEKEYCHOICE_TREE_COLLAPSE_ALL, "tree-collapse-all" }, { MODEKEYCHOICE_TREE_EXPAND, "tree-expand" }, @@ -226,6 +230,12 @@ const struct mode_key_entry mode_key_vi_choice[] = { { 'j', 0, MODEKEYCHOICE_DOWN }, { 'k', 0, MODEKEYCHOICE_UP }, { 'q', 0, MODEKEYCHOICE_CANCEL }, + { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, + { 'g', 0, MODEKEYCHOICE_STARTOFLIST }, + { 'H', 0, MODEKEYCHOICE_TOPLINE }, + { 'L', 0, MODEKEYCHOICE_BOTTOMLINE }, + { 'G', 0, MODEKEYCHOICE_ENDOFLIST }, + { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, @@ -372,6 +382,11 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCHOICE_PAGEUP }, + { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, + { '<' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTOFLIST }, + { 'R' | KEYC_ESCAPE, 0, MODEKEYCHOICE_TOPLINE }, + { '>' | KEYC_ESCAPE, 0, MODEKEYCHOICE_ENDOFLIST }, + { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, diff --git a/tmux.h b/tmux.h index 23b1b468..0a6ca4e4 100644 --- a/tmux.h +++ b/tmux.h @@ -517,14 +517,18 @@ enum mode_key_cmd { /* Menu (choice) keys. */ MODEKEYCHOICE_BACKSPACE, + MODEKEYCHOICE_BOTTOMLINE, MODEKEYCHOICE_CANCEL, MODEKEYCHOICE_CHOOSE, MODEKEYCHOICE_DOWN, + MODEKEYCHOICE_ENDOFLIST, MODEKEYCHOICE_PAGEDOWN, MODEKEYCHOICE_PAGEUP, MODEKEYCHOICE_SCROLLDOWN, MODEKEYCHOICE_SCROLLUP, MODEKEYCHOICE_STARTNUMBERPREFIX, + MODEKEYCHOICE_STARTOFLIST, + MODEKEYCHOICE_TOPLINE, MODEKEYCHOICE_TREE_COLLAPSE, MODEKEYCHOICE_TREE_COLLAPSE_ALL, MODEKEYCHOICE_TREE_EXPAND, diff --git a/window-choose.c b/window-choose.c index 7b2b32b9..e7578fe6 100644 --- a/window-choose.c +++ b/window-choose.c @@ -679,6 +679,29 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM, "Goto Item", wp, key); break; + case MODEKEYCHOICE_STARTOFLIST: + data->selected = 0; + data->top = 0; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_TOPLINE: + data->selected = data->top; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_BOTTOMLINE: + data->selected = data->top + screen_size_y(s) - 1; + if (data->selected > items - 1) + data->selected = items - 1; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_ENDOFLIST: + data->selected = items - 1; + if (screen_size_y(s) < items) + data->top = items - screen_size_y(s); + else + data->top = 0; + window_choose_redraw_screen(wp); + break; default: idx = window_choose_index_key(data, key); if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list)) From 5f05c41126c0bf69d557a976935faa416d15371b Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Mon, 3 Mar 2014 21:59:22 +0000 Subject: [PATCH 271/949] www: Add link to CHANGES file for download tarball Make the CHANGES file more prominent when choosing to download the released version. --- www/index.html.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/index.html.in b/www/index.html.in index 5e0276e8..002a473f 100644 --- a/www/index.html.in +++ b/www/index.html.in @@ -32,7 +32,8 @@ background) and reattach them to a different terminal. And do a lot more. See the manual.

-Download tmux %%VERSION%% or +Download tmux %%VERSION%% +(Changelog) or get the development version. tmux is hosted on From b65c72c45c79c3f1fea6446c83f6ac47e813a52a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 5 Mar 2014 23:28:32 +0000 Subject: [PATCH 272/949] Restore SunOS fix, noticed by Greg Onufer. --- server-client.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 07ef5697..4ffcb3a0 100644 --- a/server-client.c +++ b/server-client.c @@ -540,8 +540,18 @@ server_client_check_resize(struct window_pane *wp) ws.ws_col = wp->sx; ws.ws_row = wp->sy; - if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) + 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) +#endif fatal("ioctl failed"); + } wp->flags &= ~PANE_RESIZE; } From b7f6356053638188a162df0d1912a6317e9d593d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Mar 2014 11:25:27 +0000 Subject: [PATCH 273/949] Make session_attached a count and add session_many_attached flag. --- format.c | 6 ++---- resize.c | 2 ++ tmux.1 | 3 ++- tmux.h | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/format.c b/format.c index 10ac613e..497b5b52 100644 --- a/format.c +++ b/format.c @@ -401,10 +401,8 @@ format_session(struct format_tree *ft, struct session *s) *strchr(tim, '\n') = '\0'; format_add(ft, "session_created_string", "%s", tim); - if (s->flags & SESSION_UNATTACHED) - format_add(ft, "session_attached", "%d", 0); - else - format_add(ft, "session_attached", "%d", 1); + format_add(ft, "session_attached", "%u", s->attached); + format_add(ft, "session_many_attached", "%u", s->attached > 1); } /* Set default format keys for a client. */ diff --git a/resize.c b/resize.c index 8d0bd275..b3b031c0 100644 --- a/resize.c +++ b/resize.c @@ -55,6 +55,7 @@ recalculate_sizes(void) RB_FOREACH(s, sessions, &sessions) { has_status = options_get_number(&s->options, "status"); + s->attached = 0; ssx = ssy = UINT_MAX; for (j = 0; j < ARRAY_LENGTH(&clients); j++) { c = ARRAY_ITEM(&clients, j); @@ -69,6 +70,7 @@ recalculate_sizes(void) ssy = c->tty.sy - 1; else if (c->tty.sy < ssy) ssy = c->tty.sy; + s->attached++; } } if (ssx == UINT_MAX || ssy == UINT_MAX) { diff --git a/tmux.1 b/tmux.1 index 51d927a5..c9e5815a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3141,13 +3141,14 @@ The following variables are available, where appropriate: .It Li "saved_cursor_y" Ta "" Ta "Saved cursor Y in pane" .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 "session_attached" Ta "" Ta "1 if session attached" +.It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_created" Ta "" Ta "Integer time session created" .It Li "session_created_string" Ta "" Ta "String time session created" .It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_height" Ta "" Ta "Height of session" .It Li "session_id" Ta "" Ta "Unique session ID" +.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" diff --git a/tmux.h b/tmux.h index 0a6ca4e4..fb2fb940 100644 --- a/tmux.h +++ b/tmux.h @@ -1083,6 +1083,8 @@ struct session { #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; + u_int attached; + struct termios *tio; struct environ environ; From 8c0edcbfa3bd78bb8adaba13aad29a06d8988de8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Mar 2014 11:50:07 +0000 Subject: [PATCH 274/949] Add setb -a to append and a copy mode append command, from J Raynor with minor changes. --- cmd-set-buffer.c | 67 +++++++++++++++++++++++++++++------------------- mode-key.c | 2 ++ paste.c | 2 +- tmux.1 | 5 ++++ tmux.h | 1 + window-copy.c | 48 ++++++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 28 deletions(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index fade4fe3..30a137cd 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -24,15 +24,15 @@ #include "tmux.h" /* - * Add or set a paste buffer. + * Add, set, or append to a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", - "b:", 1, 1, - CMD_BUFFER_USAGE " data", + "ab:", 1, 1, + "[-a] " CMD_BUFFER_USAGE " data", 0, NULL, cmd_set_buffer_exec @@ -41,35 +41,48 @@ const struct cmd_entry cmd_set_buffer_entry = { enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - u_int limit; - char *pdata, *cause; - size_t psize; - int buffer; + struct args *args = self->args; + struct paste_buffer *pb; + u_int limit; + char *pdata, *cause; + size_t psize, newsize; + int buffer; limit = options_get_number(&global_options, "buffer-limit"); - pdata = xstrdup(args->argv[0]); - psize = strlen(pdata); + psize = 0; + pdata = NULL; - if (!args_has(args, 'b')) { + if (args_has(args, 'b')) { + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + cmdq_error(cmdq, "buffer %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + pb = paste_get_index(&global_buffers, buffer); + if (pb == NULL) { + cmdq_error(cmdq, "no buffer %d", buffer); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 'a')) { + psize = pb->size; + pdata = xmalloc(psize); + memcpy(pdata, pb->data, psize); + } + } else + buffer = -1; + + newsize = strlen(args->argv[0]); + + pdata = xrealloc(pdata, 1, psize + newsize); + memcpy(pdata + psize, args->argv[0], newsize); + psize += newsize; + + if (buffer == -1) paste_add(&global_buffers, pdata, psize, limit); - return (CMD_RETURN_NORMAL); - } - - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - free(pdata); - return (CMD_RETURN_ERROR); - } - - if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); - free(pdata); - return (CMD_RETURN_ERROR); - } + else + paste_replace(&global_buffers, buffer, pdata, psize); return (CMD_RETURN_NORMAL); } diff --git a/mode-key.c b/mode-key.c index 5a68bc72..57be2d81 100644 --- a/mode-key.c +++ b/mode-key.c @@ -100,6 +100,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { /* Copy keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { + { MODEKEYCOPY_APPENDSELECTION, "append-selection" }, { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" }, { MODEKEYCOPY_BOTTOMLINE, "bottom-line" }, { MODEKEYCOPY_CANCEL, "cancel" }, @@ -272,6 +273,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { '9', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { ':', 0, MODEKEYCOPY_GOTOLINE }, { '?', 0, MODEKEYCOPY_SEARCHUP }, + { 'A', 0, MODEKEYCOPY_APPENDSELECTION }, { 'B', 0, MODEKEYCOPY_PREVIOUSSPACE }, { 'D', 0, MODEKEYCOPY_COPYENDOFLINE }, { 'E', 0, MODEKEYCOPY_NEXTSPACEEND }, diff --git a/paste.c b/paste.c index bc9b4686..28f12306 100644 --- a/paste.c +++ b/paste.c @@ -171,7 +171,7 @@ paste_print(struct paste_buffer *pb, size_t width) /* Paste into a window pane, filtering '\n' according to separator. */ void -paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, +paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, const char *sep, int bracket) { const char *data = pb->data, *end = data + pb->size, *lf; diff --git a/tmux.1 b/tmux.1 index c9e5815a..4d090522 100644 --- a/tmux.1 +++ b/tmux.1 @@ -855,6 +855,7 @@ option). The following keys are supported as appropriate for the mode: .Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" +.It Li "Append selection" Ta "A" Ta "" .It Li "Back to indentation" Ta "^" Ta "M-m" .It Li "Bottom of history" Ta "G" Ta "M-<" .It Li "Clear selection" Ta "Escape" Ta "C-g" @@ -3548,12 +3549,16 @@ The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer +.Op Fl a .Op Fl b Ar buffer-index .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . +The +.Fl a +option appends to rather than overwriting the buffer. .It Xo Ic show-buffer .Op Fl b Ar buffer-index .Xc diff --git a/tmux.h b/tmux.h index fb2fb940..68790a05 100644 --- a/tmux.h +++ b/tmux.h @@ -537,6 +537,7 @@ enum mode_key_cmd { MODEKEYCHOICE_UP, /* Copy keys. */ + MODEKEYCOPY_APPENDSELECTION, MODEKEYCOPY_BACKTOINDENTATION, MODEKEYCOPY_BOTTOMLINE, MODEKEYCOPY_CANCEL, diff --git a/window-copy.c b/window-copy.c index 527c95c2..e3164f6f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -58,6 +58,7 @@ void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); void window_copy_copy_pipe( struct window_pane *, struct session *, int, const char *); void window_copy_copy_selection(struct window_pane *, int); +void window_copy_append_selection(struct window_pane *, int); void window_copy_clear_selection(struct window_pane *); void window_copy_copy_line( struct window_pane *, char **, size_t *, u_int, u_int, u_int); @@ -414,6 +415,13 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) cmd = mode_key_lookup(&data->mdata, key, &arg); switch (cmd) { + case MODEKEYCOPY_APPENDSELECTION: + if (sess != NULL) { + window_copy_append_selection(wp, data->numprefix); + window_pane_reset_mode(wp); + return; + } + break; case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); return; @@ -1491,6 +1499,46 @@ window_copy_copy_selection(struct window_pane *wp, int idx) window_copy_copy_buffer(wp, idx, buf, len); } +void +window_copy_append_selection(struct window_pane *wp, int idx) +{ + char *buf; + struct paste_buffer *pb; + size_t len; + u_int limit; + struct screen_write_ctx ctx; + + buf = window_copy_get_selection(wp, &len); + if (buf == NULL) + return; + + if (options_get_number(&global_options, "set-clipboard")) { + screen_write_start(&ctx, wp, NULL); + screen_write_setselection(&ctx, buf, len); + screen_write_stop(&ctx); + } + + if (idx == -1) + idx = 0; + + if (idx == 0 && paste_get_top(&global_buffers) == NULL) { + limit = options_get_number(&global_options, "buffer-limit"); + paste_add(&global_buffers, buf, len, limit); + return; + } + + pb = paste_get_index(&global_buffers, idx); + if (pb != NULL) { + buf = xrealloc(buf, 1, len + pb->size); + memmove(buf + pb->size, buf, len); + memcpy(buf, pb->data, pb->size); + len += pb->size; + } + + if (paste_replace(&global_buffers, idx, buf, len) != 0) + free(buf); +} + void window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, u_int sx, u_int ex) From f15fcb6a1d79b67db0aa65a8c0a40c1c15e49720 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Mar 2014 11:53:46 +0000 Subject: [PATCH 275/949] Use hex constants rather than shifts for mouse events and flags, pointed out by Marcel Partap. --- tmux.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tmux.h b/tmux.h index 68790a05..28351d0f 100644 --- a/tmux.h +++ b/tmux.h @@ -1130,14 +1130,14 @@ LIST_HEAD(tty_terms, tty_term); #define MOUSE_WHEEL_DOWN 1 /* Mouse events. */ -#define MOUSE_EVENT_DOWN (1 << 0) -#define MOUSE_EVENT_DRAG (1 << 1) -#define MOUSE_EVENT_UP (1 << 2) -#define MOUSE_EVENT_CLICK (1 << 3) -#define MOUSE_EVENT_WHEEL (1 << 4) +#define MOUSE_EVENT_DOWN 0x1 +#define MOUSE_EVENT_DRAG 0x2 +#define MOUSE_EVENT_UP 0x4 +#define MOUSE_EVENT_CLICK 0x8 +#define MOUSE_EVENT_WHEEL 0x10 /* Mouse flags. */ -#define MOUSE_RESIZE_PANE (1 << 0) +#define MOUSE_RESIZE_PANE 0x1 /* * Mouse input. When sent by xterm: From 23e944c91d45e49a3c924712c234e7db6891ad98 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Mar 2014 12:00:30 +0000 Subject: [PATCH 276/949] Add names for mouse button bits rather than using magic numbers, from Marcel Partap. --- tmux.h | 12 ++++++++++-- tty-keys.c | 10 +++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tmux.h b/tmux.h index 28351d0f..fe6bad2e 100644 --- a/tmux.h +++ b/tmux.h @@ -1125,18 +1125,26 @@ struct tty_term { }; LIST_HEAD(tty_terms, tty_term); +/* Mouse button masks. */ +#define MOUSE_MASK_BUTTONS 3 +#define MOUSE_MASK_SHIFT 4 +#define MOUSE_MASK_META 8 +#define MOUSE_MASK_CTRL 16 +#define MOUSE_MASK_DRAG 32 +#define MOUSE_MASK_WHEEL 64 + /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 #define MOUSE_WHEEL_DOWN 1 -/* Mouse events. */ +/* Mouse event bits. */ #define MOUSE_EVENT_DOWN 0x1 #define MOUSE_EVENT_DRAG 0x2 #define MOUSE_EVENT_UP 0x4 #define MOUSE_EVENT_CLICK 0x8 #define MOUSE_EVENT_WHEEL 0x10 -/* Mouse flags. */ +/* Mouse flag bits. */ #define MOUSE_RESIZE_PANE 0x1 /* diff --git a/tty-keys.c b/tty-keys.c index 7fb91a84..9dbe4503 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -748,21 +748,21 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->sgr_rel = sgr_rel; m->x = x; m->y = y; - if (b & 64) { /* wheel button */ - b &= 3; + if (b & MOUSE_MASK_WHEEL) { + b &= MOUSE_MASK_BUTTONS; if (b == 0) m->wheel = MOUSE_WHEEL_UP; else if (b == 1) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; - } else if ((b & 3) == 3) { + } else if ((b & MOUSE_MASK_BUTTONS) == 3) { if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) { m->event = MOUSE_EVENT_CLICK; } else m->event = MOUSE_EVENT_DRAG; m->event |= MOUSE_EVENT_UP; } else { - if (b & 32) /* drag motion */ + if (b & MOUSE_MASK_DRAG) m->event = MOUSE_EVENT_DRAG; else { if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y) @@ -773,7 +773,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->sy = y; m->event = MOUSE_EVENT_DOWN; } - m->button = (b & 3); + m->button = (b & MOUSE_MASK_BUTTONS); } return (0); From cbd360b7dd1f2ca1a3208d3e19af84e0e536f6b9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Mar 2014 12:46:03 +0000 Subject: [PATCH 277/949] Spaces to tabs. --- server-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 4ffcb3a0..98d1e043 100644 --- a/server-client.c +++ b/server-client.c @@ -547,7 +547,7 @@ server_client_check_resize(struct window_pane *wp) * 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) #endif fatal("ioctl failed"); From 78e783e7863eb33981da4a5ad48dd9e2aa2b08dd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Mar 2014 13:01:51 +0000 Subject: [PATCH 278/949] Don't segfaut when the parent of the layout cell is NULL, from Thomas Adam. --- window.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/window.c b/window.c index 1dd70c44..9a26b904 100644 --- a/window.c +++ b/window.c @@ -420,10 +420,15 @@ window_pane_active_set(struct window_pane *wp, struct window_pane *nextwp) void window_pane_active_lost(struct window_pane *wp, struct window_pane *nextwp) { - struct layout_cell *lc, *lc2; + struct layout_cell *lc, *lc2, *lcparent; + + /* Get the parent cell. */ + lcparent = nextwp->layout_cell->parent; + if (lcparent == NULL) + return; /* Save the target pane in its parent. */ - nextwp->layout_cell->parent->lastwp = nextwp; + lcparent->lastwp = nextwp; /* * Save the source pane in all of its parents up to, but not including, @@ -432,8 +437,7 @@ window_pane_active_lost(struct window_pane *wp, struct window_pane *nextwp) if (wp == NULL) return; for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { - lc2 = nextwp->layout_cell->parent; - for (; lc2 != NULL; lc2 = lc2->parent) { + for (lc2 = lcparent; lc2 != NULL; lc2 = lc2->parent) { if (lc == lc2) return; } From 683ca270d4b3ffc64a0d9cb86161dfb48a7fe0ca Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 7 Mar 2014 15:37:01 +0000 Subject: [PATCH 279/949] Make message-limit a server option. --- options-table.c | 14 +++++++------- status.c | 6 +----- tmux.1 | 10 +++++----- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/options-table.c b/options-table.c index 64d3edcd..7b09a687 100644 --- a/options-table.c +++ b/options-table.c @@ -80,6 +80,13 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "message-limit", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 100 + }, + { .name = "quiet", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ @@ -234,13 +241,6 @@ const struct options_table_entry session_options_table[] = { .style = "message-style" }, - { .name = "message-limit", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 20 - }, - { .name = "message-style", .type = OPTIONS_TABLE_STYLE, .default_str = "bg=yellow,fg=black" diff --git a/status.c b/status.c index 9967dc68..e8bbf4c1 100644 --- a/status.c +++ b/status.c @@ -655,7 +655,6 @@ void printflike2 status_message_set(struct client *c, const char *fmt, ...) { struct timeval tv; - struct session *s = c->session; struct message_entry *msg; va_list ap; int delay; @@ -673,10 +672,7 @@ status_message_set(struct client *c, const char *fmt, ...) msg->msg_time = time(NULL); msg->msg = xstrdup(c->message_string); - if (s == NULL) - limit = 0; - else - limit = options_get_number(&s->options, "message-limit"); + limit = options_get_number(&global_options, "message-limit"); if (ARRAY_LENGTH(&c->message_log) > limit) { limit = ARRAY_LENGTH(&c->message_log) - limit; for (i = 0; i < limit; i++) { diff --git a/tmux.1 b/tmux.1 index 4d090522..2e17c655 100644 --- a/tmux.1 +++ b/tmux.1 @@ -782,7 +782,7 @@ Show client messages or server information. Any messages displayed on the status line are saved in a per-client message log, up to a maximum of the limit set by the .Ar message-limit -session option for the session attached to that client. +server option. With .Fl t , display the log for @@ -2138,6 +2138,10 @@ passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. +.It Ic message-limit Ar number +Set the number of error or information messages to save in the message log for +each client. +The default is 100. .It Xo Ic quiet .Op Ic on | off .Xc @@ -2359,10 +2363,6 @@ With the flag to the .Ic set-option command the new style is added otherwise the existing style is replaced. -.It Ic message-limit Ar number -Set the number of error or information messages to save in the message log for -each client. -The default is 20. .It Ic message-style Ar style Set status line message style. For how to specify From ebe7bd7c8b4f3d703064b3d8b2b5ad8dd01e9e62 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 7 Mar 2014 15:49:09 +0000 Subject: [PATCH 280/949] Remove unused log functions. --- log.c | 41 +---------------------------------------- tmux.h | 3 --- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/log.c b/log.c index dbf9ee15..71ceddee 100644 --- a/log.c +++ b/log.c @@ -41,7 +41,7 @@ __dead void log_vfatal(const char *, va_list); void log_event_cb(unused int severity, const char *msg) { - log_warnx("%s", msg); + log_debug("%s", msg); } /* Open logging to file. */ @@ -86,45 +86,6 @@ log_vwrite(const char *msg, va_list ap) free(fmt); } -/* Log a warning with error string. */ -void printflike1 -log_warn(const char *msg, ...) -{ - va_list ap; - char *fmt; - - va_start(ap, msg); - if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1) - exit(1); - log_vwrite(fmt, ap); - free(fmt); - va_end(ap); -} - -/* Log a warning. */ -void printflike1 -log_warnx(const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); -} - -/* Log an informational message. */ -void printflike1 -log_info(const char *msg, ...) -{ - va_list ap; - - if (log_level > -1) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } -} - /* Log a debug message. */ void printflike1 log_debug(const char *msg, ...) diff --git a/tmux.h b/tmux.h index fe6bad2e..638ee3b2 100644 --- a/tmux.h +++ b/tmux.h @@ -2342,9 +2342,6 @@ struct event_base *osdep_event_init(void); /* log.c */ void log_open(int, const char *); void log_close(void); -void printflike1 log_warn(const char *, ...); -void printflike1 log_warnx(const char *, ...); -void printflike1 log_info(const char *, ...); void printflike1 log_debug(const char *, ...); void printflike1 log_debug2(const char *, ...); __dead void printflike1 log_fatal(const char *, ...); From c5a30513edc6f108b6387ee9cf6f1cb9dc7a674c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 7 Mar 2014 15:51:27 +0000 Subject: [PATCH 281/949] GRID_DEBUG is no longer needed. --- grid-view.c | 22 ---------------------- grid.c | 18 ------------------ tmux.h | 10 ---------- 3 files changed, 50 deletions(-) diff --git a/grid-view.c b/grid-view.c index 7ef443a3..13385cd1 100644 --- a/grid-view.c +++ b/grid-view.c @@ -59,8 +59,6 @@ grid_view_clear_history(struct grid *gd) struct grid_line *gl; u_int yy, last; - GRID_DEBUG(gd, ""); - /* Find the last used line. */ last = 0; for (yy = 0; yy < gd->sy; yy++) { @@ -82,8 +80,6 @@ grid_view_clear_history(struct grid *gd) void grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -94,8 +90,6 @@ grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) void grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) { - GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); - if (gd->flags & GRID_HISTORY) { grid_collect_history(gd); if (rupper == 0 && rlower == gd->sy - 1) @@ -116,8 +110,6 @@ grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) void grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower) { - GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); - rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); @@ -130,8 +122,6 @@ grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); @@ -145,8 +135,6 @@ grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) { u_int ny2; - GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); - rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); @@ -162,8 +150,6 @@ grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); @@ -178,8 +164,6 @@ grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) { u_int ny2; - GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); - rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); @@ -195,8 +179,6 @@ grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -214,8 +196,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -229,8 +209,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) char * grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) { - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); diff --git a/grid.c b/grid.c index 52d6f6bc..83270754 100644 --- a/grid.c +++ b/grid.c @@ -151,8 +151,6 @@ grid_collect_history(struct grid *gd) { u_int yy; - GRID_DEBUG(gd, ""); - if (gd->hsize < gd->hlimit) return; @@ -173,8 +171,6 @@ grid_scroll_history(struct grid *gd) { u_int yy; - GRID_DEBUG(gd, ""); - yy = gd->hsize + gd->sy; gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); @@ -189,8 +185,6 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) struct grid_line *gl_history, *gl_upper, *gl_lower; u_int yy; - GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower); - /* Create a space for a new line. */ yy = gd->hsize + gd->sy; gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); @@ -282,8 +276,6 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { u_int xx, yy; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); - if (nx == 0 || ny == 0) return; @@ -319,8 +311,6 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) struct grid_line *gl; u_int yy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - if (ny == 0) return; @@ -342,8 +332,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) { u_int yy; - GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); - if (ny == 0 || py == dy) return; @@ -381,8 +369,6 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) struct grid_line *gl; u_int xx; - GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); - if (nx == 0 || px == dx) return; @@ -592,8 +578,6 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int xx; const struct grid_line *gl; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - if (lastgc != NULL && *lastgc == NULL) { memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); *lastgc = &lastgc1; @@ -661,8 +645,6 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, struct grid_line *dstl, *srcl; u_int yy; - GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); - if (dy + ny > dst->hsize + dst->sy) ny = dst->hsize + dst->sy - dy; if (sy + ny > src->hsize + src->sy) diff --git a/tmux.h b/tmux.h index 638ee3b2..a9a1e088 100644 --- a/tmux.h +++ b/tmux.h @@ -660,16 +660,6 @@ struct utf8_data { u_int width; }; -/* Grid output. */ -#if defined(DEBUG) && \ - ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ - (defined(__GNUC__) && __GNUC__ >= 3)) -#define GRID_DEBUG(gd, fmt, ...) log_debug2("%s: (sx=%u, sy=%u, hsize=%u) " \ - fmt, __func__, (gd)->sx, (gd)->sy, (gd)->hsize, ## __VA_ARGS__) -#else -#define GRID_DEBUG(...) -#endif - /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 #define GRID_ATTR_DIM 0x2 From b1a87b2ee49028f7a35bc39db47833a82cd74e38 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 7 Mar 2014 16:05:29 +0000 Subject: [PATCH 282/949] Remove log_debug2 as well and simplify log.c. --- input-keys.c | 6 ++-- log.c | 77 ++++++++++++++-------------------------------------- tmux.c | 2 +- tmux.h | 3 +- 4 files changed, 26 insertions(+), 62 deletions(-) diff --git a/input-keys.c b/input-keys.c index 7582a638..46c37d0b 100644 --- a/input-keys.c +++ b/input-keys.c @@ -142,7 +142,7 @@ input_key(struct window_pane *wp, int key) char *out; u_char ch; - log_debug2("writing key 0x%x", key); + log_debug("writing key 0x%x", key); /* * If this is a normal 7-bit key, just send it, with a leading escape @@ -185,11 +185,11 @@ input_key(struct window_pane *wp, int key) break; } if (i == nitems(input_keys)) { - log_debug2("key 0x%x missing", key); + log_debug("key 0x%x missing", key); return; } dlen = strlen(ike->data); - log_debug2("found key 0x%x: \"%s\"", key, ike->data); + log_debug("found key 0x%x: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) diff --git a/log.c b/log.c index 71ceddee..7ef70ab6 100644 --- a/log.c +++ b/log.c @@ -22,20 +22,13 @@ #include #include #include -#include -#include #include "tmux.h" -/* Log file, if needed. */ -FILE *log_file; +FILE *log_file; -/* Debug level. */ -int log_level = 0; - -void log_event_cb(int, const char *); -void log_vwrite(const char *, va_list); -__dead void log_vfatal(const char *, va_list); +void log_event_cb(int, const char *); +void log_vwrite(const char *, va_list); /* Log callback for libevent. */ void @@ -46,12 +39,11 @@ log_event_cb(unused int severity, const char *msg) /* Open logging to file. */ void -log_open(int level, const char *path) +log_open(const char *path) { log_file = fopen(path, "w"); if (log_file == NULL) return; - log_level = level; setlinebuf(log_file); event_set_log_callback(log_event_cb); @@ -65,6 +57,7 @@ log_close(void) { if (log_file != NULL) fclose(log_file); + log_file = NULL; event_set_log_callback(NULL); } @@ -92,63 +85,35 @@ log_debug(const char *msg, ...) { va_list ap; - if (log_level > 0) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } + va_start(ap, msg); + log_vwrite(msg, ap); + va_end(ap); } -/* Log a debug message at level 2. */ -void printflike1 -log_debug2(const char *msg, ...) -{ - va_list ap; - - if (log_level > 1) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } -} - -/* Log a critical error, with error string if necessary, and die. */ -__dead void -log_vfatal(const char *msg, va_list ap) -{ - char *fmt; - - if (errno != 0) { - if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) - exit(1); - log_vwrite(fmt, ap); - } else { - if (asprintf(&fmt, "fatal: %s", msg) == -1) - exit(1); - log_vwrite(fmt, ap); - } - free(fmt); - - exit(1); -} - -/* Log a critical error, with error string, and die. */ +/* Log a critical error with error string and die. */ __dead void printflike1 log_fatal(const char *msg, ...) { - va_list ap; + char *fmt; + va_list ap; va_start(ap, msg); - log_vfatal(msg, ap); + if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) + exit(1); + log_vwrite(fmt, ap); + exit(1); } /* Log a critical error and die. */ __dead void printflike1 log_fatalx(const char *msg, ...) { - va_list ap; + char *fmt; + va_list ap; - errno = 0; va_start(ap, msg); - log_vfatal(msg, ap); + if (asprintf(&fmt, "fatal: %s", msg) == -1) + exit(1); + log_vwrite(fmt, ap); + exit(1); } diff --git a/tmux.c b/tmux.c index a804e6f7..7374dd5d 100644 --- a/tmux.c +++ b/tmux.c @@ -73,7 +73,7 @@ logfile(const char *name) if (debug_level > 0) { xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); - log_open(debug_level, path); + log_open(path); free(path); } } diff --git a/tmux.h b/tmux.h index a9a1e088..5aac390c 100644 --- a/tmux.h +++ b/tmux.h @@ -2330,10 +2330,9 @@ char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); /* log.c */ -void log_open(int, const char *); +void log_open(const char *); void log_close(void); void printflike1 log_debug(const char *, ...); -void printflike1 log_debug2(const char *, ...); __dead void printflike1 log_fatal(const char *, ...); __dead void printflike1 log_fatalx(const char *, ...); From 3625bcba24b2cfeaa2c3d1728933dcc9c04ff4bc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 8 Mar 2014 08:44:24 +0000 Subject: [PATCH 283/949] Add osdep-cgywin.c as a copy of osdep-linux.c, from J Raynor. --- TODO | 2 -- configure.ac | 4 +++ osdep-cygwin.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 osdep-cygwin.c diff --git a/TODO b/TODO index 04f192b6..540a6808 100644 --- a/TODO +++ b/TODO @@ -132,5 +132,3 @@ binding to a command that says "next key from $othertable" and so on. means -n can go away as well * customizable command aliases - * get_cwd for cgywin - diff --git a/configure.ac b/configure.ac index c171940f..a23e3aea 100644 --- a/configure.ac +++ b/configure.ac @@ -501,6 +501,10 @@ case "$host_os" in AC_MSG_RESULT(hpux) PLATFORM=hpux ;; + *cygwin*) + AC_MSG_RESULT(cygwin) + PLATFORM=cygwin + ;; *) AC_MSG_RESULT(unknown) PLATFORM=unknown diff --git a/osdep-cygwin.c b/osdep-cygwin.c new file mode 100644 index 00000000..01d2d0c2 --- /dev/null +++ b/osdep-cygwin.c @@ -0,0 +1,88 @@ +/* $Id$ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "tmux.h" + +char * +osdep_get_name(int fd, unused char *tty) +{ + FILE *f; + char *path, *buf; + size_t len; + int ch; + pid_t pgrp; + + if ((pgrp = tcgetpgrp(fd)) == -1) + return (NULL); + + xasprintf(&path, "/proc/%lld/cmdline", (long long) pgrp); + if ((f = fopen(path, "r")) == NULL) { + free(path); + return (NULL); + } + free(path); + + len = 0; + buf = NULL; + while ((ch = fgetc(f)) != EOF) { + if (ch == '\0') + break; + buf = xrealloc(buf, 1, len + 2); + buf[len++] = ch; + } + if (buf != NULL) + buf[len] = '\0'; + + fclose(f); + return (buf); +} + +char * +osdep_get_cwd(int fd) +{ + static char target[MAXPATHLEN + 1]; + char *path; + pid_t pgrp; + ssize_t n; + + if ((pgrp = tcgetpgrp(fd)) == -1) + return (NULL); + + xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); + n = readlink(path, target, MAXPATHLEN); + free(path); + if (n > 0) { + target[n] = '\0'; + return (target); + } + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} From 9880114aff0b48649f00266125da1dc3c94a70eb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 8 Mar 2014 08:53:48 +0000 Subject: [PATCH 284/949] Make -a append to top buffer if -b is not specified. --- cmd-set-buffer.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 30a137cd..0e754bb6 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -53,6 +53,9 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) psize = 0; pdata = NULL; + pb = NULL; + buffer = -1; + if (args_has(args, 'b')) { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { @@ -65,13 +68,17 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } - if (args_has(args, 'a')) { - psize = pb->size; - pdata = xmalloc(psize); - memcpy(pdata, pb->data, psize); - } - } else - buffer = -1; + } else if (args_has(args, 'a')) { + pb = paste_get_top(&global_buffers); + if (pb != NULL) + buffer = 0; + } + + if (args_has(args, 'a') && pb != NULL) { + psize = pb->size; + pdata = xmalloc(psize); + memcpy(pdata, pb->data, psize); + } newsize = strlen(args->argv[0]); From 7019f77c05f45ea9267f7e768d0abb0b6a928a25 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 8 Mar 2014 16:27:45 +0000 Subject: [PATCH 285/949] In four byte UTF-8 sequences, only three bits of the first byte should be used. Fix from Koga Osamu. --- utf8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utf8.c b/utf8.c index 63723d7f..5babcb3b 100644 --- a/utf8.c +++ b/utf8.c @@ -313,7 +313,7 @@ utf8_combine(const struct utf8_data *utf8data) value = utf8data->data[3] & 0x3f; value |= (utf8data->data[2] & 0x3f) << 6; value |= (utf8data->data[1] & 0x3f) << 12; - value |= (utf8data->data[0] & 0x3f) << 18; + value |= (utf8data->data[0] & 0x07) << 18; break; } return (value); From 26c42ad1e4bf8db443c5e2cbb14c0b82329a6137 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 24 Mar 2014 12:00:15 +0000 Subject: [PATCH 286/949] Don't crash with a zero-length argument to setb, from J Raynor. --- cmd-set-buffer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 0e754bb6..e7f9b95d 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -56,6 +56,9 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) pb = NULL; buffer = -1; + if ((newsize = strlen(args->argv[0])) == 0) + return (CMD_RETURN_NORMAL); + if (args_has(args, 'b')) { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { @@ -80,8 +83,6 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) memcpy(pdata, pb->data, psize); } - newsize = strlen(args->argv[0]); - pdata = xrealloc(pdata, 1, psize + newsize); memcpy(pdata + psize, args->argv[0], newsize); psize += newsize; From 04f469a3245c528abc7865841d51bc0d222a94f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:32:00 +0000 Subject: [PATCH 287/949] Change secondary device attributes response to \033[>84;0;0c which is unique for tmux. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 58064ddd..40c9d225 100644 --- a/input.c +++ b/input.c @@ -1147,7 +1147,7 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { case 0: - input_reply(ictx, "\033[>0;95;0c"); + input_reply(ictx, "\033[>84;0;0c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); From 18cb13521840f678290b0a78d4e058c92795fabb Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:32:31 +0000 Subject: [PATCH 288/949] Don't write into buffer if no arguments, reported by Filipe Rosset. --- cmd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd.c b/cmd.c index fe55109b..62f4c73d 100644 --- a/cmd.c +++ b/cmd.c @@ -139,6 +139,9 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len) size_t arglen; int i; + if (argc == 0) + return (0); + *buf = '\0'; for (i = 0; i < argc; i++) { if (strlcpy(buf, argv[i], len) >= len) From 175f215187b1c978ca4cc4988a78d067122e2b0c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:34:08 +0000 Subject: [PATCH 289/949] Having three *clock* files is ridiculous, remove clock.c. --- Makefile | 1 - clock.c | 166 ------------------------------------------------ screen-redraw.c | 2 +- tmux.h | 5 +- window-clock.c | 143 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 143 insertions(+), 174 deletions(-) delete mode 100644 clock.c diff --git a/Makefile b/Makefile index e566bb2a..63363f51 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,6 @@ SRCS= arguments.c \ attributes.c \ cfg.c \ client.c \ - clock.c \ cmd-attach-session.c \ cmd-bind-key.c \ cmd-break-pane.c \ diff --git a/clock.c b/clock.c deleted file mode 100644 index 283a4a1e..00000000 --- a/clock.c +++ /dev/null @@ -1,166 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include - -#include "tmux.h" - -const char clock_table[14][5][5] = { - { { 1,1,1,1,1 }, /* 0 */ - { 1,0,0,0,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 0,0,0,0,1 }, /* 1 */ - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 2 */ - { 0,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,0 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 3 */ - { 0,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,0,0,0,1 }, /* 4 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 5 */ - { 1,0,0,0,0 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 6 */ - { 1,0,0,0,0 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 7 */ - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 8 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 9 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 0,0,0,0,0 }, /* : */ - { 0,0,1,0,0 }, - { 0,0,0,0,0 }, - { 0,0,1,0,0 }, - { 0,0,0,0,0 } }, - { { 1,1,1,1,1 }, /* A */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* P */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,0 }, - { 1,0,0,0,0 } }, - { { 1,0,0,0,1 }, /* M */ - { 1,1,0,1,1 }, - { 1,0,1,0,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 } }, -}; - -void -clock_draw(struct screen_write_ctx *ctx, int colour, int style) -{ - struct screen *s = ctx->s; - struct grid_cell gc; - char tim[64], *ptr; - time_t t; - struct tm *tm; - u_int i, j, x, y, idx; - - t = time(NULL); - tm = localtime(&t); - if (style == 0) { - strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); - if (tm->tm_hour >= 12) - strlcat(tim, "PM", sizeof tim); - else - strlcat(tim, "AM", sizeof tim); - } else - strftime(tim, sizeof tim, "%H:%M", tm); - - - screen_write_clearscreen(ctx); - - if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { - if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { - x = (screen_size_x(s) / 2) - (strlen(tim) / 2); - y = screen_size_y(s) / 2; - screen_write_cursormove(ctx, x, y); - - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_fg(&gc, colour); - screen_write_puts(ctx, &gc, "%s", tim); - } - return; - } - - x = (screen_size_x(s) / 2) - 3 * strlen(tim); - y = (screen_size_y(s) / 2) - 3; - - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_bg(&gc, colour); - for (ptr = tim; *ptr != '\0'; ptr++) { - if (*ptr >= '0' && *ptr <= '9') - idx = *ptr - '0'; - else if (*ptr == ':') - idx = 10; - else if (*ptr == 'A') - idx = 11; - else if (*ptr == 'P') - idx = 12; - else if (*ptr == 'M') - idx = 13; - else { - x += 6; - continue; - } - - for (j = 0; j < 5; j++) { - for (i = 0; i < 5; i++) { - screen_write_cursormove(ctx, x + i, y + j); - if (clock_table[idx][j][i]) - screen_write_putc(ctx, &gc, ' '); - } - } - x += 6; - } -} diff --git a/screen-redraw.c b/screen-redraw.c index 3a082cbf..a7f713a5 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -381,7 +381,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) for (j = 0; j < 5; j++) { for (i = px; i < px + 5; i++) { tty_cursor(tty, xoff + i, yoff + py + j); - if (clock_table[idx][j][i - px]) + if (window_clock_table[idx][j][i - px]) tty_putc(tty, ' '); } } diff --git a/tmux.h b/tmux.h index 5801b35e..9900489e 100644 --- a/tmux.h +++ b/tmux.h @@ -1713,10 +1713,6 @@ char *paste_print(struct paste_buffer *, size_t); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); -/* clock.c */ -extern const char clock_table[14][5][5]; -void clock_draw(struct screen_write_ctx *, int, int); - /* arguments.c */ int args_cmp(struct args_entry *, struct args_entry *); RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); @@ -2222,6 +2218,7 @@ void layout_set_active_changed(struct window *); /* window-clock.c */ extern const struct window_mode window_clock_mode; +extern const char window_clock_table[14][5][5]; /* window-copy.c */ extern const struct window_mode window_copy_mode; diff --git a/window-clock.c b/window-clock.c index 8ec1671e..ede8df5b 100644 --- a/window-clock.c +++ b/window-clock.c @@ -46,6 +46,79 @@ struct window_clock_mode_data { time_t tim; }; +const char window_clock_table[14][5][5] = { + { { 1,1,1,1,1 }, /* 0 */ + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,1 }, /* 1 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 2 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 3 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,0,0,0,1 }, /* 4 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 5 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 6 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 7 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 8 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 9 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,0 }, /* : */ + { 0,0,1,0,0 }, + { 0,0,0,0,0 }, + { 0,0,1,0,0 }, + { 0,0,0,0,0 } }, + { { 1,1,1,1,1 }, /* A */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* P */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,0,0,0,0 } }, + { { 1,0,0,0,1 }, /* M */ + { 1,1,0,1,1 }, + { 1,0,1,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, +}; + struct screen * window_clock_init(struct window_pane *wp) { @@ -114,11 +187,77 @@ window_clock_draw_screen(struct window_pane *wp) struct window_clock_mode_data *data = wp->modedata; struct screen_write_ctx ctx; int colour, style; + struct screen *s = &data->screen; + struct grid_cell gc; + char tim[64], *ptr; + time_t t; + struct tm *tm; + u_int i, j, x, y, idx; colour = options_get_number(&wp->window->options, "clock-mode-colour"); style = options_get_number(&wp->window->options, "clock-mode-style"); - screen_write_start(&ctx, NULL, &data->screen); - clock_draw(&ctx, colour, style); + screen_write_start(&ctx, NULL, s); + + t = time(NULL); + tm = localtime(&t); + if (style == 0) { + strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); + if (tm->tm_hour >= 12) + strlcat(tim, "PM", sizeof tim); + else + strlcat(tim, "AM", sizeof tim); + } else + strftime(tim, sizeof tim, "%H:%M", tm); + + screen_write_clearscreen(&ctx); + + if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { + if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { + x = (screen_size_x(s) / 2) - (strlen(tim) / 2); + y = screen_size_y(s) / 2; + screen_write_cursormove(&ctx, x, y); + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, colour); + screen_write_puts(&ctx, &gc, "%s", tim); + } + + + screen_write_stop(&ctx); + return; + } + + x = (screen_size_x(s) / 2) - 3 * strlen(tim); + y = (screen_size_y(s) / 2) - 3; + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_bg(&gc, colour); + for (ptr = tim; *ptr != '\0'; ptr++) { + if (*ptr >= '0' && *ptr <= '9') + idx = *ptr - '0'; + else if (*ptr == ':') + idx = 10; + else if (*ptr == 'A') + idx = 11; + else if (*ptr == 'P') + idx = 12; + else if (*ptr == 'M') + idx = 13; + else { + x += 6; + continue; + } + + for (j = 0; j < 5; j++) { + for (i = 0; i < 5; i++) { + screen_write_cursormove(&ctx, x + i, y + j); + if (window_clock_table[idx][j][i]) + screen_write_putc(&ctx, &gc, ' '); + } + } + x += 6; + } + screen_write_stop(&ctx); } From 9368914ee782f6ea9d7ea8f6b28b9d87ffae8023 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:36:43 +0000 Subject: [PATCH 290/949] Add start-of-list, end-of-list, top-line and bottom-line in choice mode, from madmaverick9 at roxxmail dot eu, similar diff a few days later from Marcel Partap. --- mode-key.c | 15 +++++++++++++++ tmux.h | 4 ++++ window-choose.c | 23 +++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/mode-key.c b/mode-key.c index 80a943e4..80a2464d 100644 --- a/mode-key.c +++ b/mode-key.c @@ -76,14 +76,18 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { /* Choice keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { { MODEKEYCHOICE_BACKSPACE, "backspace" }, + { MODEKEYCHOICE_BOTTOMLINE, "bottom-line"}, { MODEKEYCHOICE_CANCEL, "cancel" }, { MODEKEYCHOICE_CHOOSE, "choose" }, { MODEKEYCHOICE_DOWN, "down" }, + { MODEKEYCHOICE_ENDOFLIST, "end-of-list"}, { MODEKEYCHOICE_PAGEDOWN, "page-down" }, { MODEKEYCHOICE_PAGEUP, "page-up" }, { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" }, { MODEKEYCHOICE_SCROLLUP, "scroll-up" }, { MODEKEYCHOICE_STARTNUMBERPREFIX, "start-number-prefix" }, + { MODEKEYCHOICE_STARTOFLIST, "start-of-list"}, + { MODEKEYCHOICE_TOPLINE, "top-line"}, { MODEKEYCHOICE_TREE_COLLAPSE, "tree-collapse" }, { MODEKEYCHOICE_TREE_COLLAPSE_ALL, "tree-collapse-all" }, { MODEKEYCHOICE_TREE_EXPAND, "tree-expand" }, @@ -226,6 +230,12 @@ const struct mode_key_entry mode_key_vi_choice[] = { { 'j', 0, MODEKEYCHOICE_DOWN }, { 'k', 0, MODEKEYCHOICE_UP }, { 'q', 0, MODEKEYCHOICE_CANCEL }, + { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, + { 'g', 0, MODEKEYCHOICE_STARTOFLIST }, + { 'H', 0, MODEKEYCHOICE_TOPLINE }, + { 'L', 0, MODEKEYCHOICE_BOTTOMLINE }, + { 'G', 0, MODEKEYCHOICE_ENDOFLIST }, + { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, @@ -372,6 +382,11 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCHOICE_PAGEUP }, + { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, + { '<' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTOFLIST }, + { 'R' | KEYC_ESCAPE, 0, MODEKEYCHOICE_TOPLINE }, + { '>' | KEYC_ESCAPE, 0, MODEKEYCHOICE_ENDOFLIST }, + { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, diff --git a/tmux.h b/tmux.h index 9900489e..3a73dece 100644 --- a/tmux.h +++ b/tmux.h @@ -520,14 +520,18 @@ enum mode_key_cmd { /* Menu (choice) keys. */ MODEKEYCHOICE_BACKSPACE, + MODEKEYCHOICE_BOTTOMLINE, MODEKEYCHOICE_CANCEL, MODEKEYCHOICE_CHOOSE, MODEKEYCHOICE_DOWN, + MODEKEYCHOICE_ENDOFLIST, MODEKEYCHOICE_PAGEDOWN, MODEKEYCHOICE_PAGEUP, MODEKEYCHOICE_SCROLLDOWN, MODEKEYCHOICE_SCROLLUP, MODEKEYCHOICE_STARTNUMBERPREFIX, + MODEKEYCHOICE_STARTOFLIST, + MODEKEYCHOICE_TOPLINE, MODEKEYCHOICE_TREE_COLLAPSE, MODEKEYCHOICE_TREE_COLLAPSE_ALL, MODEKEYCHOICE_TREE_EXPAND, diff --git a/window-choose.c b/window-choose.c index 0cf7f66b..bb881aa5 100644 --- a/window-choose.c +++ b/window-choose.c @@ -679,6 +679,29 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM, "Goto Item", wp, key); break; + case MODEKEYCHOICE_STARTOFLIST: + data->selected = 0; + data->top = 0; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_TOPLINE: + data->selected = data->top; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_BOTTOMLINE: + data->selected = data->top + screen_size_y(s) - 1; + if (data->selected > items - 1) + data->selected = items - 1; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_ENDOFLIST: + data->selected = items - 1; + if (screen_size_y(s) < items) + data->top = items - screen_size_y(s); + else + data->top = 0; + window_choose_redraw_screen(wp); + break; default: idx = window_choose_index_key(data, key); if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list)) From b11de5adc7a89a23af2a778d4de12ac697c902a0 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:37:55 +0000 Subject: [PATCH 291/949] Make session_attached a count and add session_many_attached flag. --- format.c | 6 ++---- resize.c | 2 ++ tmux.1 | 3 ++- tmux.h | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/format.c b/format.c index bcb897d6..1dca6011 100644 --- a/format.c +++ b/format.c @@ -401,10 +401,8 @@ format_session(struct format_tree *ft, struct session *s) *strchr(tim, '\n') = '\0'; format_add(ft, "session_created_string", "%s", tim); - if (s->flags & SESSION_UNATTACHED) - format_add(ft, "session_attached", "%d", 0); - else - format_add(ft, "session_attached", "%d", 1); + format_add(ft, "session_attached", "%u", s->attached); + format_add(ft, "session_many_attached", "%u", s->attached > 1); } /* Set default format keys for a client. */ diff --git a/resize.c b/resize.c index ff3ec6c5..70379420 100644 --- a/resize.c +++ b/resize.c @@ -55,6 +55,7 @@ recalculate_sizes(void) RB_FOREACH(s, sessions, &sessions) { has_status = options_get_number(&s->options, "status"); + s->attached = 0; ssx = ssy = UINT_MAX; for (j = 0; j < ARRAY_LENGTH(&clients); j++) { c = ARRAY_ITEM(&clients, j); @@ -69,6 +70,7 @@ recalculate_sizes(void) ssy = c->tty.sy - 1; else if (c->tty.sy < ssy) ssy = c->tty.sy; + s->attached++; } } if (ssx == UINT_MAX || ssy == UINT_MAX) { diff --git a/tmux.1 b/tmux.1 index ba4c8537..81626faa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3136,13 +3136,14 @@ The following variables are available, where appropriate: .It Li "saved_cursor_y" Ta "" Ta "Saved cursor Y in pane" .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 "session_attached" Ta "" Ta "1 if session attached" +.It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_created" Ta "" Ta "Integer time session created" .It Li "session_created_string" Ta "" Ta "String time session created" .It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_height" Ta "" Ta "Height of session" .It Li "session_id" Ta "" Ta "Unique session ID" +.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" diff --git a/tmux.h b/tmux.h index 3a73dece..fe4a697b 100644 --- a/tmux.h +++ b/tmux.h @@ -1086,6 +1086,8 @@ struct session { #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; + u_int attached; + struct termios *tio; struct environ environ; From 0e4d1d8493564ce908b002d8e9dddc105184039e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:39:31 +0000 Subject: [PATCH 292/949] Add setb -a to append and a copy mode append command, from J Raynor with minor changes. --- cmd-set-buffer.c | 74 ++++++++++++++++++++++++++++++------------------ mode-key.c | 2 ++ paste.c | 2 +- tmux.1 | 5 ++++ tmux.h | 1 + window-copy.c | 48 +++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 28 deletions(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index f67f7a0c..0942da9a 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -24,15 +24,15 @@ #include "tmux.h" /* - * Add or set a paste buffer. + * Add, set, or append to a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", - "b:", 1, 1, - CMD_BUFFER_USAGE " data", + "ab:", 1, 1, + "[-a] " CMD_BUFFER_USAGE " data", 0, NULL, cmd_set_buffer_exec @@ -41,35 +41,55 @@ const struct cmd_entry cmd_set_buffer_entry = { enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - u_int limit; - char *pdata, *cause; - size_t psize; - int buffer; + struct args *args = self->args; + struct paste_buffer *pb; + u_int limit; + char *pdata, *cause; + size_t psize, newsize; + int buffer; limit = options_get_number(&global_options, "buffer-limit"); - pdata = xstrdup(args->argv[0]); - psize = strlen(pdata); + psize = 0; + pdata = NULL; - if (!args_has(args, 'b')) { + pb = NULL; + buffer = -1; + + if (args_has(args, 'b')) { + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + cmdq_error(cmdq, "buffer %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + pb = paste_get_index(&global_buffers, buffer); + if (pb == NULL) { + cmdq_error(cmdq, "no buffer %d", buffer); + return (CMD_RETURN_ERROR); + } + } else if (args_has(args, 'a')) { + pb = paste_get_top(&global_buffers); + if (pb != NULL) + buffer = 0; + } + + if (args_has(args, 'a') && pb != NULL) { + psize = pb->size; + pdata = xmalloc(psize); + memcpy(pdata, pb->data, psize); + } + + newsize = strlen(args->argv[0]); + + pdata = xrealloc(pdata, 1, psize + newsize); + memcpy(pdata + psize, args->argv[0], newsize); + psize += newsize; + + if (buffer == -1) paste_add(&global_buffers, pdata, psize, limit); - return (CMD_RETURN_NORMAL); - } - - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - free(pdata); - return (CMD_RETURN_ERROR); - } - - if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); - free(pdata); - return (CMD_RETURN_ERROR); - } + else + paste_replace(&global_buffers, buffer, pdata, psize); return (CMD_RETURN_NORMAL); } diff --git a/mode-key.c b/mode-key.c index 80a2464d..7f2b9471 100644 --- a/mode-key.c +++ b/mode-key.c @@ -100,6 +100,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { /* Copy keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { + { MODEKEYCOPY_APPENDSELECTION, "append-selection" }, { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" }, { MODEKEYCOPY_BOTTOMLINE, "bottom-line" }, { MODEKEYCOPY_CANCEL, "cancel" }, @@ -272,6 +273,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { '9', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { ':', 0, MODEKEYCOPY_GOTOLINE }, { '?', 0, MODEKEYCOPY_SEARCHUP }, + { 'A', 0, MODEKEYCOPY_APPENDSELECTION }, { 'B', 0, MODEKEYCOPY_PREVIOUSSPACE }, { 'D', 0, MODEKEYCOPY_COPYENDOFLINE }, { 'E', 0, MODEKEYCOPY_NEXTSPACEEND }, diff --git a/paste.c b/paste.c index 946935a3..4ec87f35 100644 --- a/paste.c +++ b/paste.c @@ -172,7 +172,7 @@ paste_print(struct paste_buffer *pb, size_t width) /* Paste into a window pane, filtering '\n' according to separator. */ void -paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, +paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, const char *sep, int bracket) { const char *data = pb->data, *end = data + pb->size, *lf; diff --git a/tmux.1 b/tmux.1 index 81626faa..8fd63dc4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -851,6 +851,7 @@ option). The following keys are supported as appropriate for the mode: .Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" +.It Li "Append selection" Ta "A" Ta "" .It Li "Back to indentation" Ta "^" Ta "M-m" .It Li "Bottom of history" Ta "G" Ta "M-<" .It Li "Clear selection" Ta "Escape" Ta "C-g" @@ -3543,12 +3544,16 @@ The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer +.Op Fl a .Op Fl b Ar buffer-index .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . +The +.Fl a +option appends to rather than overwriting the buffer. .It Xo Ic show-buffer .Op Fl b Ar buffer-index .Xc diff --git a/tmux.h b/tmux.h index fe4a697b..b30f2080 100644 --- a/tmux.h +++ b/tmux.h @@ -540,6 +540,7 @@ enum mode_key_cmd { MODEKEYCHOICE_UP, /* Copy keys. */ + MODEKEYCOPY_APPENDSELECTION, MODEKEYCOPY_BACKTOINDENTATION, MODEKEYCOPY_BOTTOMLINE, MODEKEYCOPY_CANCEL, diff --git a/window-copy.c b/window-copy.c index 76c9c3ce..7d7f3a20 100644 --- a/window-copy.c +++ b/window-copy.c @@ -58,6 +58,7 @@ void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); void window_copy_copy_pipe( struct window_pane *, struct session *, int, const char *); void window_copy_copy_selection(struct window_pane *, int); +void window_copy_append_selection(struct window_pane *, int); void window_copy_clear_selection(struct window_pane *); void window_copy_copy_line( struct window_pane *, char **, size_t *, u_int, u_int, u_int); @@ -414,6 +415,13 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) cmd = mode_key_lookup(&data->mdata, key, &arg); switch (cmd) { + case MODEKEYCOPY_APPENDSELECTION: + if (sess != NULL) { + window_copy_append_selection(wp, data->numprefix); + window_pane_reset_mode(wp); + return; + } + break; case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); return; @@ -1491,6 +1499,46 @@ window_copy_copy_selection(struct window_pane *wp, int idx) window_copy_copy_buffer(wp, idx, buf, len); } +void +window_copy_append_selection(struct window_pane *wp, int idx) +{ + char *buf; + struct paste_buffer *pb; + size_t len; + u_int limit; + struct screen_write_ctx ctx; + + buf = window_copy_get_selection(wp, &len); + if (buf == NULL) + return; + + if (options_get_number(&global_options, "set-clipboard")) { + screen_write_start(&ctx, wp, NULL); + screen_write_setselection(&ctx, buf, len); + screen_write_stop(&ctx); + } + + if (idx == -1) + idx = 0; + + if (idx == 0 && paste_get_top(&global_buffers) == NULL) { + limit = options_get_number(&global_options, "buffer-limit"); + paste_add(&global_buffers, buf, len, limit); + return; + } + + pb = paste_get_index(&global_buffers, idx); + if (pb != NULL) { + buf = xrealloc(buf, 1, len + pb->size); + memmove(buf + pb->size, buf, len); + memcpy(buf, pb->data, pb->size); + len += pb->size; + } + + if (paste_replace(&global_buffers, idx, buf, len) != 0) + free(buf); +} + void window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, u_int sx, u_int ex) From fcdae6925a08dc06860f9552ba7a300669f4a038 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:39:59 +0000 Subject: [PATCH 293/949] Use hex constants rather than shifts for mouse events and flags, pointed out by Marcel Partap. --- tmux.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tmux.h b/tmux.h index b30f2080..b7d53727 100644 --- a/tmux.h +++ b/tmux.h @@ -1133,14 +1133,14 @@ LIST_HEAD(tty_terms, tty_term); #define MOUSE_WHEEL_DOWN 1 /* Mouse events. */ -#define MOUSE_EVENT_DOWN (1 << 0) -#define MOUSE_EVENT_DRAG (1 << 1) -#define MOUSE_EVENT_UP (1 << 2) -#define MOUSE_EVENT_CLICK (1 << 3) -#define MOUSE_EVENT_WHEEL (1 << 4) +#define MOUSE_EVENT_DOWN 0x1 +#define MOUSE_EVENT_DRAG 0x2 +#define MOUSE_EVENT_UP 0x4 +#define MOUSE_EVENT_CLICK 0x8 +#define MOUSE_EVENT_WHEEL 0x10 /* Mouse flags. */ -#define MOUSE_RESIZE_PANE (1 << 0) +#define MOUSE_RESIZE_PANE 0x1 /* * Mouse input. When sent by xterm: From 46593e7aa26b83f0ba1b0d36a700d7158ac2b178 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:40:21 +0000 Subject: [PATCH 294/949] Add names for mouse button bits rather than using magic numbers, from Marcel Partap. --- tmux.h | 12 ++++++++++-- tty-keys.c | 10 +++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tmux.h b/tmux.h index b7d53727..94eb5b8f 100644 --- a/tmux.h +++ b/tmux.h @@ -1128,18 +1128,26 @@ struct tty_term { }; LIST_HEAD(tty_terms, tty_term); +/* Mouse button masks. */ +#define MOUSE_MASK_BUTTONS 3 +#define MOUSE_MASK_SHIFT 4 +#define MOUSE_MASK_META 8 +#define MOUSE_MASK_CTRL 16 +#define MOUSE_MASK_DRAG 32 +#define MOUSE_MASK_WHEEL 64 + /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 #define MOUSE_WHEEL_DOWN 1 -/* Mouse events. */ +/* Mouse event bits. */ #define MOUSE_EVENT_DOWN 0x1 #define MOUSE_EVENT_DRAG 0x2 #define MOUSE_EVENT_UP 0x4 #define MOUSE_EVENT_CLICK 0x8 #define MOUSE_EVENT_WHEEL 0x10 -/* Mouse flags. */ +/* Mouse flag bits. */ #define MOUSE_RESIZE_PANE 0x1 /* diff --git a/tty-keys.c b/tty-keys.c index 4492df1e..e0e794cc 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -748,21 +748,21 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->sgr_rel = sgr_rel; m->x = x; m->y = y; - if (b & 64) { /* wheel button */ - b &= 3; + if (b & MOUSE_MASK_WHEEL) { + b &= MOUSE_MASK_BUTTONS; if (b == 0) m->wheel = MOUSE_WHEEL_UP; else if (b == 1) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; - } else if ((b & 3) == 3) { + } else if ((b & MOUSE_MASK_BUTTONS) == 3) { if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) { m->event = MOUSE_EVENT_CLICK; } else m->event = MOUSE_EVENT_DRAG; m->event |= MOUSE_EVENT_UP; } else { - if (b & 32) /* drag motion */ + if (b & MOUSE_MASK_DRAG) m->event = MOUSE_EVENT_DRAG; else { if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y) @@ -773,7 +773,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->sy = y; m->event = MOUSE_EVENT_DOWN; } - m->button = (b & 3); + m->button = (b & MOUSE_MASK_BUTTONS); } return (0); From 1704d4a6b799525f510860919b8c8c4315154a05 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:41:07 +0000 Subject: [PATCH 295/949] Don't segfaut when the parent of the layout cell is NULL, from Thomas Adam. --- window.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/window.c b/window.c index 842a5c63..bb69c0bc 100644 --- a/window.c +++ b/window.c @@ -423,10 +423,15 @@ window_pane_active_set(struct window_pane *wp, struct window_pane *nextwp) void window_pane_active_lost(struct window_pane *wp, struct window_pane *nextwp) { - struct layout_cell *lc, *lc2; + struct layout_cell *lc, *lc2, *lcparent; + + /* Get the parent cell. */ + lcparent = nextwp->layout_cell->parent; + if (lcparent == NULL) + return; /* Save the target pane in its parent. */ - nextwp->layout_cell->parent->lastwp = nextwp; + lcparent->lastwp = nextwp; /* * Save the source pane in all of its parents up to, but not including, @@ -435,8 +440,7 @@ window_pane_active_lost(struct window_pane *wp, struct window_pane *nextwp) if (wp == NULL) return; for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { - lc2 = nextwp->layout_cell->parent; - for (; lc2 != NULL; lc2 = lc2->parent) { + for (lc2 = lcparent; lc2 != NULL; lc2 = lc2->parent) { if (lc == lc2) return; } From dca7d1c0fd9dc16aca2337c078da7441dbf97eeb Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:41:35 +0000 Subject: [PATCH 296/949] Make message-limit a server option. --- options-table.c | 14 +++++++------- status.c | 6 +----- tmux.1 | 10 +++++----- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/options-table.c b/options-table.c index ea848259..bc25573a 100644 --- a/options-table.c +++ b/options-table.c @@ -81,6 +81,13 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "message-limit", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 100 + }, + { .name = "quiet", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ @@ -243,13 +250,6 @@ const struct options_table_entry session_options_table[] = { .style = "message-style" }, - { .name = "message-limit", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 20 - }, - { .name = "message-style", .type = OPTIONS_TABLE_STYLE, .default_str = "bg=yellow,fg=black" diff --git a/status.c b/status.c index 84589427..bf528b0d 100644 --- a/status.c +++ b/status.c @@ -655,7 +655,6 @@ void printflike2 status_message_set(struct client *c, const char *fmt, ...) { struct timeval tv; - struct session *s = c->session; struct message_entry *msg; va_list ap; int delay; @@ -673,10 +672,7 @@ status_message_set(struct client *c, const char *fmt, ...) msg->msg_time = time(NULL); msg->msg = xstrdup(c->message_string); - if (s == NULL) - limit = 0; - else - limit = options_get_number(&s->options, "message-limit"); + limit = options_get_number(&global_options, "message-limit"); if (ARRAY_LENGTH(&c->message_log) > limit) { limit = ARRAY_LENGTH(&c->message_log) - limit; for (i = 0; i < limit; i++) { diff --git a/tmux.1 b/tmux.1 index 8fd63dc4..3167a05a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -778,7 +778,7 @@ Show client messages or server information. Any messages displayed on the status line are saved in a per-client message log, up to a maximum of the limit set by the .Ar message-limit -session option for the session attached to that client. +server option. With .Fl t , display the log for @@ -2134,6 +2134,10 @@ passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. +.It Ic message-limit Ar number +Set the number of error or information messages to save in the message log for +each client. +The default is 100. .It Xo Ic quiet .Op Ic on | off .Xc @@ -2391,10 +2395,6 @@ With the flag to the .Ic set-option command the new style is added otherwise the existing style is replaced. -.It Ic message-limit Ar number -Set the number of error or information messages to save in the message log for -each client. -The default is 20. .It Ic message-style Ar style Set status line message style. For how to specify From f155316be7f24d76dd8fe6eb3dd34c5cf0aae86d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:42:05 +0000 Subject: [PATCH 297/949] Remove unused log functions. --- log.c | 41 +---------------------------------------- tmux.h | 3 --- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/log.c b/log.c index 2f1400cb..8a922c0a 100644 --- a/log.c +++ b/log.c @@ -41,7 +41,7 @@ __dead void log_vfatal(const char *, va_list); void log_event_cb(unused int severity, const char *msg) { - log_warnx("%s", msg); + log_debug("%s", msg); } /* Open logging to file. */ @@ -86,45 +86,6 @@ log_vwrite(const char *msg, va_list ap) free(fmt); } -/* Log a warning with error string. */ -void printflike1 -log_warn(const char *msg, ...) -{ - va_list ap; - char *fmt; - - va_start(ap, msg); - if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1) - exit(1); - log_vwrite(fmt, ap); - free(fmt); - va_end(ap); -} - -/* Log a warning. */ -void printflike1 -log_warnx(const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); -} - -/* Log an informational message. */ -void printflike1 -log_info(const char *msg, ...) -{ - va_list ap; - - if (log_level > -1) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } -} - /* Log a debug message. */ void printflike1 log_debug(const char *msg, ...) diff --git a/tmux.h b/tmux.h index 94eb5b8f..9990ccfe 100644 --- a/tmux.h +++ b/tmux.h @@ -2343,9 +2343,6 @@ char *get_proc_name(int, char *); /* log.c */ void log_open(int, const char *); void log_close(void); -void printflike1 log_warn(const char *, ...); -void printflike1 log_warnx(const char *, ...); -void printflike1 log_info(const char *, ...); void printflike1 log_debug(const char *, ...); void printflike1 log_debug2(const char *, ...); __dead void printflike1 log_fatal(const char *, ...); From 7bdb675469336e1c8c7c5039aff369f8245dc450 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:42:27 +0000 Subject: [PATCH 298/949] GRID_DEBUG is no longer needed. --- grid-view.c | 22 ---------------------- grid.c | 18 ------------------ tmux.h | 10 ---------- 3 files changed, 50 deletions(-) diff --git a/grid-view.c b/grid-view.c index 4fe38fed..fca5fd54 100644 --- a/grid-view.c +++ b/grid-view.c @@ -59,8 +59,6 @@ grid_view_clear_history(struct grid *gd) struct grid_line *gl; u_int yy, last; - GRID_DEBUG(gd, ""); - /* Find the last used line. */ last = 0; for (yy = 0; yy < gd->sy; yy++) { @@ -82,8 +80,6 @@ grid_view_clear_history(struct grid *gd) void grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -94,8 +90,6 @@ grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) void grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) { - GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); - if (gd->flags & GRID_HISTORY) { grid_collect_history(gd); if (rupper == 0 && rlower == gd->sy - 1) @@ -116,8 +110,6 @@ grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) void grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower) { - GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); - rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); @@ -130,8 +122,6 @@ grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); @@ -145,8 +135,6 @@ grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) { u_int ny2; - GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); - rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); @@ -162,8 +150,6 @@ grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); @@ -178,8 +164,6 @@ grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) { u_int ny2; - GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); - rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); @@ -195,8 +179,6 @@ grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -214,8 +196,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -229,8 +209,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) char * grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) { - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); diff --git a/grid.c b/grid.c index 7fb15dc3..df2f8b16 100644 --- a/grid.c +++ b/grid.c @@ -151,8 +151,6 @@ grid_collect_history(struct grid *gd) { u_int yy; - GRID_DEBUG(gd, ""); - if (gd->hsize < gd->hlimit) return; @@ -173,8 +171,6 @@ grid_scroll_history(struct grid *gd) { u_int yy; - GRID_DEBUG(gd, ""); - yy = gd->hsize + gd->sy; gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); @@ -189,8 +185,6 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) struct grid_line *gl_history, *gl_upper, *gl_lower; u_int yy; - GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower); - /* Create a space for a new line. */ yy = gd->hsize + gd->sy; gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); @@ -282,8 +276,6 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { u_int xx, yy; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); - if (nx == 0 || ny == 0) return; @@ -319,8 +311,6 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) struct grid_line *gl; u_int yy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - if (ny == 0) return; @@ -342,8 +332,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) { u_int yy; - GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); - if (ny == 0 || py == dy) return; @@ -381,8 +369,6 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) struct grid_line *gl; u_int xx; - GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); - if (nx == 0 || px == dx) return; @@ -592,8 +578,6 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int xx; const struct grid_line *gl; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - if (lastgc != NULL && *lastgc == NULL) { memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); *lastgc = &lastgc1; @@ -661,8 +645,6 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, struct grid_line *dstl, *srcl; u_int yy; - GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); - if (dy + ny > dst->hsize + dst->sy) ny = dst->hsize + dst->sy - dy; if (sy + ny > src->hsize + src->sy) diff --git a/tmux.h b/tmux.h index 9990ccfe..df7b5d16 100644 --- a/tmux.h +++ b/tmux.h @@ -663,16 +663,6 @@ struct utf8_data { u_int width; }; -/* Grid output. */ -#if defined(DEBUG) && \ - ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ - (defined(__GNUC__) && __GNUC__ >= 3)) -#define GRID_DEBUG(gd, fmt, ...) log_debug2("%s: (sx=%u, sy=%u, hsize=%u) " \ - fmt, __func__, (gd)->sx, (gd)->sy, (gd)->hsize, ## __VA_ARGS__) -#else -#define GRID_DEBUG(...) -#endif - /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 #define GRID_ATTR_DIM 0x2 From 48478ea0a990cc14d61722f9f8d3f1490d8f417a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:42:45 +0000 Subject: [PATCH 299/949] Remove log_debug2 as well and simplify log.c. --- input-keys.c | 6 ++-- log.c | 77 ++++++++++++++-------------------------------------- tmux.c | 2 +- 3 files changed, 25 insertions(+), 60 deletions(-) diff --git a/input-keys.c b/input-keys.c index 2de48e97..9247a995 100644 --- a/input-keys.c +++ b/input-keys.c @@ -143,7 +143,7 @@ input_key(struct window_pane *wp, int key) char *out; u_char ch; - log_debug2("writing key 0x%x", key); + log_debug("writing key 0x%x", key); /* * If this is a normal 7-bit key, just send it, with a leading escape @@ -186,11 +186,11 @@ input_key(struct window_pane *wp, int key) break; } if (i == nitems(input_keys)) { - log_debug2("key 0x%x missing", key); + log_debug("key 0x%x missing", key); return; } dlen = strlen(ike->data); - log_debug2("found key 0x%x: \"%s\"", key, ike->data); + log_debug("found key 0x%x: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) diff --git a/log.c b/log.c index 8a922c0a..f329107c 100644 --- a/log.c +++ b/log.c @@ -22,20 +22,13 @@ #include #include #include -#include -#include #include "tmux.h" -/* Log file, if needed. */ -FILE *log_file; +FILE *log_file; -/* Debug level. */ -int log_level = 0; - -void log_event_cb(int, const char *); -void log_vwrite(const char *, va_list); -__dead void log_vfatal(const char *, va_list); +void log_event_cb(int, const char *); +void log_vwrite(const char *, va_list); /* Log callback for libevent. */ void @@ -46,12 +39,11 @@ log_event_cb(unused int severity, const char *msg) /* Open logging to file. */ void -log_open(int level, const char *path) +log_open(const char *path) { log_file = fopen(path, "w"); if (log_file == NULL) return; - log_level = level; setlinebuf(log_file); event_set_log_callback(log_event_cb); @@ -65,6 +57,7 @@ log_close(void) { if (log_file != NULL) fclose(log_file); + log_file = NULL; event_set_log_callback(NULL); } @@ -92,63 +85,35 @@ log_debug(const char *msg, ...) { va_list ap; - if (log_level > 0) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } + va_start(ap, msg); + log_vwrite(msg, ap); + va_end(ap); } -/* Log a debug message at level 2. */ -void printflike1 -log_debug2(const char *msg, ...) -{ - va_list ap; - - if (log_level > 1) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } -} - -/* Log a critical error, with error string if necessary, and die. */ -__dead void -log_vfatal(const char *msg, va_list ap) -{ - char *fmt; - - if (errno != 0) { - if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) - exit(1); - log_vwrite(fmt, ap); - } else { - if (asprintf(&fmt, "fatal: %s", msg) == -1) - exit(1); - log_vwrite(fmt, ap); - } - free(fmt); - - exit(1); -} - -/* Log a critical error, with error string, and die. */ +/* Log a critical error with error string and die. */ __dead void printflike1 log_fatal(const char *msg, ...) { - va_list ap; + char *fmt; + va_list ap; va_start(ap, msg); - log_vfatal(msg, ap); + if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) + exit(1); + log_vwrite(fmt, ap); + exit(1); } /* Log a critical error and die. */ __dead void printflike1 log_fatalx(const char *msg, ...) { - va_list ap; + char *fmt; + va_list ap; - errno = 0; va_start(ap, msg); - log_vfatal(msg, ap); + if (asprintf(&fmt, "fatal: %s", msg) == -1) + exit(1); + log_vwrite(fmt, ap); + exit(1); } diff --git a/tmux.c b/tmux.c index 6de96ce1..8f9e520d 100644 --- a/tmux.c +++ b/tmux.c @@ -70,7 +70,7 @@ logfile(const char *name) if (debug_level > 0) { xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); - log_open(debug_level, path); + log_open(path); free(path); } } From ee19d304ff366741f6f94334bbc82124a4c44dbc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:43:35 +0000 Subject: [PATCH 300/949] In four byte UTF-8 sequences, only three bits of the first byte should be used. Fix from Koga Osamu. --- utf8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utf8.c b/utf8.c index 1c81392b..85889dcb 100644 --- a/utf8.c +++ b/utf8.c @@ -311,7 +311,7 @@ utf8_combine(const struct utf8_data *utf8data) value = utf8data->data[3] & 0x3f; value |= (utf8data->data[2] & 0x3f) << 6; value |= (utf8data->data[1] & 0x3f) << 12; - value |= (utf8data->data[0] & 0x3f) << 18; + value |= (utf8data->data[0] & 0x07) << 18; break; } return (value); From 3c06bec03fe269b8a2548c147fe7b4f2eb4d3c7a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 31 Mar 2014 21:43:55 +0000 Subject: [PATCH 301/949] Don't crash with a zero-length argument to setb, from J Raynor. --- cmd-set-buffer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 0942da9a..a206760c 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -56,6 +56,9 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) pb = NULL; buffer = -1; + if ((newsize = strlen(args->argv[0])) == 0) + return (CMD_RETURN_NORMAL); + if (args_has(args, 'b')) { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { @@ -80,8 +83,6 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) memcpy(pdata, pb->data, psize); } - newsize = strlen(args->argv[0]); - pdata = xrealloc(pdata, 1, psize + newsize); memcpy(pdata + psize, args->argv[0], newsize); psize += newsize; From d9960b2d4d4cb6697086d32b24e9e6cbc74df483 Mon Sep 17 00:00:00 2001 From: deraadt Date: Tue, 1 Apr 2014 05:50:30 +0000 Subject: [PATCH 302/949] missed commit matching log.c --- tmux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.h b/tmux.h index df7b5d16..6b35e175 100644 --- a/tmux.h +++ b/tmux.h @@ -2331,7 +2331,7 @@ u_int utf8_split2(u_int, u_char *); char *get_proc_name(int, char *); /* log.c */ -void log_open(int, const char *); +void log_open(const char *); void log_close(void); void printflike1 log_debug(const char *, ...); void printflike1 log_debug2(const char *, ...); From b52b40b2bc9e90e2e585aa2158aee45fcbe1db86 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Apr 2014 07:55:09 +0000 Subject: [PATCH 303/949] pane_start_path has gone away. --- tmux.1 | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 3167a05a..eb0be916 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3128,7 +3128,6 @@ The following variables are available, where appropriate: .It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" -.It Li "pane_start_path" Ta "" Ta "Path pane started with" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" From 806d5dcb17c26d2abcbf4328a9ec419ada3d4a3f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 2 Apr 2014 18:02:25 +0100 Subject: [PATCH 304/949] Remove LocalWords. --- CHANGES | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHANGES b/CHANGES index 92402721..abd1ac0a 100644 --- a/CHANGES +++ b/CHANGES @@ -1867,8 +1867,3 @@ The list of older changes is below. customisation. $Id$ - - LocalWords: showw utf UTF fulvio ciriaco joshe OSC APC gettime abc DEF OA clr - LocalWords: rivo nurges lscm Erdely eol smysession mysession ek dstname RB ms - LocalWords: dstidx srcname srcidx winlink lsw nabc sabc Exp Tiago Cunha dch - LocalWords: setw Chisnall renamew merdely eg Maier newname selectw neww Gass From 8880bdb67c9d939ec53506d05f5ce1d75be10c97 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Apr 2014 17:08:23 +0000 Subject: [PATCH 305/949] Do not replace ## with # in status_replace1 because it'll be done later by the format code. --- status.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/status.c b/status.c index bf528b0d..029be4c8 100644 --- a/status.c +++ b/status.c @@ -396,9 +396,6 @@ status_replace1(struct client *c, char **iptr, char **optr, char *out, case '{': ptr = (char *) "#{"; goto do_replace; - case '#': - *(*optr)++ = '#'; - break; default: xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1)); ptr = tmp; From 82f3e0e9e68d4078555cd6270473c45a3e60273b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Apr 2014 17:14:24 +0000 Subject: [PATCH 306/949] Use the same logic for bell with and without visual-bell, from Filip Moc. --- server-window.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server-window.c b/server-window.c index 39699c37..86beeef4 100644 --- a/server-window.c +++ b/server-window.c @@ -85,10 +85,11 @@ server_window_check_bell(struct session *s, struct winlink *wl) return (0); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s || (c->flags & CLIENT_CONTROL)) + if (c == NULL || c->session != s || c->flags & CLIENT_CONTROL) continue; if (!visual) { - tty_bell(&c->tty); + if (c->session->curw->window == w || action == BELL_ANY) + tty_bell(&c->tty); continue; } if (c->session->curw->window == w) From 252a7373d69646ae866e3a4fa18d46f673864c0e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Apr 2014 18:12:18 +0000 Subject: [PATCH 307/949] Support UTF-8 with choose-buffer, from Kosuke ASAMI. Also make buffer_sample bigger to let it trim at window right edge. --- cmd-choose-buffer.c | 4 +++- cmd-list-buffers.c | 2 +- format.c | 10 ++++++---- paste.c | 23 ++++++++++++----------- tmux.1 | 2 +- tmux.h | 7 ++++--- utf8.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 70 insertions(+), 21 deletions(-) diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 359de068..1e0edaa6 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -49,6 +49,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) char *action, *action_data; const char *template; u_int idx; + int utf8flag; if ((c = cmd_current_client(cmdq)) == NULL) { cmdq_error(cmdq, "no client available"); @@ -60,6 +61,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); + utf8flag = options_get_number(&wl->window->options, "utf8"); if (paste_get_top(&global_buffers) == NULL) return (CMD_RETURN_NORMAL); @@ -79,7 +81,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cdata->ft_template = xstrdup(template); format_add(cdata->ft, "line", "%u", idx - 1); - format_paste_buffer(cdata->ft, pb); + format_paste_buffer(cdata->ft, pb, utf8flag); xasprintf(&action_data, "%u", idx - 1); cdata->command = cmd_template_replace(action, action_data, 1); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index e36a7cd0..7051eae8 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -55,7 +55,7 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { ft = format_create(); format_add(ft, "line", "%u", idx - 1); - format_paste_buffer(ft, pb); + format_paste_buffer(ft, pb, 0); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); diff --git a/format.c b/format.c index 1dca6011..5fc76b2b 100644 --- a/format.c +++ b/format.c @@ -601,12 +601,14 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) /* Set default format keys for paste buffer. */ void -format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) +format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, + int utf8flag) { - char *pb_print = paste_print(pb, 50); + char *s; format_add(ft, "buffer_size", "%zu", pb->size); - format_add(ft, "buffer_sample", "%s", pb_print); - free(pb_print); + s = paste_make_sample(pb, utf8flag); + format_add(ft, "buffer_sample", "%s", s); + free(s); } diff --git a/paste.c b/paste.c index 4ec87f35..98d71a07 100644 --- a/paste.c +++ b/paste.c @@ -148,25 +148,26 @@ paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) return (0); } -/* Convert a buffer into a visible string. */ +/* Convert start of buffer into a nice string. */ char * -paste_print(struct paste_buffer *pb, size_t width) +paste_make_sample(struct paste_buffer *pb, int utf8flag) { - char *buf; - size_t len, used; - - if (width < 3) - width = 3; - buf = xmalloc(width * 4 + 1); + char *buf; + size_t len, used; + const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; + const size_t width = 200; len = pb->size; if (len > width) len = width; + buf = xmalloc(len * 4 + 4); - used = strvisx(buf, pb->data, len, VIS_OCTAL|VIS_TAB|VIS_NL); + if (utf8flag) + used = utf8_strvis(buf, pb->data, len, flags); + else + used = strvisx(buf, pb->data, len, flags); if (pb->size > width || used > width) - strlcpy(buf + width - 3, "...", 4); - + strlcpy(buf + width, "...", 4); return (buf); } diff --git a/tmux.1 b/tmux.1 index eb0be916..8205e026 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3087,7 +3087,7 @@ The following variables are available, where appropriate: .It Li "alternate_on" Ta "" Ta "If pane is in alternate screen" .It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" .It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" -.It Li "buffer_sample" Ta "" Ta "First 50 characters from buffer" +.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 "Integer time client last had activity" .It Li "client_activity_string" Ta "" Ta "String time client last had activity" diff --git a/tmux.h b/tmux.h index 6b35e175..7e541458 100644 --- a/tmux.h +++ b/tmux.h @@ -85,7 +85,7 @@ extern char **environ; /* Default template for choose-buffer. */ #define CHOOSE_BUFFER_TEMPLATE \ - "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" + "#{line}: #{buffer_size} bytes: #{buffer_sample}" /* Default template for choose-client. */ #define CHOOSE_CLIENT_TEMPLATE \ @@ -1544,7 +1544,7 @@ void format_winlink(struct format_tree *, struct session *, void format_window_pane(struct format_tree *, struct window_pane *); void format_paste_buffer(struct format_tree *, - struct paste_buffer *); + struct paste_buffer *, int); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -1714,7 +1714,7 @@ int paste_free_top(struct paste_stack *); int paste_free_index(struct paste_stack *, u_int); void paste_add(struct paste_stack *, char *, size_t, u_int); int paste_replace(struct paste_stack *, u_int, char *, size_t); -char *paste_print(struct paste_buffer *, size_t); +char *paste_make_sample(struct paste_buffer *, int); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); @@ -2326,6 +2326,7 @@ int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); u_int utf8_split2(u_int, u_char *); +int utf8_strvis(char *, const char *, size_t, int); /* procname.c */ char *get_proc_name(int, char *); diff --git a/utf8.c b/utf8.c index 85889dcb..082683e8 100644 --- a/utf8.c +++ b/utf8.c @@ -19,6 +19,7 @@ #include #include +#include #include "tmux.h" @@ -350,3 +351,45 @@ utf8_width(const struct utf8_data *utf8data) } return (1); } + +/* + * Encode len characters from src into dst, which is guaranteed to have four + * bytes available for each character from src (for \abc or UTF-8) plus space + * for \0. + */ +int +utf8_strvis(char *dst, const char *src, size_t len, int flag) +{ + struct utf8_data utf8data; + const char *start, *end; + int more; + size_t i; + + start = dst; + end = src + len; + + while (src < end) { + if (utf8_open(&utf8data, *src)) { + more = 1; + while (++src < end && more) + more = utf8_append(&utf8data, *src); + if (!more) { + /* UTF-8 character finished. */ + for (i = 0; i < utf8data.size; i++) + *dst++ = utf8data.data[i]; + continue; + } else if (utf8data.have > 0) { + /* Not a complete UTF-8 character. */ + src -= utf8data.have; + } + } + if (src < end - 1) + dst = vis(dst, src[0], flag, src[1]); + else if (src < end) + dst = vis(dst, src[0], flag, '\0'); + src++; + } + + *dst = '\0'; + return (dst - start); +} From 8824dae6f7b21f95ea824ecc1abc31140763c971 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Apr 2014 08:15:17 +0000 Subject: [PATCH 308/949] A couple of trivial mouse-related style nits. --- input-keys.c | 2 +- tty-keys.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/input-keys.c b/input-keys.c index 9247a995..0370a684 100644 --- a/input-keys.c +++ b/input-keys.c @@ -245,7 +245,7 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) paste_send_pane(pb, wp, "\r", wp->screen->mode & MODE_BRACKETPASTE); } - } else if ((m->xb & 3) != 1 && + } else if (m->button != 1 && options_get_number(&wp->window->options, "mode-mouse") == 1) { if (window_pane_set_mode(wp, &window_copy_mode) == 0) { window_copy_init_from_pane(wp); diff --git a/tty-keys.c b/tty-keys.c index e0e794cc..1116df2b 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -756,9 +756,9 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; } else if ((b & MOUSE_MASK_BUTTONS) == 3) { - if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) { + if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) m->event = MOUSE_EVENT_CLICK; - } else + else m->event = MOUSE_EVENT_DRAG; m->event |= MOUSE_EVENT_UP; } else { From acef311fe356f408690e9f94727ed63a934b742f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Apr 2014 08:20:29 +0000 Subject: [PATCH 309/949] Work out mouse scroll wheel effect when the mouse is first detected and store it in struct mouse_event, reduce the scroll size the 3 but allow shift to reduce it to 1 and meta and ctrl to multiply by 3 if the terminal supports them, also support wheel in choose mode. From Marcel Partap. --- tmux.h | 1 + tty-keys.c | 9 +++++++++ window-choose.c | 12 +++++++++++- window-copy.c | 21 +++++++++++---------- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/tmux.h b/tmux.h index 7e541458..a9edf8ff 100644 --- a/tmux.h +++ b/tmux.h @@ -1168,6 +1168,7 @@ struct mouse_event { u_int button; u_int clicks; + u_int scroll; int wheel; int event; diff --git a/tty-keys.c b/tty-keys.c index 1116df2b..4f55a80c 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -749,6 +749,15 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->x = x; m->y = y; if (b & MOUSE_MASK_WHEEL) { + if (b & MOUSE_MASK_SHIFT) + m->scroll = 1; + else + m->scroll = 3; + if (b & MOUSE_MASK_META) + m->scroll *= 3; + if (b & MOUSE_MASK_CTRL) + m->scroll *= 3; + b &= MOUSE_MASK_BUTTONS; if (b == 0) m->wheel = MOUSE_WHEEL_UP; diff --git a/window-choose.c b/window-choose.c index bb881aa5..c354d46f 100644 --- a/window-choose.c +++ b/window-choose.c @@ -721,7 +721,17 @@ window_choose_mouse( struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct window_choose_mode_item *item; - u_int idx; + u_int i, idx; + + if (m->event == MOUSE_EVENT_WHEEL) { + for (i = 0; i < m->scroll; i++) { + if (m->wheel == MOUSE_WHEEL_UP) + window_choose_key(wp, sess, KEYC_UP); + else + window_choose_key(wp, sess, KEYC_DOWN); + } + return; + } if (~m->event & MOUSE_EVENT_CLICK) return; diff --git a/window-copy.c b/window-copy.c index 7d7f3a20..6e4d6704 100644 --- a/window-copy.c +++ b/window-copy.c @@ -871,18 +871,19 @@ window_copy_mouse( /* If mouse wheel (buttons 4 and 5), scroll. */ if (m->event == MOUSE_EVENT_WHEEL) { - if (m->wheel == MOUSE_WHEEL_UP) { - for (i = 0; i < 5; i++) + for (i = 0; i < m->scroll; i++) { + if (m->wheel == MOUSE_WHEEL_UP) window_copy_cursor_up(wp, 1); - } else if (m->wheel == MOUSE_WHEEL_DOWN) { - for (i = 0; i < 5; i++) + else { window_copy_cursor_down(wp, 1); - /* - * We reached the bottom, leave copy mode, - * but only if no selection is in progress. - */ - if (data->oy == 0 && !s->sel.flag) - goto reset_mode; + + /* + * We reached the bottom, leave copy mode, but + * only if no selection is in progress. + */ + if (data->oy == 0 && !s->sel.flag) + goto reset_mode; + } } return; } From 57c514d2f85f9d1c81601bae32131f1cd2948422 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 5 Apr 2014 12:40:19 +0100 Subject: [PATCH 310/949] Remove ; not used on Linux. --- utf8.c | 1 - 1 file changed, 1 deletion(-) diff --git a/utf8.c b/utf8.c index 1328dd6e..20e35137 100644 --- a/utf8.c +++ b/utf8.c @@ -19,7 +19,6 @@ #include #include -#include #include "tmux.h" From 73c5a487c1b0f10bbc36479f425fb9cea512be7b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Apr 2014 10:32:16 +0000 Subject: [PATCH 311/949] save-buffer needs to use O_TRUNC. --- cmd-save-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 1b0a4e7b..6c15fb42 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -112,7 +112,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (fd != -1) f = fdopen(fd, "ab"); } else { - fd = openat(cwd, path, O_CREAT|O_RDWR, 0600); + fd = openat(cwd, path, O_CREAT|O_RDWR|O_TRUNC, 0600); if (fd != -1) f = fdopen(fd, "wb"); } From b8bda67f304b7c70dee891b7ca660036793c2a4b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Apr 2014 19:35:54 +0000 Subject: [PATCH 312/949] Don't blindly increase offsets by the return value of snprintf, if there wasn't enough space this will go off the end. Instead clamp to the available space. Fixes crash reported by Julien Rebetez. --- arguments.c | 12 +++++++++--- cmd-list.c | 10 +++++++--- window-copy.c | 14 ++++++++------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/arguments.c b/arguments.c index 5ff7ed2c..ca6cc760 100644 --- a/arguments.c +++ b/arguments.c @@ -125,7 +125,7 @@ args_free(struct args *args) size_t args_print(struct args *args, char *buf, size_t len) { - size_t off; + size_t off, used; int i; const char *quotes; struct args_entry *entry; @@ -165,9 +165,12 @@ args_print(struct args *args, char *buf, size_t len) quotes = "\""; else quotes = ""; - off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s", + used = xsnprintf(buf + off, len - off, "%s-%c %s%s%s", off != 0 ? " " : "", entry->flag, quotes, entry->value, quotes); + if (used > len - off) + used = len - off; + off += used; } /* And finally the argument vector. */ @@ -181,8 +184,11 @@ args_print(struct args *args, char *buf, size_t len) quotes = "\""; else quotes = ""; - off += xsnprintf(buf + off, len - off, "%s%s%s%s", + used = xsnprintf(buf + off, len - off, "%s%s%s%s", off != 0 ? " " : "", quotes, args->argv[i], quotes); + if (used > len - off) + used = len - off; + off += used; } return (off); diff --git a/cmd-list.c b/cmd-list.c index 82ae7480..6dc4493a 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -103,7 +103,7 @@ size_t cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) { struct cmd *cmd; - size_t off; + size_t off, used; off = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { @@ -112,8 +112,12 @@ cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) off += cmd_print(cmd, buf + off, len - off); if (off >= len) break; - if (TAILQ_NEXT(cmd, qentry) != NULL) - off += xsnprintf(buf + off, len - off, " ; "); + if (TAILQ_NEXT(cmd, qentry) != NULL) { + used = xsnprintf(buf + off, len - off, " ; "); + if (used > len - off) + used = len - off; + off += used; + } } return (off); } diff --git a/window-copy.c b/window-copy.c index 6e4d6704..c33a4c3b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1194,8 +1194,8 @@ window_copy_write_line( screen_write_puts(ctx, &gc, "%s", hdr); } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { limit = sizeof hdr; - if (limit > screen_size_x(s)) - limit = screen_size_x(s); + if (limit > screen_size_x(s) + 1) + limit = screen_size_x(s) + 1; if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { xoff = size = xsnprintf(hdr, limit, "Repeat: %u", data->numprefix); @@ -1208,10 +1208,12 @@ window_copy_write_line( } else size = 0; - screen_write_cursormove(ctx, xoff, py); - screen_write_copy(ctx, data->backing, xoff, - (screen_hsize(data->backing) - data->oy) + py, - screen_size_x(s) - size, 1); + if (size < screen_size_x(s)) { + screen_write_cursormove(ctx, xoff, py); + screen_write_copy(ctx, data->backing, xoff, + (screen_hsize(data->backing) - data->oy) + py, + screen_size_x(s) - size, 1); + } if (py == data->cy && data->cx == screen_size_x(s)) { memcpy(&gc, &grid_default_cell, sizeof gc); From a47d2397dfccfdebc26cfaca924cca8cff904235 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Apr 2014 22:27:30 +0000 Subject: [PATCH 313/949] Don't leak socketpair file descriptors if fork fails. Spotted by Balazs Kezes. --- job.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/job.c b/job.c index e8006308..a8e6d33a 100644 --- a/job.c +++ b/job.c @@ -60,6 +60,8 @@ job_run(const char *cmd, struct session *s, switch (pid = fork()) { case -1: environ_free(&env); + close(out[0]); + close(out[1]); return (NULL); case 0: /* child */ clear_signals(1); From 8e1cef404022422f9f57c72d139f19a82a70a791 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 15 Apr 2014 00:31:45 +0100 Subject: [PATCH 314/949] +. --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 540a6808..6e5d4259 100644 --- a/TODO +++ b/TODO @@ -132,3 +132,4 @@ binding to a command that says "next key from $othertable" and so on. means -n can go away as well * customizable command aliases + * any remaining clients in wait-for should be woken when server exits From e5d85c6c3c7f81ad9744b7306fd0cb1f7d12ebad Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Apr 2014 08:02:31 +0000 Subject: [PATCH 315/949] Because we pass the file descriptor from client to server, tmux can't usefully work if stdin is /dev/tty. Complain about it more clearly. --- server-client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server-client.c b/server-client.c index 35df7531..0f4d39d6 100644 --- a/server-client.c +++ b/server-client.c @@ -119,6 +119,11 @@ server_client_open(struct client *c, char **cause) if (c->flags & CLIENT_CONTROL) return (0); + if (strcmp(c->ttyname, "/dev/tty") == 0) { + *cause = xstrdup("can't use /dev/tty"); + return (-1); + } + if (!(c->flags & CLIENT_TERMINAL)) { *cause = xstrdup("not a terminal"); return (-1); From 14a96df9ee288b24354c3ac2e57eca21b6ad8151 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Apr 2014 21:02:41 +0000 Subject: [PATCH 316/949] Remove the choose-list command to prepare for some later choose-* work. --- Makefile | 1 - cmd-choose-list.c | 97 ----------------------------------------------- cmd.c | 1 - tmux.1 | 26 ------------- tmux.h | 1 - 5 files changed, 126 deletions(-) delete mode 100644 cmd-choose-list.c diff --git a/Makefile b/Makefile index 63363f51..d8d75053 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,6 @@ SRCS= arguments.c \ cmd-capture-pane.c \ cmd-choose-buffer.c \ cmd-choose-client.c \ - cmd-choose-list.c \ cmd-choose-tree.c \ cmd-clear-history.c \ cmd-clock-mode.c \ diff --git a/cmd-choose-list.c b/cmd-choose-list.c deleted file mode 100644 index c3caabba..00000000 --- a/cmd-choose-list.c +++ /dev/null @@ -1,97 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2012 Thomas Adam - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include - -#include - -#include "tmux.h" - -#define CMD_CHOOSE_LIST_DEFAULT_TEMPLATE "run-shell '%%'" - -/* - * Enter choose mode to choose a custom list. - */ - -enum cmd_retval cmd_choose_list_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_choose_list_entry = { - "choose-list", NULL, - "l:t:", 0, 1, - "[-l items] " CMD_TARGET_WINDOW_USAGE "[template]", - 0, - NULL, - cmd_choose_list_exec -}; - -enum cmd_retval -cmd_choose_list_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - struct client *c; - struct winlink *wl; - const char *list1; - char *template, *item, *copy, *list; - u_int idx; - - if ((c = cmd_current_client(cmdq)) == NULL) { - cmdq_error(cmdq, "no client available"); - return (CMD_RETURN_ERROR); - } - - if ((list1 = args_get(args, 'l')) == NULL) - return (CMD_RETURN_ERROR); - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) - return (CMD_RETURN_NORMAL); - - if (args->argc != 0) - template = xstrdup(args->argv[0]); - else - template = xstrdup(CMD_CHOOSE_LIST_DEFAULT_TEMPLATE); - - copy = list = xstrdup(list1); - idx = 0; - while ((item = strsep(&list, ",")) != NULL) - { - if (*item == '\0') /* no empty entries */ - continue; - window_choose_add_item(wl->window->active, c, wl, item, - template, idx); - idx++; - } - free(copy); - - if (idx == 0) { - free(template); - window_pane_reset_mode(wl->window->active); - return (CMD_RETURN_ERROR); - } - - window_choose_ready(wl->window->active, 0, NULL); - - free(template); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd.c b/cmd.c index 62f4c73d..726799a8 100644 --- a/cmd.c +++ b/cmd.c @@ -35,7 +35,6 @@ const struct cmd_entry *cmd_table[] = { &cmd_capture_pane_entry, &cmd_choose_buffer_entry, &cmd_choose_client_entry, - &cmd_choose_list_entry, &cmd_choose_session_entry, &cmd_choose_tree_entry, &cmd_choose_window_entry, diff --git a/tmux.1 b/tmux.1 index 8205e026..da4efcdc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1155,32 +1155,6 @@ flag, see the section. This command works only if at least one client is attached. .It Xo -.Ic choose-list -.Op Fl l Ar items -.Op Fl t Ar target-window -.Op Ar template -.Xc -Put a window into list choice mode, allowing -.Ar items -to be selected. -.Ar items -can be a comma-separated list to display more than one item. -If an item has spaces, that entry must be quoted. -After an item is chosen, -.Ql %% -is replaced by the chosen item in the -.Ar template -and the result is executed as a command. -If -.Ar template -is not given, "run-shell '%%'" is used. -.Ar items -also accepts format specifiers. -For the meaning of this see the -.Sx FORMATS -section. -This command works only if at least one client is attached. -.It Xo .Ic choose-session .Op Fl F Ar format .Op Fl t Ar target-window diff --git a/tmux.h b/tmux.h index a9edf8ff..0b3a2ea4 100644 --- a/tmux.h +++ b/tmux.h @@ -1759,7 +1759,6 @@ extern const struct cmd_entry cmd_break_pane_entry; extern const struct cmd_entry cmd_capture_pane_entry; extern const struct cmd_entry cmd_choose_buffer_entry; extern const struct cmd_entry cmd_choose_client_entry; -extern const struct cmd_entry cmd_choose_list_entry; extern const struct cmd_entry cmd_choose_session_entry; extern const struct cmd_entry cmd_choose_tree_entry; extern const struct cmd_entry cmd_choose_window_entry; From 871b83cbab3a490827760540f2c36fd1edfe1875 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Apr 2014 21:16:19 +0000 Subject: [PATCH 317/949] Remove a leftover prototype and fix some spacing. --- tmux.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tmux.h b/tmux.h index 0b3a2ea4..61fa4a39 100644 --- a/tmux.h +++ b/tmux.h @@ -2335,7 +2335,6 @@ char *get_proc_name(int, char *); void log_open(const char *); void log_close(void); void printflike1 log_debug(const char *, ...); -void printflike1 log_debug2(const char *, ...); __dead void printflike1 log_fatal(const char *, ...); __dead void printflike1 log_fatalx(const char *, ...); @@ -2356,7 +2355,9 @@ const char *style_tostring(struct grid_cell *); void style_update_new(struct options *, const char *, const char *); void style_update_old(struct options *, const char *, struct grid_cell *); -void style_apply(struct grid_cell *, struct options *, const char *); -void style_apply_update(struct grid_cell *, struct options *, const char *); +void style_apply(struct grid_cell *, struct options *, + const char *); +void style_apply_update(struct grid_cell *, struct options *, + const char *); #endif /* TMUX_H */ From 5acee1c04ed38afd6a32da4a66e6855ccdc52af3 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Apr 2014 23:05:38 +0000 Subject: [PATCH 318/949] Memory leak in error path and unnecessary assignment, from clang. --- cmd-save-buffer.c | 1 - grid.c | 2 +- procname.c | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 6c15fb42..b6ee2e49 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -142,7 +142,6 @@ do_print: return (CMD_RETURN_ERROR); } msg = NULL; - msglen = 0; used = 0; while (used != pb->size) { diff --git a/grid.c b/grid.c index df2f8b16..28210185 100644 --- a/grid.c +++ b/grid.c @@ -624,7 +624,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, off += size; } - if (trim) { + if (trim) { while (off > 0 && buf[off - 1] == ' ') off--; } diff --git a/procname.c b/procname.c index ee9b99dc..5d3bc306 100644 --- a/procname.c +++ b/procname.c @@ -96,7 +96,7 @@ get_proc_name(int fd, char *tty) retry: if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) - return (NULL); + goto error; len = (len * 5) / 4; if ((newbuf = realloc(buf, len)) == NULL) From 992ef70fb696d648b3ec6ed97642fd79a8392baf Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 07:36:45 +0000 Subject: [PATCH 319/949] Remove the monitor-content option and associated bits and bobs. It's never worked very well. If there is a big demand for it to return, will consider better ways to do it. --- format.c | 2 -- options-table.c | 33 ----------------------------- server-window.c | 55 ++++--------------------------------------------- status.c | 2 -- tmux.1 | 33 ++--------------------------- tmux.h | 6 ++---- window.c | 2 -- 7 files changed, 8 insertions(+), 125 deletions(-) diff --git a/format.c b/format.c index 5fc76b2b..f462a2a5 100644 --- a/format.c +++ b/format.c @@ -487,8 +487,6 @@ format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) format_add(ft, "window_bell_flag", "%u", !!(wl->flags & WINLINK_BELL)); - format_add(ft, "window_content_flag", "%u", - !!(wl->flags & WINLINK_CONTENT)); format_add(ft, "window_activity_flag", "%u", !!(wl->flags & WINLINK_ACTIVITY)); format_add(ft, "window_silence_flag", "%u", diff --git a/options-table.c b/options-table.c index bc25573a..a46f5ce6 100644 --- a/options-table.c +++ b/options-table.c @@ -491,11 +491,6 @@ const struct options_table_entry session_options_table[] = { .default_num = 0 }, - { .name = "visual-content", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - { .name = "visual-silence", .type = OPTIONS_TABLE_FLAG, .default_num = 0 @@ -629,11 +624,6 @@ const struct options_table_entry window_options_table[] = { .default_num = 0 }, - { .name = "monitor-content", - .type = OPTIONS_TABLE_STRING, - .default_str = "" - }, - { .name = "monitor-silence", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, @@ -735,29 +725,6 @@ const struct options_table_entry window_options_table[] = { .style = "window-status-style" }, - { .name = "window-status-content-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = GRID_ATTR_REVERSE, - .style = "window-status-content-style" - }, - - { .name = "window-status-content-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8, - .style = "window-status-content-style" - }, - - { .name = "window-status-content-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8, - .style = "window-status-content-style" - }, - - { .name = "window-status-content-style", - .type = OPTIONS_TABLE_STYLE, - .default_str = "reverse" - }, - { .name = "window-status-current-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0, diff --git a/server-window.c b/server-window.c index 86beeef4..a14c3150 100644 --- a/server-window.c +++ b/server-window.c @@ -27,19 +27,16 @@ int server_window_check_bell(struct session *, struct winlink *); int server_window_check_activity(struct session *, struct winlink *); int server_window_check_silence(struct session *, struct winlink *); -int server_window_check_content( - struct session *, struct winlink *, struct window_pane *); void ring_bell(struct session *); /* Window functions that need to happen every loop. */ void server_window_loop(void) { - struct window *w; - struct winlink *wl; - struct window_pane *wp; - struct session *s; - u_int i; + struct window *w; + struct winlink *wl; + struct session *s; + u_int i; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); @@ -55,8 +52,6 @@ server_window_loop(void) server_window_check_activity(s, wl) || server_window_check_silence(s, wl)) server_status_session(s); - TAILQ_FOREACH(wp, &w->panes, entry) - server_window_check_content(s, wl, wp); } } } @@ -187,48 +182,6 @@ server_window_check_silence(struct session *s, struct winlink *wl) return (1); } -/* Check for content change in window. */ -int -server_window_check_content( - struct session *s, struct winlink *wl, struct window_pane *wp) -{ - struct client *c; - struct window *w = wl->window; - u_int i; - char *found, *ptr; - - /* Activity flag must be set for new content. */ - if (s->curw->window == w) - w->flags &= ~WINDOW_ACTIVITY; - - if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_CONTENT) - return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) - return (0); - - ptr = options_get_string(&w->options, "monitor-content"); - if (ptr == NULL || *ptr == '\0') - return (0); - if ((found = window_pane_search(wp, ptr, NULL)) == NULL) - return (0); - free(found); - - if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); - wl->flags |= WINLINK_CONTENT; - - if (options_get_number(&s->options, "visual-content")) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - status_message_set(c, "Content in window %u", wl->idx); - } - } - - return (1); -} - /* Ring terminal bell. */ void ring_bell(struct session *s) diff --git a/status.c b/status.c index 029be4c8..acbf278e 100644 --- a/status.c +++ b/status.c @@ -638,8 +638,6 @@ status_print( if (wl->flags & WINLINK_BELL) style_apply_update(gc, oo, "window-status-bell-style"); - else if (wl->flags & WINLINK_CONTENT) - style_apply_update(gc, oo, "window-status-content-style"); else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) style_apply_update(gc, oo, "window-status-activity-style"); diff --git a/tmux.1 b/tmux.1 index da4efcdc..39523c99 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2644,15 +2644,6 @@ through to the terminal (which normally makes a sound). Also see the .Ic bell-action option. -.It Xo Ic visual-content -.Op Ic on | off -.Xc -Like -.Ic visual-activity , -display a message when content is present in a window -for which the -.Ic monitor-content -window option is enabled. .It Xo Ic visual-silence .Op Ic on | off .Xc @@ -2834,14 +2825,6 @@ option. Monitor for activity in the window. Windows with activity are highlighted in the status line. .Pp -.It Ic monitor-content Ar match-string -Monitor content in the window. -When -.Xr fnmatch 3 -pattern -.Ar match-string -appears in the window, it is highlighted in the status line. -.Pp .It Xo Ic monitor-silence .Op Ic interval .Xc @@ -2914,14 +2897,6 @@ see the .Ic message-command-style option. .Pp -.It Ic window-status-content-style Ar style -Set status line style for windows with a content alert. -For how to specify -.Ar style , -see the -.Ic message-command-style -option. -.Pp .It Ic window-status-current-format Ar string Like .Ar window-status-format , @@ -3124,7 +3099,6 @@ The following variables are available, where appropriate: .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" -.It Li "window_content_flag" Ta "" Ta "1 if window has content alert" .It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" .It Li "window_flags" Ta "#F" Ta "Window flags" .It Li "window_height" Ta "" Ta "Height of window" @@ -3286,18 +3260,15 @@ The flag is one of the following symbols appended to the window name: .It Li "-" Ta "Marks the last window (previously selected)." .It Li "#" Ta "Window is monitored and activity has been detected." .It Li "!" Ta "A bell has occurred in the window." -.It Li "+" Ta "Window is monitored for content and it has appeared." .It Li "~" Ta "The window has been silent for the monitor-silence interval." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp The # symbol relates to the .Ic monitor-activity -and + to the -.Ic monitor-content -window options. +window option. The window name is printed in inverted colours if an alert (bell, activity or -content) is present. +silence) is present. .Pp The colour and attributes of the status line may be configured, the entire status line using the diff --git a/tmux.h b/tmux.h index 61fa4a39..8964ae00 100644 --- a/tmux.h +++ b/tmux.h @@ -990,10 +990,8 @@ struct winlink { int flags; #define WINLINK_BELL 0x1 #define WINLINK_ACTIVITY 0x2 -#define WINLINK_CONTENT 0x4 -#define WINLINK_SILENCE 0x8 -#define WINLINK_ALERTFLAGS \ - (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_CONTENT|WINLINK_SILENCE) +#define WINLINK_SILENCE 0x4 +#define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE) RB_ENTRY(winlink) entry; TAILQ_ENTRY(winlink) sentry; diff --git a/window.c b/window.c index bb69c0bc..d934dabb 100644 --- a/window.c +++ b/window.c @@ -699,8 +699,6 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '#'; if (wl->flags & WINLINK_BELL) flags[pos++] = '!'; - if (wl->flags & WINLINK_CONTENT) - flags[pos++] = '+'; if (wl->flags & WINLINK_SILENCE) flags[pos++] = '~'; if (wl == s->curw) From ebc5cb447f9ecb8f32e4abd0de639df0fc384402 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 07:43:20 +0000 Subject: [PATCH 320/949] Do not show the -fg, -bg and -attr options. If asked for one explicitly, show the equivalent -style option instead. --- cmd-show-options.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/cmd-show-options.c b/cmd-show-options.c index 529289ea..b2c6ec3c 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -100,15 +100,17 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, struct options *oo, int quiet) { struct args *args = self->args; + const char *name = args->argv[0]; const struct options_table_entry *table, *oe; struct options_entry *o; const char *optval; - if (*args->argv[0] == '@') { - if ((o = options_find1(oo, args->argv[0])) == NULL) { +retry: + if (*name == '@') { + if ((o = options_find1(oo, name)) == NULL) { if (quiet) return (CMD_RETURN_NORMAL); - cmdq_error(cmdq, "unknown option: %s", args->argv[0]); + cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'v')) @@ -119,16 +121,20 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, } table = oe = NULL; - if (options_table_find(args->argv[0], &table, &oe) != 0) { - cmdq_error(cmdq, "ambiguous option: %s", args->argv[0]); + if (options_table_find(name, &table, &oe) != 0) { + cmdq_error(cmdq, "ambiguous option: %s", name); return (CMD_RETURN_ERROR); } if (oe == NULL) { if (quiet) return (CMD_RETURN_NORMAL); - cmdq_error(cmdq, "unknown option: %s", args->argv[0]); + cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } + if (oe->style != NULL) { + name = oe->style; + goto retry; + } if ((o = options_find1(oo, oe->name)) == NULL) return (CMD_RETURN_NORMAL); optval = options_table_print_entry(oe, o, args_has(self->args, 'v')); @@ -157,6 +163,8 @@ cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, } for (oe = table; oe->name != NULL; oe++) { + if (oe->style != NULL) + continue; if ((o = options_find1(oo, oe->name)) == NULL) continue; optval = options_table_print_entry(oe, o, From 877bdb46ed91580a3a4c430bc8c550314301352a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 07:51:38 +0000 Subject: [PATCH 321/949] Extend the -q flag to set-option to suppress errors about unknown options - this will allow options to be removed more easily. --- cmd-set-option.c | 26 ++++++++++++++++++-------- tmux.1 | 4 +--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 9882e449..67eb8083 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -117,8 +117,11 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } if (oe == NULL) { - cmdq_error(cmdq, "unknown option: %s", optstr); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "unknown option: %s", optstr); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } /* Work out the tree from the table. */ @@ -163,8 +166,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } else { if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { - if (!args_has(args, 'q')) - cmdq_print(cmdq, "already set: %s", optstr); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "already set: %s", optstr); + return (CMD_RETURN_ERROR); + } return (CMD_RETURN_NORMAL); } if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0) @@ -229,8 +234,11 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, if (args_has(args, 'u')) { if (options_find1(oo, optstr) == NULL) { - cmdq_error(cmdq, "unknown option: %s", optstr); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "unknown option: %s", optstr); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } if (valstr != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", @@ -244,8 +252,10 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, return (CMD_RETURN_ERROR); } if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { - if (!args_has(args, 'q')) - cmdq_print(cmdq, "already set: %s", optstr); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "already set: %s", optstr); + return CMD_RETURN_ERROR; + } return (CMD_RETURN_NORMAL); } options_set_string(oo, optstr, "%s", valstr); diff --git a/tmux.1 b/tmux.1 index 39523c99..df305239 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2047,9 +2047,7 @@ flag prevents setting an option that is already set. .Pp The .Fl q -flag suppresses the informational message (as if the -.Ic quiet -server option was set). +flag suppresses errors about unknown options. .Pp With .Fl a , From 2740490e279bac7b01f18cc39aa59a5de09e3a95 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 07:55:43 +0000 Subject: [PATCH 322/949] Remove the "info" message mechanism, this was only used for about five mostly useless and annoying messages. Change those commands to silence on success like all the others. Still accept the -q command line flag and "quiet" server option for now. --- cmd-queue.c | 31 ------------------------------- cmd-run-shell.c | 10 +++------- cmd-select-layout.c | 3 --- cmd-set-option.c | 12 ------------ cmd-switch-client.c | 7 ++----- key-bindings.c | 2 +- options-table.c | 2 +- tmux.1 | 12 +----------- tmux.c | 9 ++++----- tmux.h | 1 - 10 files changed, 12 insertions(+), 77 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index caa80afe..7bc154a0 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -86,37 +86,6 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) va_end(ap); } -/* Show info from command. */ -void printflike2 -cmdq_info(struct cmd_q *cmdq, const char *fmt, ...) -{ - struct client *c = cmdq->client; - va_list ap; - char *msg; - - if (options_get_number(&global_options, "quiet")) - return; - - va_start(ap, fmt); - - if (c == NULL) - /* nothing */; - else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - - evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); - } else { - xvasprintf(&msg, fmt, ap); - *msg = toupper((u_char) *msg); - status_message_set(c, "%s", msg); - free(msg); - } - - va_end(ap); - -} - /* Show error from command. */ void printflike2 cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index d835e461..8799e0ab 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -161,13 +161,9 @@ cmd_run_shell_callback(struct job *job) retcode = WTERMSIG(job->status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); } - if (msg != NULL) { - if (lines == 0) - cmdq_info(cmdq, "%s", msg); - else - cmd_run_shell_print(job, msg); - free(msg); - } + if (msg != NULL) + cmd_run_shell_print(job, msg); + free(msg); } void diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 26d8538e..a5f5519c 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -104,7 +104,6 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) else layout = layout_set_previous(wl->window); server_redraw_window(wl->window); - cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); return (CMD_RETURN_NORMAL); } @@ -115,7 +114,6 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) if (layout != -1) { layout = layout_set_select(wl->window, layout); server_redraw_window(wl->window); - cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); return (CMD_RETURN_NORMAL); } @@ -126,7 +124,6 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } server_redraw_window(wl->window); - cmdq_info(cmdq, "arranging in: %s", layoutname); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-option.c b/cmd-set-option.c index 67eb8083..3107746c 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -259,10 +259,6 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, return (CMD_RETURN_NORMAL); } options_set_string(oo, optstr, "%s", valstr); - if (!args_has(args, 'q')) { - cmdq_info(cmdq, "set option: %s -> %s", optstr, - valstr); - } } return (CMD_RETURN_NORMAL); } @@ -285,8 +281,6 @@ cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, } options_remove(oo, oe->name); - if (!args_has(args, 'q')) - cmdq_info(cmdq, "unset option: %s", oe->name); return (0); } @@ -295,9 +289,7 @@ int cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { - struct args *args = self->args; struct options_entry *o; - const char *s; if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) { cmdq_error(cmdq, "empty value"); @@ -337,10 +329,6 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, } if (o == NULL) return (-1); - - s = options_table_print_entry(oe, o, 0); - if (!args_has(args, 'q')) - cmdq_info(cmdq, "set option: %s -> %s", oe->name, s); return (0); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 99a1ae64..872570b0 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -71,13 +71,10 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); if (args_has(args, 'r')) { - if (c->flags & CLIENT_READONLY) { + if (c->flags & CLIENT_READONLY) c->flags &= ~CLIENT_READONLY; - cmdq_info(cmdq, "made client writable"); - } else { + else c->flags |= CLIENT_READONLY; - cmdq_info(cmdq, "made client read-only"); - } } tflag = args_get(args, 't'); diff --git a/key-bindings.c b/key-bindings.c index 625ffddf..db48f9e7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -212,7 +212,7 @@ key_bindings_dispatch(struct key_binding *bd, struct client *c) readonly = 0; } if (!readonly && (c->flags & CLIENT_READONLY)) { - cmdq_info(c->cmdq, "client is read-only"); + cmdq_error(c->cmdq, "client is read-only"); return; } diff --git a/options-table.c b/options-table.c index a46f5ce6..1010e84f 100644 --- a/options-table.c +++ b/options-table.c @@ -90,7 +90,7 @@ const struct options_table_entry server_options_table[] = { { .name = "quiet", .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ + .default_num = 0 }, { .name = "set-clipboard", diff --git a/tmux.1 b/tmux.1 index df305239..6aba515f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2lCquv +.Op Fl 2lCuv .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -168,10 +168,6 @@ server process to recreate it. Behave as a login shell. This flag currently has no effect and is for compatibility with other shells when using tmux as a login shell. -.It Fl q -Set the -.Ic quiet -server option to prevent the server sending various informational messages. .It Fl S Ar socket-path Specify a full alternative path to the server socket. If @@ -2110,12 +2106,6 @@ option. Set the number of error or information messages to save in the message log for each client. The default is 100. -.It Xo Ic quiet -.Op Ic on | off -.Xc -Enable or disable the display of various informational messages (see also the -.Fl q -command line flag). .It Xo Ic set-clipboard .Op Ic on | off .Xc diff --git a/tmux.c b/tmux.c index 8f9e520d..fd2d5173 100644 --- a/tmux.c +++ b/tmux.c @@ -206,7 +206,7 @@ main(int argc, char **argv) char in[256]; const char *home; long long pid; - int opt, flags, quiet, keys, session; + int opt, flags, keys, session; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -214,7 +214,7 @@ main(int argc, char **argv) setlocale(LC_TIME, ""); - quiet = flags = 0; + flags = 0; label = path = NULL; login_shell = (**argv == '-'); while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { @@ -244,7 +244,6 @@ main(int argc, char **argv) label = xstrdup(optarg); break; case 'q': - quiet = 1; break; case 'S': free(path); @@ -291,11 +290,11 @@ main(int argc, char **argv) options_init(&global_options, NULL); options_table_populate_tree(server_options_table, &global_options); - options_set_number(&global_options, "quiet", quiet); options_init(&global_s_options, NULL); options_table_populate_tree(session_options_table, &global_s_options); - options_set_string(&global_s_options, "default-shell", "%s", getshell()); + options_set_string(&global_s_options, "default-shell", "%s", + getshell()); options_init(&global_w_options, NULL); options_table_populate_tree(window_options_table, &global_w_options); diff --git a/tmux.h b/tmux.h index 8964ae00..18cfbbe1 100644 --- a/tmux.h +++ b/tmux.h @@ -1851,7 +1851,6 @@ size_t cmd_list_print(struct cmd_list *, char *, size_t); struct cmd_q *cmdq_new(struct client *); int cmdq_free(struct cmd_q *); void printflike2 cmdq_print(struct cmd_q *, const char *, ...); -void printflike2 cmdq_info(struct cmd_q *, const char *, ...); void printflike2 cmdq_error(struct cmd_q *, const char *, ...); int cmdq_guard(struct cmd_q *, const char *, int); void cmdq_run(struct cmd_q *, struct cmd_list *); From 2e98c9057de6c5700ca01bd58932373b103ef976 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 09:13:13 +0000 Subject: [PATCH 323/949] Correct the dance to fix the active pane in join-pane by pulling the (right) code from break-pane and window_remove_pane into a helper function. --- cmd-break-pane.c | 11 +---------- cmd-join-pane.c | 6 +----- tmux.h | 1 + window.c | 8 +++++++- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index defd22ec..85c5d4d9 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -65,16 +65,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); - if (wp == w->active) { - w->active = w->last; - w->last = NULL; - if (w->active == NULL) { - w->active = TAILQ_PREV(wp, window_panes, entry); - if (w->active == NULL) - w->active = TAILQ_NEXT(wp, entry); - } - } else if (wp == w->last) - w->last = NULL; + window_lost_pane(w, wp); layout_close_pane(wp); w = wp->window = window_create1(s->sx, s->sy); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index b70f93dc..7d7b1eea 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -139,11 +139,7 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) layout_close_pane(src_wp); - if (src_w->active == src_wp) { - src_w->active = TAILQ_PREV(src_wp, window_panes, entry); - if (src_w->active == NULL) - src_w->active = TAILQ_NEXT(src_wp, entry); - } + window_lost_pane(src_w, src_wp); TAILQ_REMOVE(&src_w->panes, src_wp, entry); if (window_count_panes(src_w) == 0) diff --git a/tmux.h b/tmux.h index 18cfbbe1..491027fe 100644 --- a/tmux.h +++ b/tmux.h @@ -2138,6 +2138,7 @@ struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(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); struct window_pane *window_pane_next_by_number(struct window *, diff --git a/window.c b/window.c index d934dabb..faea6049 100644 --- a/window.c +++ b/window.c @@ -591,7 +591,7 @@ window_add_pane(struct window *w, u_int hlimit) } void -window_remove_pane(struct window *w, struct window_pane *wp) +window_lost_pane(struct window *w, struct window_pane *wp) { if (wp == w->active) { w->active = w->last; @@ -603,6 +603,12 @@ window_remove_pane(struct window *w, struct window_pane *wp) } } else if (wp == w->last) w->last = NULL; +} + +void +window_remove_pane(struct window *w, struct window_pane *wp) +{ + window_lost_pane(w, wp); TAILQ_REMOVE(&w->panes, wp, entry); window_pane_destroy(wp); From bce952777a491c5099693e2c256a14e51aecfaa6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 11:38:35 +0000 Subject: [PATCH 324/949] Remove some unnecessary includes and fix a typo. --- cfg.c | 1 - client.c | 2 -- cmd-lock-server.c | 4 ---- cmd-new-session.c | 1 - options-table.c | 4 ++-- window.c | 4 ---- 6 files changed, 2 insertions(+), 14 deletions(-) diff --git a/cfg.c b/cfg.c index 35192dc0..5d98dbe4 100644 --- a/cfg.c +++ b/cfg.c @@ -17,7 +17,6 @@ */ #include -#include #include #include diff --git a/client.c b/client.c index 4cf73fb9..70c1a007 100644 --- a/client.c +++ b/client.c @@ -25,8 +25,6 @@ #include #include -#include -#include #include #include #include diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 97c86c1e..757c2e3f 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -18,10 +18,6 @@ #include -#include -#include -#include - #include "tmux.h" /* diff --git a/cmd-new-session.c b/cmd-new-session.c index db8416e0..22c3c5d6 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/options-table.c b/options-table.c index 1010e84f..abeae7af 100644 --- a/options-table.c +++ b/options-table.c @@ -28,8 +28,8 @@ * options. These tables are the master copy of the options with their real * (user-visible) types, range limits and default values. At start these are * copied into the runtime global options trees (which only has number and - * string types). These tables are then used to loop up the real type when - * the user sets an option or its value needs to be shown. + * string types). These tables are then used to look up the real type when the + * user sets an option or its value needs to be shown. */ /* Choice option type lists. */ diff --git a/window.c b/window.c index faea6049..575ac909 100644 --- a/window.c +++ b/window.c @@ -17,14 +17,10 @@ */ #include -#include #include #include #include -#include -#include -#include #include #include #include From ada75af199405c546588faacf8806e957c163621 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 12:43:38 +0000 Subject: [PATCH 325/949] Don't limit the DCS buffer to 256 bytes, expand it as needed. Requested by Suraj Kurapati. --- input.c | 55 ++++++++++++++++++++++++++++++++++++++++--------------- tmux.h | 5 ++++- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/input.c b/input.c index 40c9d225..1123f952 100644 --- a/input.c +++ b/input.c @@ -55,6 +55,7 @@ void input_set_state(struct window_pane *, const struct input_transition *); /* Transition entry/exit handlers. */ void input_clear(struct input_ctx *); +void input_ground(struct input_ctx *); void input_enter_osc(struct input_ctx *); void input_exit_osc(struct input_ctx *); void input_enter_apc(struct input_ctx *); @@ -242,7 +243,7 @@ const struct input_transition input_state_utf8_one_table[]; /* ground state definition. */ const struct input_state input_state_ground = { "ground", - NULL, NULL, + input_ground, NULL, input_state_ground_table }; @@ -701,6 +702,12 @@ input_init(struct window_pane *wp) *ictx->param_buf = '\0'; ictx->param_len = 0; + ictx->input_space = INPUT_BUF_START; + ictx->input_buf = xmalloc(INPUT_BUF_START); + + *ictx->input_buf = '\0'; + ictx->input_len = 0; + ictx->state = &input_state_ground; ictx->flags = 0; @@ -711,8 +718,11 @@ input_init(struct window_pane *wp) void input_free(struct window_pane *wp) { - if (wp != NULL) - evbuffer_free(wp->ictx.since_ground); + if (wp == NULL) + return; + + free(wp->ictx.input_buf); + evbuffer_free(wp->ictx.since_ground); } /* Change input state. */ @@ -720,14 +730,9 @@ void input_set_state(struct window_pane *wp, const struct input_transition *itr) { struct input_ctx *ictx = &wp->ictx; - struct evbuffer *ground_evb = ictx->since_ground; if (ictx->state->exit != NULL) ictx->state->exit(ictx); - - if (itr->state == &input_state_ground) - evbuffer_drain(ground_evb, EVBUFFER_LENGTH(ground_evb)); - ictx->state = itr->state; if (ictx->state->enter != NULL) ictx->state->enter(ictx); @@ -882,6 +887,18 @@ input_clear(struct input_ctx *ictx) ictx->flags &= ~INPUT_DISCARD; } +/* Reset for ground state. */ +void +input_ground(struct input_ctx *ictx) +{ + evbuffer_drain(ictx->since_ground, EVBUFFER_LENGTH(ictx->since_ground)); + + if (ictx->input_space > INPUT_BUF_START) { + ictx->input_space = INPUT_BUF_START; + ictx->input_buf = xrealloc(ictx->input_buf, 1, INPUT_BUF_START); + } +} + /* Output this character to the screen. */ int input_print(struct input_ctx *ictx) @@ -924,12 +941,20 @@ input_parameter(struct input_ctx *ictx) int input_input(struct input_ctx *ictx) { - if (ictx->input_len == (sizeof ictx->input_buf) - 1) - ictx->flags |= INPUT_DISCARD; - else { - ictx->input_buf[ictx->input_len++] = ictx->ch; - ictx->input_buf[ictx->input_len] = '\0'; + size_t available; + + available = ictx->input_space; + while (ictx->input_len + 1 >= available) { + available *= 2; + if (available > INPUT_BUF_LIMIT) { + ictx->flags |= INPUT_DISCARD; + return (0); + } + ictx->input_buf = xrealloc(ictx->input_buf, 1, available); + ictx->input_space = available; } + ictx->input_buf[ictx->input_len++] = ictx->ch; + ictx->input_buf[ictx->input_len] = '\0'; return (0); } @@ -1666,8 +1691,8 @@ input_enter_osc(struct input_ctx *ictx) void input_exit_osc(struct input_ctx *ictx) { - u_char *p = ictx->input_buf; - int option; + u_char *p = ictx->input_buf; + int option; if (ictx->flags & INPUT_DISCARD) return; diff --git a/tmux.h b/tmux.h index 491027fe..6abc41d3 100644 --- a/tmux.h +++ b/tmux.h @@ -818,8 +818,11 @@ struct input_ctx { u_char param_buf[64]; size_t param_len; - u_char input_buf[256]; +#define INPUT_BUF_START 32 +#define INPUT_BUF_LIMIT 1048576 + u_char* input_buf; size_t input_len; + size_t input_space; int param_list[24]; /* -1 not present */ u_int param_list_len; From c3b2e5eed3b3ad4fd33cf011c5f4de6d25c602c0 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 12:57:28 +0000 Subject: [PATCH 326/949] Wrap some long lines. --- options-table.c | 3 ++- tty-term.c | 3 ++- tty.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/options-table.c b/options-table.c index abeae7af..d68a41c3 100644 --- a/options-table.c +++ b/options-table.c @@ -528,7 +528,8 @@ const struct options_table_entry window_options_table[] = { { .name = "automatic-rename-format", .type = OPTIONS_TABLE_STRING, - .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}#{?pane_dead,[dead],}" + .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" + "#{?pane_dead,[dead],}" }, { .name = "c0-change-trigger", diff --git a/tty-term.c b/tty-term.c index a66aad83..bedf0cfd 100644 --- a/tty-term.c +++ b/tty-term.c @@ -506,7 +506,8 @@ tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a) } const char * -tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, const void *b) +tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, + const void *b) { return (tparm((char *) tty_term_string(term, code), a, b)); } diff --git a/tty.c b/tty.c index b545dc33..f148f420 100644 --- a/tty.c +++ b/tty.c @@ -388,7 +388,8 @@ tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a) } void -tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, const void *b) +tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, + const void *b) { if (a != NULL && b != NULL) tty_puts(tty, tty_term_ptr2(tty->term, code, a, b)); From 3e27be353d045ea231259e96892ad8be273bf9ae Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 13:02:59 +0000 Subject: [PATCH 327/949] Set PATH explicitly, either from client or session environment. Previously it came from the session environment. From J Raynor. --- cmd-new-session.c | 13 ++++++++++++- cmd-new-window.c | 16 +++++++++++++--- cmd-respawn-pane.c | 15 +++++++++++++-- cmd-respawn-window.c | 15 +++++++++++++-- cmd-split-window.c | 14 ++++++++++++-- session.c | 15 ++++++++------- tmux.h | 16 ++++++++-------- window.c | 14 +++++++++----- 8 files changed, 88 insertions(+), 30 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 22c3c5d6..3f8ebe69 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -54,10 +54,12 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; + const char *path; char *cmd, *cause, *cp; int detached, already_attached, idx, cwd, fd = -1; u_int sx, sy; struct format_tree *ft; + struct environ_entry *envent; if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { cmdq_error(cmdq, "command or window name given with target"); @@ -188,6 +190,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) else cmd = options_get_string(&global_s_options, "default-command"); + path = NULL; + if (c != NULL && c->session == NULL) + envent = environ_find(&c->environ, "PATH"); + else + envent = environ_find(&global_environ, "PATH"); + if (envent != NULL) + path = envent->value; + /* Construct the environment. */ environ_init(&env); update = options_get_string(&global_s_options, "update-environment"); @@ -196,7 +206,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); - s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause); + s = session_create(newname, cmd, path, cwd, &env, tiop, idx, sx, sy, + &cause); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); diff --git a/cmd-new-window.c b/cmd-new-window.c index e205e6bc..dc1bbfaa 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -49,10 +49,11 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct session *s; struct winlink *wl; struct client *c; - const char *cmd, *template; + const char *cmd, *path, *template; char *cause, *cp; int idx, last, detached, cwd, fd = -1; struct format_tree *ft; + struct environ_entry *envent; if (args_has(args, 'a')) { wl = cmd_find_window(cmdq, args_get(args, 't'), &s); @@ -77,7 +78,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) server_unlink_window(s, wl); } } else { - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) + idx = cmd_find_index(cmdq, args_get(args, 't'), &s); + if (idx == -2) return (CMD_RETURN_ERROR); } detached = args_has(args, 'd'); @@ -87,6 +89,14 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) else cmd = args->argv[0]; + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(&cmdq->client->environ, "PATH"); + else + envent = environ_find(&s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + if (args_has(args, 'c')) { ft = format_create(); if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) @@ -135,7 +145,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); - wl = session_new(s, args_get(args, 'n'), cmd, cwd, idx, &cause); + wl = session_new(s, args_get(args, 'n'), cmd, path, cwd, idx, &cause); if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index d7d88c27..1df592e7 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -48,9 +48,10 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct environ env; - const char *cmd; + const char *cmd, *path; char *cause; u_int idx; + struct environ_entry *envent; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); @@ -77,7 +78,17 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[0]; else cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) { + + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(&cmdq->client->environ, "PATH"); + else + envent = environ_find(&s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + + if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, + &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index b681f2f4..9dc3f07f 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -47,8 +47,9 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct environ env; - const char *cmd; + const char *cmd, *path; char *cause; + struct environ_entry *envent; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); @@ -79,7 +80,17 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[0]; else cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) { + + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(&cmdq->client->environ, "PATH"); + else + envent = environ_find(&s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + + if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, + &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-split-window.c b/cmd-split-window.c index a431e000..50949b39 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -61,7 +61,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp, *new_wp = NULL; struct environ env; - const char *cmd, *shell, *template; + const char *cmd, *path, *shell, *template; char *cause, *new_cause, *cp; u_int hlimit; int size, percentage, cwd, fd = -1; @@ -69,6 +69,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct layout_cell *lc; struct client *c; struct format_tree *ft; + struct environ_entry *envent; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); @@ -148,8 +149,17 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) goto error; } new_wp = window_add_pane(w, hlimit); + + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(&cmdq->client->environ, "PATH"); + else + envent = environ_find(&s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + if (window_pane_spawn( - new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0) + new_wp, cmd, path, shell, cwd, &env, s->tio, &cause) != 0) goto error; layout_assign_pane(lc, new_wp); diff --git a/session.c b/session.c index 11ae2d35..b7d9844d 100644 --- a/session.c +++ b/session.c @@ -85,8 +85,9 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *name, const char *cmd, int cwd, struct environ *env, - struct termios *tio, int idx, u_int sx, u_int sy, char **cause) +session_create(const char *name, const char *cmd, const char *path, int cwd, + struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, + char **cause) { struct session *s; @@ -132,7 +133,7 @@ session_create(const char *name, const char *cmd, int cwd, struct environ *env, RB_INSERT(sessions, &sessions, s); if (cmd != NULL) { - if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) { + if (session_new(s, NULL, cmd, path, cwd, idx, cause) == NULL) { session_destroy(s); return (NULL); } @@ -226,8 +227,8 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * -session_new(struct session *s, const char *name, const char *cmd, int cwd, - int idx, char **cause) +session_new(struct session *s, const char *name, const char *cmd, + const char *path, int cwd, int idx, char **cause) { struct window *w; struct winlink *wl; @@ -250,8 +251,8 @@ session_new(struct session *s, const char *name, const char *cmd, int cwd, shell = _PATH_BSHELL; hlimit = options_get_number(&s->options, "history-limit"); - w = window_create(name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, - hlimit, cause); + w = window_create(name, cmd, path, shell, cwd, &env, s->tio, s->sx, + s->sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(&env); diff --git a/tmux.h b/tmux.h index 6abc41d3..6787783e 100644 --- a/tmux.h +++ b/tmux.h @@ -2129,9 +2129,9 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *); int window_index(struct window *, u_int *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); -struct window *window_create(const char *, const char *, const char *, int, - struct environ *, struct termios *, u_int, u_int, u_int, - char **); +struct window *window_create(const char *, const char *, const char *, + const char *, int, struct environ *, struct termios *, + u_int, u_int, u_int, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); void window_set_active_at(struct window *, u_int, u_int); @@ -2156,8 +2156,8 @@ struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); void window_pane_timer_start(struct window_pane *); int window_pane_spawn(struct window_pane *, const char *, - const char *, int, struct environ *, struct termios *, - char **); + const char *, const char *, int, struct environ *, + struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); @@ -2293,7 +2293,7 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, int, +struct session *session_create(const char *, const char *, const char *, int, struct environ *, struct termios *, int, u_int, u_int, char **); void session_destroy(struct session *); @@ -2301,8 +2301,8 @@ int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); -struct winlink *session_new(struct session *, const char *, const char *, int, - int, char **); +struct winlink *session_new(struct session *, const char *, const char *, + const char *, int, int, char **); struct winlink *session_attach( struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); diff --git a/window.c b/window.c index 575ac909..2e460641 100644 --- a/window.c +++ b/window.c @@ -308,8 +308,8 @@ window_create1(u_int sx, u_int sy) } struct window * -window_create(const char *name, const char *cmd, const char *shell, - int cwd, struct environ *env, struct termios *tio, +window_create(const char *name, const char *cmd, const char *path, + const char *shell, int cwd, struct environ *env, struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; @@ -319,7 +319,8 @@ window_create(const char *name, const char *cmd, const char *shell, wp = window_add_pane(w, hlimit); layout_init(w, wp); - if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) { + if (window_pane_spawn(wp, cmd, path, shell, cwd, env, tio, + cause) != 0) { window_destroy(w); return (NULL); } @@ -810,8 +811,9 @@ window_pane_destroy(struct window_pane *wp) } int -window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, - int cwd, struct environ *env, struct termios *tio, char **cause) +window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, + const char *shell, int cwd, struct environ *env, struct termios *tio, + char **cause) { struct winsize ws; char *argv0, paneid[16]; @@ -860,6 +862,8 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, closefrom(STDERR_FILENO + 1); + if (path != NULL) + environ_set(env, "PATH", path); xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); environ_set(env, "TMUX_PANE", paneid); environ_push(env); From f194f103a2c8301ba9eea67eefc3e15f76edbca5 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 14:13:59 +0000 Subject: [PATCH 328/949] Only scroll by one line at a time in choose mode, lists are generally pretty small. --- window-choose.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/window-choose.c b/window-choose.c index c354d46f..34ec2eb7 100644 --- a/window-choose.c +++ b/window-choose.c @@ -715,21 +715,23 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) } void -window_choose_mouse( - struct window_pane *wp, unused struct session *sess, struct mouse_event *m) +window_choose_mouse(struct window_pane *wp, struct session *sess, + struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct window_choose_mode_item *item; - u_int i, idx; + u_int idx; if (m->event == MOUSE_EVENT_WHEEL) { - for (i = 0; i < m->scroll; i++) { - if (m->wheel == MOUSE_WHEEL_UP) - window_choose_key(wp, sess, KEYC_UP); - else - window_choose_key(wp, sess, KEYC_DOWN); - } + /* + * Don't use m->scroll and just move line-by-line or it's + * annoying. + */ + if (m->wheel == MOUSE_WHEEL_UP) + window_choose_key(wp, sess, KEYC_UP); + else + window_choose_key(wp, sess, KEYC_DOWN); return; } From 248ad8bdb2778ca19748441efb15c5b274256ace Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 17 Apr 2014 15:26:26 +0100 Subject: [PATCH 329/949] Next up is 2.0 not 1.10. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a23e3aea..7cc1ac5d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 1.10) +AC_INIT(tmux, 2.0) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) From a5d4b7f3d927b267e21aa34c2451669318536e46 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 14:45:49 +0000 Subject: [PATCH 330/949] Some more long lines. --- cmd-find-window.c | 3 ++- cmd-set-option.c | 24 ++++++++++++++++-------- environ.c | 3 ++- grid-view.c | 6 ++++-- layout-set.c | 4 ++-- screen-write.c | 3 ++- server-fn.c | 3 ++- server.c | 2 +- tty.c | 4 ++-- utf8.c | 3 +-- window-copy.c | 6 ++++-- window.c | 3 ++- 12 files changed, 40 insertions(+), 24 deletions(-) diff --git a/cmd-find-window.c b/cmd-find-window.c index 45dbd571..ccc14fa0 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -84,7 +84,8 @@ cmd_find_window_match_flags(struct args *args) void cmd_find_window_match(struct cmd_find_window_data_list *find_list, - int match_flags, struct winlink *wl, const char *str, const char *searchstr) + int match_flags, struct winlink *wl, const char *str, + const char *searchstr) { struct cmd_find_window_data find_data; struct window_pane *wp; diff --git a/cmd-set-option.c b/cmd-set-option.c index 3107746c..d4743d55 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -267,7 +267,8 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, /* Unset an option. */ int cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { struct args *args = self->args; @@ -287,7 +288,8 @@ cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, /* Set an option. */ int cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { struct options_entry *o; @@ -335,7 +337,8 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, /* Set a string option. */ struct options_entry * cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { struct args *args = self->args; struct options_entry *o; @@ -356,7 +359,8 @@ cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, /* Set a number option. */ struct options_entry * cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { long long ll; const char *errstr; @@ -373,7 +377,8 @@ cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, /* Set a key option. */ struct options_entry * cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { int key; @@ -388,7 +393,8 @@ cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, /* Set a colour option. */ struct options_entry * cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { int colour; @@ -403,7 +409,8 @@ cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, /* Set an attributes option. */ struct options_entry * cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { int attr; @@ -418,7 +425,8 @@ cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, /* Set a flag option. */ struct options_entry * cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { int flag; diff --git a/environ.c b/environ.c index 2374d764..284f8b55 100644 --- a/environ.c +++ b/environ.c @@ -137,7 +137,8 @@ environ_unset(struct environ *env, const char *name) * environment. */ void -environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv) +environ_update(const char *vars, struct environ *srcenv, + struct environ *dstenv) { struct environ_entry *envent; char *copyvars, *var, *next; diff --git a/grid-view.c b/grid-view.c index fca5fd54..badabd56 100644 --- a/grid-view.c +++ b/grid-view.c @@ -131,7 +131,8 @@ grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) /* Insert lines in region. */ void -grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) +grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, + u_int ny) { u_int ny2; @@ -160,7 +161,8 @@ grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) /* Delete lines inside scroll region. */ void -grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) +grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, + u_int ny) { u_int ny2; diff --git a/layout-set.c b/layout-set.c index 98ed9736..64b655a0 100644 --- a/layout-set.c +++ b/layout-set.c @@ -23,8 +23,8 @@ #include "tmux.h" /* - * Set window layouts - predefined methods to arrange windows. These are one-off - * and generate a layout tree. + * Set window layouts - predefined methods to arrange windows. These are + * one-off and generate a layout tree. */ void layout_set_even_h(struct window *); diff --git a/screen-write.c b/screen-write.c index 3da145e8..7251c93a 100644 --- a/screen-write.c +++ b/screen-write.c @@ -65,7 +65,8 @@ screen_write_reset(struct screen_write_ctx *ctx) /* Write character. */ void -screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch) +screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, + u_char ch) { grid_cell_one(gc, ch); screen_write_cell(ctx, gc); diff --git a/server-fn.c b/server-fn.c index f585cb71..5b25c925 100644 --- a/server-fn.c +++ b/server-fn.c @@ -291,7 +291,8 @@ server_kill_window(struct window *w) int server_link_window(struct session *src, struct winlink *srcwl, - struct session *dst, int dstidx, int killflag, int selectflag, char **cause) + struct session *dst, int dstidx, int killflag, int selectflag, + char **cause) { struct winlink *dstwl; struct session_group *srcsg, *dstsg; diff --git a/server.c b/server.c index 8ac9321e..ea8b77f1 100644 --- a/server.c +++ b/server.c @@ -216,7 +216,7 @@ server_loop(void) } } -/* Check if the server should be shutting down (no more clients or sessions). */ +/* Check if the server should exit (no more clients or sessions). */ int server_should_shutdown(void) { diff --git a/tty.c b/tty.c index f148f420..c8b6a84a 100644 --- a/tty.c +++ b/tty.c @@ -548,8 +548,8 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } void -tty_emulate_repeat( - struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n) +tty_emulate_repeat(struct tty *tty, enum tty_code_code code, + enum tty_code_code code1, u_int n) { if (tty_term_has(tty->term, code)) tty_putcode1(tty, code, n); diff --git a/utf8.c b/utf8.c index 082683e8..945715dd 100644 --- a/utf8.c +++ b/utf8.c @@ -247,8 +247,7 @@ utf8_append(struct utf8_data *utf8data, u_char ch) /* Check if two width tree entries overlap. */ int -utf8_overlap( - struct utf8_width_entry *item1, struct utf8_width_entry *item2) +utf8_overlap(struct utf8_width_entry *item1, struct utf8_width_entry *item2) { if (item1->first >= item2->first && item1->first <= item2->last) return (1); diff --git a/window-copy.c b/window-copy.c index c33a4c3b..ef99ed9a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2033,7 +2033,8 @@ window_copy_cursor_next_word(struct window_pane *wp, const char *separators) } void -window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) +window_copy_cursor_next_word_end(struct window_pane *wp, + const char *separators) { struct window_copy_mode_data *data = wp->modedata; struct options *oo = &wp->window->options; @@ -2084,7 +2085,8 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) /* Move to the previous place where a word begins. */ void -window_copy_cursor_previous_word(struct window_pane *wp, const char *separators) +window_copy_cursor_previous_word(struct window_pane *wp, + const char *separators) { struct window_copy_mode_data *data = wp->modedata; u_int px, py; diff --git a/window.c b/window.c index 2e460641..1b36f7d7 100644 --- a/window.c +++ b/window.c @@ -1162,7 +1162,8 @@ window_pane_visible(struct window_pane *wp) } char * -window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno) +window_pane_search(struct window_pane *wp, const char *searchstr, + u_int *lineno) { struct screen *s = &wp->base; char *newsearchstr, *line, *msg; From 806520f025dccdcbf266bb9c7cbd984bef00d733 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 15:37:55 +0000 Subject: [PATCH 331/949] Add some UTF-8 utility functions and use them to prevent the width limit on formats from splitting UTF-8 characters improperly. --- format.c | 13 +++--- tmux.h | 19 +++++---- utf8.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 12 deletions(-) diff --git a/format.c b/format.c index f462a2a5..7c8ce779 100644 --- a/format.c +++ b/format.c @@ -194,10 +194,10 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr, *saved; + char *copy, *copy0, *endptr, *ptr, *saved, *trimmed; const char *value; size_t valuelen; - u_long limit = ULONG_MAX; + u_long limit = 0; /* Make a copy of the key. */ copy0 = copy = xmalloc(keylen + 1); @@ -256,11 +256,14 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, value = ""; saved = NULL; } - valuelen = strlen(value); /* Truncate the value if needed. */ - if (valuelen > limit) - valuelen = limit; + if (limit != 0) { + value = trimmed = utf8_trimcstr(value, limit); + free(saved); + saved = trimmed; + } + valuelen = strlen(value); /* Expand the buffer and copy in the value. */ while (*len - *off < valuelen + 1) { diff --git a/tmux.h b/tmux.h index 6787783e..ed0f2e44 100644 --- a/tmux.h +++ b/tmux.h @@ -2306,7 +2306,7 @@ struct winlink *session_new(struct session *, const char *, const char *, struct winlink *session_attach( struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); -struct winlink* session_has(struct session *, struct window *); +struct winlink *session_has(struct session *, struct window *); int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); @@ -2322,12 +2322,17 @@ void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ -void utf8_build(void); -int utf8_open(struct utf8_data *, u_char); -int utf8_append(struct utf8_data *, u_char); -u_int utf8_combine(const struct utf8_data *); -u_int utf8_split2(u_int, u_char *); -int utf8_strvis(char *, const char *, size_t, int); +void utf8_build(void); +void utf8_set(struct utf8_data *, u_char); +int utf8_open(struct utf8_data *, u_char); +int utf8_append(struct utf8_data *, u_char); +u_int utf8_combine(const struct utf8_data *); +u_int utf8_split2(u_int, u_char *); +int utf8_strvis(char *, const char *, size_t, int); +struct utf8_data *utf8_fromcstr(const char *); +char *utf8_tocstr(struct utf8_data *); +u_int utf8_cstrwidth(const char *); +char *utf8_trimcstr(const char *, u_int); /* procname.c */ char *get_proc_name(int, char *); diff --git a/utf8.c b/utf8.c index 945715dd..78ed1675 100644 --- a/utf8.c +++ b/utf8.c @@ -18,6 +18,7 @@ #include +#include #include #include @@ -199,6 +200,16 @@ int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *); u_int utf8_combine(const struct utf8_data *); u_int utf8_width(const struct utf8_data *); +/* Set a single character. */ +void +utf8_set(struct utf8_data *utf8data, u_char ch) +{ + *utf8data->data = ch; + utf8data->size = 1; + + utf8data->width = 1; +} + /* * Open UTF-8 sequence. * @@ -392,3 +403,111 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) *dst = '\0'; return (dst - start); } + +/* + * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. + * Caller frees. + */ +struct utf8_data * +utf8_fromcstr(const char *src) +{ + struct utf8_data *dst; + size_t n; + int more; + + dst = NULL; + + n = 0; + while (*src != '\0') { + dst = xrealloc(dst, n + 1, sizeof *dst); + if (utf8_open(&dst[n], *src)) { + more = 1; + while (*++src != '\0' && more) + more = utf8_append(&dst[n], *src); + if (!more) { + n++; + continue; + } + src -= dst[n].have; + } + utf8_set(&dst[n], *src); + src++; + + n++; + } + + dst = xrealloc(dst, n + 1, sizeof *dst); + dst[n].size = 0; + return (dst); +} + +/* Convert from a buffer of UTF-8 characters into a string. Caller frees. */ +char * +utf8_tocstr(struct utf8_data *src) +{ + char *dst; + size_t n; + + dst = NULL; + + n = 0; + for(; src->size != 0; src++) { + dst = xrealloc(dst, n + src->size, 1); + memcpy(dst + n, src->data, src->size); + n += src->size; + } + + dst = xrealloc(dst, n + 1, 1); + dst[n] = '\0'; + return (dst); +} + +/* Get width of UTF-8 string. */ +u_int +utf8_cstrwidth(const char *s) +{ + struct utf8_data tmp; + u_int width; + int more; + + width = 0; + while (*s != '\0') { + if (utf8_open(&tmp, *s)) { + more = 1; + while (*++s != '\0' && more) + more = utf8_append(&tmp, *s); + if (!more) { + width += tmp.width; + continue; + } + s -= tmp.have; + } + width++; + s++; + } + return (width); +} + +/* Trim UTF-8 string to width. Caller frees. */ +char * +utf8_trimcstr(const char *s, u_int width) +{ + struct utf8_data *tmp, *next; + char *out; + u_int at; + + tmp = utf8_fromcstr(s); + + at = 0; + for (next = tmp; next->size != 0; next++) { + if (at + next->width > width) { + next->size = 0; + break; + } + at += next->width; + } + + out = utf8_tocstr(tmp); + free(tmp); + return (out); +} From 64613b9d411a7c76a50a2f9c66df345f082fce25 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Apr 2014 15:48:02 +0000 Subject: [PATCH 332/949] #nnT went away a while ago, remove a leftover from the manpage. --- tmux.1 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index 6aba515f..44e69f6e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2535,10 +2535,6 @@ Examples are: #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp -Where appropriate, special character sequences may be prefixed with a number to -specify the maximum length, for example -.Ql #24T . -.Pp By default, UTF-8 in .Ar string is not interpreted, to enable UTF-8, use the From 024846b4d82ad57e68b64cac2ac12b932a9042d2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 17 Apr 2014 23:48:19 +0100 Subject: [PATCH 333/949] If pgrp fails in osdep_get_cwd, try sid. Fixes eg cat foo|less. From Balazs Kezes. --- osdep-linux.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osdep-linux.c b/osdep-linux.c index ccac2670..46aea68e 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -65,7 +65,7 @@ osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; - pid_t pgrp; + pid_t pgrp, sid; ssize_t n; if ((pgrp = tcgetpgrp(fd)) == -1) @@ -74,6 +74,13 @@ osdep_get_cwd(int fd) xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); + + if (n == -1 && ioctl(fd, TIOCGSID, &sid) != -1) { + xasprintf(&path, "/proc/%lld/cwd", (long long) sid); + n = readlink(path, target, MAXPATHLEN); + free(path); + } + if (n > 0) { target[n] = '\0'; return (target); From 7ab2690be8a451d123b12906b729e163ea37e0dd Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 23 Apr 2014 10:14:29 +0000 Subject: [PATCH 334/949] Differentiate between linked and unlinked window closes and renames, like we already do for adds. From Andre Masella. --- control-notify.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/control-notify.c b/control-notify.c index a298cdc5..4ea1570b 100644 --- a/control-notify.c +++ b/control-notify.c @@ -99,14 +99,19 @@ void control_notify_window_unlinked(unused struct session *s, struct window *w) { struct client *c; + struct session *cs; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; + cs = c->session; - control_write(c, "%%window-close @%u", w->id); + if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) + control_write(c, "%%window-close @%u", w->id); + else + control_write(c, "%%unlinked-window-close @%u", w->id); } } @@ -134,14 +139,22 @@ void control_notify_window_renamed(struct window *w) { struct client *c; + struct session *cs; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; + cs = c->session; - control_write(c, "%%window-renamed @%u %s", w->id, w->name); + if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) { + control_write(c, "%%window-renamed @%u %s", w->id, + w->name); + } else { + control_write(c, "%%unlinked-window-renamed @%u %s", + w->id, w->name); + } } } From bec6c807cd580b003cd94b52a20caa2cbc7a0753 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Apr 2014 09:14:43 +0000 Subject: [PATCH 335/949] There is no longer a need for a paste_stack struct or for global_buffers to be global. Move to paste.c. --- cmd-capture-pane.c | 4 ++-- cmd-choose-buffer.c | 4 ++-- cmd-delete-buffer.c | 4 ++-- cmd-list-buffers.c | 2 +- cmd-load-buffer.c | 8 ++++---- cmd-paste-buffer.c | 8 ++++---- cmd-save-buffer.c | 4 ++-- cmd-set-buffer.c | 8 ++++---- input-keys.c | 2 +- paste.c | 50 +++++++++++++++++++++++---------------------- server.c | 3 --- status.c | 2 +- tmux.h | 16 +++++++-------- window-copy.c | 14 ++++++------- 14 files changed, 63 insertions(+), 66 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index cf9a2f49..759063d1 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -194,7 +194,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) } else { limit = options_get_number(&global_options, "buffer-limit"); if (!args_has(args, 'b')) { - paste_add(&global_buffers, buf, len, limit); + paste_add(buf, len, limit); return (CMD_RETURN_NORMAL); } @@ -206,7 +206,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if (paste_replace(&global_buffers, buffer, buf, len) != 0) { + if (paste_replace(buffer, buf, len) != 0) { cmdq_error(cmdq, "no buffer %d", buffer); free(buf); return (CMD_RETURN_ERROR); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 1e0edaa6..c6c70a0d 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -63,7 +63,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); utf8flag = options_get_number(&wl->window->options, "utf8"); - if (paste_get_top(&global_buffers) == NULL) + if (paste_get_top() == NULL) return (CMD_RETURN_NORMAL); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) @@ -75,7 +75,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) action = xstrdup("paste-buffer -b '%%'"); idx = 0; - while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { + while ((pb = paste_walk_stack(&idx)) != NULL) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = idx - 1; diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c index 32fb243b..464d2b78 100644 --- a/cmd-delete-buffer.c +++ b/cmd-delete-buffer.c @@ -45,7 +45,7 @@ cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) int buffer; if (!args_has(args, 'b')) { - paste_free_top(&global_buffers); + paste_free_top(); return (CMD_RETURN_NORMAL); } @@ -56,7 +56,7 @@ cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if (paste_free_index(&global_buffers, buffer) != 0) { + if (paste_free_index(buffer) != 0) { cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 7051eae8..2363c806 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -52,7 +52,7 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) template = LIST_BUFFERS_TEMPLATE; idx = 0; - while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { + while ((pb = paste_walk_stack(&idx)) != NULL) { ft = format_create(); format_add(ft, "line", "%u", idx - 1); format_paste_buffer(ft, pb, 0); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 636f0fcd..5e9b6748 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -119,10 +119,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) limit = options_get_number(&global_options, "buffer-limit"); if (buffer == -1) { - paste_add(&global_buffers, pdata, psize, limit); + paste_add(pdata, psize, limit); return (CMD_RETURN_NORMAL); } - if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { + if (paste_replace(buffer, pdata, psize) != 0) { cmdq_error(cmdq, "no buffer %d", buffer); free(pdata); return (CMD_RETURN_ERROR); @@ -164,8 +164,8 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) limit = options_get_number(&global_options, "buffer-limit"); if (*buffer == -1) - paste_add(&global_buffers, pdata, psize, limit); - else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) { + paste_add(pdata, psize, limit); + else if (paste_replace(*buffer, pdata, psize) != 0) { /* No context so can't use server_client_msg_error. */ evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); server_push_stderr(c); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 8f7530a2..c3837c6d 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -69,9 +69,9 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) } if (buffer == -1) - pb = paste_get_top(&global_buffers); + pb = paste_get_top(); else { - pb = paste_get_index(&global_buffers, buffer); + pb = paste_get_index(buffer); if (pb == NULL) { cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); @@ -93,9 +93,9 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) /* Delete the buffer if -d. */ if (args_has(args, 'd')) { if (buffer == -1) - paste_free_top(&global_buffers); + paste_free_top(); else - paste_free_index(&global_buffers, buffer); + paste_free_index(buffer); } return (CMD_RETURN_NORMAL); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index b6ee2e49..0a1853d4 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -66,7 +66,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) FILE *f; if (!args_has(args, 'b')) { - if ((pb = paste_get_top(&global_buffers)) == NULL) { + if ((pb = paste_get_top()) == NULL) { cmdq_error(cmdq, "no buffers"); return (CMD_RETURN_ERROR); } @@ -78,7 +78,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - pb = paste_get_index(&global_buffers, buffer); + pb = paste_get_index(buffer); if (pb == NULL) { cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index a206760c..5dabc1ee 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -66,13 +66,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) free(cause); return (CMD_RETURN_ERROR); } - pb = paste_get_index(&global_buffers, buffer); + pb = paste_get_index(buffer); if (pb == NULL) { cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'a')) { - pb = paste_get_top(&global_buffers); + pb = paste_get_top(); if (pb != NULL) buffer = 0; } @@ -88,9 +88,9 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) psize += newsize; if (buffer == -1) - paste_add(&global_buffers, pdata, psize, limit); + paste_add(pdata, psize, limit); else - paste_replace(&global_buffers, buffer, pdata, psize); + paste_replace(buffer, pdata, psize); return (CMD_RETURN_NORMAL); } diff --git a/input-keys.c b/input-keys.c index 0370a684..6e21cc0c 100644 --- a/input-keys.c +++ b/input-keys.c @@ -240,7 +240,7 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) if (m->button == 1 && (m->event & MOUSE_EVENT_CLICK) && options_get_number(&wp->window->options, "mode-mouse") == 1) { - pb = paste_get_top(&global_buffers); + pb = paste_get_top(); if (pb != NULL) { paste_send_pane(pb, wp, "\r", wp->screen->mode & MODE_BRACKETPASTE); diff --git a/paste.c b/paste.c index 98d71a07..9492fd91 100644 --- a/paste.c +++ b/paste.c @@ -30,46 +30,48 @@ * string! */ +ARRAY_DECL(, struct paste_buffer *) paste_buffers = ARRAY_INITIALIZER; + /* Return each item of the stack in turn. */ struct paste_buffer * -paste_walk_stack(struct paste_stack *ps, u_int *idx) +paste_walk_stack(u_int *idx) { struct paste_buffer *pb; - pb = paste_get_index(ps, *idx); + pb = paste_get_index(*idx); (*idx)++; return (pb); } /* Get the top item on the stack. */ struct paste_buffer * -paste_get_top(struct paste_stack *ps) +paste_get_top(void) { - if (ARRAY_LENGTH(ps) == 0) + if (ARRAY_LENGTH(&paste_buffers) == 0) return (NULL); - return (ARRAY_FIRST(ps)); + return (ARRAY_FIRST(&paste_buffers)); } /* Get an item by its index. */ struct paste_buffer * -paste_get_index(struct paste_stack *ps, u_int idx) +paste_get_index(u_int idx) { - if (idx >= ARRAY_LENGTH(ps)) + if (idx >= ARRAY_LENGTH(&paste_buffers)) return (NULL); - return (ARRAY_ITEM(ps, idx)); + return (ARRAY_ITEM(&paste_buffers, idx)); } /* Free the top item on the stack. */ int -paste_free_top(struct paste_stack *ps) +paste_free_top(void) { struct paste_buffer *pb; - if (ARRAY_LENGTH(ps) == 0) + if (ARRAY_LENGTH(&paste_buffers) == 0) return (-1); - pb = ARRAY_FIRST(ps); - ARRAY_REMOVE(ps, 0); + pb = ARRAY_FIRST(&paste_buffers); + ARRAY_REMOVE(&paste_buffers, 0); free(pb->data); free(pb); @@ -79,15 +81,15 @@ paste_free_top(struct paste_stack *ps) /* Free an item by index. */ int -paste_free_index(struct paste_stack *ps, u_int idx) +paste_free_index(u_int idx) { struct paste_buffer *pb; - if (idx >= ARRAY_LENGTH(ps)) + if (idx >= ARRAY_LENGTH(&paste_buffers)) return (-1); - pb = ARRAY_ITEM(ps, idx); - ARRAY_REMOVE(ps, idx); + pb = ARRAY_ITEM(&paste_buffers, idx); + ARRAY_REMOVE(&paste_buffers, idx); free(pb->data); free(pb); @@ -100,22 +102,22 @@ paste_free_index(struct paste_stack *ps, u_int idx) * that the caller is responsible for allocating data. */ void -paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit) +paste_add(char *data, size_t size, u_int limit) { struct paste_buffer *pb; if (size == 0) return; - while (ARRAY_LENGTH(ps) >= limit) { - pb = ARRAY_LAST(ps); + while (ARRAY_LENGTH(&paste_buffers) >= limit) { + pb = ARRAY_LAST(&paste_buffers); free(pb->data); free(pb); - ARRAY_TRUNC(ps, 1); + ARRAY_TRUNC(&paste_buffers, 1); } pb = xmalloc(sizeof *pb); - ARRAY_INSERT(ps, 0, pb); + ARRAY_INSERT(&paste_buffers, 0, pb); pb->data = data; pb->size = size; @@ -127,7 +129,7 @@ paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit) * allocating data. */ int -paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) +paste_replace(u_int idx, char *data, size_t size) { struct paste_buffer *pb; @@ -136,10 +138,10 @@ paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) return (0); } - if (idx >= ARRAY_LENGTH(ps)) + if (idx >= ARRAY_LENGTH(&paste_buffers)) return (-1); - pb = ARRAY_ITEM(ps, idx); + pb = ARRAY_ITEM(&paste_buffers, idx); free(pb->data); pb->data = data; diff --git a/server.c b/server.c index ea8b77f1..0d6c4f15 100644 --- a/server.c +++ b/server.c @@ -51,8 +51,6 @@ int server_shutdown; struct event server_ev_accept; struct event server_ev_second; -struct paste_stack global_buffers; - int server_create_socket(void); void server_loop(void); int server_should_shutdown(void); @@ -147,7 +145,6 @@ server_start(int lockfd, char *lockfile) RB_INIT(&sessions); RB_INIT(&dead_sessions); TAILQ_INIT(&session_groups); - ARRAY_INIT(&global_buffers); mode_key_init_trees(); key_bindings_init(); utf8_build(); diff --git a/status.c b/status.c index acbf278e..4ea42f1c 100644 --- a/status.c +++ b/status.c @@ -1157,7 +1157,7 @@ status_prompt_key(struct client *c, int key) c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PASTE: - if ((pb = paste_get_top(&global_buffers)) == NULL) + if ((pb = paste_get_top()) == NULL) break; for (n = 0; n < pb->size; n++) { ch = (u_char) pb->data[n]; diff --git a/tmux.h b/tmux.h index ed0f2e44..68a8517b 100644 --- a/tmux.h +++ b/tmux.h @@ -1037,7 +1037,6 @@ struct paste_buffer { char *data; size_t size; }; -ARRAY_DECL(paste_stack, struct paste_buffer *); /* Environment variable. */ struct environ_entry { @@ -1709,13 +1708,13 @@ void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* paste.c */ -struct paste_buffer *paste_walk_stack(struct paste_stack *, u_int *); -struct paste_buffer *paste_get_top(struct paste_stack *); -struct paste_buffer *paste_get_index(struct paste_stack *, u_int); -int paste_free_top(struct paste_stack *); -int paste_free_index(struct paste_stack *, u_int); -void paste_add(struct paste_stack *, char *, size_t, u_int); -int paste_replace(struct paste_stack *, u_int, char *, size_t); +struct paste_buffer *paste_walk_stack(u_int *); +struct paste_buffer *paste_get_top(void); +struct paste_buffer *paste_get_index(u_int); +int paste_free_top(void); +int paste_free_index(u_int); +void paste_add(char *, size_t, u_int); +int paste_replace(u_int, char *, size_t); char *paste_make_sample(struct paste_buffer *, int); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); @@ -1886,7 +1885,6 @@ const char *key_string_lookup_key(int); /* server.c */ extern struct clients clients; extern struct clients dead_clients; -extern struct paste_stack global_buffers; int server_start(int, char *); void server_update_socket(void); void server_add_accept(int); diff --git a/window-copy.c b/window-copy.c index ef99ed9a..296443a2 100644 --- a/window-copy.c +++ b/window-copy.c @@ -776,7 +776,7 @@ window_copy_key_input(struct window_pane *wp, int key) *data->inputstr = '\0'; break; case MODEKEYEDIT_PASTE: - if ((pb = paste_get_top(&global_buffers)) == NULL) + if ((pb = paste_get_top()) == NULL) break; for (n = 0; n < pb->size; n++) { ch = (u_char) pb->data[n]; @@ -1465,8 +1465,8 @@ window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) if (idx == -1) { limit = options_get_number(&global_options, "buffer-limit"); - paste_add(&global_buffers, buf, len, limit); - } else if (paste_replace(&global_buffers, idx, buf, len) != 0) + paste_add(buf, len, limit); + } else if (paste_replace(idx, buf, len) != 0) free(buf); } @@ -1524,13 +1524,13 @@ window_copy_append_selection(struct window_pane *wp, int idx) if (idx == -1) idx = 0; - if (idx == 0 && paste_get_top(&global_buffers) == NULL) { + if (idx == 0 && paste_get_top() == NULL) { limit = options_get_number(&global_options, "buffer-limit"); - paste_add(&global_buffers, buf, len, limit); + paste_add(buf, len, limit); return; } - pb = paste_get_index(&global_buffers, idx); + pb = paste_get_index(idx); if (pb != NULL) { buf = xrealloc(buf, 1, len + pb->size); memmove(buf + pb->size, buf, len); @@ -1538,7 +1538,7 @@ window_copy_append_selection(struct window_pane *wp, int idx) len += pb->size; } - if (paste_replace(&global_buffers, idx, buf, len) != 0) + if (paste_replace(idx, buf, len) != 0) free(buf); } From 40cb4cb086210916bf5b6140d6f78049073863e6 Mon Sep 17 00:00:00 2001 From: jsg Date: Fri, 25 Apr 2014 12:45:16 +0000 Subject: [PATCH 336/949] specifying ECHOCTL once is enough ok nicm@ --- tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty.c b/tty.c index c8b6a84a..3ae21ab5 100644 --- a/tty.c +++ b/tty.c @@ -193,7 +193,7 @@ tty_init_termios(int fd, struct termios *orig_tio, struct bufferevent *bufev) tio.c_iflag |= IGNBRK; tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| - ECHOPRT|ECHOKE|ECHOCTL|ISIG); + ECHOPRT|ECHOKE|ISIG); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr(fd, TCSANOW, &tio) == 0) From b096ad9f2227afb4a9b70b2107431c82a1528eb8 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Tue, 29 Apr 2014 23:10:22 +0100 Subject: [PATCH 337/949] Makefile.am: Honour $srcdir for mdoc2man.awk Honour out-of-tree srcdir installs when generating the man page. Noticed by Jon Tibble. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index dd6756b9..bca0064d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -247,7 +247,7 @@ install-exec-hook: >$(srcdir)/tmux.1.mdoc; \ else \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ - $(AWK) -fmdoc2man.awk >$(srcdir)/tmux.1.man; \ + $(AWK) -f $(srcdoc)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ fi $(MKDIR_P) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ From afb4dbd4655a1f0c988d8c65723172144ab79cce Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Tue, 29 Apr 2014 23:13:51 +0100 Subject: [PATCH 338/949] Typo in previous: $(srcdir) --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index bca0064d..5502de86 100644 --- a/Makefile.am +++ b/Makefile.am @@ -247,7 +247,7 @@ install-exec-hook: >$(srcdir)/tmux.1.mdoc; \ else \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ - $(AWK) -f $(srcdoc)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ + $(AWK) -f $(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ fi $(MKDIR_P) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ From 5b2c8156d528f284c63bc0a698805e4461431257 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 29 Apr 2014 22:31:22 +0000 Subject: [PATCH 339/949] fcntl.h is still needed here. --- client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client.c b/client.c index 70c1a007..ec63fc07 100644 --- a/client.c +++ b/client.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include From 6369ea10d7f45dfc5c77e7ba163403b37e254e0f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 8 May 2014 05:53:29 +0000 Subject: [PATCH 340/949] Handle colour 8 properly in the 256 colour palette, from Timothy Allen. --- style.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/style.c b/style.c index 99744086..2a049fb6 100644 --- a/style.c +++ b/style.c @@ -117,7 +117,7 @@ style_tostring(struct grid_cell *gc) *s = '\0'; - if (gc->fg != 8) { + if (gc->fg != 8 || gc->flags & GRID_FLAG_FG256) { if (gc->flags & GRID_FLAG_FG256) c = gc->fg | 0x100; else @@ -126,7 +126,7 @@ style_tostring(struct grid_cell *gc) comma = 1; } - if (gc->bg != 8) { + if (gc->bg != 8 || gc->flags & GRID_FLAG_BG256) { if (gc->flags & GRID_FLAG_BG256) c = gc->bg | 0x100; else @@ -221,13 +221,13 @@ style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) struct grid_cell *gcp; gcp = options_get_style(oo, name); - if (gcp->fg != 8) { + if (gcp->fg != 8 || gcp->flags & GRID_FLAG_FG256) { if (gcp->flags & GRID_FLAG_FG256) colour_set_fg(gc, gcp->fg | 0x100); else colour_set_fg(gc, gcp->fg); } - if (gcp->bg != 8) { + if (gcp->bg != 8 || gcp->flags & GRID_FLAG_BG256) { if (gcp->flags & GRID_FLAG_BG256) colour_set_bg(gc, gcp->bg | 0x100); else From 540f0b3e45acae9b0c5fa12f7e9170c6c78d177f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 8 May 2014 06:03:30 +0000 Subject: [PATCH 341/949] Both the two previous ways of navigating panes by direction have irritating flaws: a) The old way of always using the top or left if the choice is ambiguous is annoying when the layout is unbalanced. b) The new way of remembering the last used pane is annoying if the layout is balanced and the leftmost is obvious to the user (because clearly if we go right from the top-left in a tiled set of four we want to end up in top-right, even if we were last using the bottom-right). So instead, use a combination of both: if there is only one possible pane alongside the current pane, move to it, otherwise choose the most recently used of the choice. --- layout.c | 1 - tmux.h | 4 +- window.c | 267 +++++++++++++++++++++++++++++-------------------------- 3 files changed, 143 insertions(+), 129 deletions(-) diff --git a/layout.c b/layout.c index 1c76f986..b91b86cd 100644 --- a/layout.c +++ b/layout.c @@ -53,7 +53,6 @@ layout_create_cell(struct layout_cell *lcparent) lc->yoff = UINT_MAX; lc->wp = NULL; - lc->lastwp = NULL; return (lc); } diff --git a/tmux.h b/tmux.h index 68a8517b..b0a37f25 100644 --- a/tmux.h +++ b/tmux.h @@ -892,6 +892,7 @@ struct window_choose_mode_item { /* Child window structure. */ struct window_pane { u_int id; + u_int active_point; struct window *window; @@ -948,6 +949,7 @@ struct window_pane { }; TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); +ARRAY_DECL(window_pane_list, struct window_pane *); /* Window structure. */ struct window { @@ -1025,8 +1027,6 @@ struct layout_cell { u_int yoff; struct window_pane *wp; - struct window_pane *lastwp; - struct layout_cells cells; TAILQ_ENTRY(layout_cell) entry; diff --git a/window.c b/window.c index 1b36f7d7..40ebcdb5 100644 --- a/window.c +++ b/window.c @@ -56,15 +56,14 @@ struct windows windows; struct window_pane_tree all_window_panes; u_int next_window_pane_id; u_int next_window_id; - -struct window_pane *window_pane_active_set(struct window_pane *, - struct window_pane *); -void window_pane_active_lost(struct window_pane *, struct window_pane *); +u_int next_active_point; void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); +struct window_pane *window_pane_choose_best(struct window_pane_list *); + RB_GENERATE(winlinks, winlink, entry, winlink_cmp); int @@ -387,64 +386,6 @@ window_resize(struct window *w, u_int sx, u_int sy) w->sy = sy; } -/* - * Restore previously active pane when changing from wp to nextwp. The intended - * pane is in nextwp and it returns the previously focused pane. - */ -struct window_pane * -window_pane_active_set(struct window_pane *wp, struct window_pane *nextwp) -{ - struct layout_cell *lc; - struct window_pane *lastwp; - - /* Target pane's parent must not be an ancestor of source pane. */ - for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { - if (lc == nextwp->layout_cell->parent) - return (nextwp); - } - - /* - * Previously active pane, if any, must not be the same as the source - * pane. - */ - lc = nextwp->layout_cell->parent; - if (lc != NULL && lc->lastwp != NULL) { - lastwp = lc->lastwp; - if (lastwp != wp && window_pane_visible(lastwp)) - return (lastwp); - } - return (nextwp); -} - -/* Remember previously active pane when changing from wp to nextwp. */ -void -window_pane_active_lost(struct window_pane *wp, struct window_pane *nextwp) -{ - struct layout_cell *lc, *lc2, *lcparent; - - /* Get the parent cell. */ - lcparent = nextwp->layout_cell->parent; - if (lcparent == NULL) - return; - - /* Save the target pane in its parent. */ - lcparent->lastwp = nextwp; - - /* - * Save the source pane in all of its parents up to, but not including, - * the common ancestor of itself and the target panes. - */ - if (wp == NULL) - return; - for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { - for (lc2 = lcparent; lc2 != NULL; lc2 = lc2->parent) { - if (lc == lc2) - return; - } - lc->lastwp = wp; - } -} - void window_set_active_pane(struct window *w, struct window_pane *wp) { @@ -452,7 +393,6 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return; w->last = w->active; w->active = wp; - window_pane_active_lost(w->last, wp); while (!window_pane_visible(w->active)) { w->active = TAILQ_PREV(w->active, window_panes, entry); if (w->active == NULL) @@ -460,6 +400,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp) if (w->active == wp) return; } + w->active->active_point = next_active_point++; } struct window_pane * @@ -771,16 +712,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) void window_pane_destroy(struct window_pane *wp) { - struct window_pane *wp2; - - /* Forget removed pane in all layout cells that remember it. */ - RB_FOREACH(wp2, window_pane_tree, &all_window_panes) { - if (wp2->layout_cell != NULL && - wp2->layout_cell->parent != NULL && - wp2->layout_cell->parent->lastwp == wp) - wp2->layout_cell->parent->lastwp = NULL; - } - window_pane_reset_mode(wp); if (event_initialized(&wp->changes_timer)) @@ -1187,114 +1118,198 @@ window_pane_search(struct window_pane *wp, const char *searchstr, return (msg); } -/* Find the pane directly above another. */ +/* Get MRU pane from a list. */ +struct window_pane * +window_pane_choose_best(struct window_pane_list *list) +{ + struct window_pane *next, *best; + u_int i; + + if (ARRAY_LENGTH(list) == 0) + return (NULL); + + best = ARRAY_FIRST(list); + for (i = 1; i < ARRAY_LENGTH(list); i++) { + next = ARRAY_ITEM(list, i); + if (next->active_point > best->active_point) + best = next; + } + return (best); +} + +/* + * Find the pane directly above another. We build a list of those adjacent to + * top edge and then choose the best. + */ struct window_pane * window_pane_find_up(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, top; + struct window_pane *next, *best; + u_int edge, left, right, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->yoff; + if (edge == 0) + edge = wp->window->sy + 1; - top = wp->yoff; - if (top == 0) - top = wp->window->sy + 1; left = wp->xoff; + right = wp->xoff + wp->sx; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->yoff + wp2->sy + 1 != top) + if (next->yoff + next->sy + 1 != edge) continue; - if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (window_pane_active_set(wp, wp2)); + end = next->xoff + next->sx - 1; + + found = 0; + if (next->xoff < left && end > right) + found = 1; + else if (next->xoff >= left && next->xoff <= right) + found = 1; + else if (end >= left && end <= right) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } /* Find the pane directly below another. */ struct window_pane * window_pane_find_down(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, bottom; + struct window_pane *next, *best; + u_int edge, left, right, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->yoff + wp->sy + 1; + if (edge >= wp->window->sy) + edge = 0; - bottom = wp->yoff + wp->sy + 1; - if (bottom >= wp->window->sy) - bottom = 0; left = wp->xoff; + right = wp->xoff + wp->sx; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->yoff != bottom) + if (next->yoff != edge) continue; - if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (window_pane_active_set(wp, wp2)); + end = next->xoff + next->sx - 1; + + found = 0; + if (next->xoff < left && end > right) + found = 1; + else if (next->xoff >= left && next->xoff <= right) + found = 1; + else if (end >= left && end <= right) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } -/* - * Find the pane directly to the left of another, adjacent to the left side and - * containing the top edge. - */ +/* Find the pane directly to the left of another. */ struct window_pane * window_pane_find_left(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, top; + struct window_pane *next, *best; + u_int edge, top, bottom, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->xoff; + if (edge == 0) + edge = wp->window->sx + 1; - left = wp->xoff; - if (left == 0) - left = wp->window->sx + 1; top = wp->yoff; + bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->xoff + wp2->sx + 1 != left) + if (next->xoff + next->sx + 1 != edge) continue; - if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (window_pane_active_set(wp, wp2)); + end = next->yoff + next->sy - 1; + + found = 0; + if (next->yoff < top && end > bottom) + found = 1; + else if (next->yoff >= top && next->yoff <= bottom) + found = 1; + else if (end >= top && end <= bottom) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } -/* - * Find the pane directly to the right of another, that is adjacent to the - * right edge and including the top edge. - */ +/* Find the pane directly to the right of another. */ struct window_pane * window_pane_find_right(struct window_pane *wp) { - struct window_pane *wp2; - u_int right, top; + struct window_pane *next, *best; + u_int edge, top, bottom, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->xoff + wp->sx + 1; + if (edge >= wp->window->sx) + edge = 0; - right = wp->xoff + wp->sx + 1; - if (right >= wp->window->sx) - right = 0; top = wp->yoff; + bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->xoff != right) + if (next->xoff != edge) continue; - if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (window_pane_active_set(wp, wp2)); + end = next->yoff + next->sy - 1; + + found = 0; + if (next->yoff < top && end > bottom) + found = 1; + else if (next->yoff >= top && next->yoff <= bottom) + found = 1; + else if (end >= top && end <= bottom) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } /* Clear alert flags for a winlink */ From 189017c078b7870c18ced485c1fd99f65fcc4801 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 8 May 2014 06:06:07 +0000 Subject: [PATCH 342/949] Plug a memory leak, from J Raynor. --- cmd-find-window.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-find-window.c b/cmd-find-window.c index ccc14fa0..ccf7f0f2 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -200,6 +200,8 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: + for (i = 0; i < ARRAY_LENGTH(&find_list); i++) + free(ARRAY_ITEM(&find_list, i).list_ctx); ARRAY_FREE(&find_list); return (CMD_RETURN_NORMAL); } From 94ccc6aeaa72c84d66436d3f0cd23b4ab4d69c8e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 8 May 2014 07:54:47 +0000 Subject: [PATCH 343/949] Instead of forcing mouse scroll to 1 in choose mode, scale it down instead. Means modifier keys still increase the line count, just not as much. Based on a diff from Marcel Partap. --- tmux.h | 3 +++ tty-keys.c | 6 +++--- window-choose.c | 19 ++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tmux.h b/tmux.h index b0a37f25..04bbd935 100644 --- a/tmux.h +++ b/tmux.h @@ -1130,6 +1130,9 @@ LIST_HEAD(tty_terms, tty_term); #define MOUSE_WHEEL_UP 0 #define MOUSE_WHEEL_DOWN 1 +/* Mouse wheel multipler. */ +#define MOUSE_WHEEL_SCALE 3 + /* Mouse event bits. */ #define MOUSE_EVENT_DOWN 0x1 #define MOUSE_EVENT_DRAG 0x2 diff --git a/tty-keys.c b/tty-keys.c index 4f55a80c..297e22c8 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -752,11 +752,11 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) if (b & MOUSE_MASK_SHIFT) m->scroll = 1; else - m->scroll = 3; + m->scroll = MOUSE_WHEEL_SCALE; if (b & MOUSE_MASK_META) - m->scroll *= 3; + m->scroll *= MOUSE_WHEEL_SCALE; if (b & MOUSE_MASK_CTRL) - m->scroll *= 3; + m->scroll *= MOUSE_WHEEL_SCALE; b &= MOUSE_MASK_BUTTONS; if (b == 0) diff --git a/window-choose.c b/window-choose.c index 34ec2eb7..99e88036 100644 --- a/window-choose.c +++ b/window-choose.c @@ -721,17 +721,22 @@ window_choose_mouse(struct window_pane *wp, struct session *sess, struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct window_choose_mode_item *item; - u_int idx; + u_int idx, i, n; if (m->event == MOUSE_EVENT_WHEEL) { /* - * Don't use m->scroll and just move line-by-line or it's - * annoying. + * Multiple line scrolling by default is annoying, so scale + * m->scroll back down. */ - if (m->wheel == MOUSE_WHEEL_UP) - window_choose_key(wp, sess, KEYC_UP); - else - window_choose_key(wp, sess, KEYC_DOWN); + n = m->scroll; + if (n >= MOUSE_WHEEL_SCALE) + n /= MOUSE_WHEEL_SCALE; + for (i = 0; i < n; i++) { + if (m->wheel == MOUSE_WHEEL_UP) + window_choose_key(wp, sess, KEYC_UP); + else + window_choose_key(wp, sess, KEYC_DOWN); + } return; } From 353d1825d5a3a6efb1c3c71d9afbb96ba57cc948 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 8 May 2014 07:59:16 +0000 Subject: [PATCH 344/949] Send up and down keys for mouse wheel in alternate screen mode (when it normally does nothing), from Marcel Partap. --- input-keys.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/input-keys.c b/input-keys.c index 6e21cc0c..24566dfd 100644 --- a/input-keys.c +++ b/input-keys.c @@ -205,6 +205,21 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) char buf[40]; size_t len; struct paste_buffer *pb; + u_int i; + + /* + * If the alternate screen is active and hasn't enabled the mouse, send + * up and down key presses for the mouse wheel. + */ + if (wp->saved_grid != NULL && !(wp->screen->mode & ALL_MOUSE_MODES)) { + for (i = 0; i < m->scroll; i++) { + if (m->wheel == MOUSE_WHEEL_UP) + input_key(wp, KEYC_UP); + else + input_key(wp, KEYC_DOWN); + } + return; + } if (wp->screen->mode & ALL_MOUSE_MODES) { /* From f4ffaf5a7fad4c02ad2d08c7b2c80fdec21b64a9 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 9 May 2014 09:11:24 +0000 Subject: [PATCH 345/949] Just use char ** for argv like normal people, not char *const *. --- cmd.c | 2 +- tmux.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd.c b/cmd.c index 726799a8..5660dc72 100644 --- a/cmd.c +++ b/cmd.c @@ -180,7 +180,7 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) } char ** -cmd_copy_argv(int argc, char *const *argv) +cmd_copy_argv(int argc, char **argv) { char **new_argv; int i; diff --git a/tmux.h b/tmux.h index 04bbd935..a866d5b7 100644 --- a/tmux.h +++ b/tmux.h @@ -1738,7 +1738,7 @@ long long args_strtonum( /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); -char **cmd_copy_argv(int, char *const *); +char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); size_t cmd_print(struct cmd *, char *, size_t); From 3dbacbb62b033c04185fb87da5b0622f0aafee86 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 May 2014 07:34:35 +0000 Subject: [PATCH 346/949] Add support for named buffers. If you don't name a buffer, things work much as before - buffers are automatically named "buffer0000", "buffer0001" and so on and ordered as a stack. Buffers can be named explicitly when creating ("loadb -b foo" etc) or renamed ("setb -b buffer0000 -n foo"). If buffers are named explicitly, they are not deleted when buffer-limit is reached. Diff from J Raynor. --- cmd-capture-pane.c | 25 ++--- cmd-choose-buffer.c | 9 +- cmd-delete-buffer.c | 15 +-- cmd-list-buffers.c | 6 +- cmd-load-buffer.c | 55 ++++------- cmd-paste-buffer.c | 29 ++---- cmd-save-buffer.c | 18 ++-- cmd-set-buffer.c | 67 +++++++++----- format.c | 1 + paste.c | 218 +++++++++++++++++++++++++++++++++----------- tmux.1 | 63 +++++++++---- tmux.h | 25 +++-- window-copy.c | 70 +++++++------- 13 files changed, 353 insertions(+), 248 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 759063d1..c6a80ebd 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -38,7 +38,7 @@ char *cmd_capture_pane_history(struct args *, struct cmd_q *, const struct cmd_entry cmd_capture_pane_entry = { "capture-pane", "capturep", "ab:CeE:JpPqS:t:", 0, 0, - "[-aCeJpPq] [-b buffer-index] [-E end-line] [-S start-line]" + "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, 0, NULL, @@ -165,8 +165,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c; struct window_pane *wp; char *buf, *cause; - int buffer; - u_int limit; + const char *bufname; size_t len; if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) @@ -192,25 +191,17 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); } else { - limit = options_get_number(&global_options, "buffer-limit"); - if (!args_has(args, 'b')) { - paste_add(buf, len, limit); - return (CMD_RETURN_NORMAL); - } - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); + + if (paste_set(buf, len, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(buf); free(cause); return (CMD_RETURN_ERROR); } - - if (paste_replace(buffer, buf, len) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); - free(buf); - return (CMD_RETURN_ERROR); - } } return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index c6c70a0d..42caa7cd 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -75,19 +75,20 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) action = xstrdup("paste-buffer -b '%%'"); idx = 0; - while ((pb = paste_walk_stack(&idx)) != NULL) { + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = idx - 1; + cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_add(cdata->ft, "line", "%u", idx - 1); format_paste_buffer(cdata->ft, pb, utf8flag); - xasprintf(&action_data, "%u", idx - 1); + xasprintf(&action_data, "%s", pb->name); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); window_choose_add(wl->window->active, cdata); + idx++; } free(action); diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c index 464d2b78..755d7ea3 100644 --- a/cmd-delete-buffer.c +++ b/cmd-delete-buffer.c @@ -41,23 +41,16 @@ enum cmd_retval cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - char *cause; - int buffer; + const char *bufname; if (!args_has(args, 'b')) { paste_free_top(); return (CMD_RETURN_NORMAL); } + bufname = args_get(args, 'b'); - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - if (paste_free_index(buffer) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_free_name(bufname) != 0) { + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 2363c806..9d79072e 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -44,17 +44,15 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; - u_int idx; char *line; const char *template; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; - idx = 0; - while ((pb = paste_walk_stack(&idx)) != NULL) { + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { ft = format_create(); - format_add(ft, "line", "%u", idx - 1); format_paste_buffer(ft, pb, 0); line = format_expand(ft, template); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 5e9b6748..6c69391c 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -50,30 +50,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path; + const char *path, *bufname; char *pdata, *new_pdata, *cause; size_t psize; - u_int limit; - int ch, error, buffer, *buffer_ptr, cwd, fd; + int ch, error, cwd, fd; - if (!args_has(args, 'b')) - buffer = -1; - else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); path = args->argv[0]; if (strcmp(path, "-") == 0) { - buffer_ptr = xmalloc(sizeof *buffer_ptr); - *buffer_ptr = buffer; - error = server_set_stdin_callback(c, cmd_load_buffer_callback, - buffer_ptr, &cause); + (void*)bufname, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); @@ -117,14 +106,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) fclose(f); - limit = options_get_number(&global_options, "buffer-limit"); - if (buffer == -1) { - paste_add(pdata, psize, limit); - return (CMD_RETURN_NORMAL); - } - if (paste_replace(buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_set(pdata, psize, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(pdata); + free(cause); return (CMD_RETURN_ERROR); } @@ -140,10 +125,9 @@ error: void cmd_load_buffer_callback(struct client *c, int closed, void *data) { - int *buffer = data; - char *pdata; - size_t psize; - u_int limit; + const char *bufname = data; + char *pdata, *cause; + size_t psize; if (!closed) return; @@ -154,26 +138,21 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) return; psize = EVBUFFER_LENGTH(c->stdin_data); - if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { - free(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); - limit = options_get_number(&global_options, "buffer-limit"); - if (*buffer == -1) - paste_add(pdata, psize, limit); - else if (paste_replace(*buffer, pdata, psize) != 0) { + if (paste_set(pdata, psize, bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ - evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); + evbuffer_add_printf(c->stderr_data, "%s", cause); server_push_stderr(c); free(pdata); + free(cause); } - free(data); - out: cmdq_continue(c->cmdq); } diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index c3837c6d..9462b28f 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -36,7 +36,7 @@ void cmd_paste_buffer_filter(struct window_pane *, const struct cmd_entry cmd_paste_buffer_entry = { "paste-buffer", "pasteb", "db:prs:t:", 0, 0, - "[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE, + "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, 0, NULL, cmd_paste_buffer_exec @@ -49,31 +49,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct paste_buffer *pb; - const char *sepstr; - char *cause; - int buffer; + const char *sepstr, *bufname; int pflag; if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); - if (!args_has(args, 'b')) - buffer = -1; - else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); - if (buffer == -1) + if (bufname == NULL) pb = paste_get_top(); else { - pb = paste_get_index(buffer); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } @@ -92,10 +83,10 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) /* Delete the buffer if -d. */ if (args_has(args, 'd')) { - if (buffer == -1) + if (bufname == NULL) paste_free_top(); else - paste_free_index(buffer); + paste_free_name(bufname); } return (CMD_RETURN_NORMAL); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 0a1853d4..ea238450 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -59,10 +59,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path; - char *cause, *start, *end, *msg; + const char *path, *bufname; + char *start, *end, *msg; size_t size, used, msglen; - int cwd, fd, buffer; + int cwd, fd; FILE *f; if (!args_has(args, 'b')) { @@ -71,16 +71,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } } else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - pb = paste_get_index(buffer); + bufname = args_get(args, 'b'); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 5dabc1ee..5a82b4d8 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", - "ab:", 1, 1, - "[-a] " CMD_BUFFER_USAGE " data", + "ab:n:", 0, 1, + "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", 0, NULL, cmd_set_buffer_exec @@ -43,38 +43,59 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; - u_int limit; char *pdata, *cause; + const char *bufname; size_t psize, newsize; - int buffer; - limit = options_get_number(&global_options, "buffer-limit"); + bufname = NULL; + + if (args_has(args, 'n')) { + if (args->argc > 0) { + cmdq_error(cmdq, "don't provide data with n flag"); + return (CMD_RETURN_ERROR); + } + + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); + + if (bufname == NULL) { + pb = paste_get_top(); + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); + return (CMD_RETURN_ERROR); + } + bufname = pb->name; + } + + if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { + cmdq_error(cmdq, "%s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + + return (CMD_RETURN_NORMAL); + } + + if (args->argc != 1) { + cmdq_error(cmdq, "no data specified"); + return (CMD_RETURN_ERROR); + } psize = 0; pdata = NULL; pb = NULL; - buffer = -1; if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); if (args_has(args, 'b')) { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - pb = paste_get_index(buffer); - if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); - return (CMD_RETURN_ERROR); - } + bufname = args_get(args, 'b'); + pb = paste_get_name(bufname); } else if (args_has(args, 'a')) { pb = paste_get_top(); if (pb != NULL) - buffer = 0; + bufname = pb->name; } if (args_has(args, 'a') && pb != NULL) { @@ -87,10 +108,12 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) memcpy(pdata + psize, args->argv[0], newsize); psize += newsize; - if (buffer == -1) - paste_add(pdata, psize, limit); - else - paste_replace(buffer, pdata, psize); + if (paste_set(pdata, psize, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); + free(pdata); + free(cause); + return (CMD_RETURN_ERROR); + } return (CMD_RETURN_NORMAL); } diff --git a/format.c b/format.c index 7c8ce779..028031a0 100644 --- a/format.c +++ b/format.c @@ -608,6 +608,7 @@ format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, char *s; format_add(ft, "buffer_size", "%zu", pb->size); + format_add(ft, "buffer_name", "%s", pb->name); s = paste_make_sample(pb, utf8flag); format_add(ft, "buffer_sample", "%s", s); diff --git a/paste.c b/paste.c index 9492fd91..11a4f006 100644 --- a/paste.c +++ b/paste.c @@ -26,127 +26,237 @@ #include "tmux.h" /* - * Stack of paste buffers. Note that paste buffer data is not necessarily a C + * Set of paste buffers. Note that paste buffer data is not necessarily a C * string! */ -ARRAY_DECL(, struct paste_buffer *) paste_buffers = ARRAY_INITIALIZER; +u_int paste_next_index; +u_int paste_next_order; +u_int paste_num_automatic; +RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; +RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; -/* Return each item of the stack in turn. */ -struct paste_buffer * -paste_walk_stack(u_int *idx) +int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); +RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); +RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); + +int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); +RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); +RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); + +int +paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) { - struct paste_buffer *pb; - - pb = paste_get_index(*idx); - (*idx)++; - return (pb); + return (strcmp(a->name, b->name)); } -/* Get the top item on the stack. */ +int +paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) +{ + if (a->order > b->order) + return (-1); + if (a->order < b->order) + return (1); + return (0); +} + +/* Walk paste buffers by name. */ +struct paste_buffer * +paste_walk(struct paste_buffer *pb) +{ + if (pb == NULL) + return (RB_MIN(paste_time_tree, &paste_by_time)); + return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); +} + +/* Get the most recent automatic buffer */ struct paste_buffer * paste_get_top(void) { - if (ARRAY_LENGTH(&paste_buffers) == 0) + struct paste_buffer *pb; + + pb = RB_MIN(paste_time_tree, &paste_by_time); + if (pb == NULL) return (NULL); - return (ARRAY_FIRST(&paste_buffers)); + return (pb); } -/* Get an item by its index. */ -struct paste_buffer * -paste_get_index(u_int idx) -{ - if (idx >= ARRAY_LENGTH(&paste_buffers)) - return (NULL); - return (ARRAY_ITEM(&paste_buffers, idx)); -} - -/* Free the top item on the stack. */ +/* Free the most recent buffer */ int paste_free_top(void) { struct paste_buffer *pb; - if (ARRAY_LENGTH(&paste_buffers) == 0) + pb = paste_get_top(); + if (pb == NULL) return (-1); - - pb = ARRAY_FIRST(&paste_buffers); - ARRAY_REMOVE(&paste_buffers, 0); - - free(pb->data); - free(pb); - - return (0); + return (paste_free_name(pb->name)); } -/* Free an item by index. */ -int -paste_free_index(u_int idx) +/* Get a paste buffer by name. */ +struct paste_buffer * +paste_get_name(const char *name) { - struct paste_buffer *pb; + struct paste_buffer pbfind; - if (idx >= ARRAY_LENGTH(&paste_buffers)) + if (name == NULL || *name == '\0') + return (NULL); + + pbfind.name = (char*)name; + return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); +} + +/* Free a paste buffer by name. */ +int +paste_free_name(const char *name) +{ + struct paste_buffer *pb, pbfind; + + if (name == NULL || *name == '\0') return (-1); - pb = ARRAY_ITEM(&paste_buffers, idx); - ARRAY_REMOVE(&paste_buffers, idx); + pbfind.name = (char*)name; + pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); + if (pb == NULL) + return (-1); + + RB_REMOVE(paste_name_tree, &paste_by_name, pb); + RB_REMOVE(paste_time_tree, &paste_by_time, pb); + if (pb->automatic) + paste_num_automatic--; free(pb->data); + free(pb->name); free(pb); - return (0); } /* - * Add an item onto the top of the stack, freeing the bottom if at limit. Note + * Add an automatic buffer, freeing the oldest automatic item if at limit. Note * that the caller is responsible for allocating data. */ void -paste_add(char *data, size_t size, u_int limit) +paste_add(char *data, size_t size) { - struct paste_buffer *pb; + struct paste_buffer *pb, *pb1; + u_int limit; if (size == 0) return; - while (ARRAY_LENGTH(&paste_buffers) >= limit) { - pb = ARRAY_LAST(&paste_buffers); - free(pb->data); - free(pb); - ARRAY_TRUNC(&paste_buffers, 1); + limit = options_get_number(&global_options, "buffer-limit"); + RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { + if (paste_num_automatic < limit) + break; + if (pb->automatic) + paste_free_name(pb->name); } pb = xmalloc(sizeof *pb); - ARRAY_INSERT(&paste_buffers, 0, pb); + + pb->name = NULL; + do { + free(pb->name); + xasprintf(&pb->name, "buffer%04u", paste_next_index); + paste_next_index++; + } while (paste_get_name(pb->name) != NULL); pb->data = data; pb->size = size; + + pb->automatic = 1; + paste_num_automatic++; + + pb->order = paste_next_order++; + RB_INSERT(paste_name_tree, &paste_by_name, pb); + RB_INSERT(paste_time_tree, &paste_by_time, pb); } +/* Rename a paste buffer. */ +int +paste_rename(const char *oldname, const char *newname, char **cause) +{ + struct paste_buffer *pb; + + if (cause != NULL) + *cause = NULL; + + if (oldname == NULL || *oldname == '\0') { + if (cause != NULL) + *cause = xstrdup("no buffer"); + return (-1); + } + if (newname == NULL || *newname == '\0') { + if (cause != NULL) + *cause = xstrdup("new name is empty"); + return (-1); + } + + pb = paste_get_name(oldname); + if (pb == NULL) { + if (cause != NULL) + xasprintf(cause, "no buffer %s", oldname); + return (-1); + } + + RB_REMOVE(paste_name_tree, &paste_by_name, pb); + + free(pb->name); + pb->name = xstrdup(newname); + + if (pb->automatic) + paste_num_automatic--; + pb->automatic = 0; + + RB_INSERT(paste_name_tree, &paste_by_name, pb); + + return (0); +} /* - * Replace an item on the stack. Note that the caller is responsible for + * Add or replace an item in the store. Note that the caller is responsible for * allocating data. */ int -paste_replace(u_int idx, char *data, size_t size) +paste_set(char *data, size_t size, const char *name, char **cause) { struct paste_buffer *pb; + if (cause != NULL) + *cause = NULL; + if (size == 0) { free(data); return (0); } + if (name == NULL) { + paste_add(data, size); + return (0); + } - if (idx >= ARRAY_LENGTH(&paste_buffers)) + if (*name == '\0') { + if (cause != NULL) + *cause = xstrdup("empty buffer name"); return (-1); + } - pb = ARRAY_ITEM(&paste_buffers, idx); - free(pb->data); + pb = paste_get_name(name); + if (pb != NULL) + paste_free_name(name); + + pb = xmalloc(sizeof *pb); + + pb->name = xstrdup(name); pb->data = data; pb->size = size; + pb->automatic = 0; + pb->order = paste_next_order++; + + RB_INSERT(paste_name_tree, &paste_by_name, pb); + RB_INSERT(paste_time_tree, &paste_by_time, pb); + return (0); } diff --git a/tmux.1 b/tmux.1 index 44e69f6e..ae6190e4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -930,9 +930,6 @@ in emacs mode, and .Ql 10w in vi. .Pp -When copying the selection, the repeat count indicates the buffer index to -replace, if used. -.Pp Mode key bindings are defined in a set of named tables: .Em vi-edit and @@ -1090,7 +1087,7 @@ but a different format may be specified with .Fl F . .It Xo Ic capture-pane .Op Fl aepPq -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line .Op Fl t Ar target-pane @@ -3366,19 +3363,40 @@ is given, otherwise the active pane for the session attached to .El .Sh BUFFERS .Nm -maintains a stack of +maintains a set of named .Em paste buffers . -Up to the value of the +Each buffer may be either explicitly or automatically named. +Explicitly named buffers are named when created with the +.Ic set-buffer +or +.Ic load-buffer +commands, or by renaming an automatically named buffer with +.Ic set-buffer +.Fl n . +Automatically named buffers are given a name such as +.Ql buffer0001 , +.Ql buffer0002 +and so on. +When the .Ic buffer-limit -option are kept; when a new buffer is added, the buffer at the bottom of the -stack is removed. +option is reached, the oldest automatically named buffer is deleted. +Explicitly named are not subject to +.Ic buffer-limit +and may be deleted with +.Ic delete-buffer +command. +.Pp Buffers may be added using .Ic copy-mode or the .Ic set-buffer -command, and pasted into a window using the +and +.Ic load-buffer +commands, and pasted into a window using the .Ic paste-buffer command. +If a buffer command is used and no buffer is specified, the most +recently added automatically named buffer is assumed. .Pp A configurable history buffer is also maintained for each window. By default, up to 2000 lines are kept; this can be altered with the @@ -3399,7 +3417,7 @@ Put a window into buffer choice mode, where a buffer may be chosen interactively from a list. After a buffer is selected, .Ql %% -is replaced by the buffer index in +is replaced by the buffer name in .Ar template and the result executed as a command. If @@ -3414,11 +3432,11 @@ This command works only if at least one client is attached. .It Ic clear-history Op Fl t Ar target-pane .D1 (alias: Ic clearhist ) Remove and free the history for the specified pane. -.It Ic delete-buffer Op Fl b Ar buffer-index +.It Ic delete-buffer Op Fl b Ar buffer-name .D1 (alias: Ic deleteb ) -Delete the buffer at -.Ar buffer-index , -or the top buffer if not specified. +Delete the buffer named +.Ar buffer-name , +or the most recently added automatically named buffer if not specified. .It Xo Ic list-buffers .Op Fl F Ar format .Xc @@ -3430,7 +3448,7 @@ flag, see the .Sx FORMATS section. .It Xo Ic load-buffer -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic loadb ) @@ -3438,7 +3456,7 @@ Load the contents of the specified paste buffer from .Ar path . .It Xo Ic paste-buffer .Op Fl dpr -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Op Fl s Ar separator .Op Fl t Ar target-pane .Xc @@ -3447,7 +3465,7 @@ Insert the contents of a paste buffer into the specified pane. If not specified, paste into the current one. With .Fl d , -also delete the paste buffer from the stack. +also delete the paste buffer. When output, any linefeed (LF) characters in the paste buffer are replaced with a separator, by default carriage return (CR). A custom separator may be specified using the @@ -3462,7 +3480,7 @@ is specified, paste bracket control codes are inserted around the buffer if the application has requested bracketed paste mode. .It Xo Ic save-buffer .Op Fl a -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic saveb ) @@ -3473,7 +3491,8 @@ The option appends to rather than overwriting the file. .It Xo Ic set-buffer .Op Fl a -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name +.Op Fl n Ar new-buffer-name .Ar data .Xc .D1 (alias: Ic setb ) @@ -3482,8 +3501,12 @@ Set the contents of the specified buffer to The .Fl a option appends to rather than overwriting the buffer. +The +.Fl n +option renames the buffer to +.Ar new-buffer-name . .It Xo Ic show-buffer -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Xc .D1 (alias: Ic showb ) Display the contents of the specified buffer. diff --git a/tmux.h b/tmux.h index a866d5b7..9e70915b 100644 --- a/tmux.h +++ b/tmux.h @@ -85,7 +85,7 @@ extern char **environ; /* Default template for choose-buffer. */ #define CHOOSE_BUFFER_TEMPLATE \ - "#{line}: #{buffer_size} bytes: #{buffer_sample}" + "#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}" /* Default template for choose-client. */ #define CHOOSE_CLIENT_TEMPLATE \ @@ -118,7 +118,8 @@ extern char **environ; /* Default template for list-buffers. */ #define LIST_BUFFERS_TEMPLATE \ - "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" + "#{buffer_name}: #{buffer_size} bytes: " \ + "\"#{buffer_sample}\"" /* Default template for list-clients. */ #define LIST_CLIENTS_TEMPLATE \ @@ -1036,6 +1037,13 @@ struct layout_cell { struct paste_buffer { char *data; size_t size; + + char *name; + int automatic; + u_int order; + + RB_ENTRY(paste_buffer) name_entry; + RB_ENTRY(paste_buffer) time_entry; }; /* Environment variable. */ @@ -1499,7 +1507,7 @@ RB_HEAD(format_tree, format_entry); #define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" #define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" -#define CMD_BUFFER_USAGE "[-b buffer-index]" +#define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ extern struct options global_options; @@ -1711,13 +1719,14 @@ void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* paste.c */ -struct paste_buffer *paste_walk_stack(u_int *); +struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(void); -struct paste_buffer *paste_get_index(u_int); +struct paste_buffer *paste_get_name(const char *); int paste_free_top(void); -int paste_free_index(u_int); -void paste_add(char *, size_t, u_int); -int paste_replace(u_int, char *, size_t); +int paste_free_name(const char *); +void paste_add(char *, size_t); +int paste_rename(const char *, const char *, char **); +int paste_set(char *, size_t, const char *, char **); char *paste_make_sample(struct paste_buffer *, int); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); diff --git a/window-copy.c b/window-copy.c index 296443a2..4763c230 100644 --- a/window-copy.c +++ b/window-copy.c @@ -54,11 +54,12 @@ void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); int window_copy_update_selection(struct window_pane *, int); void *window_copy_get_selection(struct window_pane *, size_t *); -void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); -void window_copy_copy_pipe( - struct window_pane *, struct session *, int, const char *); -void window_copy_copy_selection(struct window_pane *, int); -void window_copy_append_selection(struct window_pane *, int); +void window_copy_copy_buffer(struct window_pane *, const char *, void *, + size_t); +void window_copy_copy_pipe(struct window_pane *, struct session *, + const char *, const char *); +void window_copy_copy_selection(struct window_pane *, const char *); +void window_copy_append_selection(struct window_pane *, const char *); void window_copy_clear_selection(struct window_pane *); void window_copy_copy_line( struct window_pane *, char **, size_t *, u_int, u_int, u_int); @@ -417,7 +418,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: if (sess != NULL) { - window_copy_append_selection(wp, data->numprefix); + window_copy_append_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -543,7 +544,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) if (sess != NULL && (cmd == MODEKEYCOPY_COPYLINE || cmd == MODEKEYCOPY_COPYENDOFLINE)) { - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -554,14 +555,14 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) break; case MODEKEYCOPY_COPYPIPE: if (sess != NULL) { - window_copy_copy_pipe(wp, sess, data->numprefix, arg); + window_copy_copy_pipe(wp, sess, NULL, arg); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { - window_copy_copy_selection(wp, data->numprefix); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -918,7 +919,7 @@ reset_mode: s->mode &= ~MODE_MOUSE_BUTTON; s->mode |= MODE_MOUSE_STANDARD; if (sess != NULL) { - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); } } @@ -1452,9 +1453,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) } void -window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) +window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, + size_t len) { - u_int limit; struct screen_write_ctx ctx; if (options_get_number(&global_options, "set-clipboard")) { @@ -1463,16 +1464,13 @@ window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) screen_write_stop(&ctx); } - if (idx == -1) { - limit = options_get_number(&global_options, "buffer-limit"); - paste_add(buf, len, limit); - } else if (paste_replace(idx, buf, len) != 0) + if (paste_set(buf, len, bufname, NULL) != 0) free(buf); } void -window_copy_copy_pipe( - struct window_pane *wp, struct session *sess, int idx, const char *arg) +window_copy_copy_pipe(struct window_pane *wp, struct session *sess, + const char *bufname, const char *arg) { void *buf; size_t len; @@ -1486,11 +1484,11 @@ window_copy_copy_pipe( job = job_run(arg, sess, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); - window_copy_copy_buffer(wp, idx, buf, len); + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_copy_selection(struct window_pane *wp, int idx) +window_copy_copy_selection(struct window_pane *wp, const char *bufname) { void* buf; size_t len; @@ -1499,17 +1497,16 @@ window_copy_copy_selection(struct window_pane *wp, int idx) if (buf == NULL) return; - window_copy_copy_buffer(wp, idx, buf, len); + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_append_selection(struct window_pane *wp, int idx) +window_copy_append_selection(struct window_pane *wp, const char *bufname) { - char *buf; - struct paste_buffer *pb; - size_t len; - u_int limit; - struct screen_write_ctx ctx; + char *buf; + struct paste_buffer *pb; + size_t len; + struct screen_write_ctx ctx; buf = window_copy_get_selection(wp, &len); if (buf == NULL) @@ -1521,24 +1518,19 @@ window_copy_append_selection(struct window_pane *wp, int idx) screen_write_stop(&ctx); } - if (idx == -1) - idx = 0; - - if (idx == 0 && paste_get_top() == NULL) { - limit = options_get_number(&global_options, "buffer-limit"); - paste_add(buf, len, limit); - return; - } - - pb = paste_get_index(idx); + if (bufname == NULL || *bufname == '\0') { + pb = paste_get_top(); + if (pb != NULL) + bufname = pb->name; + } else + pb = paste_get_name(bufname); if (pb != NULL) { buf = xrealloc(buf, 1, len + pb->size); memmove(buf + pb->size, buf, len); memcpy(buf, pb->data, pb->size); len += pb->size; } - - if (paste_replace(idx, buf, len) != 0) + if (paste_set(buf, len, bufname, NULL) != 0) free(buf); } From b1a06ef22e54e943d733db8dcc98fe60051c93de Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 May 2014 07:54:20 +0000 Subject: [PATCH 347/949] Add a copy mode key binding to copy to a named buffer. From J Raynor. --- mode-key.c | 2 ++ tmux.1 | 1 + tmux.h | 1 + window-copy.c | 16 ++++++++++++++-- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mode-key.c b/mode-key.c index 7f2b9471..c2bc0439 100644 --- a/mode-key.c +++ b/mode-key.c @@ -141,6 +141,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" }, { MODEKEYCOPY_SEARCHUP, "search-backward" }, { MODEKEYCOPY_SELECTLINE, "select-line" }, + { MODEKEYCOPY_STARTNAMEDBUFFER, "start-named-buffer" }, { MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" }, { MODEKEYCOPY_STARTOFLINE, "start-of-line" }, { MODEKEYCOPY_STARTSELECTION, "begin-selection" }, @@ -257,6 +258,7 @@ struct mode_key_tree mode_key_tree_vi_choice; /* vi copy mode keys. */ const struct mode_key_entry mode_key_vi_copy[] = { { ' ', 0, MODEKEYCOPY_STARTSELECTION }, + { '"', 0, MODEKEYCOPY_STARTNAMEDBUFFER }, { '$', 0, MODEKEYCOPY_ENDOFLINE }, { ',', 0, MODEKEYCOPY_JUMPREVERSE }, { ';', 0, MODEKEYCOPY_JUMPAGAIN }, diff --git a/tmux.1 b/tmux.1 index ae6190e4..397a228b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -849,6 +849,7 @@ The following keys are supported as appropriate for the mode: .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" .It Li "Append selection" Ta "A" Ta "" .It Li "Back to indentation" Ta "^" Ta "M-m" +.It Li "Copy to named buffer" Ta \&" Ta "" .It Li "Bottom of history" Ta "G" Ta "M-<" .It Li "Clear selection" Ta "Escape" Ta "C-g" .It Li "Copy selection" Ta "Enter" Ta "M-w" diff --git a/tmux.h b/tmux.h index 9e70915b..a8d8e01f 100644 --- a/tmux.h +++ b/tmux.h @@ -583,6 +583,7 @@ enum mode_key_cmd { MODEKEYCOPY_SEARCHREVERSE, MODEKEYCOPY_SEARCHUP, MODEKEYCOPY_SELECTLINE, + MODEKEYCOPY_STARTNAMEDBUFFER, MODEKEYCOPY_STARTNUMBERPREFIX, MODEKEYCOPY_STARTOFLINE, MODEKEYCOPY_STARTSELECTION, diff --git a/window-copy.c b/window-copy.c index 4763c230..aa6b2d73 100644 --- a/window-copy.c +++ b/window-copy.c @@ -61,8 +61,8 @@ void window_copy_copy_pipe(struct window_pane *, struct session *, void window_copy_copy_selection(struct window_pane *, const char *); void window_copy_append_selection(struct window_pane *, const char *); void window_copy_clear_selection(struct window_pane *); -void window_copy_copy_line( - struct window_pane *, char **, size_t *, u_int, u_int, u_int); +void window_copy_copy_line(struct window_pane *, char **, size_t *, u_int, + u_int, u_int); int window_copy_in_set(struct window_pane *, u_int, u_int, const char *); u_int window_copy_find_length(struct window_pane *, u_int); void window_copy_cursor_start_of_line(struct window_pane *); @@ -95,6 +95,7 @@ const struct window_mode window_copy_mode = { enum window_copy_input_type { WINDOW_COPY_OFF, + WINDOW_COPY_NAMEDBUFFER, WINDOW_COPY_NUMERICPREFIX, WINDOW_COPY_SEARCHUP, WINDOW_COPY_SEARCHDOWN, @@ -677,6 +678,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) case WINDOW_COPY_JUMPBACK: case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOBACK: + case WINDOW_COPY_NAMEDBUFFER: case WINDOW_COPY_NUMERICPREFIX: break; case WINDOW_COPY_SEARCHUP: @@ -712,6 +714,11 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) data->inputprompt = "Goto Line"; *data->inputstr = '\0'; goto input_on; + case MODEKEYCOPY_STARTNAMEDBUFFER: + data->inputtype = WINDOW_COPY_NAMEDBUFFER; + data->inputprompt = "Buffer"; + *data->inputstr = '\0'; + goto input_on; case MODEKEYCOPY_STARTNUMBERPREFIX: key &= KEYC_MASK_KEY; if (key >= '0' && key <= '9') { @@ -815,6 +822,11 @@ window_copy_key_input(struct window_pane *wp, int key) data->searchtype = data->inputtype; data->searchstr = xstrdup(data->inputstr); break; + case WINDOW_COPY_NAMEDBUFFER: + window_copy_copy_selection(wp, data->inputstr); + *data->inputstr = '\0'; + window_pane_reset_mode(wp); + return (0); case WINDOW_COPY_GOTOLINE: window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; From b3e8d440ed0477e88232c3ba1779c67eafce3a48 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 May 2014 08:08:32 +0000 Subject: [PATCH 348/949] If multiple arguments are given to new-session, new-window, split-window, respawn-window or respawn-pane, pass them directly to execvp() to help avoid quoting problems. One argument still goes to "sh -c" like before. Requested by many over the years. Patch from J Raynor. --- cmd-new-session.c | 34 ++++++++++++++++--------- cmd-new-window.c | 24 ++++++++++++------ cmd-respawn-pane.c | 13 +++------- cmd-respawn-window.c | 12 +++------ cmd-split-window.c | 25 ++++++++++++------ cmd.c | 28 ++++++++++++++++++++- format.c | 8 +++--- names.c | 12 ++++++--- session.c | 18 +++++++------ tmux.1 | 29 +++++++++++++++++++-- tmux.h | 20 ++++++++------- window.c | 60 ++++++++++++++++++++++++++++---------------- 12 files changed, 192 insertions(+), 91 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 3f8ebe69..802cb6c9 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -35,10 +35,10 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", - "Ac:dDF:n:Ps:t:x:y:", 0, 1, + "Ac:dDF:n:Ps:t:x:y:", 0, -1, "[-AdDP] [-c start-directory] [-F format] [-n window-name] " - "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] " - "[command]", + "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " + "[-y height] [command]", CMD_STARTSERVER|CMD_CANTNEST, NULL, cmd_new_session_exec @@ -55,8 +55,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; const char *path; - char *cmd, *cause, *cp; + char **argv, *cmd, *cause, *cp; int detached, already_attached, idx, cwd, fd = -1; + int argc; u_int sx, sy; struct format_tree *ft; struct environ_entry *envent; @@ -183,12 +184,21 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) sy = 1; /* Figure out the command for the new window. */ - if (target != NULL) - cmd = NULL; - else if (args->argc != 0) - cmd = args->argv[0]; - else + argc = -1; + argv = NULL; + if (target == NULL && args->argc != 0) { + argc = args->argc; + argv = args->argv; + } else if (target == NULL) { cmd = options_get_string(&global_s_options, "default-command"); + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = &cmd; + } else { + argc = 0; + argv = NULL; + } + } path = NULL; if (c != NULL && c->session == NULL) @@ -206,8 +216,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); - s = session_create(newname, cmd, path, cwd, &env, tiop, idx, sx, sy, - &cause); + s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, + sy, &cause); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); @@ -216,7 +226,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) environ_free(&env); /* Set the initial window name if one given. */ - if (cmd != NULL && args_has(args, 'n')) { + if (argc >= 0 && args_has(args, 'n')) { w = s->curw->window; window_set_name(w, args_get(args, 'n')); options_set_number(&w->options, "automatic-rename", 0); diff --git a/cmd-new-window.c b/cmd-new-window.c index dc1bbfaa..fc2eb120 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -34,7 +34,7 @@ enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { "new-window", "neww", - "ac:dF:kn:Pt:", 0, 1, + "ac:dF:kn:Pt:", 0, -1, "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", 0, @@ -50,8 +50,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; struct client *c; const char *cmd, *path, *template; - char *cause, *cp; - int idx, last, detached, cwd, fd = -1; + char **argv, *cause, *cp; + int argc, idx, last, detached, cwd, fd = -1; struct format_tree *ft; struct environ_entry *envent; @@ -84,10 +84,19 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } detached = args_has(args, 'd'); - if (args->argc == 0) + if (args->argc == 0) { cmd = options_get_string(&s->options, "default-command"); - else - cmd = args->argv[0]; + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char**)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = args->argc; + argv = args->argv; + } path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -145,7 +154,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); - wl = session_new(s, args_get(args, 'n'), cmd, path, cwd, idx, &cause); + wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, + &cause); if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 1df592e7..c2dcf9e6 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -32,7 +32,7 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { "respawn-pane", "respawnp", - "kt:", 0, 1, + "kt:", 0, -1, "[-k] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, @@ -48,7 +48,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct environ env; - const char *cmd, *path; + const char *path; char *cause; u_int idx; struct environ_entry *envent; @@ -74,11 +74,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) screen_reinit(&wp->base); input_init(wp); - if (args->argc != 0) - cmd = args->argv[0]; - else - cmd = NULL; - path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) envent = environ_find(&cmdq->client->environ, "PATH"); @@ -87,8 +82,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, - &cause) != 0) { + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 9dc3f07f..5f59cb8b 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -31,7 +31,7 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { "respawn-window", "respawnw", - "kt:", 0, 1, + "kt:", 0, -1, "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, @@ -47,7 +47,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct environ env; - const char *cmd, *path; + const char *path; char *cause; struct environ_entry *envent; @@ -76,10 +76,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) window_destroy_panes(w); TAILQ_INSERT_HEAD(&w->panes, wp, entry); window_pane_resize(wp, w->sx, w->sy); - if (args->argc != 0) - cmd = args->argv[0]; - else - cmd = NULL; path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -89,8 +85,8 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, - &cause) != 0) { + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-split-window.c b/cmd-split-window.c index 50949b39..55231134 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -36,7 +36,7 @@ enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { "split-window", "splitw", - "c:dF:l:hp:Pt:v", 0, 1, + "c:dF:l:hp:Pt:v", 0, -1, "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", 0, @@ -62,9 +62,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp, *new_wp = NULL; struct environ env; const char *cmd, *path, *shell, *template; - char *cause, *new_cause, *cp; + char **argv, *cause, *new_cause, *cp; u_int hlimit; - int size, percentage, cwd, fd = -1; + int argc, size, percentage, cwd, fd = -1; enum layout_type type; struct layout_cell *lc; struct client *c; @@ -81,10 +81,19 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) environ_copy(&s->environ, &env); server_fill_environ(s, &env); - if (args->argc == 0) + if (args->argc == 0) { cmd = options_get_string(&s->options, "default-command"); - else - cmd = args->argv[0]; + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char**)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = args->argc; + argv = args->argv; + } if (args_has(args, 'c')) { ft = format_create(); @@ -158,8 +167,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn( - new_wp, cmd, path, shell, cwd, &env, s->tio, &cause) != 0) + if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, &env, + s->tio, &cause) != 0) goto error; layout_assign_pane(lc, new_wp); diff --git a/cmd.c b/cmd.c index 5660dc72..d8e010cb 100644 --- a/cmd.c +++ b/cmd.c @@ -187,7 +187,7 @@ cmd_copy_argv(int argc, char **argv) if (argc == 0) return (NULL); - new_argv = xcalloc(argc, sizeof *new_argv); + new_argv = xcalloc(argc + 1, sizeof *new_argv); for (i = 0; i < argc; i++) { if (argv[i] != NULL) new_argv[i] = xstrdup(argv[i]); @@ -207,6 +207,32 @@ cmd_free_argv(int argc, char **argv) free(argv); } +char * +cmd_stringify_argv(int argc, char **argv) +{ + char *buf; + int i; + size_t len; + + if (argc == 0) + return (xstrdup("")); + + len = 0; + buf = NULL; + + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; + buf = xrealloc(buf, 1, len); + + if (i == 0) + *buf = '\0'; + else + strlcat(buf, " ", len); + strlcat(buf, argv[i], len); + } + return (buf); +} + struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { diff --git a/format.c b/format.c index 028031a0..10e5e0a8 100644 --- a/format.c +++ b/format.c @@ -368,7 +368,7 @@ format_get_command(struct window_pane *wp) cmd = get_proc_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { free(cmd); - cmd = xstrdup(wp->cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = xstrdup(wp->shell); @@ -559,8 +559,10 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) if (wp->tty != NULL) format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); - if (wp->cmd != NULL) - format_add(ft, "pane_start_command", "%s", wp->cmd); + if ((cmd = cmd_stringify_argv(wp->argc, wp->argv)) != NULL) { + format_add(ft, "pane_start_command", "%s", cmd); + free(cmd); + } if ((cmd = format_get_command(wp)) != NULL) { format_add(ft, "pane_current_command", "%s", cmd); free(cmd); diff --git a/names.c b/names.c index cfdaff83..8e01d347 100644 --- a/names.c +++ b/names.c @@ -68,9 +68,15 @@ window_name_callback(unused int fd, unused short events, void *data) char * default_window_name(struct window *w) { - if (w->active->cmd != NULL && *w->active->cmd != '\0') - return (parse_window_name(w->active->cmd)); - return (parse_window_name(w->active->shell)); + char *cmd, *s; + + cmd = cmd_stringify_argv(w->active->argc, w->active->argv); + if (cmd != NULL && *cmd != '\0') + s = parse_window_name(cmd); + else + s = parse_window_name(w->active->shell); + free(cmd); + return (s); } char * diff --git a/session.c b/session.c index b7d9844d..3bc2363e 100644 --- a/session.c +++ b/session.c @@ -85,11 +85,12 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *name, const char *cmd, const char *path, int cwd, - struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, - char **cause) +session_create(const char *name, int argc, char **argv, const char *path, + int cwd, struct environ *env, struct termios *tio, int idx, u_int sx, + u_int sy, char **cause) { struct session *s; + struct winlink *wl; s = xmalloc(sizeof *s); s->references = 0; @@ -132,8 +133,9 @@ session_create(const char *name, const char *cmd, const char *path, int cwd, } RB_INSERT(sessions, &sessions, s); - if (cmd != NULL) { - if (session_new(s, NULL, cmd, path, cwd, idx, cause) == NULL) { + if (argc >= 0) { + wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); + if (wl == NULL) { session_destroy(s); return (NULL); } @@ -227,7 +229,7 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * -session_new(struct session *s, const char *name, const char *cmd, +session_new(struct session *s, const char *name, int argc, char **argv, const char *path, int cwd, int idx, char **cause) { struct window *w; @@ -251,8 +253,8 @@ session_new(struct session *s, const char *name, const char *cmd, shell = _PATH_BSHELL; hlimit = options_get_number(&s->options, "history-limit"); - w = window_create(name, cmd, path, shell, cwd, &env, s->tio, s->sx, - s->sy, hlimit, cause); + w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, + s->sx, s->sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(&env); diff --git a/tmux.1 b/tmux.1 index 397a228b..24dde9fa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -478,12 +478,37 @@ It may be used alone to target a pane or the window containing it. arguments are .Xr sh 1 commands. -These must be passed as a single item, which typically means quoting them, for -example: +This may be a single argument passed to the shell, for example: .Bd -literal -offset indent new-window 'vi /etc/passwd' .Ed .Pp +Will run: +.Bd -literal -offset indent +/bin/sh -c 'vi /etc/passwd' +.Ed +.Pp +Additionally, the +.Ic new-window , +.Ic new-session , +.Ic split-window , +.Ic respawn-window +and +.Ic respawn-pane +commands allow +.Ar shell-command +to be given as multiple arguments and executed directly (without +.Ql sh -c ) . +This can avoid issues with shell quoting. +For example: +.Bd -literal -offset indent +$ tmux new-window vi /etc/passwd +.Ed +.Pp +Will run +.Xr vi 1 +directly without invoking the shell. +.Pp .Ar command .Op Ar arguments refers to a diff --git a/tmux.h b/tmux.h index a8d8e01f..c42df6ab 100644 --- a/tmux.h +++ b/tmux.h @@ -914,7 +914,8 @@ struct window_pane { #define PANE_RESIZE 0x8 #define PANE_FOCUSPUSH 0x10 - char *cmd; + int argc; + char **argv; char *shell; int cwd; @@ -1750,6 +1751,7 @@ int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); +char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); size_t cmd_print(struct cmd *, char *, size_t); struct session *cmd_current_session(struct cmd_q *, int); @@ -2140,7 +2142,7 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *); int window_index(struct window *, u_int *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); -struct window *window_create(const char *, const char *, const char *, +struct window *window_create(const char *, int, char **, const char *, const char *, int, struct environ *, struct termios *, u_int, u_int, u_int, char **); void window_destroy(struct window *); @@ -2166,7 +2168,7 @@ struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); void window_pane_timer_start(struct window_pane *); -int window_pane_spawn(struct window_pane *, const char *, +int window_pane_spawn(struct window_pane *, int, char **, const char *, const char *, int, struct environ *, struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); @@ -2304,18 +2306,18 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, const char *, int, - struct environ *, struct termios *, int, u_int, u_int, - char **); +struct session *session_create(const char *, int, char **, const char *, + int, struct environ *, struct termios *, int, u_int, + u_int, char **); void session_destroy(struct session *); int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); -struct winlink *session_new(struct session *, const char *, const char *, +struct winlink *session_new(struct session *, const char *, int, char **, const char *, int, int, char **); -struct winlink *session_attach( - struct session *, struct window *, int, char **); +struct winlink *session_attach(struct session *, struct window *, int, + char **); int session_detach(struct session *, struct winlink *); struct winlink *session_has(struct session *, struct window *); int session_next(struct session *, int); diff --git a/window.c b/window.c index 40ebcdb5..9a7226e9 100644 --- a/window.c +++ b/window.c @@ -307,7 +307,7 @@ window_create1(u_int sx, u_int sy) } struct window * -window_create(const char *name, const char *cmd, const char *path, +window_create(const char *name, int argc, char **argv, const char *path, const char *shell, int cwd, struct environ *env, struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { @@ -318,7 +318,7 @@ window_create(const char *name, const char *cmd, const char *path, wp = window_add_pane(w, hlimit); layout_init(w, wp); - if (window_pane_spawn(wp, cmd, path, shell, cwd, env, tio, + if (window_pane_spawn(wp, argc, argv, path, shell, cwd, env, tio, cause) != 0) { window_destroy(w); return (NULL); @@ -678,7 +678,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); - wp->cmd = NULL; + wp->argc = 0; + wp->argv = NULL; wp->shell = NULL; wp->cwd = -1; @@ -737,27 +738,29 @@ window_pane_destroy(struct window_pane *wp) close(wp->cwd); free(wp->shell); - free(wp->cmd); + cmd_free_argv(wp->argc, wp->argv); free(wp); } int -window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, - const char *shell, int cwd, struct environ *env, struct termios *tio, - char **cause) +window_pane_spawn(struct window_pane *wp, int argc, char **argv, + const char *path, const char *shell, int cwd, struct environ *env, + struct termios *tio, char **cause) { struct winsize ws; - char *argv0, paneid[16]; - const char *ptr; + char *argv0, *cmd, **argvp, paneid[16]; + const char *ptr, *first; struct termios tio2; + int i; if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); } - if (cmd != NULL) { - free(wp->cmd); - wp->cmd = xstrdup(cmd); + if (argc > 0) { + cmd_free_argv(wp->argc, wp->argv); + wp->argc = argc; + wp->argv = cmd_copy_argv(argc, argv); } if (shell != NULL) { free(wp->shell); @@ -768,7 +771,10 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, wp->cwd = dup(cwd); } - log_debug("spawn: %s -- %s", wp->shell, wp->cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); + log_debug("spawn: %s -- %s", wp->shell, cmd); + for (i = 0; i < wp->argc; i++) + log_debug("spawn: argv[%d] = %s", i, wp->argv[i]); memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&wp->base); @@ -778,6 +784,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, case -1: wp->fd = -1; xasprintf(cause, "%s: %s", cmd, strerror(errno)); + free(cmd); return (-1); case 0: if (fchdir(wp->cwd) != 0) @@ -805,31 +812,42 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, setenv("SHELL", wp->shell, 1); ptr = strrchr(wp->shell, '/'); - if (*wp->cmd != '\0') { - /* Use the command. */ + /* + * If given one argument, assume it should be passed to sh -c; + * with more than one argument, use execvp(). If there is no + * arguments, create a login shell. + */ + if (wp->argc > 0) { + if (wp->argc != 1) { + /* Copy to ensure argv ends in NULL. */ + argvp = cmd_copy_argv(wp->argc, wp->argv); + execvp(argvp[0], argvp); + fatal("execvp failed"); + } + first = wp->argv[0]; + if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "%s", ptr + 1); else xasprintf(&argv0, "%s", wp->shell); - execl(wp->shell, argv0, "-c", wp->cmd, (char *) NULL); + execl(wp->shell, argv0, "-c", first, (char *)NULL); fatal("execl failed"); } - - /* No command; fork a login shell. */ if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "-%s", ptr + 1); else xasprintf(&argv0, "-%s", wp->shell); - execl(wp->shell, argv0, (char *) NULL); + execl(wp->shell, argv0, (char *)NULL); fatal("execl failed"); } setblocking(wp->fd, 0); - wp->event = bufferevent_new(wp->fd, - window_pane_read_callback, NULL, window_pane_error_callback, wp); + wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, + window_pane_error_callback, wp); bufferevent_enable(wp->event, EV_READ|EV_WRITE); + free(cmd); return (0); } From 92af7f5901bd944e385e7fdbe8696b2c911ed776 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Tue, 13 May 2014 22:01:03 +0100 Subject: [PATCH 349/949] Linux: Add to log.c This is needed, otherwise tzset() is undefined. --- log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/log.c b/log.c index 7ef70ab6..5c2889c7 100644 --- a/log.c +++ b/log.c @@ -17,6 +17,7 @@ */ #include +#include #include #include From b2e791b574a60e481d2a5813157ab00cb99cc32f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 May 2014 22:54:18 +0000 Subject: [PATCH 350/949] Don't allow multiple buffers with the same name, from Thomas Adam. --- paste.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/paste.c b/paste.c index 11a4f006..245cca37 100644 --- a/paste.c +++ b/paste.c @@ -176,7 +176,7 @@ paste_add(char *data, size_t size) int paste_rename(const char *oldname, const char *newname, char **cause) { - struct paste_buffer *pb; + struct paste_buffer *pb, *pb_new; if (cause != NULL) *cause = NULL; @@ -195,7 +195,14 @@ paste_rename(const char *oldname, const char *newname, char **cause) pb = paste_get_name(oldname); if (pb == NULL) { if (cause != NULL) - xasprintf(cause, "no buffer %s", oldname); + xasprintf(cause, "no buffer %s", oldname); + return (-1); + } + + pb_new = paste_get_name(newname); + if (pb_new != NULL) { + if (cause != NULL) + xasprintf(cause, "buffer %s already exists", newname); return (-1); } From 53cbae544f79daede0f9457f31947f5d001ac788 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 May 2014 06:21:19 +0000 Subject: [PATCH 351/949] Now that cmdlists are reference counted, there is no need for two-step deletion via the dead_key_bindings tree. From Keith Amling. --- key-bindings.c | 17 ++--------------- server.c | 1 - tmux.h | 1 - 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index db48f9e7..55c8aebe 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -27,7 +27,6 @@ RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); struct key_bindings key_bindings; -struct key_bindings dead_key_bindings; int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) @@ -78,20 +77,8 @@ key_bindings_remove(int key) if ((bd = key_bindings_lookup(key)) == NULL) return; RB_REMOVE(key_bindings, &key_bindings, bd); - RB_INSERT(key_bindings, &dead_key_bindings, bd); -} - -void -key_bindings_clean(void) -{ - struct key_binding *bd; - - while (!RB_EMPTY(&dead_key_bindings)) { - bd = RB_ROOT(&dead_key_bindings); - RB_REMOVE(key_bindings, &dead_key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); - } + cmd_list_free(bd->cmdlist); + free(bd); } void diff --git a/server.c b/server.c index 0d6c4f15..7ddc6ec1 100644 --- a/server.c +++ b/server.c @@ -208,7 +208,6 @@ server_loop(void) server_window_loop(); server_client_loop(); - key_bindings_clean(); server_clean_dead(); } } diff --git a/tmux.h b/tmux.h index c42df6ab..8a49b9d2 100644 --- a/tmux.h +++ b/tmux.h @@ -1889,7 +1889,6 @@ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); struct key_binding *key_bindings_lookup(int); void key_bindings_add(int, int, struct cmd_list *); void key_bindings_remove(int); -void key_bindings_clean(void); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *); From 58c97695c9516ae2d95e1f26c8fa2539fece862a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 May 2014 06:39:58 +0000 Subject: [PATCH 352/949] Simplify copy lines, from Keith Amling. --- window-copy.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/window-copy.c b/window-copy.c index aa6b2d73..1d603353 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1442,17 +1442,10 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) } /* Copy the lines. */ - if (sy == ey) - window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); - else { - window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); - if (ey - sy > 1) { - for (i = sy + 1; i < ey; i++) { - window_copy_copy_line( - wp, &buf, &off, i, restsx, restex); - } - } - window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex); + for (i = sy; i <= ey; i++) { + window_copy_copy_line(wp, &buf, &off, i, + (i == sy ? firstsx : restsx), + (i == ey ? lastex : restex)); } /* Don't bother if no data. */ From 191f695bad17f89c1af67d6b03dcfe55997fc5ff Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 May 2014 06:45:35 +0000 Subject: [PATCH 353/949] Change key-table to mode-table to allow for some future work. From Keith Amling. --- cmd-bind-key.c | 8 ++++---- cmd-unbind-key.c | 8 ++++---- tmux.1 | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 1ca31484..dce0bbf0 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -29,12 +29,12 @@ enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", "cnrt:", 1, -1, - "[-cnr] [-t key-table] key command [arguments]", + "[-cnr] [-t mode-table] key command [arguments]", 0, NULL, cmd_bind_key_exec @@ -67,7 +67,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 't')) - return (cmd_bind_key_table(self, cmdq, key)); + return (cmd_bind_key_mode_table(self, cmdq, key)); cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, &cause); @@ -84,7 +84,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 5d86665b..92657c29 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -27,12 +27,12 @@ */ enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", "acnt:", 0, 1, - "[-acn] [-t key-table] key", + "[-acn] [-t mode-table] key", 0, NULL, cmd_unbind_key_exec @@ -64,7 +64,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 't')) - return (cmd_unbind_key_table(self, cmdq, key)); + return (cmd_unbind_key_mode_table(self, cmdq, key)); if (key == KEYC_NONE) { while (!RB_EMPTY(&key_bindings)) { @@ -81,7 +81,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; diff --git a/tmux.1 b/tmux.1 index 24dde9fa..4aa66f9c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1861,7 +1861,7 @@ Commands related to key bindings are as follows: .Bl -tag -width Ds .It Xo Ic bind-key .Op Fl cnr -.Op Fl t Ar key-table +.Op Fl t Ar mode-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) @@ -1890,7 +1890,7 @@ If is present, .Ar key is bound in -.Ar key-table : +.Ar mode-table : the binding for command mode with .Fl c or for normal mode without. @@ -1948,7 +1948,7 @@ Send the prefix key, or with the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key .Op Fl acn -.Op Fl t Ar key-table +.Op Fl t Ar mode-table .Ar key .Xc .D1 (alias: Ic unbind ) @@ -1970,7 +1970,7 @@ If is present, .Ar key in -.Ar key-table +.Ar mode-table is unbound: the binding for command mode with .Fl c or for normal mode without. From 7160b8c2d5d17565ef8da0fb532cb49b2d5718bb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 May 2014 12:49:36 +0000 Subject: [PATCH 354/949] Add some formats for pane bounds. --- format.c | 7 +++++++ tmux.1 | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 10e5e0a8..e10f08c9 100644 --- a/format.c +++ b/format.c @@ -552,6 +552,13 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_active", "%d", wp == wp->window->active); format_add(ft, "pane_dead", "%d", wp->fd == -1); + if (window_pane_visible(wp)) { + format_add(ft, "pane_left", "%u", wp->xoff); + format_add(ft, "pane_top", "%u", wp->yoff); + 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_in_mode", "%d", wp->screen != &wp->base); format_add(ft, "pane_synchronized", "%d", !!options_get_number(&wp->window->options, "synchronize-panes")); diff --git a/tmux.1 b/tmux.1 index 4aa66f9c..9a5a1c4d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3075,17 +3075,21 @@ The following variables are available, where appropriate: .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" .It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "pane_active" Ta "" Ta "1 if active pane" +.It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .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 "If pane is in a mode" -.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" .It Li "pane_index" Ta "#P" Ta "Index of pane" +.It Li "pane_left" Ta "" Ta "Left of pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" +.It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_synchronized" Ta "" Ta "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_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" .It Li "saved_cursor_x" Ta "" Ta "Saved cursor X in pane" From 193b6bcf36dda3754c99beb0dcc3dfdfceebdb09 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 May 2014 13:04:42 +0000 Subject: [PATCH 355/949] Handle the top bit of xterm(1)-style modifier keys, based on a diff from Balazs Kezes. --- xterm-keys.c | 79 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/xterm-keys.c b/xterm-keys.c index 0e20165b..a013cbde 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -40,8 +40,8 @@ * We accept any but always output the latter (it comes first in the table). */ -int xterm_keys_match(const char *, const char *, size_t); -int xterm_keys_modifiers(const char *, const char *, size_t); +int xterm_keys_match(const char *, const char *, size_t, size_t *, u_int *); +int xterm_keys_modifiers(const char *, size_t, size_t *, u_int *); struct xterm_keys_entry { int key; @@ -122,49 +122,62 @@ const struct xterm_keys_entry xterm_keys_table[] = { * 0 for match, 1 if the end of the buffer is reached (need more data). */ int -xterm_keys_match(const char *template, const char *buf, size_t len) +xterm_keys_match(const char *template, const char *buf, size_t len, + size_t *size, u_int *modifiers) { size_t pos; + int retval; if (len == 0) return (0); pos = 0; do { - if (*template == '_' && buf[pos] >= '1' && buf[pos] <= '8') + if (*template == '_') { + retval = xterm_keys_modifiers(buf, len, &pos, + modifiers); + if (retval != 0) + return (retval); continue; + } if (buf[pos] != *template) return (-1); - } while (*++template != '\0' && ++pos != len); + pos++; + } while (*++template != '\0' && pos != len); if (*template != '\0') /* partial */ return (1); + *size = pos; return (0); } -/* Find modifiers based on template. */ +/* Find modifiers from buffer. */ int -xterm_keys_modifiers(const char *template, const char *buf, size_t len) +xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) { - size_t idx; - int param, modifiers; + u_int flags; - idx = strcspn(template, "_"); - if (idx >= len) - return (0); - param = buf[idx] - '1'; + if (len - *pos < 2) + return (1); - modifiers = 0; - if (param & 1) - modifiers |= KEYC_SHIFT; - if (param & 2) - modifiers |= KEYC_ESCAPE; - if (param & 4) - modifiers |= KEYC_CTRL; - if (param & 8) - modifiers |= KEYC_ESCAPE; - return (modifiers); + if (buf[*pos] < '0' || buf[*pos] > '9') + return (-1); + flags = buf[(*pos)++] - '0'; + if (buf[*pos] >= '0' && buf[*pos] <= '9') + flags = (flags * 10) + (buf[(*pos)++] - '0'); + flags -= 1; + + *modifiers = 0; + if (flags & 1) + *modifiers |= KEYC_SHIFT; + if (flags & 2) + *modifiers |= KEYC_ESCAPE; + if (flags & 4) + *modifiers |= KEYC_CTRL; + if (flags & 8) + *modifiers |= KEYC_ESCAPE; + return (0); } /* @@ -175,19 +188,19 @@ int xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) { const struct xterm_keys_entry *entry; - u_int i; + u_int i, modifiers; + int matched; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; - switch (xterm_keys_match(entry->template, buf, len)) { - case 0: - *size = strlen(entry->template); - *key = entry->key; - *key |= xterm_keys_modifiers(entry->template, buf, len); - return (0); - case 1: - return (1); - } + + matched = xterm_keys_match(entry->template, buf, len, size, + &modifiers); + if (matched == -1) + continue; + if (matched == 0) + *key = entry->key | modifiers; + return (matched); } return (-1); } From 74becbfd6f0f497424d32c03eb00a9bc226b8454 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 5 Jun 2014 22:14:29 +0000 Subject: [PATCH 356/949] Reset properly when c0-change-trigger is increased from zero so panes don't get stuck. --- window.c | 1 - 1 file changed, 1 deletion(-) diff --git a/window.c b/window.c index 9a7226e9..d29ee2f2 100644 --- a/window.c +++ b/window.c @@ -877,7 +877,6 @@ window_pane_timer_callback(unused int fd, unused short events, void *data) if (wp->changes_redraw++ == interval) { wp->flags |= PANE_REDRAW; wp->changes_redraw = 0; - } if (trigger == 0 || wp->changes < trigger) { From 21ade85f24c122ecc3148a3bfeed3044a59c3214 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Jun 2014 13:21:41 +0000 Subject: [PATCH 357/949] Properly track switching G0 and G1 modes between US-ASCII and VT100 line drawing rather than just treating them as SO and SI. --- input.c | 104 +++++++++++++++++++++++++++++++++----------------------- tmux.h | 14 ++++++-- 2 files changed, 72 insertions(+), 46 deletions(-) diff --git a/input.c b/input.c index 1123f952..4c1e0730 100644 --- a/input.c +++ b/input.c @@ -52,6 +52,7 @@ int input_split(struct input_ctx *); int input_get(struct input_ctx *, u_int, int, int); void input_reply(struct input_ctx *, const char *, ...); void input_set_state(struct window_pane *, const struct input_transition *); +void input_reset_cell(struct input_ctx *); /* Transition entry/exit handlers. */ void input_clear(struct input_ctx *); @@ -104,19 +105,23 @@ enum input_esc_type { INPUT_ESC_NEL, INPUT_ESC_RI, INPUT_ESC_RIS, - INPUT_ESC_SCSOFF_G0, - INPUT_ESC_SCSON_G0, + INPUT_ESC_SCSG0_OFF, + INPUT_ESC_SCSG0_ON, + INPUT_ESC_SCSG1_OFF, + INPUT_ESC_SCSG1_ON, }; /* Escape command table. */ const struct input_table_entry input_esc_table[] = { - { '0', "(", INPUT_ESC_SCSOFF_G0 }, + { '0', "(", INPUT_ESC_SCSG0_ON }, + { '0', ")", INPUT_ESC_SCSG1_ON }, { '7', "", INPUT_ESC_DECSC }, { '8', "", INPUT_ESC_DECRC }, { '8', "#", INPUT_ESC_DECALN }, { '=', "", INPUT_ESC_DECKPAM }, { '>', "", INPUT_ESC_DECKPNM }, - { 'B', "(", INPUT_ESC_SCSON_G0 }, + { 'B', "(", INPUT_ESC_SCSG0_OFF }, + { 'B', ")", INPUT_ESC_SCSG1_OFF }, { 'D', "", INPUT_ESC_IND }, { 'E', "", INPUT_ESC_NEL }, { 'H', "", INPUT_ESC_HTS }, @@ -684,17 +689,26 @@ input_table_compare(const void *key, const void *value) return (strcmp(ictx->interm_buf, entry->interm)); } +/* Reset cell state to default. */ +void +input_reset_cell(struct input_ctx *ictx) +{ + memcpy(&ictx->cell.cell, &grid_default_cell, sizeof ictx->cell.cell); + ictx->cell.set = 0; + ictx->cell.g0set = ictx->cell.g1set = 0; + + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; +} + /* Initialise input parser. */ void input_init(struct window_pane *wp) { struct input_ctx *ictx = &wp->ictx; - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - - memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; + input_reset_cell(ictx); *ictx->interm_buf = '\0'; ictx->interm_len = 0; @@ -903,8 +917,18 @@ input_ground(struct input_ctx *ictx) int input_print(struct input_ctx *ictx) { - grid_cell_one(&ictx->cell, ictx->ch); - screen_write_cell(&ictx->ctx, &ictx->cell); + int set; + + set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set; + if (set == 1) + ictx->cell.cell.attr |= GRID_ATTR_CHARSET; + else + ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; + + grid_cell_one(&ictx->cell.cell, ictx->ch); + screen_write_cell(&ictx->ctx, &ictx->cell.cell); + + ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; return (0); } @@ -1000,10 +1024,10 @@ input_c0_dispatch(struct input_ctx *ictx) screen_write_carriagereturn(sctx); goto count_c0; case '\016': /* SO */ - ictx->cell.attr |= GRID_ATTR_CHARSET; + ictx->cell.set = 1; break; case '\017': /* SI */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; + ictx->cell.set = 0; break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1014,7 +1038,7 @@ input_c0_dispatch(struct input_ctx *ictx) count_c0: trigger = options_get_number(&wp->window->options, "c0-change-trigger"); - if (++wp->changes == trigger) { + if (trigger != 0 && ++wp->changes >= trigger) { wp->flags |= PANE_DROP; window_pane_timer_start(wp); } @@ -1043,11 +1067,7 @@ input_esc_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_ESC_RIS: - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; - + input_reset_cell(ictx); screen_write_reset(sctx); break; case INPUT_ESC_IND: @@ -1082,16 +1102,17 @@ input_esc_dispatch(struct input_ctx *ictx) case INPUT_ESC_DECALN: screen_write_alignmenttest(sctx); break; - case INPUT_ESC_SCSON_G0: - /* - * Not really supported, but fake it up enough for those that - * use it to switch character sets (by redefining G0 to - * graphics set, rather than switching to G1). - */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; + case INPUT_ESC_SCSG0_ON: + ictx->cell.g0set = 1; break; - case INPUT_ESC_SCSOFF_G0: - ictx->cell.attr |= GRID_ATTR_CHARSET; + case INPUT_ESC_SCSG0_OFF: + ictx->cell.g0set = 0; + break; + case INPUT_ESC_SCSG1_ON: + ictx->cell.g1set = 1; + break; + case INPUT_ESC_SCSG1_OFF: + ictx->cell.g1set = 0; break; } @@ -1332,7 +1353,8 @@ input_csi_dispatch_rm(struct input_ctx *ictx) void input_csi_dispatch_rm_private(struct input_ctx *ictx) { - u_int i; + struct window_pane *wp = ictx->wp; + u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { @@ -1366,10 +1388,10 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_off(ictx->wp, &ictx->cell, 0); + window_pane_alternate_off(wp, &ictx->cell.cell, 0); break; case 1049: - window_pane_alternate_off(ictx->wp, &ictx->cell, 1); + window_pane_alternate_off(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); @@ -1403,7 +1425,8 @@ input_csi_dispatch_sm(struct input_ctx *ictx) void input_csi_dispatch_sm_private(struct input_ctx *ictx) { - u_int i; + struct window_pane *wp = ictx->wp; + u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { @@ -1436,7 +1459,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) if (ictx->ctx.s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - ictx->wp->flags |= PANE_FOCUSPUSH; /* force update */ + wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); @@ -1446,10 +1469,10 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_on(ictx->wp, &ictx->cell, 0); + window_pane_alternate_on(wp, &ictx->cell.cell, 0); break; case 1049: - window_pane_alternate_on(ictx->wp, &ictx->cell, 1); + window_pane_alternate_on(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); @@ -1514,15 +1537,12 @@ input_csi_dispatch_winops(struct input_ctx *ictx) void input_csi_dispatch_sgr(struct input_ctx *ictx) { - struct grid_cell *gc = &ictx->cell; + struct grid_cell *gc = &ictx->cell.cell; u_int i; int n, m; - u_char attr; if (ictx->param_list_len == 0) { - attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); - gc->attr |= (attr & GRID_ATTR_CHARSET); return; } @@ -1560,9 +1580,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) switch (n) { case 0: case 10: - attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); - gc->attr |= (attr & GRID_ATTR_CHARSET); break; case 1: gc->attr |= GRID_ATTR_BRIGHT; @@ -1806,8 +1824,8 @@ input_utf8_close(struct input_ctx *ictx) utf8_append(&ictx->utf8data, ictx->ch); - grid_cell_set(&ictx->cell, &ictx->utf8data); - screen_write_cell(&ictx->ctx, &ictx->cell); + grid_cell_set(&ictx->cell.cell, &ictx->utf8data); + screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); } diff --git a/tmux.h b/tmux.h index 8a49b9d2..4f937f3a 100644 --- a/tmux.h +++ b/tmux.h @@ -803,14 +803,22 @@ struct screen_write_ctx { #define screen_hsize(s) ((s)->grid->hsize) #define screen_hlimit(s) ((s)->grid->hlimit) +/* Input parser cell. */ +struct input_cell { + struct grid_cell cell; + int set; + int g0set; /* 1 if ACS */ + int g1set; /* 1 if ACS */ +}; + /* Input parser context. */ struct input_ctx { struct window_pane *wp; struct screen_write_ctx ctx; - struct grid_cell cell; + struct input_cell cell; - struct grid_cell old_cell; + struct input_cell old_cell; u_int old_cx; u_int old_cy; @@ -822,7 +830,7 @@ struct input_ctx { #define INPUT_BUF_START 32 #define INPUT_BUF_LIMIT 1048576 - u_char* input_buf; + u_char *input_buf; size_t input_len; size_t input_space; From f5973b77763e3f8da8e8f6483464f6f4bc2f3981 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Jun 2014 08:21:08 +0100 Subject: [PATCH 358/949] Build DEBUG with -O2 as well. --- Makefile.am | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 5502de86..5f9e0116 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,17 +25,15 @@ endif # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. if IS_GCC -CFLAGS += -std=gnu99 +CFLAGS += -std=gnu99 -O2 if IS_DEBUG -CFLAGS += -O0 -g +CFLAGS += -g CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align CFLAGS += -Wdeclaration-after-statement CPPFLAGS += -DDEBUG -else -CFLAGS += -O2 endif if IS_GCC4 CPPFLAGS += -iquote. -I/usr/local/include @@ -247,7 +245,7 @@ install-exec-hook: >$(srcdir)/tmux.1.mdoc; \ else \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ - $(AWK) -f $(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ + $(AWK) -f$(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ fi $(MKDIR_P) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ From a94696defa108dcddc39d50596e69266e595eb74 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Jun 2014 07:26:43 +0000 Subject: [PATCH 359/949] Some terminals send spurious releases for mouse wheel in SGR mouse mode, this causes confusion when tmux uses SGR outside but the application inside tmux is using conventional xterm mouse reporting. So suppress obviously bad input. From Timothy Allen, SF bug 128. --- tty-keys.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index 297e22c8..aaea06c6 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -475,6 +475,8 @@ tty_keys_next(struct tty *tty) goto complete_key; case -1: /* no, or not valid */ break; + case -2: /* yes, but we don't care. */ + goto discard_key; case 1: /* partial */ goto partial_key; } @@ -586,6 +588,14 @@ complete_key: server_client_handle_key(tty->client, key); return (1); + +discard_key: + log_debug("discard key %.*s %#x", (int) size, buf, key); + + /* Remove data from buffer. */ + evbuffer_drain(tty->event->input, size); + + return (1); } /* Key timer callback. */ @@ -730,6 +740,15 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) sgr = 1; sgr_rel = (c == 'm'); + /* + * Some terminals (like PuTTY 0.63) mistakenly send + * button-release events for scroll-wheel button-press event. + * Discard it before it reaches any program running inside + * tmux. + */ + if (sgr_rel && (sgr_b & 64)) + return (-2); + /* Figure out what b would be in old format. */ b = sgr_b; if (sgr_rel) From fd9a53b4a46f53c864fd1bbf5175a8752c68d348 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Jun 2014 07:32:12 +0000 Subject: [PATCH 360/949] Reset the buttons when the wheel is used, from Balazs Kezes. --- tty-keys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index aaea06c6..02be49fa 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -783,6 +783,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) else if (b == 1) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; + + m->button = 3; } else if ((b & MOUSE_MASK_BUTTONS) == 3) { if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) m->event = MOUSE_EVENT_CLICK; From 00ac1af43fe6fe51cc199a9d983499ef7fd78cf5 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Jun 2014 07:37:59 +0000 Subject: [PATCH 361/949] Copy newline when at EOL in vi(1) mode, from Balazs Kezes. --- window-copy.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index 1d603353..17f9751b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1360,7 +1360,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) struct screen *s = &data->screen; char *buf; size_t off; - u_int i, xx, yy, sx, sy, ex, ey; + u_int i, xx, yy, sx, sy, ex, ey, ey_last; u_int firstsx, lastex, restex, restsx; int keys; @@ -1389,9 +1389,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) } /* Trim ex to end of line. */ - xx = window_copy_find_length(wp, ey); - if (ex > xx) - ex = xx; + ey_last = window_copy_find_length(wp, ey); + if (ex > ey_last) + ex = ey_last; /* * Deal with rectangle-copy if necessary; four situations: start of @@ -1453,7 +1453,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) free(buf); return (NULL); } - *len = off - 1; /* remove final \n */ + if (keys == MODEKEY_EMACS || lastex <= ey_last) + off -= 1; /* remove final \n (unless at end in vi mode) */ + *len = off; return (buf); } From 19cb0a1a921164acb9591d7837d066ca9ea94a98 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 19 Jun 2014 23:15:06 +0100 Subject: [PATCH 362/949] Might as well still allow autoconf 2.59. --- Makefile.am | 2 +- configure.ac | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 5f9e0116..5f622acb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -247,7 +247,7 @@ install-exec-hook: sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ $(AWK) -f$(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ fi - $(MKDIR_P) $(DESTDIR)$(mandir)/man1 + $(mkdir_p) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ $(DESTDIR)$(mandir)/man1/tmux.1 diff --git a/configure.ac b/configure.ac index 7cc1ac5d..a81ecc42 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,6 @@ AC_CANONICAL_HOST # Set up the compiler in two different ways and say yes we may want to install. AC_PROG_CC AM_PROG_CC_C_O -AC_PROG_MKDIR_P AC_PROG_INSTALL # Default tmux.conf goes in /etc not ${prefix}/etc. From c8efffb4db5c521640dd5687d2a640ca04664aad Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Jun 2014 10:46:59 +0000 Subject: [PATCH 363/949] Make -S- and -E- mean the start and end to capture-pane to avoid having to faff around with huge numbers to get everything. --- cmd-capture-pane.c | 47 ++++++++++++++++++++++++++++------------------ tmux.1 | 6 ++++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index c6a80ebd..e3c551ab 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -94,6 +94,7 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, int n, with_codes, escape_c0, join_lines; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; + const char *Sflag, *Eflag; size_t linelen; sx = screen_size_x(&wp->base); @@ -109,27 +110,37 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, } else gd = wp->base.grid; - n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); - if (cause != NULL) { - top = gd->hsize; - free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) + Sflag = args_get(args, 'S'); + if (Sflag != NULL && strcmp(Sflag, "-") == 0) top = 0; - else - top = gd->hsize + n; - if (top > gd->hsize + gd->sy - 1) - top = gd->hsize + gd->sy - 1; + else { + n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + top = gd->hsize; + free(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + top = 0; + else + top = gd->hsize + n; + if (top > gd->hsize + gd->sy - 1) + top = gd->hsize + gd->sy - 1; + } - n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); - if (cause != NULL) { - bottom = gd->hsize + gd->sy - 1; - free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) - bottom = 0; - else - bottom = gd->hsize + n; - if (bottom > gd->hsize + gd->sy - 1) + Eflag = args_get(args, 'E'); + if (Eflag != NULL && strcmp(Eflag, "-") == 0) bottom = gd->hsize + gd->sy - 1; + else { + n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + bottom = gd->hsize + gd->sy - 1; + free(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + bottom = 0; + else + bottom = gd->hsize + n; + if (bottom > gd->hsize + gd->sy - 1) + bottom = gd->hsize + gd->sy - 1; + } if (bottom < top) { tmp = bottom; diff --git a/tmux.1 b/tmux.1 index 9a5a1c4d..f70ac391 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1148,6 +1148,12 @@ and .Fl E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. +.Ql - +to +.Fl S +is the start of the history and to +.Fl E +the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client From 8d0819bff16dbfbc61a97135742e1a0f1c144f77 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Jun 2014 11:00:19 +0000 Subject: [PATCH 364/949] Comment style nits. --- paste.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paste.c b/paste.c index 245cca37..08f4bab5 100644 --- a/paste.c +++ b/paste.c @@ -69,7 +69,7 @@ paste_walk(struct paste_buffer *pb) return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); } -/* Get the most recent automatic buffer */ +/* Get the most recent automatic buffer. */ struct paste_buffer * paste_get_top(void) { @@ -81,7 +81,7 @@ paste_get_top(void) return (pb); } -/* Free the most recent buffer */ +/* Free the most recent buffer. */ int paste_free_top(void) { From 8049baf0bda9dcdd4134b32a29cc2731dfa7900c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 Jun 2014 12:19:51 +0100 Subject: [PATCH 365/949] Remove some done, tweak some others. --- TODO | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index 6e5d4259..76b4e39c 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ - command bits and pieces: - * use "--" to mark start of command w/ neww etc to avoid quoting * why doesn't command-prompt work if made read-only? * allow multiple targets: fnmatch for -t/-c, for example detach all clients with -t* @@ -20,10 +19,10 @@ * way to set socket path from config file - format improvements: + * last bits of status_replace into formats? * option to quote format (#{session_name:quoted}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces, #!2T maybe - * status stuff is redundant with formats * last window update time and format for it * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple @@ -33,7 +32,7 @@ * choose-pane command (augment choose-tree to do this?) * choose-mode and copy-mode are very similar, make choose-mode a subset? * flag to choose-* for sort order - * choose mode would be better per client than per window + * choose mode would be better per client than per window? * two choices (first one then second, for swap-pane and join-pane) - improve monitor-*: @@ -44,9 +43,8 @@ - improve mouse support: * bind commands to mouse in different areas? - * more fine-grained options + * more fine-grained options? * commands executed when clicking on a pattern (URL) - * send arrow key sequences for mouse scroll wheel in alternate screen * mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the flag on w/o checking the others before calling tty_update_mode) @@ -58,18 +56,15 @@ * split-window -> split-pane?? - better UTF-8 support: - * #22T can split in the middle of UTF-8 characters! * window names and titles * message display * prompt input * multibyte key input - * buffer_sample and the choose-* could show UTF-8 properly - copy/paste improvements: * incremental searching * append to buffer * paste w/o trailing whitespace - * named buffers and allow gaps in the stack * command to toggle selection not to move it in copy-mode - layout stuff @@ -91,8 +86,6 @@ * use screen-256color when started on 256 colour terminal? * need a tmux terminfo entry to document the extensions we are using in upstream terminfo - * support title stack, both internally and externally (restore on - detach) http://docs.freebsd.org/cgi/getmsg.cgi?fetch=1149299+0+archive/2010/freebsd-questions/20100207.freebsd-questions - code cleanup * instead of separate window and session options, just one master @@ -125,7 +118,9 @@ * live update: server started with -U connects to server, requests sessions and windows, receives file descriptors * there are inconsistencies in what we get from old shell and what - comes from config for new sessions and windows + comes from config for new sessions and windows. likewise, panes and + jobs and run-shell and lock command all start with slightly different + environments * multiline status line? * bind commands to key sequences -- make it so ALL keys go through a table, first an implicit table in which C-b is the only default From 0c5ed177c4e5178127dc8fe3528985df8ff988a6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 Jun 2014 13:17:28 +0100 Subject: [PATCH 366/949] We have utmp with utempter now and that's the best we're going to get. --- FAQ | 3 --- 1 file changed, 3 deletions(-) diff --git a/FAQ b/FAQ index 6181ad89..97d8f6b6 100644 --- a/FAQ +++ b/FAQ @@ -95,9 +95,6 @@ aware of are (bearing in mind I haven't used screen for a few years now): - screen has builtin serial and telnet support; this is bloat and is unlikely to be added to tmux. -- screen has support for updating utmp. Nobody has really come up with a clean, - portable way to do this without making tmux setuid or setgid yet. - - Environment handling is different. - tmux tends to be more demanding on the terminal so tends to show up terminal From cddf50b715be8732ffd0732d33a886b788a231c5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 21 Jun 2014 19:45:52 +0100 Subject: [PATCH 367/949] Notes for better mouse support. --- TODO | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/TODO b/TODO index 76b4e39c..de92d7cd 100644 --- a/TODO +++ b/TODO @@ -47,6 +47,25 @@ * commands executed when clicking on a pattern (URL) * mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the flag on w/o checking the others before calling tty_update_mode) + * mouse can be like normal key bindings? + - {button-{1,2,3},wheel-{up,down}}-{status,pane,border} and + drag-{start,end}-{status,pane,border} plus the modifiers + - resize and copy can be special cases - once you call something + like copy-mode -M or resize-pane -M to start the drag, it tracks + mouse until you call -m to stop the drag. or just keep drags + entirely special? + - what happens with stuff that wants mouse inside? especially for + pane clicks which need to run command AND pass event through + (like mouse-select-pane). maybe just a flag to say whether it + always runs or only if pane hasn't taken mouse? or it could be + eg bind Button1Pane "select-pane -t=; send-keys -Mt=' + - also need a) some way to tell commands bound to key which + window or pane the mouse key binding applies to (maybe a new + special char in target, or pass targets through formats?) b) a + way to bind repeat count to mode keys so that wheel up/down can + do multiple lines c) send-keys -M to pass a mouse event through? + - what does the mouse->KEYC_* conversion and find-the-pane bit? + server_client_handle_key? - hooks! From 61605c68839efa51ba049b549e67e62d68616940 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Jun 2014 09:52:56 +0000 Subject: [PATCH 368/949] Count mouse clicks correctly, from Balazs Kezes. --- tty-keys.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 02be49fa..932367ef 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -786,25 +786,24 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->button = 3; } else if ((b & MOUSE_MASK_BUTTONS) == 3) { - if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) + if (~m->event & MOUSE_EVENT_DRAG && x == m->sx && y == m->sy) { m->event = MOUSE_EVENT_CLICK; - else + m->clicks = (m->clicks + 1) % 3; + } else m->event = MOUSE_EVENT_DRAG; m->event |= MOUSE_EVENT_UP; } else { if (b & MOUSE_MASK_DRAG) m->event = MOUSE_EVENT_DRAG; else { - if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y) - m->clicks = (m->clicks + 1) % 3; - else - m->clicks = 0; - m->sx = x; - m->sy = y; m->event = MOUSE_EVENT_DOWN; + if (x != m->sx || y != m->sy) + m->clicks = 0; } m->button = (b & MOUSE_MASK_BUTTONS); } + m->sx = x; + m->sy = y; return (0); } From 54782af61632846a48af35b33c056243fea7df8e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Jun 2014 10:27:05 +0000 Subject: [PATCH 369/949] Allow keys and send-keys to invisible panes, from saggy-kun at users dot sf dot net. --- window.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/window.c b/window.c index d29ee2f2..a0c53ca8 100644 --- a/window.c +++ b/window.c @@ -1051,9 +1051,6 @@ window_pane_key(struct window_pane *wp, struct session *sess, int key) { struct window_pane *wp2; - if (!window_pane_visible(wp)) - return; - if (wp->mode != NULL) { if (wp->mode->key != NULL) wp->mode->key(wp, sess, key); From 1aae53596dd1bd657d534c7a0392826cbff2265c Mon Sep 17 00:00:00 2001 From: tobias Date: Wed, 25 Jun 2014 19:17:27 +0000 Subject: [PATCH 370/949] Handle escaped back slashes and missing new lines at end of line by using fparseln instead of reimplementing it on our own. with input by and ok nicm@ --- cfg.c | 64 ++++++++++++++++------------------------------------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/cfg.c b/cfg.c index 5d98dbe4..7502cee7 100644 --- a/cfg.c +++ b/cfg.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "tmux.h" @@ -36,9 +37,10 @@ int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) { FILE *f; - u_int n, found; - char *buf, *copy, *line, *cause1, *msg; - size_t len, oldlen; + char delim[3] = { '\\', '\\', '\0' }; + u_int found; + size_t line = 0; + char *buf, *cause1, *msg, *p; struct cmd_list *cmdlist; log_debug("loading %s", path); @@ -47,60 +49,30 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) return (-1); } - n = found = 0; - line = NULL; - while ((buf = fgetln(f, &len))) { - /* Trim \n. */ - if (buf[len - 1] == '\n') - len--; - log_debug("%s: %.*s", path, (int)len, buf); - - /* Current line is the continuation of the previous one. */ - if (line != NULL) { - oldlen = strlen(line); - line = xrealloc(line, 1, oldlen + len + 1); - } else { - oldlen = 0; - line = xmalloc(len + 1); - } - - /* Append current line to the previous. */ - memcpy(line + oldlen, buf, len); - line[oldlen + len] = '\0'; - n++; - - /* Continuation: get next line? */ - len = strlen(line); - if (len > 0 && line[len - 1] == '\\') { - line[len - 1] = '\0'; - - /* Ignore escaped backslash at EOL. */ - if (len > 1 && line[len - 2] != '\\') - continue; - } - copy = line; - line = NULL; + found = 0; + while ((buf = fparseln(f, NULL, &line, delim, 0))) { + log_debug("%s: %s", path, buf); /* Skip empty lines. */ - buf = copy; - while (isspace((u_char)*buf)) - buf++; - if (*buf == '\0') { - free(copy); + p = buf; + while (isspace((u_char) *p)) + p++; + if (*p == '\0') { + free(buf); continue; } /* Parse and run the command. */ - if (cmd_string_parse(buf, &cmdlist, path, n, &cause1) != 0) { - free(copy); + if (cmd_string_parse(p, &cmdlist, path, line, &cause1) != 0) { + free(buf); if (cause1 == NULL) continue; - xasprintf(&msg, "%s:%u: %s", path, n, cause1); + xasprintf(&msg, "%s:%zu: %s", path, line, cause1); ARRAY_ADD(&cfg_causes, msg); free(cause1); continue; } - free(copy); + free(buf); if (cmdlist == NULL) continue; @@ -108,8 +80,6 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) cmd_list_free(cmdlist); found++; } - if (line != NULL) - free(line); fclose(f); return (found); From d1f939cede4a1eb7137feb892450e0e2b650cc6c Mon Sep 17 00:00:00 2001 From: guenther Date: Fri, 4 Jul 2014 05:58:31 +0000 Subject: [PATCH 371/949] Track whether a process is a zombie or not yet fully built via flags PS_{ZOMBIE,EMBRYO} on the process instead of peeking into the process's thread data. This eliminates the need for the thread-level SDEAD state. Change kvm_getprocs() (both the sysctl() and kvm backends) to report the "most active" scheduler state for the process's threads. tweaks kettenis@ feedback and ok matthew@ --- procname.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procname.c b/procname.c index 5d3bc306..7ee076e7 100644 --- a/procname.c +++ b/procname.c @@ -33,7 +33,7 @@ #define is_runnable(p) \ ((p)->p_stat == SRUN || (p)->p_stat == SIDL || (p)->p_stat == SONPROC) #define is_stopped(p) \ - ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB || (p)->p_stat == SDEAD) + ((p)->p_stat == SSTOP || (p)->p_stat == SDEAD) struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *get_proc_name(int, char *); From f117c7d94a2abb0561540a1fe33dd729b1cb8b34 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Jul 2014 20:23:10 +0000 Subject: [PATCH 372/949] If a client is killed while suspended with ^Z so has gone through the MSG_EXITED dance, don't try to resume it since a) it's pointless and b) the tty structures have been cleaned up and tmux will crash. --- server-client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server-client.c b/server-client.c index 0f4d39d6..1ef3f311 100644 --- a/server-client.c +++ b/server-client.c @@ -875,6 +875,9 @@ server_client_msg_dispatch(struct client *c) break; c->flags &= ~CLIENT_SUSPENDED; + if (c->tty.fd == -1) /* exited in the meantime */ + break; + if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday"); if (c->session != NULL) From 2b67907176805e3df5bd913174617d768277bfff Mon Sep 17 00:00:00 2001 From: krw Date: Sun, 13 Jul 2014 20:51:08 +0000 Subject: [PATCH 373/949] An EOF is a good reason to close a connection. ok nicm@ --- client.c | 2 +- server-client.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index ec63fc07..ab7847fe 100644 --- a/client.c +++ b/client.c @@ -469,7 +469,7 @@ client_callback(unused int fd, short events, void *data) } if (events & EV_WRITE) { - if (msgbuf_write(&client_ibuf.w) < 0 && errno != EAGAIN) + if (msgbuf_write(&client_ibuf.w) <= 0 && errno != EAGAIN) goto lost_server; } diff --git a/server-client.c b/server-client.c index 1ef3f311..5616115c 100644 --- a/server-client.c +++ b/server-client.c @@ -224,7 +224,7 @@ server_client_callback(int fd, short events, void *data) return; if (fd == c->ibuf.fd) { - if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0 && + if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) goto client_lost; From c5253ad8f7236662d0186dec1080288eb2d38b72 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Jul 2014 20:57:46 +0000 Subject: [PATCH 374/949] Show an error if cmd_find_session can't find the current session, like the other functions. --- cmd.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd.c b/cmd.c index d8e010cb..185d39b7 100644 --- a/cmd.c +++ b/cmd.c @@ -795,8 +795,11 @@ cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached) int ambiguous; /* A NULL argument means the current session. */ - if (arg == NULL) - return (cmd_current_session(cmdq, prefer_unattached)); + if (arg == NULL) { + if ((s = cmd_current_session(cmdq, prefer_unattached)) == NULL) + cmdq_error(cmdq, "can't establish current session"); + return (s); + } /* Lookup as pane id or window id. */ if ((wp = cmd_lookup_paneid(arg)) != NULL) @@ -813,7 +816,9 @@ cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached) /* An empty session name is the current session. */ if (*tmparg == '\0') { free(tmparg); - return (cmd_current_session(cmdq, prefer_unattached)); + if ((s = cmd_current_session(cmdq, prefer_unattached)) == NULL) + cmdq_error(cmdq, "can't establish current session"); + return (s); } /* Find the session, if any. */ From 2056a9ef9e91996c294b5db0a3d01ed415e95e56 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 21 Jul 2014 10:25:48 +0000 Subject: [PATCH 375/949] Drop explicit support for F13-F20 and change to match the xterm terminfo entry: F13-F24 are S-F1 to S-F12 F25-F36 are C-F1 to C-F12 F37-F48 are C-S-F1 to C-S-F12 F49-F60 are M-F1 to M-F12 and F61-F63 are M-S-F1 to M-S-F3 This should be no difference for applications inside tmux, but means that any key binding for F13 will need to be replaced by S-F1 and so on. --- input-keys.c | 16 ++++----- key-string.c | 8 ----- tmux.h | 91 ++++++++++++++++++++++++++++++++++++---------------- tty-keys.c | 89 ++++++++++++++++++++++++++++++++------------------ tty-term.c | 43 +++++++++++++++++++++++++ xterm-keys.c | 8 ----- 6 files changed, 171 insertions(+), 84 deletions(-) diff --git a/input-keys.c b/input-keys.c index 24566dfd..c75e28d3 100644 --- a/input-keys.c +++ b/input-keys.c @@ -57,14 +57,14 @@ const struct input_key_ent input_keys[] = { { KEYC_F10, "\033[21~", 0 }, { KEYC_F11, "\033[23~", 0 }, { KEYC_F12, "\033[24~", 0 }, - { KEYC_F13, "\033[25~", 0 }, - { KEYC_F14, "\033[26~", 0 }, - { KEYC_F15, "\033[28~", 0 }, - { KEYC_F16, "\033[29~", 0 }, - { KEYC_F17, "\033[31~", 0 }, - { KEYC_F18, "\033[32~", 0 }, - { KEYC_F19, "\033[33~", 0 }, - { KEYC_F20, "\033[34~", 0 }, + { KEYC_F1|KEYC_SHIFT, "\033[25~", 0 }, + { KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, + { KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, + { KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, + { KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, + { KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, + { KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, + { KEYC_F8|KEYC_SHIFT, "\033[34~", 0 }, { KEYC_IC, "\033[2~", 0 }, { KEYC_DC, "\033[3~", 0 }, { KEYC_HOME, "\033[1~", 0 }, diff --git a/key-string.c b/key-string.c index 7aba190b..db968279 100644 --- a/key-string.c +++ b/key-string.c @@ -42,14 +42,6 @@ const struct { { "F10", KEYC_F10 }, { "F11", KEYC_F11 }, { "F12", KEYC_F12 }, - { "F13", KEYC_F13 }, - { "F14", KEYC_F14 }, - { "F15", KEYC_F15 }, - { "F16", KEYC_F16 }, - { "F17", KEYC_F17 }, - { "F18", KEYC_F18 }, - { "F19", KEYC_F19 }, - { "F20", KEYC_F20 }, { "IC", KEYC_IC }, { "DC", KEYC_DC }, { "Home", KEYC_HOME }, diff --git a/tmux.h b/tmux.h index 4f937f3a..bbf4e9dd 100644 --- a/tmux.h +++ b/tmux.h @@ -195,14 +195,6 @@ enum key_code { KEYC_F10, KEYC_F11, KEYC_F12, - KEYC_F13, - KEYC_F14, - KEYC_F15, - KEYC_F16, - KEYC_F17, - KEYC_F18, - KEYC_F19, - KEYC_F20, KEYC_IC, KEYC_DC, KEYC_HOME, @@ -308,26 +300,69 @@ enum tty_code_code { TTYC_KEND5, TTYC_KEND6, TTYC_KEND7, - TTYC_KF1, /* key_f1, k1 */ - TTYC_KF10, /* key_f10, k; */ - TTYC_KF11, /* key_f11, F1 */ - TTYC_KF12, /* key_f12, F2 */ - TTYC_KF13, /* key_f13, F3 */ - TTYC_KF14, /* key_f14, F4 */ - TTYC_KF15, /* key_f15, F5 */ - TTYC_KF16, /* key_f16, F6 */ - TTYC_KF17, /* key_f17, F7 */ - TTYC_KF18, /* key_f18, F8 */ - TTYC_KF19, /* key_f19, F9 */ - TTYC_KF2, /* key_f2, k2 */ - TTYC_KF20, /* key_f20, F10 */ - TTYC_KF3, /* key_f3, k3 */ - TTYC_KF4, /* key_f4, k4 */ - TTYC_KF5, /* key_f5, k5 */ - TTYC_KF6, /* key_f6, k6 */ - TTYC_KF7, /* key_f7, k7 */ - TTYC_KF8, /* key_f8, k8 */ - TTYC_KF9, /* key_f9, k9 */ + TTYC_KF1, + TTYC_KF10, + TTYC_KF11, + TTYC_KF12, + TTYC_KF13, + TTYC_KF14, + TTYC_KF15, + TTYC_KF16, + TTYC_KF17, + TTYC_KF18, + TTYC_KF19, + TTYC_KF2, + TTYC_KF20, + TTYC_KF21, + TTYC_KF22, + TTYC_KF23, + TTYC_KF24, + TTYC_KF25, + TTYC_KF26, + TTYC_KF27, + TTYC_KF28, + TTYC_KF29, + TTYC_KF3, + TTYC_KF30, + TTYC_KF31, + TTYC_KF32, + TTYC_KF33, + TTYC_KF34, + TTYC_KF35, + TTYC_KF36, + TTYC_KF37, + TTYC_KF38, + TTYC_KF39, + TTYC_KF4, + TTYC_KF40, + TTYC_KF41, + TTYC_KF42, + TTYC_KF43, + TTYC_KF44, + TTYC_KF45, + TTYC_KF46, + TTYC_KF47, + TTYC_KF48, + TTYC_KF49, + TTYC_KF5, + TTYC_KF50, + TTYC_KF51, + TTYC_KF52, + TTYC_KF53, + TTYC_KF54, + TTYC_KF55, + TTYC_KF56, + TTYC_KF57, + TTYC_KF58, + TTYC_KF59, + TTYC_KF6, + TTYC_KF60, + TTYC_KF61, + TTYC_KF62, + TTYC_KF63, + TTYC_KF7, + TTYC_KF8, + TTYC_KF9, TTYC_KHOM2, TTYC_KHOM3, TTYC_KHOM4, diff --git a/tty-keys.c b/tty-keys.c index 932367ef..967decaf 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -113,14 +113,6 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21^", KEYC_F10|KEYC_CTRL }, { "\033[23^", KEYC_F11|KEYC_CTRL }, { "\033[24^", KEYC_F12|KEYC_CTRL }, - { "\033[25^", KEYC_F13|KEYC_CTRL }, - { "\033[26^", KEYC_F14|KEYC_CTRL }, - { "\033[28^", KEYC_F15|KEYC_CTRL }, - { "\033[29^", KEYC_F16|KEYC_CTRL }, - { "\033[31^", KEYC_F17|KEYC_CTRL }, - { "\033[32^", KEYC_F18|KEYC_CTRL }, - { "\033[33^", KEYC_F19|KEYC_CTRL }, - { "\033[34^", KEYC_F20|KEYC_CTRL }, { "\033[2^", KEYC_IC|KEYC_CTRL }, { "\033[3^", KEYC_DC|KEYC_CTRL }, { "\033[7^", KEYC_HOME|KEYC_CTRL }, @@ -140,14 +132,6 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21$", KEYC_F10|KEYC_SHIFT }, { "\033[23$", KEYC_F11|KEYC_SHIFT }, { "\033[24$", KEYC_F12|KEYC_SHIFT }, - { "\033[25$", KEYC_F13|KEYC_SHIFT }, - { "\033[26$", KEYC_F14|KEYC_SHIFT }, - { "\033[28$", KEYC_F15|KEYC_SHIFT }, - { "\033[29$", KEYC_F16|KEYC_SHIFT }, - { "\033[31$", KEYC_F17|KEYC_SHIFT }, - { "\033[32$", KEYC_F18|KEYC_SHIFT }, - { "\033[33$", KEYC_F19|KEYC_SHIFT }, - { "\033[34$", KEYC_F20|KEYC_SHIFT }, { "\033[2$", KEYC_IC|KEYC_SHIFT }, { "\033[3$", KEYC_DC|KEYC_SHIFT }, { "\033[7$", KEYC_HOME|KEYC_SHIFT }, @@ -167,14 +151,6 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, - { "\033[25@", KEYC_F13|KEYC_CTRL|KEYC_SHIFT }, - { "\033[26@", KEYC_F14|KEYC_CTRL|KEYC_SHIFT }, - { "\033[28@", KEYC_F15|KEYC_CTRL|KEYC_SHIFT }, - { "\033[29@", KEYC_F16|KEYC_CTRL|KEYC_SHIFT }, - { "\033[31@", KEYC_F17|KEYC_CTRL|KEYC_SHIFT }, - { "\033[32@", KEYC_F18|KEYC_CTRL|KEYC_SHIFT }, - { "\033[33@", KEYC_F19|KEYC_CTRL|KEYC_SHIFT }, - { "\033[34@", KEYC_F20|KEYC_CTRL|KEYC_SHIFT }, { "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT }, { "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT }, { "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT }, @@ -206,14 +182,63 @@ const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KF10, KEYC_F10 }, { TTYC_KF11, KEYC_F11 }, { TTYC_KF12, KEYC_F12 }, - { TTYC_KF13, KEYC_F13 }, - { TTYC_KF14, KEYC_F14 }, - { TTYC_KF15, KEYC_F15 }, - { TTYC_KF16, KEYC_F16 }, - { TTYC_KF17, KEYC_F17 }, - { TTYC_KF18, KEYC_F18 }, - { TTYC_KF19, KEYC_F19 }, - { TTYC_KF20, KEYC_F20 }, + + { TTYC_KF13, KEYC_F1|KEYC_SHIFT }, + { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, + { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, + { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, + { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, + { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, + { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, + { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, + { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, + { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, + { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, + { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, + + { TTYC_KF25, KEYC_F1|KEYC_CTRL }, + { TTYC_KF26, KEYC_F2|KEYC_CTRL }, + { TTYC_KF27, KEYC_F3|KEYC_CTRL }, + { TTYC_KF28, KEYC_F4|KEYC_CTRL }, + { TTYC_KF29, KEYC_F5|KEYC_CTRL }, + { TTYC_KF30, KEYC_F6|KEYC_CTRL }, + { TTYC_KF31, KEYC_F7|KEYC_CTRL }, + { TTYC_KF32, KEYC_F8|KEYC_CTRL }, + { TTYC_KF33, KEYC_F9|KEYC_CTRL }, + { TTYC_KF34, KEYC_F10|KEYC_CTRL }, + { TTYC_KF35, KEYC_F11|KEYC_CTRL }, + { TTYC_KF36, KEYC_F12|KEYC_CTRL }, + + { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, + + { TTYC_KF49, KEYC_F1|KEYC_ESCAPE }, + { TTYC_KF50, KEYC_F2|KEYC_ESCAPE }, + { TTYC_KF51, KEYC_F3|KEYC_ESCAPE }, + { TTYC_KF52, KEYC_F4|KEYC_ESCAPE }, + { TTYC_KF53, KEYC_F5|KEYC_ESCAPE }, + { TTYC_KF54, KEYC_F6|KEYC_ESCAPE }, + { TTYC_KF55, KEYC_F7|KEYC_ESCAPE }, + { TTYC_KF56, KEYC_F8|KEYC_ESCAPE }, + { TTYC_KF57, KEYC_F9|KEYC_ESCAPE }, + { TTYC_KF58, KEYC_F10|KEYC_ESCAPE }, + { TTYC_KF59, KEYC_F11|KEYC_ESCAPE }, + { TTYC_KF60, KEYC_F12|KEYC_ESCAPE }, + + { TTYC_KF61, KEYC_F1|KEYC_ESCAPE|KEYC_SHIFT }, + { TTYC_KF62, KEYC_F2|KEYC_ESCAPE|KEYC_SHIFT }, + { TTYC_KF63, KEYC_F3|KEYC_ESCAPE|KEYC_SHIFT }, + { TTYC_KICH1, KEYC_IC }, { TTYC_KDCH1, KEYC_DC }, { TTYC_KHOME, KEYC_HOME }, diff --git a/tty-term.c b/tty-term.c index bedf0cfd..f866a2d8 100644 --- a/tty-term.c +++ b/tty-term.c @@ -113,10 +113,53 @@ const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { { TTYC_KF19, TTYCODE_STRING, "kf19" }, { TTYC_KF2, TTYCODE_STRING, "kf2" }, { TTYC_KF20, TTYCODE_STRING, "kf20" }, + { TTYC_KF21, TTYCODE_STRING, "kf21" }, + { TTYC_KF22, TTYCODE_STRING, "kf22" }, + { TTYC_KF23, TTYCODE_STRING, "kf23" }, + { TTYC_KF24, TTYCODE_STRING, "kf24" }, + { TTYC_KF25, TTYCODE_STRING, "kf25" }, + { TTYC_KF26, TTYCODE_STRING, "kf26" }, + { TTYC_KF27, TTYCODE_STRING, "kf27" }, + { TTYC_KF28, TTYCODE_STRING, "kf28" }, + { TTYC_KF29, TTYCODE_STRING, "kf29" }, { TTYC_KF3, TTYCODE_STRING, "kf3" }, + { TTYC_KF30, TTYCODE_STRING, "kf30" }, + { TTYC_KF31, TTYCODE_STRING, "kf31" }, + { TTYC_KF32, TTYCODE_STRING, "kf32" }, + { TTYC_KF33, TTYCODE_STRING, "kf33" }, + { TTYC_KF34, TTYCODE_STRING, "kf34" }, + { TTYC_KF35, TTYCODE_STRING, "kf35" }, + { TTYC_KF36, TTYCODE_STRING, "kf36" }, + { TTYC_KF37, TTYCODE_STRING, "kf37" }, + { TTYC_KF38, TTYCODE_STRING, "kf38" }, + { TTYC_KF39, TTYCODE_STRING, "kf39" }, { TTYC_KF4, TTYCODE_STRING, "kf4" }, + { TTYC_KF40, TTYCODE_STRING, "kf40" }, + { TTYC_KF41, TTYCODE_STRING, "kf41" }, + { TTYC_KF42, TTYCODE_STRING, "kf42" }, + { TTYC_KF43, TTYCODE_STRING, "kf43" }, + { TTYC_KF44, TTYCODE_STRING, "kf44" }, + { TTYC_KF45, TTYCODE_STRING, "kf45" }, + { TTYC_KF46, TTYCODE_STRING, "kf46" }, + { TTYC_KF47, TTYCODE_STRING, "kf47" }, + { TTYC_KF48, TTYCODE_STRING, "kf48" }, + { TTYC_KF49, TTYCODE_STRING, "kf49" }, { TTYC_KF5, TTYCODE_STRING, "kf5" }, + { TTYC_KF50, TTYCODE_STRING, "kf50" }, + { TTYC_KF51, TTYCODE_STRING, "kf51" }, + { TTYC_KF52, TTYCODE_STRING, "kf52" }, + { TTYC_KF53, TTYCODE_STRING, "kf53" }, + { TTYC_KF54, TTYCODE_STRING, "kf54" }, + { TTYC_KF55, TTYCODE_STRING, "kf55" }, + { TTYC_KF56, TTYCODE_STRING, "kf56" }, + { TTYC_KF57, TTYCODE_STRING, "kf57" }, + { TTYC_KF58, TTYCODE_STRING, "kf58" }, + { TTYC_KF59, TTYCODE_STRING, "kf59" }, { TTYC_KF6, TTYCODE_STRING, "kf6" }, + { TTYC_KF60, TTYCODE_STRING, "kf60" }, + { TTYC_KF61, TTYCODE_STRING, "kf61" }, + { TTYC_KF62, TTYCODE_STRING, "kf62" }, + { TTYC_KF63, TTYCODE_STRING, "kf63" }, { TTYC_KF7, TTYCODE_STRING, "kf7" }, { TTYC_KF8, TTYCODE_STRING, "kf8" }, { TTYC_KF9, TTYCODE_STRING, "kf9" }, diff --git a/xterm-keys.c b/xterm-keys.c index a013cbde..5ca75d89 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -69,14 +69,6 @@ const struct xterm_keys_entry xterm_keys_table[] = { { KEYC_F10, "\033[21;_~" }, { KEYC_F11, "\033[23;_~" }, { KEYC_F12, "\033[24;_~" }, - { KEYC_F13, "\033[25;_~" }, - { KEYC_F14, "\033[26;_~" }, - { KEYC_F15, "\033[28;_~" }, - { KEYC_F16, "\033[29;_~" }, - { KEYC_F17, "\033[31;_~" }, - { KEYC_F18, "\033[32;_~" }, - { KEYC_F19, "\033[33;_~" }, - { KEYC_F20, "\033[34;_~" }, { KEYC_UP, "\033[1;_A" }, { KEYC_DOWN, "\033[1;_B" }, { KEYC_RIGHT, "\033[1;_C" }, From 8e4ae12b4d0559a827f740f60b11f386f27f89dd Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 21 Jul 2014 10:52:48 +0000 Subject: [PATCH 376/949] lockf is entirely useless and it was a mistake to change to it, go back to using flock which actually works sensibly. Also always retry the lock to fix a potential race, and add some extra logging. --- client.c | 48 ++++++++++++++++++++++++++++++++++++------------ server.c | 1 + 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/client.c b/client.c index ab7847fe..f5f5556d 100644 --- a/client.c +++ b/client.c @@ -77,13 +77,18 @@ client_get_lock(char *lockfile) if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) fatal("open failed"); + log_debug("lock file is %s", lockfile); - if (lockf(lockfd, F_TLOCK, 0) == -1 && errno == EAGAIN) { - while (lockf(lockfd, F_LOCK, 0) == -1 && errno == EINTR) + if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { + log_debug("flock failed: %s", strerror(errno)); + if (errno != EAGAIN) + return (lockfd); + while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); return (-1); } + log_debug("flock succeeded"); return (lockfd); } @@ -94,8 +99,8 @@ client_connect(char *path, int start_server) { struct sockaddr_un sa; size_t size; - int fd, lockfd; - char *lockfile; + int fd, lockfd = -1, locked = 0; + char *lockfile = NULL; memset(&sa, 0, sizeof sa); sa.sun_family = AF_UNIX; @@ -104,29 +109,48 @@ client_connect(char *path, int start_server) errno = ENAMETOOLONG; return (-1); } + log_debug("socket is %s", path); retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("socket failed"); + log_debug("trying connect"); if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { + log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; if (!start_server) goto failed; close(fd); - xasprintf(&lockfile, "%s.lock", path); - if ((lockfd = client_get_lock(lockfile)) == -1) { - free(lockfile); + if (!locked) { + xasprintf(&lockfile, "%s.lock", path); + if ((lockfd = client_get_lock(lockfile)) == -1) { + log_debug("didn't get lock"); + free(lockfile); + goto retry; + } + log_debug("got lock"); + + /* + * Always retry at least once, even if we got the lock, + * because another client could have taken the lock, + * started the server and released the lock between our + * connect() and flock(). + */ + locked = 1; goto retry; } + if (unlink(path) != 0 && errno != ENOENT) { free(lockfile); close(lockfd); return (-1); } fd = server_start(lockfd, lockfile); + } + if (locked) { free(lockfile); close(lockfd); } @@ -233,7 +257,11 @@ client_main(int argc, char **argv, int flags) return (1); } - /* Initialise the client socket and start the server. */ + /* Set process title, log and signals now this is the client. */ + setproctitle("client (%s)", socket_path); + logfile("client"); + + /* Initialize the client socket and start the server. */ fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { fprintf(stderr, "failed to connect to server: %s\n", @@ -241,10 +269,6 @@ client_main(int argc, char **argv, int flags) return (1); } - /* Set process title, log and signals now this is the client. */ - setproctitle("client (%s)", socket_path); - logfile("client"); - /* Create imsg. */ imsg_init(&client_ibuf, fd); event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); diff --git a/server.c b/server.c index 7ddc6ec1..2cab88f8 100644 --- a/server.c +++ b/server.c @@ -111,6 +111,7 @@ server_start(int lockfd, char *lockfile) /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); + log_debug("starting server"); switch (fork()) { case -1: From b8b00aad5d3de0015367a6e23e800950ff7fc2df Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 21 Jul 2014 20:45:35 +0000 Subject: [PATCH 377/949] Revert the up/down wheel emulation for now, there will be a better way to do this along later for those who want it. --- input-keys.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/input-keys.c b/input-keys.c index c75e28d3..7ce71931 100644 --- a/input-keys.c +++ b/input-keys.c @@ -205,21 +205,6 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) char buf[40]; size_t len; struct paste_buffer *pb; - u_int i; - - /* - * If the alternate screen is active and hasn't enabled the mouse, send - * up and down key presses for the mouse wheel. - */ - if (wp->saved_grid != NULL && !(wp->screen->mode & ALL_MOUSE_MODES)) { - for (i = 0; i < m->scroll; i++) { - if (m->wheel == MOUSE_WHEEL_UP) - input_key(wp, KEYC_UP); - else - input_key(wp, KEYC_DOWN); - } - return; - } if (wp->screen->mode & ALL_MOUSE_MODES) { /* From fd3b7f357200b85a6f3d6e39bae676c7c70ba082 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Fri, 8 Aug 2014 01:28:22 +0100 Subject: [PATCH 378/949] FAQ: Mention Git before filing bug reports It's tedious having to tell people all the time to try the Git version to see if a given problem is reproducible; put this in the FAQ and hope people read it. --- FAQ | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FAQ b/FAQ index 97d8f6b6..a930671d 100644 --- a/FAQ +++ b/FAQ @@ -104,6 +104,10 @@ aware of are (bearing in mind I haven't used screen for a few years now): * I found a bug! What do I do? +Check the latest version of tmux from Git to see if the problem is still +reproducible. Sometimes the length of time between releases means a lot of +fixes can be sitting in Git and the problem might already be fixed. + Please send bug reports by email to nicm@users.sourceforge.net or tmux-users@lists.sourceforge.net. Please include as much of the following information as possible: From 1ac96200a7462597b137719b376f40ad0cd85216 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 9 Aug 2014 07:33:37 +0000 Subject: [PATCH 379/949] Remove support for the continuous reporting "any" mouse mode which never really worked properly and is rarely used. --- format.c | 2 -- input.c | 5 ----- server-client.c | 2 +- tmux.h | 4 ++-- tty.c | 8 ++------ 5 files changed, 5 insertions(+), 16 deletions(-) diff --git a/format.c b/format.c index e10f08c9..400e4320 100644 --- a/format.c +++ b/format.c @@ -601,8 +601,6 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", !!(wp->base.mode & MODE_MOUSE_BUTTON)); - format_add(ft, "mouse_any_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_ANY)); format_add(ft, "mouse_utf8_flag", "%d", !!(wp->base.mode & MODE_MOUSE_UTF8)); diff --git a/input.c b/input.c index 4c1e0730..ac1db677 100644 --- a/input.c +++ b/input.c @@ -1374,7 +1374,6 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 1000: case 1001: case 1002: - case 1003: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); break; case 1004: @@ -1451,10 +1450,6 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_BUTTON); break; - case 1003: - screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY); - break; case 1004: if (ictx->ctx.s->mode & MODE_FOCUSON) break; diff --git a/server-client.c b/server-client.c index 5616115c..4c58170f 100644 --- a/server-client.c +++ b/server-client.c @@ -647,7 +647,7 @@ server_client_reset_state(struct client *c) */ mode = s->mode; if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) && - !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY))) + !(mode & MODE_MOUSE_BUTTON)) mode |= MODE_MOUSE_BUTTON; /* diff --git a/tmux.h b/tmux.h index bbf4e9dd..614b7077 100644 --- a/tmux.h +++ b/tmux.h @@ -682,13 +682,13 @@ struct mode_key_table { #define MODE_WRAP 0x10 /* whether lines wrap */ #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 -#define MODE_MOUSE_ANY 0x80 +/* 0x80 unused */ #define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 -#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY) +#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON) /* A single UTF-8 character. */ struct utf8_data { diff --git a/tty.c b/tty.c index 3ae21ab5..9f57c365 100644 --- a/tty.c +++ b/tty.c @@ -513,16 +513,12 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_puts(tty, "\033[?1005l"); tty_puts(tty, "\033[?1006h"); - if (mode & MODE_MOUSE_ANY) - tty_puts(tty, "\033[?1003h"); - else if (mode & MODE_MOUSE_BUTTON) + if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); } else { - if (tty->mode & MODE_MOUSE_ANY) - tty_puts(tty, "\033[?1003l"); - else if (tty->mode & MODE_MOUSE_BUTTON) + if (tty->mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); From 92997b781a035626bef4e84fbcd7f2282f761a3c Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 24 Jul 2014 21:31:07 +0100 Subject: [PATCH 380/949] Add compat/fparseln() for non-BSD systems Linux and friends don't natively have fparseln() so add it to compat/ and ensure autotools can pick it up. --- Makefile.am | 3 +++ cfg.c | 1 - compat.h | 4 ++++ configure.ac | 7 +++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 5f622acb..9fcb45f0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -213,6 +213,9 @@ endif if NO_FGETLN nodist_tmux_SOURCES += compat/fgetln.c endif +if NO_FPARSELN +nodist_tmux_SOURCES += compat/fparseln.c +endif if NO_GETOPT nodist_tmux_SOURCES += compat/getopt.c endif diff --git a/cfg.c b/cfg.c index 5a6d67ef..0e0f55bc 100644 --- a/cfg.c +++ b/cfg.c @@ -23,7 +23,6 @@ #include #include #include -#include #include "tmux.h" diff --git a/compat.h b/compat.h index 65d6ec7a..bf6e5360 100644 --- a/compat.h +++ b/compat.h @@ -239,6 +239,10 @@ int vasprintf(char **, const char *, va_list); char *fgetln(FILE *, size_t *); #endif +#ifndef HAVE_FPARSELN +char *fparseln(FILE *, size_t *, size_t *, const char *, int); +#endif + #ifndef HAVE_SETENV /* setenv.c */ int setenv(const char *, const char *, int); diff --git a/configure.ac b/configure.ac index a81ecc42..82533a35 100644 --- a/configure.ac +++ b/configure.ac @@ -295,6 +295,13 @@ if test "x$found_fgetln" = xyes; then fi AM_CONDITIONAL(NO_FGETLN, [test "x$found_fgetln" = xno]) +# Look for fparseln, compat/fparseln.c used if missing. +AC_CHECK_FUNC(fparseln, found_fparseln=yes, found_fparseln=no) +if test "x$found_fparseln" = xyes; then + AC_DEFINE(HAVE_FPARSELN) +fi +AM_CONDITIONAL(NO_FPARSELN, [test "x$found_fparseln" = xno]) + # Look for strcasestr, compat/strcasestr.c used if missing. AC_CHECK_FUNC(strcasestr, found_strcasestr=yes, found_strcasestr=no) if test "x$found_strcasestr" = xyes; then From 0e23ab4ccef2848a90b876db3c0e6a796422544c Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 9 Aug 2014 20:17:26 +0100 Subject: [PATCH 381/949] Sync libutil from OpenBSD (imsg) Changes in the imsg API need to be reflected here as tmux wasn't creating any clients because of it. --- compat/imsg-buffer.c | 38 +++++++++-------- compat/imsg.c | 99 ++++++++++++++++++++++++++++++++++++-------- compat/imsg.h | 11 +++-- 3 files changed, 110 insertions(+), 38 deletions(-) diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 2ddf0f77..2242e01e 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -1,5 +1,5 @@ /* $Id$ */ -/* $OpenBSD: imsg-buffer.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ +/* $OpenBSD: imsg-buffer.c,v 1.4 2014/06/30 00:25:17 deraadt Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -17,17 +17,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include +#include #include #include +#include #include #include #include -#include #include #include "tmux.h" +#include "imsg.h" int ibuf_realloc(struct ibuf *, size_t); void ibuf_enqueue(struct msgbuf *, struct ibuf *); @@ -158,22 +160,23 @@ ibuf_write(struct msgbuf *msgbuf) i++; } +again: if ((n = writev(msgbuf->fd, iov, i)) == -1) { - if (errno == EAGAIN || errno == ENOBUFS || - errno == EINTR) /* try later */ - return (0); - else - return (-1); + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); } if (n == 0) { /* connection closed */ errno = 0; - return (-2); + return (0); } msgbuf_drain(msgbuf, n); - return (0); + return (1); } void @@ -257,17 +260,18 @@ msgbuf_write(struct msgbuf *msgbuf) *(int *)CMSG_DATA(cmsg) = buf->fd; } +again: if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { - if (errno == EAGAIN || errno == ENOBUFS || - errno == EINTR) /* try later */ - return (0); - else - return (-1); + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); } if (n == 0) { /* connection closed */ errno = 0; - return (-2); + return (0); } /* @@ -281,7 +285,7 @@ msgbuf_write(struct msgbuf *msgbuf) msgbuf_drain(msgbuf, n); - return (0); + return (1); } void diff --git a/compat/imsg.c b/compat/imsg.c index c4dd191d..c99cda1f 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -1,5 +1,5 @@ /* $Id$ */ -/* $OpenBSD: imsg.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ +/* $OpenBSD: imsg.c,v 1.6 2014/06/30 00:26:22 deraadt Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -17,20 +17,55 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include +#include #include #include #include #include #include -#include #include #include "tmux.h" +#include "imsg.h" + +int imsg_fd_overhead = 0; int imsg_get_fd(struct imsgbuf *); +int available_fds(unsigned int); + +/* TA: 2014-09-08: Note that the original code calls getdtablecount() which is + * OpenBSD specific. Until such time that it's ported elsewhere from + * , I've mimicked what OpenSMTPD are doing, by using available_fds() + * instead. + */ + +int +available_fds(unsigned int n) +{ + unsigned int i; + int ret, fds[256]; + + if (n > (sizeof(fds)/sizeof(fds[0]))) + return (1); + + ret = 0; + for (i = 0; i < n; i++) { + fds[i] = -1; + if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + ret = 1; + break; + } + } + + for (i = 0; i < n && fds[i] >= 0; i++) + close(fds[i]); + + return (ret); +} + void imsg_init(struct imsgbuf *ibuf, int fd) { @@ -49,10 +84,10 @@ imsg_read(struct imsgbuf *ibuf) struct cmsghdr *cmsg; union { struct cmsghdr hdr; - char buf[CMSG_SPACE(sizeof(int) * 16)]; + char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; - ssize_t n; + ssize_t n = -1; int fd; struct imsg_fd *ifd; @@ -65,11 +100,23 @@ imsg_read(struct imsgbuf *ibuf) msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); + if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) + return (-1); + +again: + if (available_fds(imsg_fd_overhead + + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { + errno = EAGAIN; + free(ifd); + return (-1); + } + if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { - if (errno != EINTR && errno != EAGAIN) { - return (-1); - } - return (-2); + if (errno == EMSGSIZE) + goto fail; + if (errno != EINTR && errno != EAGAIN) + goto fail; + goto again; } ibuf->r.wpos += n; @@ -78,17 +125,33 @@ imsg_read(struct imsgbuf *ibuf) cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - fd = (*(int *)CMSG_DATA(cmsg)); - if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) { - close(fd); - return (-1); + int i; + int j; + + /* + * We only accept one file descriptor. Due to C + * padding rules, our control buffer might contain + * more than one fd, and we must close them. + */ + j = ((char *)cmsg + cmsg->cmsg_len - + (char *)CMSG_DATA(cmsg)) / sizeof(int); + for (i = 0; i < j; i++) { + fd = ((int *)CMSG_DATA(cmsg))[i]; + if (ifd != NULL) { + ifd->fd = fd; + TAILQ_INSERT_TAIL(&ibuf->fds, ifd, + entry); + ifd = NULL; + } else + close(fd); } - ifd->fd = fd; - TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); } /* we do not handle other ctl data level */ } +fail: + if (ifd) + free(ifd); return (n); } @@ -112,7 +175,7 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) return (0); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; - if ((imsg->data = malloc(datalen)) == NULL && datalen != 0) + if ((imsg->data = malloc(datalen)) == NULL) return (-1); if (imsg->hdr.flags & IMSGF_HASFD) @@ -134,7 +197,7 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) int imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, - pid_t pid, int fd, void *data, u_int16_t datalen) + pid_t pid, int fd, const void *data, u_int16_t datalen) { struct ibuf *wbuf; @@ -204,7 +267,7 @@ imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, } int -imsg_add(struct ibuf *msg, void *data, u_int16_t datalen) +imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen) { if (datalen) if (ibuf_add(msg, data, datalen) == -1) { diff --git a/compat/imsg.h b/compat/imsg.h index f8a78e22..49a2aeb3 100644 --- a/compat/imsg.h +++ b/compat/imsg.h @@ -1,5 +1,5 @@ /* $Id$ */ -/* $OpenBSD: imsg.h,v 1.4 2010/05/26 13:56:07 nicm Exp $ */ +/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -21,6 +21,9 @@ #include "tmux.h" +#ifndef _IMSG_H_ +#define _IMSG_H_ + #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 @@ -98,13 +101,15 @@ void imsg_init(struct imsgbuf *, int); ssize_t imsg_read(struct imsgbuf *); ssize_t imsg_get(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, - int, void *, u_int16_t); + int, const void *, u_int16_t); int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, int, const struct iovec *, int); struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, u_int16_t); -int imsg_add(struct ibuf *, void *, u_int16_t); +int imsg_add(struct ibuf *, const void *, u_int16_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_flush(struct imsgbuf *); void imsg_clear(struct imsgbuf *); + +#endif From fab8ca07373ad4a50b1af8dabf3a2a55e1d9d190 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 9 Aug 2014 20:22:54 +0100 Subject: [PATCH 382/949] SYNCING: Make a note about libutils Don't forget that compat/* needs updating periodically as well. --- SYNCING | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SYNCING b/SYNCING index eba24177..218685c3 100644 --- a/SYNCING +++ b/SYNCING @@ -131,6 +131,15 @@ And if happy: % git push origin master +Keeping an eye on libutil in OpenBSD +==================================== + +A lot of the compat/ code in tmux comes from libutil, especially imsg. +Sometimes the API can change, etc., which might cause interesting problems +trying to run the portable version of tmux. It's worth checking +periodically for any changes to libutil in OpenBSD and syncing those files +to compat/ as and when appropriate. + Release tmux for next version ============================= From 72797074f3bfe84914b25d6e1a5c9fc99e90e721 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 9 Aug 2014 21:16:21 +0100 Subject: [PATCH 383/949] Add compat/fparseln.c --- compat/fparseln.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 compat/fparseln.c diff --git a/compat/fparseln.c b/compat/fparseln.c new file mode 100644 index 00000000..f10fbd51 --- /dev/null +++ b/compat/fparseln.c @@ -0,0 +1,221 @@ +/* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */ +/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christos Zoulas. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libutil/fparseln.c */ + +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * fparseln() specific operation flags. + */ +#define FPARSELN_UNESCESC 0x01 +#define FPARSELN_UNESCCONT 0x02 +#define FPARSELN_UNESCCOMM 0x04 +#define FPARSELN_UNESCREST 0x08 +#define FPARSELN_UNESCALL 0x0f + +static int isescaped(const char *, const char *, int); + +/* isescaped(): + * Return true if the character in *p that belongs to a string + * that starts in *sp, is escaped by the escape character esc. + */ +static int +isescaped(const char *sp, const char *p, int esc) +{ + const char *cp; + size_t ne; + + /* No escape character */ + if (esc == '\0') + return 1; + + /* Count the number of escape characters that precede ours */ + for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) + continue; + + /* Return true if odd number of escape characters */ + return (ne & 1) != 0; +} + + +/* fparseln(): + * Read a line from a file parsing continuations ending in \ + * and eliminating trailing newlines, or comments starting with + * the comment char. + */ +char * +fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], + int flags) +{ + static const char dstr[3] = { '\\', '\\', '#' }; + char *buf = NULL, *ptr, *cp, esc, con, nl, com; + size_t s, len = 0; + int cnt = 1; + + if (str == NULL) + str = dstr; + + esc = str[0]; + con = str[1]; + com = str[2]; + + /* + * XXX: it would be cool to be able to specify the newline character, + * but unfortunately, fgetln does not let us + */ + nl = '\n'; + + while (cnt) { + cnt = 0; + + if (lineno) + (*lineno)++; + + if ((ptr = fgetln(fp, &s)) == NULL) + break; + + if (s && com) { /* Check and eliminate comments */ + for (cp = ptr; cp < ptr + s; cp++) + if (*cp == com && !isescaped(ptr, cp, esc)) { + s = cp - ptr; + cnt = s == 0 && buf == NULL; + break; + } + } + + if (s && nl) { /* Check and eliminate newlines */ + cp = &ptr[s - 1]; + + if (*cp == nl) + s--; /* forget newline */ + } + + if (s && con) { /* Check and eliminate continuations */ + cp = &ptr[s - 1]; + + if (*cp == con && !isescaped(ptr, cp, esc)) { + s--; /* forget escape */ + cnt = 1; + } + } + + if (s == 0 && buf != NULL) + continue; + + if ((cp = realloc(buf, len + s + 1)) == NULL) { + free(buf); + return NULL; + } + buf = cp; + + (void) memcpy(buf + len, ptr, s); + len += s; + buf[len] = '\0'; + } + + if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && + strchr(buf, esc) != NULL) { + ptr = cp = buf; + while (cp[0] != '\0') { + int skipesc; + + while (cp[0] != '\0' && cp[0] != esc) + *ptr++ = *cp++; + if (cp[0] == '\0' || cp[1] == '\0') + break; + + skipesc = 0; + if (cp[1] == com) + skipesc += (flags & FPARSELN_UNESCCOMM); + if (cp[1] == con) + skipesc += (flags & FPARSELN_UNESCCONT); + if (cp[1] == esc) + skipesc += (flags & FPARSELN_UNESCESC); + if (cp[1] != com && cp[1] != con && cp[1] != esc) + skipesc = (flags & FPARSELN_UNESCREST); + + if (skipesc) + cp++; + else + *ptr++ = *cp++; + *ptr++ = *cp++; + } + *ptr = '\0'; + len = strlen(buf); + } + + if (size) + *size = len; + return buf; +} + +#ifdef TEST + +int main(int, char **); + +int +main(argc, argv) + int argc; + char **argv; +{ + char *ptr; + size_t size, line; + + line = 0; + while ((ptr = fparseln(stdin, &size, &line, NULL, + FPARSELN_UNESCALL)) != NULL) + printf("line %d (%d) |%s|\n", line, size, ptr); + return 0; +} + +/* + +# This is a test +line 1 +line 2 \ +line 3 # Comment +line 4 \# Not comment \\\\ + +# And a comment \ +line 5 \\\ +line 6 + +*/ + +#endif /* TEST */ From f518a077b176e65a30a187af21b00b0b9031dad7 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 Aug 2014 22:14:30 +0000 Subject: [PATCH 384/949] Add flags to selectp to enable and disable input to a pane, from Anish Athalye. --- cmd-select-pane.c | 34 +++++++++++++++++++++++----------- tmux.1 | 17 ++++++++++++++--- tmux.h | 1 + window.c | 7 ++++--- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 1a1072d8..a491c433 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -29,8 +29,8 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", - "lDLRt:U", 0, 0, - "[-lDLRU] " CMD_TARGET_PANE_USAGE, + "DdeLlRt:U", 0, 0, + "[-DdeLlRU] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_key_binding, cmd_select_pane_exec @@ -38,8 +38,8 @@ const struct cmd_entry cmd_select_pane_entry = { const struct cmd_entry cmd_last_pane_entry = { "last-pane", "lastp", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, + "det:", 0, 0, + "[-de] " CMD_TARGET_WINDOW_USAGE, 0, NULL, cmd_select_pane_exec @@ -78,10 +78,16 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - server_unzoom_window(wl->window); - window_set_active_pane(wl->window, wl->window->last); - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + if (args_has(self->args, 'e')) + wl->window->last->flags &= ~PANE_INPUTOFF; + else if (args_has(self->args, 'd')) + wl->window->last->flags |= PANE_INPUTOFF; + else { + server_unzoom_window(wl->window); + window_set_active_pane(wl->window, wl->window->last); + server_status_window(wl->window); + server_redraw_window_borders(wl->window); + } return (CMD_RETURN_NORMAL); } @@ -108,9 +114,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - window_set_active_pane(wl->window, wp); - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + if (args_has(self->args, 'e')) + wp->flags &= ~PANE_INPUTOFF; + else if (args_has(self->args, 'd')) + wp->flags |= PANE_INPUTOFF; + else { + window_set_active_pane(wl->window, wp); + server_status_window(wl->window); + server_redraw_window_borders(wl->window); + } return (CMD_RETURN_NORMAL); } diff --git a/tmux.1 b/tmux.1 index f70ac391..ab51689c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1292,7 +1292,7 @@ flag, see the section. This command works only if at least one client is attached. .It Ic display-panes Op Fl t Ar target-client -.D1 (alias: Ic displayp) +.D1 (alias: Ic displayp ) Display a visible indicator of each pane shown by .Ar target-client . See the @@ -1382,9 +1382,16 @@ The .Fl a option kills all but the window given with .Fl t . -.It Ic last-pane Op Fl t Ar target-window +.It Xo Ic last-pane +.Op Fl de +.Op Fl t Ar target-window +.Xc .D1 (alias: Ic lastp ) Select the last (previously selected) pane. +.Fl e +enables or +.Fl d +disables input to the pane. .It Ic last-window Op Fl t Ar target-session .D1 (alias: Ic last ) Select the last (previously selected) window. @@ -1701,7 +1708,7 @@ and .Ic previous-layout commands. .It Xo Ic select-pane -.Op Fl lDLRU +.Op Fl DdeLlRU .Op Fl t Ar target-pane .Xc .D1 (alias: Ic selectp ) @@ -1721,6 +1728,10 @@ target pane is used. is the same as using the .Ic last-pane command. +.Fl e +enables or +.Fl d +disables input to the pane. .It Xo Ic select-window .Op Fl lnpT .Op Fl t Ar target-window diff --git a/tmux.h b/tmux.h index 614b7077..a0cbbd41 100644 --- a/tmux.h +++ b/tmux.h @@ -956,6 +956,7 @@ struct window_pane { #define PANE_FOCUSED 0x4 #define PANE_RESIZE 0x8 #define PANE_FOCUSPUSH 0x10 +#define PANE_INPUTOFF 0x20 int argc; char **argv; diff --git a/window.c b/window.c index a0c53ca8..21dd8d55 100644 --- a/window.c +++ b/window.c @@ -1057,8 +1057,9 @@ window_pane_key(struct window_pane *wp, struct session *sess, int key) return; } - if (wp->fd == -1) + if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) return; + input_key(wp, key); if (options_get_number(&wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { @@ -1071,8 +1072,8 @@ window_pane_key(struct window_pane *wp, struct session *sess, int key) } void -window_pane_mouse( - struct window_pane *wp, struct session *sess, struct mouse_event *m) +window_pane_mouse(struct window_pane *wp, struct session *sess, + struct mouse_event *m) { if (!window_pane_visible(wp)) return; From 29d20a55b645600feca1b54a13333e598336dad2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 Aug 2014 22:18:16 +0000 Subject: [PATCH 385/949] Fix two copy mode problems: 1. In vi mode the selection doesn't include the last character if you moved the cursor up or left. 2. In emacs mode the selection includes the last character if you moved the cursor to the left. From Balazs Kezes. --- screen.c | 23 ++++++++++++++++++----- tmux.h | 1 + window-copy.c | 1 + 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/screen.c b/screen.c index 2b0e9fba..3e7d9924 100644 --- a/screen.c +++ b/screen.c @@ -276,6 +276,7 @@ int screen_check_selection(struct screen *s, u_int px, u_int py) { struct screen_sel *sel = &s->sel; + u_int xx; if (!sel->flag) return (0); @@ -325,16 +326,24 @@ screen_check_selection(struct screen *s, u_int px, u_int py) if (py < sel->sy || py > sel->ey) return (0); - if ((py == sel->sy && px < sel->sx) - || (py == sel->ey && px > sel->ex)) + if (py == sel->sy && px < sel->sx) + return 0; + + if (py == sel->ey && px > sel->ex) return (0); } else if (sel->sy > sel->ey) { /* starting line > ending line -- upward selection. */ if (py > sel->sy || py < sel->ey) return (0); - if ((py == sel->sy && px >= sel->sx) - || (py == sel->ey && px < sel->ex)) + if (py == sel->ey && px < sel->ex) + return (0); + + if (sel->modekeys == MODEKEY_EMACS) + xx = sel->sx - 1; + else + xx = sel->sx; + if (py == sel->sy && px > xx) return (0); } else { /* starting line == ending line. */ @@ -343,7 +352,11 @@ screen_check_selection(struct screen *s, u_int px, u_int py) if (sel->ex < sel->sx) { /* cursor (ex) is on the left */ - if (px > sel->sx || px < sel->ex) + if (sel->modekeys == MODEKEY_EMACS) + xx = sel->sx - 1; + else + xx = sel->sx; + if (px > xx || px < sel->ex) return (0); } else { /* selection start (sx) is on the left */ diff --git a/tmux.h b/tmux.h index a0cbbd41..bfc70544 100644 --- a/tmux.h +++ b/tmux.h @@ -794,6 +794,7 @@ LIST_HEAD(joblist, job); struct screen_sel { int flag; int rectflag; + int modekeys; u_int sx; u_int sy; diff --git a/window-copy.c b/window-copy.c index 17f9751b..24f94121 100644 --- a/window-copy.c +++ b/window-copy.c @@ -199,6 +199,7 @@ window_copy_init(struct window_pane *wp) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else mode_key_init(&data->mdata, &mode_key_tree_vi_copy); + s->sel.modekeys = keys; data->backing = NULL; From 2b79d366528294b80960f3a7ed0923bfa0bdc89c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 Aug 2014 22:39:57 +0000 Subject: [PATCH 386/949] No need to repeat other-end more than once, from Juho Pohjala. Also add it to the commands list while here. --- mode-key.c | 1 + window-copy.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mode-key.c b/mode-key.c index c2bc0439..77e7b695 100644 --- a/mode-key.c +++ b/mode-key.c @@ -130,6 +130,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_NEXTSPACEEND, "next-space-end" }, { MODEKEYCOPY_NEXTWORD, "next-word" }, { MODEKEYCOPY_NEXTWORDEND, "next-word-end" }, + { MODEKEYCOPY_OTHEREND, "other-end" }, { MODEKEYCOPY_PREVIOUSPAGE, "page-up" }, { MODEKEYCOPY_PREVIOUSSPACE, "previous-space" }, { MODEKEYCOPY_PREVIOUSWORD, "previous-word" }, diff --git a/window-copy.c b/window-copy.c index 24f94121..cb8ef57f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -429,7 +429,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) window_pane_reset_mode(wp); return; case MODEKEYCOPY_OTHEREND: - for (; np != 0; np--) + if (np % 2) window_copy_other_end(wp); break; case MODEKEYCOPY_LEFT: From e07519804957d5c41aa70e7f1132e29fe5f84862 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 Aug 2014 13:13:19 +0000 Subject: [PATCH 387/949] Don't allow pasting into input-disabled panes, from Anish R Athalye. --- paste.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/paste.c b/paste.c index 08f4bab5..586fbec8 100644 --- a/paste.c +++ b/paste.c @@ -298,6 +298,9 @@ paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, const char *data = pb->data, *end = data + pb->size, *lf; size_t seplen; + if (wp->flags & PANE_INPUTOFF) + return; + if (bracket) bufferevent_write(wp->event, "\033[200~", 6); From 4e956d545a52fe6b8310cfb30a26a89976d55cd9 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Sep 2014 21:50:18 +0000 Subject: [PATCH 388/949] Various minor style and spacing nits. --- client.c | 4 ++-- cmd-list.c | 2 +- cmd-load-buffer.c | 2 +- cmd-new-window.c | 2 +- cmd-queue.c | 6 +++--- cmd-set-option.c | 4 ++-- cmd-split-window.c | 2 +- cmd-wait-for.c | 4 ++-- grid.c | 2 +- paste.c | 4 ++-- screen.c | 2 +- server-client.c | 2 +- tmux.h | 2 +- window-copy.c | 4 ++-- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/client.c b/client.c index f5f5556d..e92724a2 100644 --- a/client.c +++ b/client.c @@ -311,7 +311,7 @@ client_main(int argc, char **argv, int flags) /* Prepare command for server. */ data->argc = argc; - if (cmd_pack_argv(argc, argv, (char*)(data + 1), size) != 0) { + if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) { fprintf(stderr, "command too long\n"); free(data); return (1); @@ -393,7 +393,7 @@ client_write_one(enum msgtype type, int fd, const void *buf, size_t len) int retval; retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd, - (void*)buf, len); + (void *)buf, len); if (retval != 1) return (-1); return (0); diff --git a/cmd-list.c b/cmd-list.c index 6dc4493a..0c75ed49 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -24,7 +24,7 @@ #include "tmux.h" struct cmd_list * -cmd_list_parse(int argc, char **argv, const char* file, u_int line, +cmd_list_parse(int argc, char **argv, const char *file, u_int line, char **cause) { struct cmd_list *cmdlist; diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 6c69391c..26d6297a 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -62,7 +62,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) path = args->argv[0]; if (strcmp(path, "-") == 0) { error = server_set_stdin_callback(c, cmd_load_buffer_callback, - (void*)bufname, &cause); + (void *)bufname, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); diff --git a/cmd-new-window.c b/cmd-new-window.c index fc2eb120..00fa53f8 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -88,7 +88,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; - argv = (char**)&cmd; + argv = (char **)&cmd; } else { argc = 0; argv = NULL; diff --git a/cmd-queue.c b/cmd-queue.c index 7bc154a0..77460b2f 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -124,14 +124,14 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) struct client *c = cmdq->client; if (c == NULL) - return 0; + return (0); if (!(c->flags & CLIENT_CONTROL)) - return 0; + return (0); evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long) cmdq->time, cmdq->number, flags); server_push_stdout(c); - return 1; + return (1); } /* Add command list to queue and begin processing if needed. */ diff --git a/cmd-set-option.c b/cmd-set-option.c index d4743d55..5cea4932 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -201,7 +201,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Set user option. */ enum cmd_retval -cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, +cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, const char *valstr) { struct args *args = self->args; @@ -254,7 +254,7 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "already set: %s", optstr); - return CMD_RETURN_ERROR; + return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 55231134..8a608bdb 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -85,7 +85,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) cmd = options_get_string(&s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; - argv = (char**)&cmd; + argv = (char **)&cmd; } else { argc = 0; argv = NULL; diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 42c7f42d..3654d4e5 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -108,7 +108,7 @@ cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, if (!wc->locked) { RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); + free((void *)wc->name); free(wc); } @@ -186,7 +186,7 @@ cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, wc->locked = 0; if (TAILQ_EMPTY(&wc->waiters)) { RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); + free((void *)wc->name); free(wc); } } diff --git a/grid.c b/grid.c index 28210185..784ff72f 100644 --- a/grid.c +++ b/grid.c @@ -572,7 +572,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, const struct grid_cell *gc; static struct grid_cell lastgc1; struct utf8_data ud; - const char* data; + const char *data; char *buf, code[128]; size_t len, off, size, codelen; u_int xx; diff --git a/paste.c b/paste.c index 586fbec8..e8ea0c34 100644 --- a/paste.c +++ b/paste.c @@ -102,7 +102,7 @@ paste_get_name(const char *name) if (name == NULL || *name == '\0') return (NULL); - pbfind.name = (char*)name; + pbfind.name = (char *)name; return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); } @@ -115,7 +115,7 @@ paste_free_name(const char *name) if (name == NULL || *name == '\0') return (-1); - pbfind.name = (char*)name; + pbfind.name = (char *)name; pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); if (pb == NULL) return (-1); diff --git a/screen.c b/screen.c index 3e7d9924..703f3e10 100644 --- a/screen.c +++ b/screen.c @@ -327,7 +327,7 @@ screen_check_selection(struct screen *s, u_int px, u_int py) return (0); if (py == sel->sy && px < sel->sx) - return 0; + return (0); if (py == sel->ey && px > sel->ex) return (0); diff --git a/server-client.c b/server-client.c index 4c58170f..093f0a15 100644 --- a/server-client.c +++ b/server-client.c @@ -914,7 +914,7 @@ server_client_msg_command(struct client *c, struct imsg *imsg) fatalx("bad MSG_COMMAND size"); memcpy(&data, imsg->data, sizeof data); - buf = (char*)imsg->data + sizeof data; + buf = (char *)imsg->data + sizeof data; len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data; if (len > 0 && buf[len - 1] != '\0') fatalx("bad MSG_COMMAND string"); diff --git a/tmux.h b/tmux.h index bfc70544..c64459ba 100644 --- a/tmux.h +++ b/tmux.h @@ -2325,7 +2325,7 @@ void set_signals(void(*)(int, short, void *)); void clear_signals(int); /* control.c */ -void control_callback(struct client *, int, void*); +void control_callback(struct client *, int, void *); void printflike2 control_write(struct client *, const char *, ...); void control_write_buffer(struct client *, struct evbuffer *); diff --git a/window-copy.c b/window-copy.c index cb8ef57f..c9ddf84f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1498,8 +1498,8 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, void window_copy_copy_selection(struct window_pane *wp, const char *bufname) { - void* buf; - size_t len; + void *buf; + size_t len; buf = window_copy_get_selection(wp, &len); if (buf == NULL) From 733cea8847fadf922e239f05b4cdd8afc2f6c943 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Sep 2014 21:58:41 +0000 Subject: [PATCH 389/949] Wake up any clients waiting with the wait-for command when the server exits. --- cmd-wait-for.c | 22 ++++++++++++++++++++++ server.c | 18 +++++++++++++++++- tmux.h | 3 +++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 3654d4e5..48a6fe3c 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -194,3 +194,25 @@ cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, return (CMD_RETURN_NORMAL); } +void +cmd_wait_for_flush(void) +{ + struct wait_channel *wc, *wc1; + struct cmd_q *wq, *wq1; + + RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + while ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { + TAILQ_REMOVE(&wc->lockers, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void *)wc->name); + free(wc); + } +} diff --git a/server.c b/server.c index 2cab88f8..acf5aaf6 100644 --- a/server.c +++ b/server.c @@ -217,16 +217,30 @@ server_loop(void) int server_should_shutdown(void) { - u_int i; + struct client *c; + u_int i; if (!options_get_number(&global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session != NULL) + return (0); + } + + /* + * No attached clients therefore want to exit - flush any waiting + * clients but don't actually exit until they've gone. + */ + cmd_wait_for_flush(); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if (ARRAY_ITEM(&clients, i) != NULL) return (0); } + return (1); } @@ -238,6 +252,8 @@ server_send_shutdown(void) struct session *s, *next_s; u_int i; + cmd_wait_for_flush(); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL) { diff --git a/tmux.h b/tmux.h index c64459ba..9be247f8 100644 --- a/tmux.h +++ b/tmux.h @@ -1924,6 +1924,9 @@ void cmdq_flush(struct cmd_q *); int cmd_string_parse(const char *, struct cmd_list **, const char *, u_int, char **); +/* cmd-wait-for.c */ +void cmd_wait_for_flush(void); + /* client.c */ int client_main(int, char **, int); From 8a473b57576a7deab9f498db1399b0d798fb45b2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Sep 2014 22:00:42 +0000 Subject: [PATCH 390/949] Fix typo (paneas -> panes). --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index ab51689c..e2ff7fc0 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2436,7 +2436,7 @@ see the option. Attributes are ignored. .It Ic pane-border-style Ar style -Set the pane border style for paneas aside from the active pane. +Set the pane border style for panes aside from the active pane. For how to specify .Ar style , see the From 9bda7e881a01af4deabeccc7a292d873c9c7f4e6 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Sep 2014 14:29:05 +0000 Subject: [PATCH 391/949] Add window_last_flag and window_zoomed_flag. From John Morrissey. --- format.c | 5 ++++- tmux.1 | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 400e4320..ed3c2037 100644 --- a/format.c +++ b/format.c @@ -494,7 +494,10 @@ format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) !!(wl->flags & WINLINK_ACTIVITY)); format_add(ft, "window_silence_flag", "%u", !!(wl->flags & WINLINK_SILENCE)); - + format_add(ft, "window_last_flag", "%u", + !!(wl == TAILQ_FIRST(&s->lastw))); + format_add(ft, "window_zoomed_flag", "%u", + !!(wl->flags & WINDOW_ZOOMED)); free(flags); } diff --git a/tmux.1 b/tmux.1 index e2ff7fc0..4e86e9c1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3132,11 +3132,13 @@ The following variables are available, where appropriate: .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" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_width" Ta "" Ta "Width of window" +.It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" .It Li "wrap_flag" Ta "" Ta "Pane wrap flag" .El .Sh NAMES AND TITLES From 93fe1b8659bb74a2931b857d8ba1231e151b770c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 8 Sep 2014 23:33:41 +0100 Subject: [PATCH 392/949] Stupid comment. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 82533a35..17cd0da6 100644 --- a/configure.ac +++ b/configure.ac @@ -528,5 +528,5 @@ AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos) AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux) AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) -# autoconf should create a Makefile. A shock! +# autoconf should create a Makefile. AC_OUTPUT(Makefile) From 938d91d2c37f8837e5e808c9315f551ac7bc4636 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 16 Sep 2014 04:33:34 +0100 Subject: [PATCH 393/949] Tweak www and add RELEASE variable for current release version so we don't need to turn it back and forth when updating. --- Makefile.am | 2 +- configure.ac | 2 ++ www/index.html.in | 11 +++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Makefile.am b/Makefile.am index 9fcb45f0..04107abd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -267,4 +267,4 @@ update-index.html: convert "$$i" -resize 200x150 "small-$$i"; \ done \ ) - sed "s/%%VERSION%%/${VERSION}/g" www/index.html.in >www/index.html + sed "s/%%RELEASE%%/${RELEASE}/g" www/index.html.in >www/index.html diff --git a/configure.ac b/configure.ac index 17cd0da6..b524ff8e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,6 +2,8 @@ # Miscellaneous autofoo bullshit. AC_INIT(tmux, 2.0) +RELEASE=1.9a +AC_SUBST(RELEASE) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) diff --git a/www/index.html.in b/www/index.html.in index 002a473f..e9ec46bd 100644 --- a/www/index.html.in +++ b/www/index.html.in @@ -11,7 +11,7 @@

tmux

From 7382ba82c5b366be84ca55c7842426bcf3d1f521 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Apr 2015 15:59:08 +0000 Subject: [PATCH 563/949] If default-terminal is set to "screen" or "screen-*", emulate screen's historical (incorrect) behaviour for SGR 3 and send smso (standout). Previously, we would send sitm (italics) if the terminal outside had it and smso otherwise. This was acceptably until recently because xterm's terminfo entry lacked sitm, so most users got smso. People who want italics should set default-terminal to the forthcoming "tmux" entry (and be prepared to deal with it being missing on older hosts). As a side-effect this changes default-terminal to be a server rather than a session option. suggested by and ok naddy --- options-table.c | 10 +++++----- server-fn.c | 2 +- tmux.1 | 25 +++++++++++++------------ tty.c | 24 ++++++++++++++++++------ 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/options-table.c b/options-table.c index 5e21c692..089a9b86 100644 --- a/options-table.c +++ b/options-table.c @@ -61,6 +61,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 20 }, + { .name = "default-terminal", + .type = OPTIONS_TABLE_STRING, + .default_str = "screen" + }, + { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, @@ -143,11 +148,6 @@ const struct options_table_entry session_options_table[] = { .default_str = _PATH_BSHELL }, - { .name = "default-terminal", - .type = OPTIONS_TABLE_STRING, - .default_str = "screen" - }, - { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .default_num = 0 diff --git a/server-fn.c b/server-fn.c index 85067a87..0e6e4d46 100644 --- a/server-fn.c +++ b/server-fn.c @@ -36,7 +36,7 @@ server_fill_environ(struct session *s, struct environ *env) long pid; if (s != NULL) { - term = options_get_string(&s->options, "default-terminal"); + term = options_get_string(&global_options, "default-terminal"); environ_set(env, "TERM", term); idx = s->id; diff --git a/tmux.1 b/tmux.1 index 5bf77829..a7f5f3ef 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2266,6 +2266,19 @@ Available server options are: 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 length. +.It Ic default-terminal Ar terminal +Set the default terminal for new windows created in this session - the +default value of the +.Ev TERM +environment variable. +For +.Nm +to work correctly, this +.Em must +be set to +.Ql screen , +.Ql tmux +or a derivative of them. .It Ic escape-time Ar time Set the time in milliseconds for which .Nm @@ -2405,18 +2418,6 @@ or This option should be configured when .Nm is used as a login shell. -.It Ic default-terminal Ar terminal -Set the default terminal for new windows created in this session - the -default value of the -.Ev TERM -environment variable. -For -.Nm -to work correctly, this -.Em must -be set to -.Ql screen -or a derivative of it. .It Xo Ic destroy-unattached .Op Ic on | off .Xc diff --git a/tty.c b/tty.c index ee52d038..f6db8e20 100644 --- a/tty.c +++ b/tty.c @@ -34,6 +34,7 @@ void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); +void tty_set_italics(struct tty *); int tty_try_256(struct tty *, u_char, const char *); void tty_colours(struct tty *, const struct grid_cell *); @@ -456,6 +457,21 @@ tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) tty->cx += width; } +void +tty_set_italics(struct tty *tty) +{ + const char *s; + + if (tty_term_has(tty->term, TTYC_SITM)) { + s = options_get_string(&global_options, "default-terminal"); + if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { + tty_putcode(tty, TTYC_SITM); + return; + } + } + tty_putcode(tty, TTYC_SMSO); +} + void tty_set_title(struct tty *tty, const char *title) { @@ -1396,12 +1412,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, tty_putcode(tty, TTYC_BOLD); if (changed & GRID_ATTR_DIM) tty_putcode(tty, TTYC_DIM); - if (changed & GRID_ATTR_ITALICS) { - if (tty_term_has(tty->term, TTYC_SITM)) - tty_putcode(tty, TTYC_SITM); - else - tty_putcode(tty, TTYC_SMSO); - } + if (changed & GRID_ATTR_ITALICS) + tty_set_italics(tty); if (changed & GRID_ATTR_UNDERSCORE) tty_putcode(tty, TTYC_SMUL); if (changed & GRID_ATTR_BLINK) From 69b8f100b70061ee2520fb30368a955cf39e47db Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Apr 2015 16:26:17 +0000 Subject: [PATCH 564/949] Do not complain when directions fail. --- cmd-select-pane.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e7f2249e..f237e8fd 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -100,10 +100,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) wp = window_pane_find_up(wp); else if (args_has(self->args, 'D')) wp = window_pane_find_down(wp); - if (wp == NULL) { - cmdq_error(cmdq, "pane not found"); - return (CMD_RETURN_ERROR); - } + if (wp == NULL) + return (CMD_RETURN_NORMAL); if (args_has(self->args, 'e')) { wp->flags &= ~PANE_INPUTOFF; From 672df72b715117e9994e72f0248c530eda8fbb8d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 4 May 2015 13:04:10 +0000 Subject: [PATCH 565/949] Use the right index when expanding/collapsing tree, from Thomas Adam. --- window-choose.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window-choose.c b/window-choose.c index 5de87572..8672212a 100644 --- a/window-choose.c +++ b/window-choose.c @@ -559,10 +559,10 @@ window_choose_key(struct window_pane *wp, unused struct client *c, break; if (item->state & TREE_EXPANDED) { window_choose_collapse(wp, item->wcd->tree_session, - item->wcd->idx); + data->selected); } else { window_choose_expand(wp, item->wcd->tree_session, - item->wcd->idx); + data->selected); } window_choose_redraw_screen(wp); break; From 33a585c47fcceb55e79a67a5fc47902d6992e68d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 May 2015 07:52:06 +0000 Subject: [PATCH 566/949] Turn cursor off during redraw, pointed out by George Nachman. --- server-client.c | 24 +++++++++++++++++------- tty.c | 33 +++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/server-client.c b/server-client.c index b24e1afd..ce3f3d5e 100644 --- a/server-client.c +++ b/server-client.c @@ -875,15 +875,13 @@ void server_client_check_redraw(struct client *c) { struct session *s = c->session; + struct tty *tty = &c->tty; struct window_pane *wp; int flags, redraw; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; - flags = c->tty.flags & TTY_FREEZE; - c->tty.flags &= ~TTY_FREEZE; - if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { if (options_get_number(&s->options, "set-titles")) server_client_set_title(c); @@ -898,27 +896,39 @@ server_client_check_redraw(struct client *c) c->flags &= ~CLIENT_STATUS; } + flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR); + tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR; + if (c->flags & CLIENT_REDRAW) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 1, 1, 1); c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); } else if (c->flags & CLIENT_REDRAWWINDOW) { + tty_update_mode(tty, tty->mode, NULL); TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) screen_redraw_pane(c, wp); c->flags &= ~CLIENT_REDRAWWINDOW; } else { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { - if (wp->flags & PANE_REDRAW) + if (wp->flags & PANE_REDRAW) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); + } } } - if (c->flags & CLIENT_BORDERS) + if (c->flags & CLIENT_BORDERS) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 0, 0, 1); + } - if (c->flags & CLIENT_STATUS) + if (c->flags & CLIENT_STATUS) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 0, 1, 0); + } - c->tty.flags |= flags; + tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; + tty_update_mode(tty, tty->mode, NULL); c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS); } diff --git a/tty.c b/tty.c index f6db8e20..a9f49c9b 100644 --- a/tty.c +++ b/tty.c @@ -500,7 +500,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; - if (strcmp(s->ccolour, tty->ccolour)) + if (s != NULL && strcmp(s->ccolour, tty->ccolour)) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) @@ -517,7 +517,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } else tty_putcode(tty, TTYC_CIVIS); } - if (tty->cstyle != s->cstyle) { + if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) @@ -667,8 +667,11 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, struct grid_cell tmpgc; struct utf8_data ud; u_int i, sx; + int flags; - tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s); + flags = tty->flags & TTY_NOCURSOR; + tty->flags |= TTY_NOCURSOR; + tty_update_mode(tty, tty->mode, s); sx = screen_size_x(s); if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) @@ -703,18 +706,20 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_cell(tty, gc, wp); } - if (sx >= tty->sx) { - tty_update_mode(tty, tty->mode, s); - return; - } - tty_attributes(tty, &grid_default_cell, wp); + if (sx < tty->sx) { + tty_attributes(tty, &grid_default_cell, wp); - tty_cursor(tty, ox + sx, oy + py); - if (sx != screen_size_x(s) && ox + screen_size_x(s) >= tty->sx && - tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) - tty_putcode(tty, TTYC_EL); - else - tty_repeat_space(tty, screen_size_x(s) - sx); + tty_cursor(tty, ox + sx, oy + py); + if (sx != screen_size_x(s) && + ox + screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL) && + !tty_fake_bce(tty, wp)) + tty_putcode(tty, TTYC_EL); + else + tty_repeat_space(tty, screen_size_x(s) - sx); + } + + tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; tty_update_mode(tty, tty->mode, s); } From 31b1ab48521b4b608d87abd5413441905da84da8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 May 2015 08:35:39 +0000 Subject: [PATCH 567/949] Add a format window_linked which is 1 if a window has been linked multiple times, also remove the default space in window_flags and use a conditional to add it in window-status-format (this means additional flags can be added in the option without extra spaces). From Thomas Adam with tweaks by me. --- cmd-kill-window.c | 17 +++++------------ format.c | 1 + options-table.c | 4 ++-- session.c | 14 ++++++++++++++ tmux.1 | 1 + tmux.h | 1 + window.c | 4 +--- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/cmd-kill-window.c b/cmd-kill-window.c index d402acce..4d346a71 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -45,24 +45,17 @@ const struct cmd_entry cmd_unlink_window_entry = { enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl, *wl2, *wl3; - struct window *w; - struct session *s; - struct session_group *sg; - u_int references; + struct args *args = self->args; + struct winlink *wl, *wl2, *wl3; + struct window *w; + struct session *s; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; if (self->entry == &cmd_unlink_window_entry) { - sg = session_group_find(s); - if (sg != NULL) - references = session_group_count(sg); - else - references = 1; - if (!args_has(self->args, 'k') && w->references == references) { + if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { cmdq_error(cmdq, "window only linked to one session"); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 7ded05d2..ac28604d 100644 --- a/format.c +++ b/format.c @@ -621,6 +621,7 @@ format_defaults_winlink(struct format_tree *ft, struct session *s, !!(wl->flags & WINLINK_SILENCE)); format_add(ft, "window_last_flag", "%d", !!(wl == TAILQ_FIRST(&s->lastw))); + format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); free(flags); } diff --git a/options-table.c b/options-table.c index 089a9b86..21758543 100644 --- a/options-table.c +++ b/options-table.c @@ -737,7 +737,7 @@ const struct options_table_entry window_options_table[] = { { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, - .default_str = "#I:#W#F" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-current-style", @@ -753,7 +753,7 @@ const struct options_table_entry window_options_table[] = { { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, - .default_str = "#I:#W#F" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-last-attr", diff --git a/session.c b/session.c index 224f1f32..907bdee9 100644 --- a/session.c +++ b/session.c @@ -337,6 +337,20 @@ session_has(struct session *s, struct window *w) return (0); } +/* + * Return 1 if a window is linked outside this session (not including session + * groups). The window must be in this session! + */ +int +session_is_linked(struct session *s, struct window *w) +{ + struct session_group *sg; + + if ((sg = session_group_find(s)) != NULL) + return (w->references != session_group_count(sg)); + return (w->references != 1); +} + struct winlink * session_next_alert(struct winlink *wl) { diff --git a/tmux.1 b/tmux.1 index a7f5f3ef..ea178b78 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3345,6 +3345,7 @@ The following variables are available, where appropriate: .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" +.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" diff --git a/tmux.h b/tmux.h index b315114e..83b6e4a8 100644 --- a/tmux.h +++ b/tmux.h @@ -2328,6 +2328,7 @@ struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); int session_has(struct session *, struct window *); +int session_is_linked(struct session *, struct window *); int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); diff --git a/window.c b/window.c index e4e41155..a2277e2e 100644 --- a/window.c +++ b/window.c @@ -643,7 +643,7 @@ window_destroy_panes(struct window *w) } } -/* Return list of printable window flag symbols. No flags is just a space. */ +/* Retuns the printable flags on a window, empty string if no flags set. */ char * window_printable_flags(struct session *s, struct winlink *wl) { @@ -663,8 +663,6 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '-'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; - if (pos == 0) - flags[pos++] = ' '; flags[pos] = '\0'; return (xstrdup(flags)); } From e362d42dc68472621d9f8bd09e40cc8f480ad851 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Wed, 6 May 2015 23:45:58 +0100 Subject: [PATCH 568/949] CHANGES for tmux 2.0 --- CHANGES | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/CHANGES b/CHANGES index abd1ac0a..6e0d8f80 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,62 @@ +CHANGES FROM 1.9a to 2.0 6 March 2015 + +Incompatible Changes +==================== + +* The choose-list command has been removed. +* 'terminal-overrides' is now a server option, not a session option. +* 'message-limit' is now a server option, not a session option. +* 'monitor-content' option has been removed. +* 'pane_start_path' option has been removed. +* The "info" mechanism which used to (for some commands) provide feedback + has been removed, and like other commands, they now produce nothing on + success. + +Normal Changes +============== + +* tmux can now write an entry to utmp if the library 'utempter' is present + at compile time. +* set-buffer learned append mode (-a), and a corresponding + 'append-selection' command has been added to copy-mode. +* choose-mode now has the following commands which can be bound: + - start-of-list + - end-of-list + - top-line + - bottom-line + +* choose-buffer now understands UTF-8. +* Pane navigation has changed: + - The old way of always using the top or left if the choice is ambiguous. + - The new way of remembering the last used pane is annoying if the + layout is balanced and the leftmost is obvious to the user (because + clearly if we go right from the top-left in a tiled set of four we want + to end up in top-right, even if we were last using the bottom-right). + + So instead, use a combination of both: if there is only one possible + pane alongside the current pane, move to it, otherwise choose the most + recently used of the choice. +* 'set-buffer' can now be told to give names to buffers. +* The 'new-session', 'new-window', 'split-window', and 'respawn-pane' commands + now understand multiple arguments and handle quoting problems correctly. +* 'capture-pane' understands '-S-' to mean the start of the pane, and '-E-' to + mean the end of the pane. +* Support for function keys beyond F12 has changed. The following explains: + - F13-F24 are S-F1 to S-F12 + - F25-F36 are C-F1 to C-F12 + - F37-F48 are C-S-F1 to C-S-F12 + - F49-F60 are M-F1 to M-F12 + - F61-F63 are M-S-F1 to M-S-F3 + + Therefore, F13 becomes a binding of S-F1, etc. +* Support using pane id as part of session or window specifier (so % means + session-of-%1 or window-of-%1) and window id as part of session + (so @1 means session-of-@1). +* 'copy-pipe' command now understands formats via -F +* 'if-shell' command now understands formats via -F +* 'split-window' and 'join-window' understand -b to create the pane to the left + or above the target pane. + CHANGES FROM 1.9 to 1.9a 22 February 2014 NOTE: This is a bug-fix release to address some important bugs which just From 6525ca51584636ef781bda147d37d5d0d10899e0 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 7 May 2015 00:00:02 +0100 Subject: [PATCH 569/949] Start working on 2.1 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4a321d5c..cccd656b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 2.0) -RELEASE=1.9a +AC_INIT(tmux, 2.1) +RELEASE=2.0 AC_SUBST(RELEASE) AC_CONFIG_AUX_DIR(etc) From 0b39e6427fde3fadb9a3453b6423865829a59ad6 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 May 2015 23:56:46 +0000 Subject: [PATCH 570/949] Remove ARRAY_* from history and expand completion to complete a) layout names and b) targets beginning with -t or -s. --- status.c | 270 ++++++++++++++++++++++++++++++++++++++++--------------- tmux.h | 3 - 2 files changed, 198 insertions(+), 75 deletions(-) diff --git a/status.c b/status.c index e3d335fd..9bd5111c 100644 --- a/status.c +++ b/status.c @@ -45,10 +45,15 @@ void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); const char *status_prompt_down_history(u_int *); void status_prompt_add_history(const char *); -char *status_prompt_complete(const char *); + +const char **status_prompt_complete_list(u_int *, const char *); +char *status_prompt_complete_prefix(const char **, u_int); +char *status_prompt_complete(struct session *, const char *); /* Status prompt history. */ -ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER; +#define PROMPT_HISTORY 100 +char **status_prompt_hlist; +u_int status_prompt_hsize; /* Status output tree. */ RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); @@ -977,7 +982,7 @@ status_prompt_key(struct client *c, int key) word[last - first] = '\0'; /* And try to complete it. */ - if ((s = status_prompt_complete(word)) == NULL) + if ((s = status_prompt_complete(sess, word)) == NULL) break; /* Trim out word. */ @@ -1235,114 +1240,235 @@ status_prompt_key(struct client *c, int key) const char * status_prompt_up_history(u_int *idx) { - u_int size; - /* - * History runs from 0 to size - 1. - * - * Index is from 0 to size. Zero is empty. + * History runs from 0 to size - 1. Index is from 0 to size. Zero is + * empty. */ - size = ARRAY_LENGTH(&status_prompt_history); - if (size == 0 || *idx == size) + if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) return (NULL); (*idx)++; - return (ARRAY_ITEM(&status_prompt_history, size - *idx)); + return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Get next line from the history. */ const char * status_prompt_down_history(u_int *idx) { - u_int size; - - size = ARRAY_LENGTH(&status_prompt_history); - if (size == 0 || *idx == 0) + if (status_prompt_hsize == 0 || *idx == 0) return (""); (*idx)--; if (*idx == 0) return (""); - return (ARRAY_ITEM(&status_prompt_history, size - *idx)); + return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Add line to the history. */ void status_prompt_add_history(const char *line) { - u_int size; + size_t size; - size = ARRAY_LENGTH(&status_prompt_history); - if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0) + if (status_prompt_hsize > 0 && + strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) return; - if (size == PROMPT_HISTORY) { - free(ARRAY_FIRST(&status_prompt_history)); - ARRAY_REMOVE(&status_prompt_history, 0); + if (status_prompt_hsize == PROMPT_HISTORY) { + free(status_prompt_hlist[0]); + + size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; + memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); + + status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); + return; } - ARRAY_ADD(&status_prompt_history, xstrdup(line)); + status_prompt_hlist = xreallocarray(status_prompt_hlist, + status_prompt_hsize + 1, sizeof *status_prompt_hlist); + status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); +} + +/* Build completion list. */ +const char ** +status_prompt_complete_list(u_int *size, const char *s) +{ + const char **list = NULL, **layout; + const struct cmd_entry **cmdent; + const struct options_table_entry *oe; + const char *layouts[] = { + "even-horizontal", "even-vertical", "main-horizontal", + "main-vertical", "tiled", NULL + }; + + *size = 0; + for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { + if (strncmp((*cmdent)->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = (*cmdent)->name; + } + } + for (oe = server_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = oe->name; + } + } + for (oe = session_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = oe->name; + } + } + for (oe = window_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = oe->name; + } + } + for (layout = layouts; *layout != NULL; layout++) { + if (strncmp(*layout, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = *layout; + } + } + return (list); +} + +/* Find longest prefix. */ +char * +status_prompt_complete_prefix(const char **list, u_int size) +{ + char *out; + u_int i; + size_t j; + + out = xstrdup(list[0]); + for (i = 1; i < size; i++) { + j = strlen(list[i]); + if (j > strlen(out)) + j = strlen(out); + for (; j > 0; j--) { + if (out[j - 1] != list[i][j - 1]) + out[j - 1] = '\0'; + } + } + return (out); } /* Complete word. */ char * -status_prompt_complete(const char *s) +status_prompt_complete(struct session *sess, const char *s) { - const struct cmd_entry **cmdent; - const struct options_table_entry *oe; - ARRAY_DECL(, const char *) list; - char *prefix, *s2; - u_int i; - size_t j; + const char **list = NULL, *colon; + u_int size = 0, i; + struct session *s_loop; + struct winlink *wl; + struct window *w; + char *copy, *out, *tmp; if (*s == '\0') return (NULL); + out = NULL; - /* First, build a list of all the possible matches. */ - ARRAY_INIT(&list); - for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { - if (strncmp((*cmdent)->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, (*cmdent)->name); - } - for (oe = server_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); - } - for (oe = session_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); - } - for (oe = window_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); + if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { + list = status_prompt_complete_list(&size, s); + if (size == 0) + out = NULL; + else if (size == 1) + xasprintf(&out, "%s ", list[0]); + else + out = status_prompt_complete_prefix(list, size); + free(list); + return (out); } + copy = xstrdup(s); - /* If none, bail now. */ - if (ARRAY_LENGTH(&list) == 0) { - ARRAY_FREE(&list); - return (NULL); - } + colon = ":"; + if (copy[strlen(copy) - 1] == ':') + copy[strlen(copy) - 1] = '\0'; + else + colon = ""; + s = copy + 2; - /* If an exact match, return it, with a trailing space. */ - if (ARRAY_LENGTH(&list) == 1) { - xasprintf(&s2, "%s ", ARRAY_FIRST(&list)); - ARRAY_FREE(&list); - return (s2); - } - - /* Now loop through the list and find the longest common prefix. */ - prefix = xstrdup(ARRAY_FIRST(&list)); - for (i = 1; i < ARRAY_LENGTH(&list); i++) { - s = ARRAY_ITEM(&list, i); - - j = strlen(s); - if (j > strlen(prefix)) - j = strlen(prefix); - for (; j > 0; j--) { - if (prefix[j - 1] != s[j - 1]) - prefix[j - 1] = '\0'; + RB_FOREACH(s_loop, sessions, &sessions) { + if (strncmp(s_loop->name, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 2, sizeof *list); + list[size++] = s_loop->name; } } + if (size == 1) { + out = xstrdup(list[0]); + if (session_find(list[0]) != NULL) + colon = ":"; + } else if (size != 0) + out = status_prompt_complete_prefix(list, size); + if (out != NULL) { + xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); + out = tmp; + goto found; + } - ARRAY_FREE(&list); - return (prefix); + colon = ""; + if (*s == ':') { + RB_FOREACH(wl, winlinks, &sess->windows) { + xasprintf(&tmp, ":%s", wl->window->name); + if (strncmp(tmp, s, strlen(s)) == 0){ + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + + xasprintf(&tmp, ":%d", wl->idx); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + } + } else { + RB_FOREACH(s_loop, sessions, &sessions) { + RB_FOREACH(wl, winlinks, &s_loop->windows) { + w = wl->window; + + xasprintf(&tmp, "%s:%s", s_loop->name, w->name); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + + xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + } + } + } + if (size == 1) { + out = xstrdup(list[0]); + colon = " "; + } else if (size != 0) + out = status_prompt_complete_prefix(list, size); + if (out != NULL) { + xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); + out = tmp; + } + + for (i = 0; i < size; i++) + free((void *)list[i]); + +found: + free(copy); + free(list); + return (out); } diff --git a/tmux.h b/tmux.h index 83b6e4a8..9783a65f 100644 --- a/tmux.h +++ b/tmux.h @@ -43,9 +43,6 @@ extern char **environ; /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" -/* Default prompt history length. */ -#define PROMPT_HISTORY 100 - /* * Minimum layout cell size, NOT including separator line. The scroll region * cannot be one line in height so this must be at least two. From b6be03f01aaf10230bf4de4c49d56bc7d9bee9bf Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 07:16:14 +0000 Subject: [PATCH 571/949] If status line is at the top, the offset needs to be adjusted when drawing pane numbers. Based on a diff from John O'Meara. --- screen-redraw.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index e3369b82..babc74a9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -32,7 +32,7 @@ int screen_redraw_check_active(u_int, u_int, int, struct window *, void screen_redraw_draw_borders(struct client *, int, u_int); void screen_redraw_draw_panes(struct client *, u_int); void screen_redraw_draw_status(struct client *, u_int); -void screen_redraw_draw_number(struct client *, struct window_pane *); +void screen_redraw_draw_number(struct client *, struct window_pane *, u_int); #define CELL_INSIDE 0 #define CELL_LEFTRIGHT 1 @@ -354,7 +354,7 @@ screen_redraw_draw_panes(struct client *c, u_int top) for (i = 0; i < wp->sy; i++) tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); if (c->flags & CLIENT_IDENTIFY) - screen_redraw_draw_number(c, wp); + screen_redraw_draw_number(c, wp, top); } } @@ -372,7 +372,7 @@ screen_redraw_draw_status(struct client *c, u_int top) /* Draw number on a pane. */ void -screen_redraw_draw_number(struct client *c, struct window_pane *wp) +screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) { struct tty *tty = &c->tty; struct session *s = c->session; @@ -396,6 +396,9 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) px = wp->sx / 2; py = wp->sy / 2; xoff = wp->xoff; yoff = wp->yoff; + if (top) + yoff++; + if (wp->sx < len * 6 || wp->sy < 5) { tty_cursor(tty, xoff + px - len / 2, yoff + py); goto draw_text; From 73c871ba0a075bdd458a1b4208afd0e9bb293096 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 07:35:31 +0000 Subject: [PATCH 572/949] Simplify environ_push so it doesn't need the ARRAY_* functions. --- environ.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/environ.c b/environ.c index 0dd91783..11b8c849 100644 --- a/environ.c +++ b/environ.c @@ -157,23 +157,16 @@ environ_update(const char *vars, struct environ *srcenv, void environ_push(struct environ *env) { - ARRAY_DECL(, char *) varlist; - struct environ_entry *envent; - char **varp, *var; - u_int i; + struct environ_entry *envent; + char **vp, *v; - ARRAY_INIT(&varlist); - for (varp = environ; *varp != NULL; varp++) { - var = xstrdup(*varp); - var[strcspn(var, "=")] = '\0'; - ARRAY_ADD(&varlist, var); + for (vp = environ; *vp != NULL; vp++) { + v = xstrdup(*vp); + v[strcspn(v, "=")] = '\0'; + + unsetenv(v); + free(v); } - for (i = 0; i < ARRAY_LENGTH(&varlist); i++) { - var = ARRAY_ITEM(&varlist, i); - unsetenv(var); - free(var); - } - ARRAY_FREE(&varlist); RB_FOREACH(envent, environ, env) { if (envent->value != NULL) From 7becf326e3b756016f2df6230db9fcf714d44955 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 07:59:52 +0000 Subject: [PATCH 573/949] Use a TAILQ not array for find-window. --- cmd-find-window.c | 67 ++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/cmd-find-window.c b/cmd-find-window.c index 64e092bf..e92ae60f 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -59,11 +59,12 @@ struct cmd_find_window_data { struct winlink *wl; char *list_ctx; u_int pane_id; + TAILQ_ENTRY(cmd_find_window_data) entry; }; -ARRAY_DECL(cmd_find_window_data_list, struct cmd_find_window_data); +TAILQ_HEAD(cmd_find_window_list, cmd_find_window_data); u_int cmd_find_window_match_flags(struct args *); -void cmd_find_window_match(struct cmd_find_window_data_list *, int, +void cmd_find_window_match(struct cmd_find_window_list *, int, struct winlink *, const char *, const char *); u_int @@ -87,16 +88,16 @@ cmd_find_window_match_flags(struct args *args) } void -cmd_find_window_match(struct cmd_find_window_data_list *find_list, +cmd_find_window_match(struct cmd_find_window_list *find_list, int match_flags, struct winlink *wl, const char *str, const char *searchstr) { - struct cmd_find_window_data find_data; + struct cmd_find_window_data *find_data; struct window_pane *wp; u_int i, line; char *sres; - memset(&find_data, 0, sizeof find_data); + find_data = xcalloc(1, sizeof *find_data); i = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { @@ -104,30 +105,32 @@ cmd_find_window_match(struct cmd_find_window_data_list *find_list, if ((match_flags & CMD_FIND_WINDOW_BY_NAME) && fnmatch(searchstr, wl->window->name, 0) == 0) { - find_data.list_ctx = xstrdup(""); + find_data->list_ctx = xstrdup(""); break; } if ((match_flags & CMD_FIND_WINDOW_BY_TITLE) && fnmatch(searchstr, wp->base.title, 0) == 0) { - xasprintf(&find_data.list_ctx, + xasprintf(&find_data->list_ctx, "pane %u title: \"%s\"", i - 1, wp->base.title); break; } if (match_flags & CMD_FIND_WINDOW_BY_CONTENT && (sres = window_pane_search(wp, str, &line)) != NULL) { - xasprintf(&find_data.list_ctx, + xasprintf(&find_data->list_ctx, "pane %u line %u: \"%s\"", i - 1, line + 1, sres); free(sres); break; } } - if (find_data.list_ctx != NULL) { - find_data.wl = wl; - find_data.pane_id = i - 1; - ARRAY_ADD(find_list, find_data); - } + + if (find_data->list_ctx != NULL) { + find_data->wl = wl; + find_data->pane_id = i - 1; + TAILQ_INSERT_TAIL(find_list, find_data, entry); + } else + free(find_data); } enum cmd_retval @@ -138,7 +141,9 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window_choose_data *cdata; struct session *s; struct winlink *wl, *wm; - struct cmd_find_window_data_list find_list; + struct cmd_find_window_list find_list; + struct cmd_find_window_data *find_data; + struct cmd_find_window_data *find_data1; char *str, *searchstr; const char *template; u_int i, match_flags; @@ -158,21 +163,20 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) match_flags = cmd_find_window_match_flags(args); str = args->argv[0]; - ARRAY_INIT(&find_list); + TAILQ_INIT(&find_list); xasprintf(&searchstr, "*%s*", str); RB_FOREACH(wm, winlinks, &s->windows) cmd_find_window_match(&find_list, match_flags, wm, str, searchstr); free(searchstr); - if (ARRAY_LENGTH(&find_list) == 0) { + if (TAILQ_EMPTY(&find_list)) { cmdq_error(cmdq, "no windows matching: %s", str); - ARRAY_FREE(&find_list); return (CMD_RETURN_ERROR); } - if (ARRAY_LENGTH(&find_list) == 1) { - if (session_select(s, ARRAY_FIRST(&find_list).wl->idx) == 0) + if (TAILQ_NEXT(TAILQ_FIRST(&find_list), entry) == NULL) { + if (session_select(s, TAILQ_FIRST(&find_list)->wl->idx) == 0) server_redraw_session(s); recalculate_sizes(); goto out; @@ -181,30 +185,33 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) goto out; - for (i = 0; i < ARRAY_LENGTH(&find_list); i++) { - wm = ARRAY_ITEM(&find_list, i).wl; - + i = 0; + TAILQ_FOREACH(find_data, &find_list, entry) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = wm->idx; - cdata->wl = wm; + cdata->idx = find_data->wl->idx; + cdata->wl = find_data->wl; cdata->ft_template = xstrdup(template); - cdata->pane_id = ARRAY_ITEM(&find_list, i).pane_id; + cdata->pane_id = find_data->pane_id; format_add(cdata->ft, "line", "%u", i); format_add(cdata->ft, "window_find_matches", "%s", - ARRAY_ITEM(&find_list, i).list_ctx); - format_defaults(cdata->ft, NULL, s, wm, NULL); + find_data->list_ctx); + format_defaults(cdata->ft, NULL, s, find_data->wl, NULL); window_choose_add(wl->window->active, cdata); + + i++; } window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: - for (i = 0; i < ARRAY_LENGTH(&find_list); i++) - free(ARRAY_ITEM(&find_list, i).list_ctx); - ARRAY_FREE(&find_list); + TAILQ_FOREACH_SAFE(find_data, &find_list, entry, find_data1) { + free(find_data->list_ctx); + TAILQ_REMOVE(&find_list, find_data, entry); + free(find_data); + } return (CMD_RETURN_NORMAL); } From 1282bb81fe9fbb1b2ff1b63a9c4e8978444703c4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 08:08:54 +0000 Subject: [PATCH 574/949] array.h can be local to window-choose.c now. --- tmux.h | 2 -- window-choose.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tmux.h b/tmux.h index 9783a65f..72e172e6 100644 --- a/tmux.h +++ b/tmux.h @@ -35,8 +35,6 @@ #include #include -#include "array.h" - extern char *__progname; extern char **environ; diff --git a/window-choose.c b/window-choose.c index 8672212a..d4b20bca 100644 --- a/window-choose.c +++ b/window-choose.c @@ -22,6 +22,7 @@ #include #include +#include "array.h" #include "tmux.h" struct screen *window_choose_init(struct window_pane *); From 63b7a031a52786e9ec72d8d284a46806bfdc848b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 7 May 2015 11:43:52 +0100 Subject: [PATCH 575/949] queue.h should come from compat.h. --- compat/imsg-buffer.c | 1 - compat/imsg.c | 1 - 2 files changed, 2 deletions(-) diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 450fdf1b..6756de0a 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -17,7 +17,6 @@ */ #include -#include #include #include diff --git a/compat/imsg.c b/compat/imsg.c index af0da44f..9db26ad6 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -17,7 +17,6 @@ */ #include -#include #include #include From 8e9b6e09485f3400aac9e6cd8a711e2cc1819b03 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 11:42:56 +0000 Subject: [PATCH 576/949] Style spacing nits. --- cfg.c | 2 +- cmd-find.c | 8 ++++---- key-bindings.c | 2 +- style.c | 4 ++-- window.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cfg.c b/cfg.c index 8b44ce24..ff43976e 100644 --- a/cfg.c +++ b/cfg.c @@ -121,7 +121,7 @@ cfg_add_cause(const char *fmt, ...) va_start(ap, fmt); xvasprintf(&msg, fmt, ap); - va_end (ap); + va_end(ap); cfg_ncauses++; cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); diff --git a/cmd-find.c b/cmd-find.c index ec4cf6c1..98f1e187 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -193,7 +193,7 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, fs->w)) continue; - slist = xreallocarray (slist, ssize + 1, sizeof *slist); + slist = xreallocarray(slist, ssize + 1, sizeof *slist); slist[ssize++] = s; } if (ssize == 0) @@ -201,7 +201,7 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) fs->s = cmd_find_best_session(slist, ssize, fs->flags); if (fs->s == NULL) goto fail; - free (slist); + free(slist); return (cmd_find_best_winlink_with_window(fs)); fail: @@ -329,7 +329,7 @@ cmd_find_current_client(struct cmd_q *cmdq) TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; - clist = xreallocarray (clist, csize + 1, sizeof *clist); + clist = xreallocarray(clist, csize + 1, sizeof *clist); clist[csize++] = c; } if (csize != 0) { @@ -739,7 +739,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) void cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) { - memset (fs, 0, sizeof *fs); + memset(fs, 0, sizeof *fs); fs->cmdq = cmdq; fs->flags = flags; diff --git a/key-bindings.c b/key-bindings.c index d56428b1..a3cb0bea 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -236,7 +236,7 @@ key_bindings_init(void) if (error != 0) fatalx("bad default key"); cmdq_run(cmdq, cmdlist, NULL); - cmd_list_free (cmdlist); + cmd_list_free(cmdlist); } cmdq_free(cmdq); } diff --git a/style.c b/style.c index 5534f118..9fafdd1d 100644 --- a/style.c +++ b/style.c @@ -168,12 +168,12 @@ style_update_new(struct options *oo, const char *name, const char *newname) o = options_find1(oo, newname); if (o == NULL) - o = options_set_style (oo, newname, "default", 0); + o = options_set_style(oo, newname, "default", 0); gc = &o->style; o = options_find1(oo, name); if (o == NULL) - o = options_set_number (oo, name, 8); + o = options_set_number(oo, name, 8); value = o->num; if (strstr(name, "-bg") != NULL) diff --git a/window.c b/window.c index a2277e2e..8366833a 100644 --- a/window.c +++ b/window.c @@ -302,7 +302,7 @@ window_create1(u_int sx, u_int sy) w->references = 0; w->id = next_window_id++; - RB_INSERT (windows, &windows, w); + RB_INSERT(windows, &windows, w); return (w); } From d174b9cfcc2d4c60bd89804ef63e5ee41ca6d490 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 14:07:16 +0000 Subject: [PATCH 577/949] Update environment when switching sessions as well as attaching, from Si Beaumont. --- cmd-switch-client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 18de0eb1..369fc917 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -46,7 +46,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; - const char *tflag, *tablename; + const char *tflag, *tablename, *update; struct key_table *table; if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) @@ -119,6 +119,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } } + if (c != NULL && s != c->session) { + update = options_get_string(&s->options, "update-environment"); + environ_update(update, &c->environ, &s->environ); + } + if (c->session != NULL) c->last_session = c->session; c->session = s; From 879de25583a5a0bce57bd816ced96e0caf62c436 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 15:56:49 +0000 Subject: [PATCH 578/949] Remove some stuff that accidentally ended up here from portable, and remove a little-used debug function. --- grid.c | 29 ++++++++++------------------- server-client.c | 5 ----- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/grid.c b/grid.c index 2dc266d7..7e086143 100644 --- a/grid.c +++ b/grid.c @@ -49,15 +49,16 @@ const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; int grid_check_y(struct grid *, u_int); -#ifdef DEBUG -int -grid_check_y(struct grid *gd, u_int py) -{ - if ((py) >= (gd)->hsize + (gd)->sy) - log_fatalx("y out of range: %u", py); - return (0); -} -#else +void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); +void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, + u_int); +void grid_reflow_move(struct grid *, u_int *, struct grid_line *); +size_t grid_string_cells_fg(const struct grid_cell *, int *); +size_t grid_string_cells_bg(const struct grid_cell *, int *); +void grid_string_cells_code(const struct grid_cell *, + const struct grid_cell *, char *, size_t, int); + +/* Check grid y position. */ int grid_check_y(struct grid *gd, u_int py) { @@ -67,16 +68,6 @@ grid_check_y(struct grid *gd, u_int py) } return (0); } -#endif - -void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); -void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, - u_int); -void grid_reflow_move(struct grid *, u_int *, struct grid_line *); -size_t grid_string_cells_fg(const struct grid_cell *, int *); -size_t grid_string_cells_bg(const struct grid_cell *, int *); -void grid_string_cells_code(const struct grid_cell *, - const struct grid_cell *, char *, size_t, int); /* Create a new grid. */ struct grid * diff --git a/server-client.c b/server-client.c index ce3f3d5e..dd6eda1d 100644 --- a/server-client.c +++ b/server-client.c @@ -1178,11 +1178,6 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) return; c->flags |= CLIENT_IDENTIFIED; -#ifdef __CYGWIN__ - c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); - c->cwd = open(".", O_RDONLY); -#endif - if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; From c4a4bd6ac5748939c7c17beba9e14bee5929c552 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:18:04 +0000 Subject: [PATCH 579/949] Move input parser structs into input.c (removing fairly useless saved_cursor_[xy] formats as a side-effect). --- cmd-capture-pane.c | 14 ++++--- cmd-send-keys.c | 18 +-------- format.c | 2 - input.c | 93 ++++++++++++++++++++++++++++++++++++++++++---- tmux.1 | 2 - tmux.h | 54 ++------------------------- 6 files changed, 100 insertions(+), 83 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index b44ebe9d..a348e155 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -57,15 +57,17 @@ char * cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t *len) { - char *buf, *line, tmp[5]; - size_t linelen; - u_int i; + struct evbuffer *pending; + char *buf, *line, tmp[5]; + size_t linelen; + u_int i; - if (wp->ictx.since_ground == NULL) + pending = input_pending(wp); + if (pending == NULL) return (xstrdup("")); - line = EVBUFFER_DATA(wp->ictx.since_ground); - linelen = EVBUFFER_LENGTH(wp->ictx.since_ground); + line = EVBUFFER_DATA(pending); + linelen = EVBUFFER_LENGTH(pending); buf = xstrdup(""); if (args_has(args, 'C')) { diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 27da410d..b1f8d672 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -52,7 +52,6 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct mouse_event *m = &cmdq->item->mouse; struct window_pane *wp; struct session *s; - struct input_ctx *ictx; const u_char *str; int i, key; @@ -78,21 +77,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'R')) { - ictx = &wp->ictx; - - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; - - if (wp->mode == NULL) - screen_write_start(&ictx->ctx, wp, &wp->base); - else - screen_write_start(&ictx->ctx, NULL, &wp->base); - screen_write_reset(&ictx->ctx); - screen_write_stop(&ictx->ctx); - } + if (args_has(args, 'R')) + input_reset(wp); for (i = 0; i < args->argc; i++) { str = args->argv[i]; diff --git a/format.c b/format.c index ac28604d..39351fc3 100644 --- a/format.c +++ b/format.c @@ -715,8 +715,6 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "cursor_y", "%u", wp->base.cy); format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); - format_add(ft, "saved_cursor_x", "%u", wp->ictx.old_cx); - format_add(ft, "saved_cursor_y", "%u", wp->ictx.old_cy); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); diff --git a/input.c b/input.c index 9f7d4413..384ab45d 100644 --- a/input.c +++ b/input.c @@ -46,6 +46,55 @@ * be passed to the underlying teminal(s). */ +/* Input parser cell. */ +struct input_cell { + struct grid_cell cell; + int set; + int g0set; /* 1 if ACS */ + int g1set; /* 1 if ACS */ +}; + +/* Input parser context. */ +struct input_ctx { + struct window_pane *wp; + struct screen_write_ctx ctx; + + struct input_cell cell; + + struct input_cell old_cell; + u_int old_cx; + u_int old_cy; + + u_char interm_buf[4]; + size_t interm_len; + + u_char param_buf[64]; + size_t param_len; + +#define INPUT_BUF_START 32 +#define INPUT_BUF_LIMIT 1048576 + u_char *input_buf; + size_t input_len; + size_t input_space; + + int param_list[24]; /* -1 not present */ + u_int param_list_len; + + struct utf8_data utf8data; + + int ch; + int flags; +#define INPUT_DISCARD 0x1 + + const struct input_state *state; + + /* + * All input received since we were last in the ground state. Sent to + * control clients on connection. + */ + struct evbuffer *since_ground; +}; + /* Helper functions. */ struct input_transition; int input_split(struct input_ctx *); @@ -706,7 +755,9 @@ input_reset_cell(struct input_ctx *ictx) void input_init(struct window_pane *wp) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx; + + ictx = wp->ictx = xcalloc(1, sizeof *ictx); input_reset_cell(ictx); @@ -732,18 +783,46 @@ input_init(struct window_pane *wp) void input_free(struct window_pane *wp) { - if (wp == NULL) - return; + struct input_ctx *ictx = wp->ictx; - free(wp->ictx.input_buf); - evbuffer_free(wp->ictx.since_ground); + free(ictx->input_buf); + evbuffer_free(ictx->since_ground); + + free (ictx); + wp->ictx = NULL; +} + +/* Reset input state and clear screen. */ +void +input_reset(struct window_pane *wp) +{ + struct input_ctx *ictx = wp->ictx; + + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; + + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + screen_write_reset(&ictx->ctx); + screen_write_stop(&ictx->ctx); +} + +/* Return pending data. */ +struct evbuffer * +input_pending(struct window_pane *wp) +{ + return (wp->ictx->since_ground); } /* Change input state. */ void input_set_state(struct window_pane *wp, const struct input_transition *itr) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx = wp->ictx; if (ictx->state->exit != NULL) ictx->state->exit(ictx); @@ -756,7 +835,7 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr) void input_parse(struct window_pane *wp) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx = wp->ictx; const struct input_transition *itr; struct evbuffer *evb = wp->event->input; u_char *buf; diff --git a/tmux.1 b/tmux.1 index ea178b78..9debeaf4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3318,8 +3318,6 @@ The following variables are available, where appropriate: .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" -.It Li "saved_cursor_x" Ta "" Ta "Saved cursor X in pane" -.It Li "saved_cursor_y" Ta "" Ta "Saved cursor Y in pane" .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 "session_attached" Ta "" Ta "Number of clients session is attached to" diff --git a/tmux.h b/tmux.h index 72e172e6..ad79d79d 100644 --- a/tmux.h +++ b/tmux.h @@ -785,55 +785,6 @@ struct screen_write_ctx { #define screen_hsize(s) ((s)->grid->hsize) #define screen_hlimit(s) ((s)->grid->hlimit) -/* Input parser cell. */ -struct input_cell { - struct grid_cell cell; - int set; - int g0set; /* 1 if ACS */ - int g1set; /* 1 if ACS */ -}; - -/* Input parser context. */ -struct input_ctx { - struct window_pane *wp; - struct screen_write_ctx ctx; - - struct input_cell cell; - - struct input_cell old_cell; - u_int old_cx; - u_int old_cy; - - u_char interm_buf[4]; - size_t interm_len; - - u_char param_buf[64]; - size_t param_len; - -#define INPUT_BUF_START 32 -#define INPUT_BUF_LIMIT 1048576 - u_char *input_buf; - size_t input_len; - size_t input_space; - - int param_list[24]; /* -1 not present */ - u_int param_list_len; - - struct utf8_data utf8data; - - int ch; - int flags; -#define INPUT_DISCARD 0x1 - - const struct input_state *state; - - /* - * All input received since we were last in the ground state. Sent to - * control clients on connection. - */ - struct evbuffer *since_ground; -}; - /* * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. @@ -881,6 +832,7 @@ struct window_choose_mode_item { }; /* Child window structure. */ +struct input_ctx; struct window_pane { u_int id; u_int active_point; @@ -920,7 +872,7 @@ struct window_pane { int fd; struct bufferevent *event; - struct input_ctx ictx; + struct input_ctx *ictx; struct grid_cell colgc; @@ -1983,6 +1935,8 @@ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); +void input_reset(struct window_pane *); +struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ From a538141a72882e10988b68d7d1ede94cbb7140c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:23:34 +0000 Subject: [PATCH 580/949] window_choose_mode_item can move into window-choose.c. --- tmux.h | 8 -------- window-choose.c | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tmux.h b/tmux.h index ad79d79d..f48797dc 100644 --- a/tmux.h +++ b/tmux.h @@ -823,14 +823,6 @@ struct window_choose_data { char *command; }; -struct window_choose_mode_item { - struct window_choose_data *wcd; - char *name; - int pos; - int state; -#define TREE_EXPANDED 0x1 -}; - /* Child window structure. */ struct input_ctx; struct window_pane { diff --git a/window-choose.c b/window-choose.c index d4b20bca..2af56e23 100644 --- a/window-choose.c +++ b/window-choose.c @@ -60,6 +60,14 @@ const struct window_mode window_choose_mode = { NULL, }; +struct window_choose_mode_item { + struct window_choose_data *wcd; + char *name; + int pos; + int state; +#define TREE_EXPANDED 0x1 +}; + struct window_choose_mode_data { struct screen screen; From 74b2c40b1b2f38b1aafe4178cb33db63d3be8de0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:33:29 +0000 Subject: [PATCH 581/949] mode_key_entry can go into mode-key.c; also a few spaces->tabs. --- mode-key.c | 14 ++++++++++++++ tmux.h | 27 ++++++++------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/mode-key.c b/mode-key.c index c06d7ed5..5ed45bd8 100644 --- a/mode-key.c +++ b/mode-key.c @@ -38,6 +38,20 @@ * (any matching MODEKEYEDIT_SWITCHMODE*) are special-cased to do this. */ +/* Entry in the default mode key tables. */ +struct mode_key_entry { + int key; + + /* + * Editing mode for vi: 0 is edit mode, keys not in the table are + * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table + * are returned as MODEKEY_NONE. This is also matched on, allowing some + * keys to be bound in edit mode. + */ + int mode; + enum mode_key_cmd cmd; +}; + /* Edit keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_BACKSPACE, "backspace" }, diff --git a/tmux.h b/tmux.h index f48797dc..3c9db501 100644 --- a/tmux.h +++ b/tmux.h @@ -566,20 +566,6 @@ enum mode_key_cmd { MODEKEYCOPY_UP, }; -/* Entry in the default mode key tables. */ -struct mode_key_entry { - int key; - - /* - * Editing mode for vi: 0 is edit mode, keys not in the table are - * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table - * are returned as MODEKEY_NONE. This is also matched on, allowing some - * keys to be bound in edit mode. - */ - int mode; - enum mode_key_cmd cmd; -}; - /* Data required while mode keys are in use. */ struct mode_key_data { struct mode_key_tree *tree; @@ -607,6 +593,7 @@ struct mode_key_cmdstr { }; /* Named mode key table description. */ +struct mode_key_entry; struct mode_key_table { const char *name; const struct mode_key_cmdstr *cmdstr; @@ -1192,16 +1179,16 @@ struct tty_ctx { /* Saved message entry. */ struct message_entry { - char *msg; - u_int msg_num; - time_t msg_time; + char *msg; + u_int msg_num; + time_t msg_time; TAILQ_ENTRY(message_entry) entry; }; /* Status output data from a job. */ struct status_out { - char *cmd; - char *out; + char *cmd; + char *out; RB_ENTRY(status_out) entry; }; @@ -1322,6 +1309,7 @@ struct cmd { TAILQ_ENTRY(cmd) qentry; }; + struct cmd_list { int references; TAILQ_HEAD(, cmd) list; @@ -1394,6 +1382,7 @@ struct key_binding { RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); + struct key_table { const char *name; struct key_bindings key_bindings; From 592cb73a69e23d321a29ae367d52b9e9b4f506c2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:44:03 +0000 Subject: [PATCH 582/949] grid_marker_cell is no longer used. --- tmux.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.h b/tmux.h index 3c9db501..f1575d21 100644 --- a/tmux.h +++ b/tmux.h @@ -1940,7 +1940,6 @@ int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; -extern const struct grid_cell grid_marker_cell; struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); From 92faa2eaebd32117f01b0b7d7ae81abdfde2d935 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:48:12 +0000 Subject: [PATCH 583/949] Put the tty structs together, and tabify. --- tmux.h | 96 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/tmux.h b/tmux.h index f1575d21..6d4e29fd 100644 --- a/tmux.h +++ b/tmux.h @@ -689,7 +689,7 @@ struct options_entry { } type; char *str; - long long num; + long long num; struct grid_cell style; RB_ENTRY(options_entry) entry; @@ -804,7 +804,7 @@ struct window_choose_data { struct winlink *wl; int pane_id; - char *ft_template; + char *ft_template; struct format_tree *ft; char *command; @@ -882,7 +882,7 @@ struct window { u_int id; char *name; struct event name_timer; - struct timeval silence_timer; + struct timeval silence_timer; struct window_pane *active; struct window_pane *last; @@ -923,7 +923,7 @@ struct winlink { struct grid_cell status_cell; char *status_text; - int flags; + int flags; #define WINLINK_BELL 0x1 #define WINLINK_ACTIVITY 0x2 #define WINLINK_SILENCE 0x4 @@ -1015,7 +1015,7 @@ struct session { #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; - u_int attached; + u_int attached; struct termios *tio; @@ -1028,33 +1028,6 @@ struct session { }; RB_HEAD(sessions, session); -/* TTY information. */ -struct tty_key { - char ch; - int key; - - struct tty_key *left; - struct tty_key *right; - - struct tty_key *next; -}; - -struct tty_term { - char *name; - u_int references; - - char acs[UCHAR_MAX + 1][2]; - - struct tty_code codes[NTTYCODE]; - -#define TERM_256COLOURS 0x1 -#define TERM_EARLYWRAP 0x2 - int flags; - - LIST_ENTRY(tty_term) entry; -}; -LIST_HEAD(tty_terms, tty_term); - /* Mouse button masks. */ #define MOUSE_MASK_BUTTONS 3 #define MOUSE_MASK_SHIFT 4 @@ -1096,6 +1069,33 @@ struct mouse_event { u_int sgr_b; }; +/* TTY information. */ +struct tty_key { + char ch; + int key; + + struct tty_key *left; + struct tty_key *right; + + struct tty_key *next; +}; + +struct tty_term { + char *name; + u_int references; + + char acs[UCHAR_MAX + 1][2]; + + struct tty_code codes[NTTYCODE]; + +#define TERM_256COLOURS 0x1 +#define TERM_EARLYWRAP 0x2 + int flags; + + LIST_ENTRY(tty_term) entry; +}; +LIST_HEAD(tty_terms, tty_term); + struct tty { struct client *client; @@ -1143,7 +1143,7 @@ struct tty { void (*mouse_drag_update)(struct client *, struct mouse_event *); void (*mouse_drag_release)(struct client *, - struct mouse_event *); + struct mouse_event *); struct event key_timer; struct tty_key *key_tree; @@ -1217,7 +1217,7 @@ struct client { void (*stdin_callback)(struct client *, int, void *); void *stdin_callback_data; struct evbuffer *stdin_data; - int stdin_closed; + int stdin_closed; struct evbuffer *stdout_data; struct evbuffer *stderr_data; @@ -1263,7 +1263,7 @@ struct client { int (*prompt_callbackfn)(void *, const char *); void (*prompt_freefn)(void *); void *prompt_data; - u_int prompt_hindex; + u_int prompt_hindex; #define PROMPT_SINGLE 0x1 int prompt_flags; @@ -1292,8 +1292,8 @@ RB_HEAD(args_tree, args_entry); struct args { struct args_tree tree; - int argc; - char **argv; + int argc; + char **argv; }; /* Command and list of commands. */ @@ -1311,8 +1311,8 @@ struct cmd { }; struct cmd_list { - int references; - TAILQ_HEAD(, cmd) list; + int references; + TAILQ_HEAD(, cmd) list; }; /* Command return values. */ @@ -1351,7 +1351,7 @@ struct cmd_q { void (*emptyfn)(struct cmd_q *); void *data; - TAILQ_ENTRY(cmd_q) waitentry; + TAILQ_ENTRY(cmd_q) waitentry; }; /* Command definition. */ @@ -1413,8 +1413,8 @@ struct options_table_entry { const char *name; enum options_table_type type; - u_int minimum; - u_int maximum; + u_int minimum; + u_int maximum; const char **choices; const char *default_str; @@ -1801,7 +1801,7 @@ void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...); void cmdq_guard(struct cmd_q *, const char *, int); void cmdq_run(struct cmd_q *, struct cmd_list *, - struct mouse_event *); + struct mouse_event *); void cmdq_append(struct cmd_q *, struct cmd_list *, struct mouse_event *); int cmdq_continue(struct cmd_q *); @@ -1823,8 +1823,8 @@ RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); extern struct key_tables key_tables; int key_table_cmp(struct key_table *, struct key_table *); int key_bindings_cmp(struct key_binding *, struct key_binding *); -struct key_table *key_bindings_get_table(const char *, int); -void key_bindings_unref_table(struct key_table *); +struct key_table *key_bindings_get_table(const char *, int); +void key_bindings_unref_table(struct key_table *); void key_bindings_add(const char *, int, int, struct cmd_list *); void key_bindings_remove(const char *, int); void key_bindings_remove_table(const char *); @@ -1846,7 +1846,7 @@ void server_add_accept(int); /* server-client.c */ void server_client_handle_key(struct client *, int); void server_client_create(int); -int server_client_open(struct client *, char **); +int server_client_open(struct client *, char **); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); @@ -2097,9 +2097,9 @@ 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); struct window_pane *window_pane_next_by_number(struct window *, - struct window_pane *, u_int); + struct window_pane *, u_int); struct window_pane *window_pane_previous_by_number(struct window *, - struct window_pane *, u_int); + struct window_pane *, u_int); int window_pane_index(struct window_pane *, u_int *); u_int window_count_panes(struct window *); void window_destroy_panes(struct window *); From 4165ed96f8f0c494c7bd4f793176e508b6b6581d Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 9 May 2015 20:03:24 +0100 Subject: [PATCH 584/949] Add back __CYGWIN__ block This went missing during the merge from OpenBSD. --- server-client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server-client.c b/server-client.c index a9348997..84ae86bc 100644 --- a/server-client.c +++ b/server-client.c @@ -1187,6 +1187,11 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) return; c->flags |= CLIENT_IDENTIFIED; +#ifdef __CYGWIN__ + c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); + c->cwd = open(".", O_RDONLY); +#endif + if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; From 44364d7112037bebb0bb1d20f72b12b1f736fa95 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 May 2015 10:10:16 +0000 Subject: [PATCH 585/949] Remove the c0-* options which never really worked satisfactorily. Going to try something else... --- input.c | 16 +++------------- tmux.1 | 18 ------------------ tmux.h | 5 ----- window.c | 40 ---------------------------------------- 4 files changed, 3 insertions(+), 76 deletions(-) diff --git a/input.c b/input.c index 384ab45d..886d561d 100644 --- a/input.c +++ b/input.c @@ -1069,7 +1069,6 @@ input_c0_dispatch(struct input_ctx *ictx) struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; - u_int trigger; log_debug("%s: '%c", __func__, ictx->ch); @@ -1081,7 +1080,7 @@ input_c0_dispatch(struct input_ctx *ictx) break; case '\010': /* BS */ screen_write_backspace(sctx); - goto count_c0; + break; case '\011': /* HT */ /* Don't tab beyond the end of the line. */ if (s->cx >= screen_size_x(s) - 1) @@ -1098,10 +1097,10 @@ input_c0_dispatch(struct input_ctx *ictx) case '\013': /* VT */ case '\014': /* FF */ screen_write_linefeed(sctx, 0); - goto count_c0; + break; case '\015': /* CR */ screen_write_carriagereturn(sctx); - goto count_c0; + break; case '\016': /* SO */ ictx->cell.set = 1; break; @@ -1113,15 +1112,6 @@ input_c0_dispatch(struct input_ctx *ictx) break; } - return (0); - -count_c0: - trigger = options_get_number(&wp->window->options, "c0-change-trigger"); - if (trigger != 0 && ++wp->changes >= trigger) { - wp->flags |= PANE_DROP; - window_pane_timer_start(wp); - } - return (0); } diff --git a/tmux.1 b/tmux.1 index 9debeaf4..79dfa4b4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2886,24 +2886,6 @@ used when the .Ic automatic-rename option is enabled. .Pp -.It Ic c0-change-interval Ar interval -.It Ic c0-change-trigger Ar trigger -These two options configure a simple form of rate limiting for a pane. -If -.Nm -sees more than -.Ar trigger -C0 sequences that modify the screen (for example, carriage returns, linefeeds -or backspaces) in one millisecond, it will stop updating the pane immediately and -instead redraw it entirely every -.Ar interval -milliseconds. -This helps to prevent fast output (such as -.Xr yes 1 ) -overwhelming the terminal. -The default is a trigger of 250 and an interval of 100. -A trigger of zero disables the rate limiting. -.Pp .It Ic clock-mode-colour Ar colour Set clock colour. .Pp diff --git a/tmux.h b/tmux.h index 6d4e29fd..ccbdb763 100644 --- a/tmux.h +++ b/tmux.h @@ -844,10 +844,6 @@ struct window_pane { char tty[TTY_NAME_MAX]; int status; - u_int changes; - struct event changes_timer; - u_int changes_redraw; - int fd; struct bufferevent *event; @@ -2107,7 +2103,6 @@ struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); -void window_pane_timer_start(struct window_pane *); int window_pane_spawn(struct window_pane *, int, char **, const char *, const char *, int, struct environ *, struct termios *, char **); diff --git a/window.c b/window.c index 8366833a..7edfed42 100644 --- a/window.c +++ b/window.c @@ -58,7 +58,6 @@ u_int next_window_pane_id; u_int next_window_id; u_int next_active_point; -void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); @@ -741,9 +740,6 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); - if (event_initialized(&wp->changes_timer)) - evtimer_del(&wp->changes_timer); - if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); @@ -877,42 +873,6 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, return (0); } -void -window_pane_timer_start(struct window_pane *wp) -{ - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = 1000; - - evtimer_del(&wp->changes_timer); - evtimer_set(&wp->changes_timer, window_pane_timer_callback, wp); - evtimer_add(&wp->changes_timer, &tv); -} - -void -window_pane_timer_callback(unused int fd, unused short events, void *data) -{ - struct window_pane *wp = data; - struct window *w = wp->window; - u_int interval, trigger; - - interval = options_get_number(&w->options, "c0-change-interval"); - trigger = options_get_number(&w->options, "c0-change-trigger"); - - if (wp->changes_redraw++ == interval) { - wp->flags |= PANE_REDRAW; - wp->changes_redraw = 0; - } - - if (trigger == 0 || wp->changes < trigger) { - wp->flags |= PANE_REDRAW; - wp->flags &= ~PANE_DROP; - } else - window_pane_timer_start(wp); - wp->changes = 0; -} - void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { From b833fabeb2334c44406bd54ff7cc804d20c546f6 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 May 2015 10:58:22 +0000 Subject: [PATCH 586/949] Left the c0-* options behind in the table. --- options-table.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/options-table.c b/options-table.c index 21758543..9869e14f 100644 --- a/options-table.c +++ b/options-table.c @@ -485,20 +485,6 @@ const struct options_table_entry window_options_table[] = { "#{?pane_dead,[dead],}" }, - { .name = "c0-change-trigger", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 250, - .minimum = 0, - .maximum = USHRT_MAX - }, - - { .name = "c0-change-interval", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 100, - .minimum = 1, - .maximum = USHRT_MAX - }, - { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .default_num = 4 From e958db09a72c2308d3edc8384be02714eb068ae7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 15:27:46 +0000 Subject: [PATCH 587/949] Add bell-action "other" to pass through bells in all windows except the current, suggested by Jan ONDREJ. --- options-table.c | 2 +- server-window.c | 11 ++++++++--- tmux.1 | 8 +++++--- tmux.h | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/options-table.c b/options-table.c index 9869e14f..44d36dca 100644 --- a/options-table.c +++ b/options-table.c @@ -49,7 +49,7 @@ const char *options_table_status_position_list[] = { "top", "bottom", NULL }; const char *options_table_bell_action_list[] = { - "none", "any", "current", NULL + "none", "any", "current", "other", NULL }; /* Server options. */ diff --git a/server-window.c b/server-window.c index 4385dd90..1b3938d3 100644 --- a/server-window.c +++ b/server-window.c @@ -77,13 +77,18 @@ server_window_check_bell(struct session *s, struct winlink *wl) if (c->session != s || c->flags & CLIENT_CONTROL) continue; if (!visual) { - if (c->session->curw->window == w || action == BELL_ANY) + if ((action == BELL_CURRENT && + c->session->curw->window == w) || + (action == BELL_OTHER && + c->session->curw->window != w) || + action == BELL_ANY) tty_bell(&c->tty); continue; } - if (c->session->curw->window == w) + if (action == BELL_CURRENT && c->session->curw->window == w) status_message_set(c, "Bell in current window"); - else if (action == BELL_ANY) + else if (action == BELL_ANY || (action == BELL_OTHER && + c->session->curw->window != w)) status_message_set(c, "Bell in window %d", wl->idx); } diff --git a/tmux.1 b/tmux.1 index 79dfa4b4..7d9c0dc8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2375,16 +2375,18 @@ Set the base index from which an unused index should be searched when a new window is created. The default is zero. .It Xo Ic bell-action -.Op Ic any | none | current +.Op Ic any | none | current | other .Xc Set action on window bell. .Ic any means a bell in any window linked to a session causes a bell in the current window of that session, .Ic none -means all bells are ignored and +means all bells are ignored, .Ic current -means only bells in windows other than the current window are ignored. +means only bells in windows other than the current window are ignored and +.Ic other +means bells in the current window are ignored but not those in other windows. .It Xo Ic bell-on-alert .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index ccbdb763..f7c44b20 100644 --- a/tmux.h +++ b/tmux.h @@ -75,6 +75,7 @@ extern char **environ; #define BELL_NONE 0 #define BELL_ANY 1 #define BELL_CURRENT 2 +#define BELL_OTHER 3 /* Special key codes. */ #define KEYC_NONE 0xfff From ec34439f9cddcbde78d5fa2c628e454d2df5dd53 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 15:29:29 +0000 Subject: [PATCH 588/949] Add a session_alerts format which is a list of all the alerts in the current session in symbolic form (something like "0!,4~,5!"). Use this in the default set-titles-string. Prompted by a request from Jan ONDREJ. --- format.c | 20 ++++++++++++++++++++ options-table.c | 2 +- tmux.1 | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 39351fc3..db6f9231 100644 --- a/format.c +++ b/format.c @@ -494,6 +494,8 @@ format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; time_t t; + struct winlink *wl; + char alerts[256], tmp[16]; ft->s = s; @@ -518,6 +520,24 @@ 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); + + *alerts = '\0'; + RB_FOREACH (wl, winlinks, &s->windows) { + if ((wl->flags & WINLINK_ALERTFLAGS) == 0) + continue; + snprintf(tmp, sizeof tmp, "%u", wl->idx); + + if (*alerts != '\0') + strlcat(alerts, ",", sizeof alerts); + strlcat(alerts, tmp, sizeof alerts); + if (wl->flags & WINLINK_ACTIVITY) + strlcat(alerts, "#", sizeof alerts); + if (wl->flags & WINLINK_BELL) + strlcat(alerts, "!", sizeof alerts); + if (wl->flags & WINLINK_SILENCE) + strlcat(alerts, "~", sizeof alerts); + } + format_add(ft, "session_alerts", "%s", alerts); } /* Set default format keys for a client. */ diff --git a/options-table.c b/options-table.c index 44d36dca..4ad45d37 100644 --- a/options-table.c +++ b/options-table.c @@ -296,7 +296,7 @@ const struct options_table_entry session_options_table[] = { { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, - .default_str = "#S:#I:#W - \"#T\"" + .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "status", diff --git a/tmux.1 b/tmux.1 index 7d9c0dc8..f5a0aaa3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3304,6 +3304,7 @@ The following variables are available, where appropriate: .It Li "pane_width" Ta "" Ta "Width of pane" .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 "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_activity" Ta "" Ta "Integer time of session last activity" .It Li "session_activity_string" Ta "" Ta "String time of session last activity" From 37ae8a9e0fc2cd7ab2387a9a762a39dc0ff6723c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 19:36:08 +0000 Subject: [PATCH 589/949] Tidy blank lines when outputting server info. --- cmd-show-messages.c | 48 +++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 028b4bb0..2a04bd93 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -47,11 +47,11 @@ const struct cmd_entry cmd_server_info_entry = { cmd_show_messages_exec }; -void cmd_show_messages_server(struct cmd_q *); -void cmd_show_messages_terminals(struct cmd_q *); -void cmd_show_messages_jobs(struct cmd_q *); +int cmd_show_messages_server(struct cmd_q *); +int cmd_show_messages_terminals(struct cmd_q *, int); +int cmd_show_messages_jobs(struct cmd_q *, int); -void +int cmd_show_messages_server(struct cmd_q *cmdq) { char *tim; @@ -63,10 +63,12 @@ cmd_show_messages_server(struct cmd_q *cmdq) cmdq_print(cmdq, "socket path %s", socket_path); cmdq_print(cmdq, "debug level %d", debug_level); cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); + + return (1); } -void -cmd_show_messages_terminals(struct cmd_q *cmdq) +int +cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -76,8 +78,11 @@ cmd_show_messages_terminals(struct cmd_q *cmdq) n = 0; LIST_FOREACH(term, &tty_terms, entry) { - cmdq_print(cmdq, - "Terminal %u: %s [references=%u, flags=0x%x]:", + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Terminal %u: %s [references=%u, flags=0x%x]:", n, term->name, term->references, term->flags); n++; for (i = 0; i < NTTYCODE; i++) { @@ -106,21 +111,26 @@ cmd_show_messages_terminals(struct cmd_q *cmdq) } } } + return (n != 0); } -void -cmd_show_messages_jobs(struct cmd_q *cmdq) +int +cmd_show_messages_jobs(struct cmd_q *cmdq, int blank) { struct job *job; u_int n; n = 0; LIST_FOREACH(job, &all_jobs, lentry) { - cmdq_print(cmdq, - "Job %u: %s [fd=%d, pid=%d, status=%d]", + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Job %u: %s [fd=%d, pid=%d, status=%d]", n, job->cmd, job->fd, job->pid, job->status); n++; } + return (n != 0); } enum cmd_retval @@ -130,23 +140,19 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c; struct message_entry *msg; char *tim; - int done; + int done, blank; - done = 0; + done = blank = 0; if (args_has(args, 'I') || self->entry == &cmd_server_info_entry) { - cmd_show_messages_server(cmdq); + blank = cmd_show_messages_server(cmdq); done = 1; } if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { - if (done) - cmdq_print(cmdq, "%s", ""); - cmd_show_messages_terminals(cmdq); + blank = cmd_show_messages_terminals(cmdq, blank); done = 1; } if (args_has(args, 'J') || self->entry == &cmd_server_info_entry) { - if (done) - cmdq_print(cmdq, "%s", ""); - cmd_show_messages_jobs(cmdq); + cmd_show_messages_jobs(cmdq, blank); done = 1; } if (done) From 3f4ee98162cd5bb7000f93fec0e631e123b1281d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 22:40:38 +0000 Subject: [PATCH 590/949] To replace c0-*, add a high watermark to the pty event, and also backoff when the any of the ttys the pane is going to write to has buffered enough data. --- tmux.h | 16 ++++++++++++++-- tty.c | 26 +++++++++++++++++--------- window.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 79 insertions(+), 19 deletions(-) diff --git a/tmux.h b/tmux.h index f7c44b20..bc47a268 100644 --- a/tmux.h +++ b/tmux.h @@ -56,6 +56,16 @@ extern char **environ; */ #define UTF8_SIZE 9 +/* + * READ_SIZE is the maximum size of data to hold from a pty (the event high + * watermark). READ_BACKOFF is the amount of data waiting to be output to a tty + * before pty reads will be backed off. READ_TIME is how long to back off + * before the next read (in microseconds) if a tty is above READ_BACKOFF. + */ +#define READ_SIZE 1024 +#define READ_BACKOFF 512 +#define READ_TIME 100 + /* Fatal errors. */ #define fatal(msg) log_fatal("%s: %s", __func__, msg); #define fatalx(msg) log_fatalx("%s: %s", __func__, msg); @@ -847,6 +857,7 @@ struct window_pane { int fd; struct bufferevent *event; + struct event timer; struct input_ctx *ictx; @@ -1595,8 +1606,9 @@ void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_write( - void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); +void tty_write(void (*)(struct tty *, const struct tty_ctx *), + struct tty_ctx *); +int tty_client_ready(struct client *, struct window_pane *wp); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_cell(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofline(struct tty *, const struct tty_ctx *); diff --git a/tty.c b/tty.c index a9f49c9b..a58ca937 100644 --- a/tty.c +++ b/tty.c @@ -723,9 +723,23 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_update_mode(tty, tty->mode, s); } +int +tty_client_ready(struct client *c, struct window_pane *wp) +{ + if (c->session == NULL || c->tty.term == NULL) + return (0); + if (c->flags & CLIENT_SUSPENDED) + return (0); + if (c->tty.flags & TTY_FREEZE) + return (0); + if (c->session->curw->window != wp->window) + return (0); + return (1); +} + void -tty_write( - void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) +tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), + struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct client *c; @@ -740,13 +754,7 @@ tty_write( return; TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || c->tty.term == NULL) - continue; - if (c->flags & CLIENT_SUSPENDED) - continue; - if (c->tty.flags & TTY_FREEZE) - continue; - if (c->session->curw->window != wp->window) + if (!tty_client_ready(c, wp)) continue; ctx->xoff = wp->xoff; diff --git a/window.c b/window.c index 7edfed42..bc17656d 100644 --- a/window.c +++ b/window.c @@ -58,6 +58,7 @@ u_int next_window_pane_id; u_int next_window_id; u_int next_active_point; +void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); @@ -740,6 +741,9 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); + if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); @@ -867,28 +871,53 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); + + bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); free(cmd); return (0); } +void +window_pane_timer_callback(unused int fd, unused short events, void *data) +{ + window_pane_read_callback(NULL, data); +} + void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { - struct window_pane *wp = data; - char *new_data; - size_t new_size; + struct window_pane *wp = data; + struct evbuffer *evb = wp->event->input; + char *new_data; + size_t new_size, available; + struct client *c; + struct timeval tv; - new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); + + log_debug("%%%u has %zu bytes", wp->id, EVBUFFER_LENGTH(evb)); + + TAILQ_FOREACH(c, &clients, entry) { + if (!tty_client_ready(c, wp)) + continue; + + available = EVBUFFER_LENGTH(c->tty.event->output); + if (available > READ_BACKOFF) + goto start_timer; + } + + new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(wp->event->input); + new_data = EVBUFFER_DATA(evb); bufferevent_write(wp->pipe_event, new_data, new_size); } input_parse(wp); - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + wp->pipe_off = EVBUFFER_LENGTH(evb); /* * If we get here, we're not outputting anymore, so set the silence @@ -897,11 +926,22 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) wp->window->flags |= WINDOW_SILENCE; if (gettimeofday(&wp->window->silence_timer, NULL) != 0) fatal("gettimeofday failed."); + return; + +start_timer: + log_debug("%%%u backing off (%s %zu > %d)", wp->id, c->ttyname, + available, READ_BACKOFF); + + tv.tv_sec = 0; + tv.tv_usec = READ_TIME; + + evtimer_set(&wp->timer, window_pane_timer_callback, wp); + evtimer_add(&wp->timer, &tv); } void -window_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +window_pane_error_callback(unused struct bufferevent *bufev, unused short what, + void *data) { struct window_pane *wp = data; From beb0c01c27de2cc9d42684571e905446b76bc79d Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 17 May 2015 14:34:11 +0100 Subject: [PATCH 591/949] Hook repo to Travis-CI From now on, all pushes to master will result in tmux compiling against a linux-based distribution (Debian). This will make it easier for automatic merges between OpenBSD and portable to be tested, without the need for so much manual syncing. Any build failures will be reported to me, and fixed accordingly. --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..7aa698f0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: c +matrix: + include: + - compiler: gcc + - compiler: clang + env: CFLAGS="-g -O2" +before_install: + - sudo apt-get update -qq + - sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential +script: (CFLAGS= ./autogen.sh) && configure --enable-debug && make From 35d21be19ae0069b8cc09a0701251c19aeae77ab Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 17 May 2015 14:39:04 +0100 Subject: [PATCH 592/949] TRAVIS-CI: correct path to configure Specify path to ./configure --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7aa698f0..a1d7e427 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ matrix: before_install: - sudo apt-get update -qq - sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential -script: (CFLAGS= ./autogen.sh) && configure --enable-debug && make +script: (CFLAGS= ./autogen.sh) && ./configure --enable-debug && make From 4123d69b51c1806fe37dde7f2bd28ac916cb1407 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 17 May 2015 14:52:58 +0100 Subject: [PATCH 593/949] README.md: github-specific readme This is the same as the current README, but allows for markdown to be used. We could switch this over to using the README file at some point. --- README.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..d3b53829 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +Welcome to tmux! +================ + +[![Build Status](https://travis-ci.org/ThomasAdam/tmux.svg?branch=master)](https://travis-ci.org/ThomasAdam/tmux) + +tmux is a "terminal multiplexer", it enables a number of terminals (or windows) +to be accessed and controlled from a single terminal. tmux is intended to be a +simple, modern, BSD-licensed alternative to programs such as GNU screen. + +This release runs on FreeBSD, NetBSD, Linux and OS X and may still run on +Solaris and AIX. + +tmux depends on libevent 2.x. Download it from: + + http://www.monkey.org/~provos/libevent/ + +To build tmux from a release tarball, do: + + $ ./configure && make + $ sudo make install + +To get and build the latest from version control: + + $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux + $ cd tmux + $ sh autogen.sh + $ ./configure && make + +For more information see https://sourceforge.net/scm/?type=git&group_id=200378 +and http://git-scm.com. Patches should be sent by email to the mailing list at +tmux-users@lists.sourceforge.net. + +For documentation on using tmux, see the tmux.1 manpage. It can be viewed from +the source tree with: + + $ nroff -mdoc tmux.1|less + +Some common questions are answered in the FAQ file and a more extensive (but +slightly out of date) guide is available in the OpenBSD FAQ at +http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO +file and some example configurations and a Vim syntax file are in the examples +directory. + +For debugging, running tmux with -v or -vv will generate server and client log +files in the current directory. + +tmux mailing lists are available. Visit: + + https://sourceforge.net/mail/?group_id=200378 + +Bug reports, feature suggestions and especially code contributions are most +welcome. Please send by email to: + + tmux-users@lists.sourceforge.net + +This file and the CHANGES, FAQ, TODO, and SYNCING files are licensed under +the ISC license. Files under examples/ remain copyright their authors unless +otherwise stated in the file but permission has been received to distribute +them with tmux. All other files have a license and copyright notice at their +start. From 2c53b23d5968da2e796ead6ed9f8ff3c33b8bbfb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 May 2015 08:48:37 +0000 Subject: [PATCH 594/949] In terminfo, sometimes cvvis implies cnorm and sometimes it doesn't, so don't assume it does. Fixes missing cursor with emacs-in-tmux-in-tmux. --- tty.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tty.c b/tty.c index a58ca937..63380c29 100644 --- a/tty.c +++ b/tty.c @@ -507,14 +507,17 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; - if (changed & (MODE_CURSOR|MODE_BLINKING)) { - if (mode & MODE_CURSOR) { - if (mode & MODE_BLINKING && - tty_term_has(tty->term, TTYC_CVVIS)) - tty_putcode(tty, TTYC_CVVIS); - else - tty_putcode(tty, TTYC_CNORM); - } else + if (changed & MODE_BLINKING) { + if (tty_term_has(tty->term, TTYC_CVVIS)) + tty_putcode(tty, TTYC_CVVIS); + else + tty_putcode(tty, TTYC_CNORM); + changed |= MODE_CURSOR; + } + if (changed & MODE_CURSOR) { + if (mode & MODE_CURSOR) + tty_putcode(tty, TTYC_CNORM); + else tty_putcode(tty, TTYC_CIVIS); } if (s != NULL && tty->cstyle != s->cstyle) { From 7140cce7f31eb2135491fdc62645b4753d941520 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 May 2015 06:39:02 +0000 Subject: [PATCH 595/949] Return empty string if format is empty rather than attempting to allocate zero bytes. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index db6f9231..eff1668d 100644 --- a/format.c +++ b/format.c @@ -335,7 +335,7 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) size_t tmplen; struct tm *tm; - if (fmt == NULL) + if (fmt == NULL || *fmt == '\0') return (xstrdup("")); tm = localtime(&t); From 379400cfa69f8df9ac13b070c60d5f8a282ddf6e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 27 May 2015 13:28:04 +0000 Subject: [PATCH 596/949] Move the jobs output cache into the formats code so that #() work more generally (for example, again working in set-titles-string). --- cmd-refresh-client.c | 5 +- format.c | 192 ++++++++++++++++++++++++++++++++--- server-client.c | 6 +- server.c | 2 + status.c | 236 ++----------------------------------------- tmux.1 | 32 +++--- tmux.h | 2 + 7 files changed, 206 insertions(+), 269 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index f693872c..b6d5d624 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -65,10 +65,9 @@ cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) } if (tty_set_size(&c->tty, w, h)) recalculate_sizes(); - } else if (args_has(args, 'S')) { - status_update_jobs(c); + } else if (args_has(args, 'S')) server_status_client(c); - } else + else server_redraw_client(c); return (CMD_RETURN_NORMAL); diff --git a/format.c b/format.c index eff1668d..4fe1e924 100644 --- a/format.c +++ b/format.c @@ -35,6 +35,9 @@ * string. */ +void format_job_callback(struct job *); +const char *format_job_get(struct format_tree *, const char *); + int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); char *format_time_string(time_t); @@ -46,6 +49,32 @@ void format_defaults_client(struct format_tree *, struct client *); void format_defaults_winlink(struct format_tree *, struct session *, struct winlink *); +/* Entry in format job tree. */ +struct format_job { + const char *cmd; + + time_t last; + char *out; + + struct job *job; + int status; + + RB_ENTRY(format_job) entry; +}; + +/* Format job tree. */ +int format_job_cmp(struct format_job *, struct format_job *); +RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); +RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp); +RB_GENERATE(format_job_tree, format_job, entry, format_job_cmp); + +/* Format job tree comparison function. */ +int +format_job_cmp(struct format_job *fj1, struct format_job *fj2) +{ + return (strcmp(fj1->cmd, fj2->cmd)); +} + /* Entry in format tree. */ struct format_entry { char *key; @@ -54,22 +83,22 @@ struct format_entry { RB_ENTRY(format_entry) entry; }; -/* Tree of format entries. */ +/* Format entry tree. */ struct format_tree { struct window *w; struct session *s; - RB_HEAD(format_rb_tree, format_entry) tree; + int status; + + RB_HEAD(format_entry_tree, format_entry) tree; }; +int format_entry_cmp(struct format_entry *, struct format_entry *); +RB_PROTOTYPE(format_entry_tree, format_entry, entry, format_entry_cmp); +RB_GENERATE(format_entry_tree, format_entry, entry, format_entry_cmp); -/* Format key-value replacement entry. */ -int format_cmp(struct format_entry *, struct format_entry *); -RB_PROTOTYPE(format_rb_tree, format_entry, entry, format_cmp); -RB_GENERATE(format_rb_tree, format_entry, entry, format_cmp); - -/* Format tree comparison function. */ +/* Format entry tree comparison function. */ int -format_cmp(struct format_entry *fe1, struct format_entry *fe2) +format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2) { return (strcmp(fe1->key, fe2->key)); } @@ -134,15 +163,118 @@ const char *format_lower[] = { NULL /* z */ }; +/* Format job callback. */ +void +format_job_callback(struct job *job) +{ + struct format_job *fj = job->data; + char *line, *buf; + size_t len; + struct client *c; + + fj->job = NULL; + free(fj->out); + + if (WIFEXITED(job->status) && WEXITSTATUS(job->status) != 0) { + xasprintf(&fj->out, "<'%s' exited with %d>", fj->cmd, + WEXITSTATUS(job->status)); + return; + } + if (WIFSIGNALED(job->status)) { + xasprintf(&fj->out, "<'%s' got signal %d>", fj->cmd, + WTERMSIG(job->status)); + return; + } + + buf = NULL; + if ((line = evbuffer_readline(job->event->input)) == NULL) { + len = EVBUFFER_LENGTH(job->event->input); + buf = xmalloc(len + 1); + if (len != 0) + memcpy(buf, EVBUFFER_DATA(job->event->input), len); + buf[len] = '\0'; + } else + buf = line; + fj->out = buf; + + if (fj->status) { + TAILQ_FOREACH(c, &clients, entry) + server_status_client(c); + fj->status = 0; + } +} + +/* Find a job. */ +const char * +format_job_get(struct format_tree *ft, const char *cmd) +{ + struct format_job fj0, *fj; + + fj0.cmd = cmd; + if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) + { + fj = xcalloc(1, sizeof *fj); + fj->cmd = xstrdup(cmd); + fj->status = ft->status; + + xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); + + RB_INSERT(format_job_tree, &format_jobs, fj); + } + + if (fj->job == NULL && fj->last != time(NULL)) { + fj->job = job_run(fj->cmd, NULL, -1, format_job_callback, + NULL, fj); + if (fj->job == NULL) { + free(fj->out); + xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); + } + } + fj->last = time(NULL); + + return (fj->out); +} + +/* Remove old jobs. */ +void +format_clean(void) +{ + struct format_job *fj, *fj1; + time_t now; + + now = time(NULL); + RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) { + if (fj->last > now || now - fj->last < 3600) + continue; + RB_REMOVE(format_job_tree, &format_jobs, fj); + + if (fj->job != NULL) + job_free(fj->job); + + free((void*)fj->cmd); + free(fj->out); + + free(fj); + } +} + /* Create a new tree. */ struct format_tree * format_create(void) +{ + return (format_create_status(0)); +} + +/* Create a new tree for the status line. */ +struct format_tree * +format_create_status(int status) { struct format_tree *ft; - char host[HOST_NAME_MAX+1], *ptr; + char host[HOST_NAME_MAX + 1], *ptr; ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); + ft->status = status; if (gethostname(host, sizeof host) == 0) { format_add(ft, "host", "%s", host); @@ -160,8 +292,8 @@ format_free(struct format_tree *ft) { struct format_entry *fe, *fe1; - RB_FOREACH_SAFE(fe, format_rb_tree, &ft->tree, fe1) { - RB_REMOVE(format_rb_tree, &ft->tree, fe); + RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { + RB_REMOVE(format_entry_tree, &ft->tree, fe); free(fe->value); free(fe->key); free(fe); @@ -185,7 +317,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) xvasprintf(&fe->value, fmt, ap); va_end(ap); - fe_now = RB_INSERT(format_rb_tree, &ft->tree, fe); + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe_now->value); fe_now->value = fe->value; @@ -224,7 +356,7 @@ format_find(struct format_tree *ft, const char *key) } fe_find.key = (char *) key; - fe = RB_FIND(format_rb_tree, &ft->tree, &fe_find); + fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe == NULL) return (NULL); return (fe->value); @@ -358,9 +490,9 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf; + char *buf, *tmp; const char *ptr, *s; - size_t off, len, n; + size_t off, len, n, slen; int ch, brackets; if (fmt == NULL) @@ -383,6 +515,34 @@ format_expand(struct format_tree *ft, const char *fmt) ch = (u_char) *fmt++; switch (ch) { + case '(': + brackets = 1; + for (ptr = fmt; *ptr != '\0'; ptr++) { + if (*ptr == '(') + brackets++; + if (*ptr == ')' && --brackets == 0) + break; + } + if (*ptr != ')' || brackets != 0) + break; + n = ptr - fmt; + + tmp = xmalloc(n + 1); + memcpy(tmp, fmt, n); + tmp[n] = '\0'; + + s = format_job_get(ft, tmp); + slen = strlen(s); + + while (len - off < slen + 1) { + buf = xreallocarray(buf, 2, len); + len *= 2; + } + memcpy(buf + off, s, slen); + off += slen; + + fmt += n + 1; + continue; case '{': brackets = 1; for (ptr = fmt; *ptr != '\0'; ptr++) { diff --git a/server-client.c b/server-client.c index dd6eda1d..36408e91 100644 --- a/server-client.c +++ b/server-client.c @@ -157,8 +157,6 @@ server_client_lost(struct client *c) if (c->stderr_data != c->stdout_data) evbuffer_free(c->stderr_data); - status_free_jobs(&c->status_new); - status_free_jobs(&c->status_old); screen_free(&c->status); free(c->title); @@ -269,10 +267,8 @@ server_client_status_timer(void) interval = options_get_number(&s->options, "status-interval"); difference = tv.tv_sec - c->status_timer.tv_sec; - if (interval != 0 && difference >= interval) { - status_update_jobs(c); + if (interval != 0 && difference >= interval) c->flags |= CLIENT_STATUS; - } } } diff --git a/server.c b/server.c index ba161c4b..a5cd50a7 100644 --- a/server.c +++ b/server.c @@ -485,6 +485,8 @@ server_second_callback(unused int fd, unused short events, unused void *arg) server_client_status_timer(); + format_clean(); + evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/status.c b/status.c index 9bd5111c..0ffc0427 100644 --- a/status.c +++ b/status.c @@ -33,13 +33,10 @@ char *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *, size_t *); char *status_redraw_get_right(struct client *, time_t, int, struct grid_cell *, size_t *); -char *status_find_job(struct client *, char **); -void status_job_free(void *); -void status_job_callback(struct job *); char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); -void status_replace1(struct client *, char **, char **, char *, size_t); +void status_replace1(char **, char **, char *, size_t); void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); @@ -368,246 +365,25 @@ out: return (1); } -/* Replace a single special sequence (prefixed by #). */ -void -status_replace1(struct client *c, char **iptr, char **optr, char *out, - size_t outsize) -{ - char ch, tmp[256], *ptr, *endptr; - size_t ptrlen; - long limit; - - errno = 0; - limit = strtol(*iptr, &endptr, 10); - if ((limit == 0 && errno != EINVAL) || - (limit == LONG_MIN && errno != ERANGE) || - (limit == LONG_MAX && errno != ERANGE) || - limit != 0) - *iptr = endptr; - if (limit <= 0) - limit = LONG_MAX; - - switch (*(*iptr)++) { - case '(': - if ((ptr = status_find_job(c, iptr)) == NULL) - return; - goto do_replace; - case '[': - /* - * Embedded style, handled at display time. Leave present and - * skip input until ]. - */ - ch = ']'; - goto skip_to; - case '{': - ptr = (char *) "#{"; - goto do_replace; - default: - xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1)); - ptr = tmp; - goto do_replace; - } - - return; - -do_replace: - ptrlen = strlen(ptr); - if ((size_t) limit < ptrlen) - ptrlen = limit; - - if (*optr + ptrlen >= out + outsize - 1) - return; - while (ptrlen > 0 && *ptr != '\0') { - *(*optr)++ = *ptr++; - ptrlen--; - } - - return; - -skip_to: - *(*optr)++ = '#'; - - (*iptr)--; /* include ch */ - while (**iptr != ch && **iptr != '\0') { - if (*optr >= out + outsize - 1) - break; - *(*optr)++ = *(*iptr)++; - } -} - /* Replace special sequences in fmt. */ char * status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) { - static char out[BUFSIZ]; - char in[BUFSIZ], ch, *iptr, *optr, *expanded; - size_t len; struct format_tree *ft; + char *expanded; if (fmt == NULL) return (xstrdup("")); - len = strftime(in, sizeof in, fmt, localtime(&t)); - in[len] = '\0'; - - iptr = in; - optr = out; - - while (*iptr != '\0') { - if (optr >= out + (sizeof out) - 1) - break; - ch = *iptr++; - - if (ch != '#' || *iptr == '\0') { - *optr++ = ch; - continue; - } - status_replace1(c, &iptr, &optr, out, sizeof out); - } - *optr = '\0'; - - ft = format_create(); + ft = format_create_status(1); format_defaults(ft, c, NULL, wl, NULL); - expanded = format_expand(ft, out); + + expanded = format_expand_time(ft, fmt, t); + format_free(ft); return (expanded); } -/* Figure out job name and get its result, starting it off if necessary. */ -char * -status_find_job(struct client *c, char **iptr) -{ - struct status_out *so, so_find; - char *cmd; - int lastesc; - size_t len; - - if (**iptr == '\0') - return (NULL); - if (**iptr == ')') { /* no command given */ - (*iptr)++; - return (NULL); - } - - cmd = xmalloc(strlen(*iptr) + 1); - len = 0; - - lastesc = 0; - for (; **iptr != '\0'; (*iptr)++) { - if (!lastesc && **iptr == ')') - break; /* unescaped ) is the end */ - if (!lastesc && **iptr == '\\') { - lastesc = 1; - continue; /* skip \ if not escaped */ - } - lastesc = 0; - cmd[len++] = **iptr; - } - if (**iptr == '\0') /* no terminating ) */ { - free(cmd); - return (NULL); - } - (*iptr)++; /* skip final ) */ - cmd[len] = '\0'; - - /* First try in the new tree. */ - so_find.cmd = cmd; - so = RB_FIND(status_out_tree, &c->status_new, &so_find); - if (so != NULL && so->out != NULL) { - free(cmd); - return (so->out); - } - - /* If not found at all, start the job and add to the tree. */ - if (so == NULL) { - job_run(cmd, NULL, -1, status_job_callback, status_job_free, c); - c->references++; - - so = xmalloc(sizeof *so); - so->cmd = xstrdup(cmd); - so->out = NULL; - RB_INSERT(status_out_tree, &c->status_new, so); - } - - /* Lookup in the old tree. */ - so_find.cmd = cmd; - so = RB_FIND(status_out_tree, &c->status_old, &so_find); - free(cmd); - if (so != NULL) - return (so->out); - return (NULL); -} - -/* Free job tree. */ -void -status_free_jobs(struct status_out_tree *sotree) -{ - struct status_out *so, *so_next; - - so_next = RB_MIN(status_out_tree, sotree); - while (so_next != NULL) { - so = so_next; - so_next = RB_NEXT(status_out_tree, sotree, so); - - RB_REMOVE(status_out_tree, sotree, so); - free(so->out); - free(so->cmd); - free(so); - } -} - -/* Update jobs on status interval. */ -void -status_update_jobs(struct client *c) -{ - /* Free the old tree. */ - status_free_jobs(&c->status_old); - - /* Move the new to old. */ - memcpy(&c->status_old, &c->status_new, sizeof c->status_old); - RB_INIT(&c->status_new); -} - -/* Free status job. */ -void -status_job_free(void *data) -{ - struct client *c = data; - - c->references--; -} - -/* Job has finished: save its result. */ -void -status_job_callback(struct job *job) -{ - struct client *c = job->data; - struct status_out *so, so_find; - char *line, *buf; - size_t len; - - if (c->flags & CLIENT_DEAD) - return; - - so_find.cmd = job->cmd; - so = RB_FIND(status_out_tree, &c->status_new, &so_find); - if (so == NULL || so->out != NULL) - return; - - buf = NULL; - if ((line = evbuffer_readline(job->event->input)) == NULL) { - len = EVBUFFER_LENGTH(job->event->input); - buf = xmalloc(len + 1); - if (len != 0) - memcpy(buf, EVBUFFER_DATA(job->event->input), len); - buf[len] = '\0'; - } else - buf = line; - - so->out = buf; - server_status_client(c); -} - /* Return winlink status line entry and adjust gc as necessary. */ char * status_print(struct client *c, struct winlink *wl, time_t t, diff --git a/tmux.1 b/tmux.1 index f5a0aaa3..1233d1be 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2662,25 +2662,10 @@ will be expanded. It may also contain any of the following special character sequences: .Bl -column "Character pair" "Replaced with" -offset indent .It Sy "Character pair" Ta Sy "Replaced with" -.It Li "#(shell-command)" Ta "First line of the command's output" .It Li "#[attributes]" Ta "Colour or attribute change" .It Li "##" Ta "A literal" Ql # .El .Pp -The #(shell-command) form executes -.Ql shell-command -and inserts the first line of its output. -Note that shell commands are only executed once at the interval specified by -the -.Ic status-interval -option: if the status line is redrawn in the meantime, the previous result is -used. -Shell commands are executed with the -.Nm -global environment set (see the -.Sx ENVIRONMENT -section). -.Pp For details on how the names and titles can be set see the .Sx "NAMES AND TITLES" section. @@ -3245,6 +3230,23 @@ a number and a colon, so .Ql #{=10:pane_title} will include at most the first 10 characters of the pane title. .Pp +In addition, the first line of a shell command's output may be inserted using +.Ql #() . +For example, +.Ql #(uptime) +will insert the system's uptime. +When constructing formats, +.Nm +does not wait for +.Ql #() +commands to finish; instead, the previous result from running the same command is used, +or a placeholder if the command has not been run before. +Commands are executed with the +.Nm +global environment set (see the +.Sx ENVIRONMENT +section). +.Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" diff --git a/tmux.h b/tmux.h index bc47a268..78e64170 100644 --- a/tmux.h +++ b/tmux.h @@ -1475,7 +1475,9 @@ void cfg_show_causes(struct session *); /* format.c */ struct format_tree; +void format_clean(void); struct format_tree *format_create(void); +struct format_tree *format_create_status(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); From a55e569af513deb99258354822ee75cbdce83619 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 May 2015 23:02:27 +0000 Subject: [PATCH 597/949] Use RB_MIN to get the lowest index for the current window when creating grouped sessions, rather than using RB_ROOT. --- cmd-new-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 199e82c2..9c767489 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -254,7 +254,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (groupwith != NULL) { session_group_add(groupwith, s); session_group_synchronize_to(s); - session_select(s, RB_ROOT(&s->windows)->idx); + session_select(s, RB_MIN(winlinks, &s->windows)->idx); } /* From 74c755f2aba6402f9ec51fc6b4b61560884af46d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 May 2015 23:12:38 +0000 Subject: [PATCH 598/949] Expand formats again inside #(), and free the temporaries. --- format.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 4fe1e924..a1165de6 100644 --- a/format.c +++ b/format.c @@ -490,7 +490,7 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *tmp; + char *buf, *tmp, *cmd; const char *ptr, *s; size_t off, len, n, slen; int ch, brackets; @@ -530,10 +530,14 @@ format_expand(struct format_tree *ft, const char *fmt) tmp = xmalloc(n + 1); memcpy(tmp, fmt, n); tmp[n] = '\0'; + cmd = format_expand(ft, tmp); - s = format_job_get(ft, tmp); + s = format_job_get(ft, cmd); slen = strlen(s); + free(cmd); + free(tmp); + while (len - off < slen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; From 2a8c2648f0375994715b2fea9de144b15c9588a2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 May 2015 23:26:52 +0000 Subject: [PATCH 599/949] Don't use special strings if #() commands fail, just remove the format (as if the command produced nothing). Makes constructions that can fail like '#(test whatever && echo foo)' work as they did before. --- format.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/format.c b/format.c index a1165de6..72d6bfc9 100644 --- a/format.c +++ b/format.c @@ -175,17 +175,6 @@ format_job_callback(struct job *job) fj->job = NULL; free(fj->out); - if (WIFEXITED(job->status) && WEXITSTATUS(job->status) != 0) { - xasprintf(&fj->out, "<'%s' exited with %d>", fj->cmd, - WEXITSTATUS(job->status)); - return; - } - if (WIFSIGNALED(job->status)) { - xasprintf(&fj->out, "<'%s' got signal %d>", fj->cmd, - WTERMSIG(job->status)); - return; - } - buf = NULL; if ((line = evbuffer_readline(job->event->input)) == NULL) { len = EVBUFFER_LENGTH(job->event->input); From 7e067cb9dc122ac3296af116c7028d7aa5f4322e Mon Sep 17 00:00:00 2001 From: deraadt Date: Sun, 31 May 2015 23:27:06 +0000 Subject: [PATCH 600/949] does not need syslog.h --- server.c | 1 - 1 file changed, 1 deletion(-) diff --git a/server.c b/server.c index a5cd50a7..6438fe6d 100644 --- a/server.c +++ b/server.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include From 58b50fb543168128f1eef30ad9908cf27ee91f41 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2015 09:20:19 +0000 Subject: [PATCH 601/949] Clear signal handlers before event_reinit as apparently it can otherwise cause libevent to go strange. --- server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.c b/server.c index 6438fe6d..b53648f0 100644 --- a/server.c +++ b/server.c @@ -129,9 +129,9 @@ server_start(int lockfd, char *lockfile) fatal("daemon failed"); /* event_init() was called in our parent, need to reinit. */ + clear_signals(0); if (event_reinit(ev_base) != 0) fatal("event_reinit failed"); - clear_signals(0); logfile("server"); log_debug("server started, pid %ld", (long) getpid()); From a3c617249527d96fa8c12e7470ebebec58fde00e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2015 13:59:57 +0000 Subject: [PATCH 602/949] Missing t at end of response, from Vincent Bernat. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 886d561d..6be5d906 100644 --- a/input.c +++ b/input.c @@ -1599,7 +1599,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) return; break; case 18: - input_reply(ictx, "\033[8;%u;%u", wp->sy, wp->sx); + input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); From 3821ca4917e9f508ec09a24f43dfc9c29066fa20 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Jun 2015 15:16:13 +0100 Subject: [PATCH 603/949] Update TODO. --- TODO | 42 ++---------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/TODO b/TODO index 34b6cfe3..93d11f4f 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ * allow multiple targets: fnmatch for -t/-c, for example detach all clients with -t* * add -c for new-session like new-window - * attach should take a pane and select it as well as attaching * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions @@ -19,10 +18,9 @@ * way to set socket path from config file - format improvements: - * last bits of status_replace into formats? * option to quote format (#{session_name:quoted}) * formats need conditions for >0 (for #P) - * some way to pad # stuff with spaces, #!2T maybe + * some way to pad # stuff with spaces * last window update time and format for it * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple @@ -43,36 +41,14 @@ - improve mouse support: * bind commands to mouse in different areas? - * more fine-grained options? * commands executed when clicking on a pattern (URL) - * mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets - the flag on w/o checking the others before calling tty_update_mode) - * mouse can be like normal key bindings? - - {button-{1,2,3},wheel-{up,down}}-{status,pane,border} and - drag-{start,end}-{status,pane,border} plus the modifiers - - resize and copy can be special cases - once you call something - like copy-mode -M or resize-pane -M to start the drag, it tracks - mouse until you call -m to stop the drag. or just keep drags - entirely special? - - what happens with stuff that wants mouse inside? especially for - pane clicks which need to run command AND pass event through - (like mouse-select-pane). maybe just a flag to say whether it - always runs or only if pane hasn't taken mouse? or it could be - eg bind Button1Pane "select-pane -t=; send-keys -Mt=' - - also need a) some way to tell commands bound to key which - window or pane the mouse key binding applies to (maybe a new - special char in target, or pass targets through formats?) b) a - way to bind repeat count to mode keys so that wheel up/down can - do multiple lines c) send-keys -M to pass a mouse event through? - - what does the mouse->KEYC_* conversion and find-the-pane bit? - server_client_handle_key? - hooks! - warts on current naming: * display-time but message-fg/bg/attr * list-* vs show-* - * split-window -> split-pane?? + * split-window -> split-pane? - better UTF-8 support: * window names and titles @@ -82,7 +58,6 @@ - copy/paste improvements: * incremental searching - * append to buffer * paste w/o trailing whitespace * command to toggle selection not to move it in copy-mode * regex searching @@ -107,8 +82,6 @@ - terminfo bits * use a better termcap internally instead of screen, perhaps xterm * use screen-256color when started on 256 colour terminal? - * need a tmux terminfo entry to document the extensions we are using in - upstream terminfo - code cleanup * instead of separate window and session options, just one master @@ -134,12 +107,6 @@ tell when in the config file - then we use cmdq->c if we need a client w/o a session else cmd_current_client * optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx - * cmd_find_* could be much simpler - parse everything the same, only - difference is what to choose when not given a ":" or "." (such as a - plain "0" could be session, window or pane). So just cmd_find_target - with a type (session, window, or pane) - * instead of all the obscure =, ^ -t codes, we should support something - simpler and easier like -t:mouse:, -t:first: - miscellaneous * way to keep a job running just read its last line of output for #() @@ -151,9 +118,4 @@ jobs and run-shell and lock command all start with slightly different environments * multiline status line? - * bind commands to key sequences -- make it so ALL keys go through a - table, first an implicit table in which C-b is the only default - binding to a command that says "next key from $othertable" and so - on. means -n can go away as well * customizable command aliases - * any remaining clients in wait-for should be woken when server exits From 1c3e1bae41bcdc0d5c590bb2f95b987a42254de7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:26:25 +0100 Subject: [PATCH 604/949] Remove $Id$. --- autogen.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 300b54db..cbf6df1f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,5 +1,4 @@ #!/bin/sh -# $Id$ if [ "x$(uname)" = "xOpenBSD" ]; then [ -z "$AUTOMAKE_VERSION" ] && export AUTOMAKE_VERSION=1.10 From 09bcbc57da3dde96830d62d64866657a950227f7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:28:26 +0100 Subject: [PATCH 605/949] $Id$ -> $OpenBSD$. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index be79e273..7b538ddb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $Id$ +.\" $OpenBSD$ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" From 11ae6d16e534add51c28a665e42196b694e92515 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:35:44 +0100 Subject: [PATCH 606/949] $Id$ -> $OpenBSD$. --- array.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/array.h b/array.h index 2c70c310..671bea42 100644 --- a/array.h +++ b/array.h @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2006 Nicholas Marriott From dfd72f525009b5140c3ac41fd011787c98ebea08 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:42:36 +0100 Subject: [PATCH 607/949] -$Id$. --- compat.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/compat.h b/compat.h index 5f3ff8fc..2c6f1c67 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2007 Nicholas Marriott * From fc2fb0eb9504ecfbaa162ffa6db173be38031363 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:57:35 +0100 Subject: [PATCH 608/949] Update mailing list addresses. --- FAQ | 6 +++--- README | 19 ++++++++++--------- README.md | 27 +++++++++++++++------------ www/index.html.in | 4 ++-- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/FAQ b/FAQ index 2ca0a596..5913ba59 100644 --- a/FAQ +++ b/FAQ @@ -108,8 +108,8 @@ Check the latest version of tmux from Git to see if the problem is still reproducible. Sometimes the length of time between releases means a lot of fixes can be sitting in Git and the problem might already be fixed. -Please send bug reports by email to nicm@users.sourceforge.net or -tmux-users@lists.sourceforge.net. Please include as much of the following +Please send bug reports by email to nicholas.marriott@gmail.com or +tmux-users@googlegroups.com. Please include as much of the following information as possible: - the version of tmux you are running; @@ -123,7 +123,7 @@ information as possible: * Why doesn't tmux do $x? -Please send feature requests by email to nicm@users.sourceforge.net. +Please send feature requests by email to tmux-users@googlegroups.com. * Why do you use the screen terminal description inside tmux? It sucks. diff --git a/README b/README index fad67b71..a5898a65 100644 --- a/README +++ b/README @@ -4,8 +4,7 @@ tmux is a "terminal multiplexer", it enables a number of terminals (or windows) to be accessed and controlled from a single terminal. tmux is intended to be a simple, modern, BSD-licensed alternative to programs such as GNU screen. -This release runs on OpenBSD, FreeBSD, NetBSD, Linux and OS X and may still -run on Solaris and AIX (although they haven't been tested in a while). +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. tmux depends on libevent 2.x. Download it from: @@ -25,7 +24,7 @@ To get and build the latest from version control: For more information see https://sourceforge.net/scm/?type=git&group_id=200378 and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@lists.sourceforge.net. +tmux-users@googlegroups.com. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: @@ -41,20 +40,22 @@ directory. For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. -tmux mailing lists are available. Visit: +tmux mailing lists are available. For general discussion and bug reports: - https://sourceforge.net/mail/?group_id=200378 + https://groups.google.com/forum/#!forum/tmux-users + +And for Git commit emails: + + https://groups.google.com/forum/#!forum/tmux-git Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: - tmux-users@lists.sourceforge.net + tmux-users@googlegroups.com This file and the CHANGES, FAQ and TODO files are licensed under the ISC license. Files under examples/ remain copyright their authors unless otherwise stated in the file but permission has been received to distribute them with tmux. All other files have a license and copyright notice at their start. --- Nicholas Marriott - -$Id$ +-- Nicholas Marriott diff --git a/README.md b/README.md index d3b53829..eab63eaf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ Welcome to tmux! -================ [![Build Status](https://travis-ci.org/ThomasAdam/tmux.svg?branch=master)](https://travis-ci.org/ThomasAdam/tmux) @@ -7,8 +6,7 @@ tmux is a "terminal multiplexer", it enables a number of terminals (or windows) to be accessed and controlled from a single terminal. tmux is intended to be a simple, modern, BSD-licensed alternative to programs such as GNU screen. -This release runs on FreeBSD, NetBSD, Linux and OS X and may still run on -Solaris and AIX. +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. tmux depends on libevent 2.x. Download it from: @@ -28,7 +26,7 @@ To get and build the latest from version control: For more information see https://sourceforge.net/scm/?type=git&group_id=200378 and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@lists.sourceforge.net. +tmux-users@googlegroups.com. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: @@ -44,17 +42,22 @@ directory. For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. -tmux mailing lists are available. Visit: +tmux mailing lists are available. For general discussion and bug reports: - https://sourceforge.net/mail/?group_id=200378 + https://groups.google.com/forum/#!forum/tmux-users + +And for Git commit emails: + + https://groups.google.com/forum/#!forum/tmux-git Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: - tmux-users@lists.sourceforge.net + tmux-users@googlegroups.com -This file and the CHANGES, FAQ, TODO, and SYNCING files are licensed under -the ISC license. Files under examples/ remain copyright their authors unless -otherwise stated in the file but permission has been received to distribute -them with tmux. All other files have a license and copyright notice at their -start. +This file and the CHANGES, FAQ and TODO files are licensed under the ISC +license. Files under examples/ remain copyright their authors unless otherwise +stated in the file but permission has been received to distribute them with +tmux. All other files have a license and copyright notice at their start. + +-- Nicholas Marriott diff --git a/www/index.html.in b/www/index.html.in index 72f5f12e..771e01aa 100644 --- a/www/index.html.in +++ b/www/index.html.in @@ -17,7 +17,7 @@
  • FAQ
  • Source Code
  • SourceForge Page
  • -
  • Mailing List
  • +
  • Mailing List
  • IRC Channel
  • @@ -44,7 +44,7 @@ and ncurses.

    For support contact the -tmux-users@lists.sf.net +tmux-users@googlegroups.com mailing list or IRC channel #tmux on freenode.

    From 89131c3e907e14eb3c3d6c34291bb289a16c6f1c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 00:38:01 +0100 Subject: [PATCH 609/949] No $Id$. --- CHANGES | 2 -- FAQ | 2 -- 2 files changed, 4 deletions(-) diff --git a/CHANGES b/CHANGES index 6e0d8f80..cf695936 100644 --- a/CHANGES +++ b/CHANGES @@ -1924,5 +1924,3 @@ The list of older changes is below. emacs) that don't require scrolling regions (ESC[r) mostly work fine (including mutt, emacs). No status bar yet and no key remapping or other customisation. - -$Id$ diff --git a/FAQ b/FAQ index 5913ba59..c7d3d15a 100644 --- a/FAQ +++ b/FAQ @@ -461,5 +461,3 @@ Or from inside tmux by detaching individual clients with C-b D or all using: C-b : attach -d - -$Id$ From d2b35e19cdd61d163d26c4babccc1550e72a9623 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 00:44:22 +0100 Subject: [PATCH 610/949] No more SF. --- www/index.html.in | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/www/index.html.in b/www/index.html.in index 771e01aa..78b48b6c 100644 --- a/www/index.html.in +++ b/www/index.html.in @@ -11,12 +11,11 @@
    tmux @@ -31,13 +30,13 @@ background) and reattach them to a different terminal. And do a lot more. See the manual.

    -

    +

    Download tmux %%RELEASE%% -(changelog) or - +(changelog) or + get the development version. tmux is hosted on -SourceForge +GitHub and needs libevent and From 32bc8f4dd4a2cfa814ebf96691f2723d5e43971a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 01:21:41 +0100 Subject: [PATCH 611/949] HTML bits are now elsewhere. --- Makefile.am | 19 ------------- configure.ac | 4 +-- www/favicon.ico | Bin 6518 -> 0 bytes www/images/tmux1.png | Bin 15972 -> 0 bytes www/images/tmux2.png | Bin 108143 -> 0 bytes www/images/tmux3.png | Bin 97986 -> 0 bytes www/images/tmux4.png | Bin 328784 -> 0 bytes www/index.html.in | 64 ------------------------------------------- www/logo.png | Bin 2701 -> 0 bytes www/main.css | 55 ------------------------------------- 10 files changed, 1 insertion(+), 141 deletions(-) delete mode 100644 www/favicon.ico delete mode 100644 www/images/tmux1.png delete mode 100644 www/images/tmux2.png delete mode 100644 www/images/tmux3.png delete mode 100644 www/images/tmux4.png delete mode 100644 www/index.html.in delete mode 100644 www/logo.png delete mode 100644 www/main.css diff --git a/Makefile.am b/Makefile.am index 1b306a01..29294d87 100644 --- a/Makefile.am +++ b/Makefile.am @@ -248,22 +248,3 @@ install-exec-hook: $(mkdir_p) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ $(DESTDIR)$(mandir)/man1/tmux.1 - -# Update SF web site. -upload-index.html: update-index.html - scp www/index.html www/main.css www/images/*.png \ - www/logo.png www/favicon.ico \ - ${USER},tmux@web.sf.net:/home/groups/t/tm/tmux/htdocs - rm -f www/index.html www/images/small-* - -update-index.html: - (cd www/images && \ - rm -f small-* && \ - for i in *.png; do \ - convert "$$i" \ - -resize '150x' -resize 'x100<' \ - -gravity center -crop 150x100+0+0 +repage \ - "small-$$i"; \ - done \ - ) - sed "s/%%RELEASE%%/${RELEASE}/g" www/index.html.in >www/index.html diff --git a/configure.ac b/configure.ac index cccd656b..0dd1151c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,7 @@ # $Id$ -# Miscellaneous autofoo bullshit. +# Miscellaneous bits. AC_INIT(tmux, 2.1) -RELEASE=2.0 -AC_SUBST(RELEASE) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) diff --git a/www/favicon.ico b/www/favicon.ico deleted file mode 100644 index 6e5398a7ab2be493892911b021e80015865f396d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6518 zcmeHLL2BDT6rCEpEZ89=!J@Vxw8Vg&=d3kZBn{zFX1@J5wh(C>_wp= zWD_Ts$fEVCn@}9LrRsl@C%B5nnab75BY6Drpa1@wc^bypj6GmERwyuvCfmw0_JlEJ zn$yp#jJ>4X7UigfDxy3E#wZ_u3a7cZ>z|50jgz(D(dYFL7#lRMoRvag9G?fLaF*Mv z?ORvYQT5wSuljZS&>C&`tWoX2`cmswuOA<8egrT^4s)nMEjYEVb;Z~Cgg@kCXO-VL zD^nDmYrpIVZr*(bVC+!tcAjd%3AvsdaQ|=EPwijc3qP=*+wXvIOu2X+C$8(>1?T*S z?-Sp)cL9u%OR7!E34Xm^-vbumH=E7Zp63OQ<9tWoIOgetOY48%Znp#Uz&hfP@lg+M zqtP%aexUe9>}NJ+#m71qSwHw#=Y9T>ufOAaPm#ndO%zBn8z12;D8&y50>pS?n#sIjDY(zeCQ5MdMf>mPI*uoR{F-IGBAQnK^l?P zMh9zwcBXps_9ON^uO`E}a^E}Pb{UQBd&{5G!s##PY+US7@AO7`Sq|q0i3vJOu MN9}-iVEG*Q1FWUhEdT%j diff --git a/www/images/tmux1.png b/www/images/tmux1.png deleted file mode 100644 index a55f86b2d3c47f09b2321e05793ee155c1ac93fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15972 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

    2&aoSo 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

    PaX+#8Y-*USR}qnrR_!{Yrq0qmk{FfiLls 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

  • 9c;F+UUMvcV^1}e3gGb~i9d(H#m;?}u-E5l^i9`7kYyS?K89*!Lre2*u6D*;J3 z5Gc%fK(*sV)(p0ax+0;(7}nhhr~a*SLfjznExK^4{Heeph%csYBz+3V#OW&Czslol z*%YTW$_eNd7clIFbL@lyGr3zn7y_b1-ZmEKivVl|ccgAVi;%)mWNqhTy)IMqB5W3} z9C0?hKq$S&R66AMtx!|vwi!^$B=B%&60A$)rvfewF@gF7Xy{hI_Yn^wZsOLM=2K20 zz|D;J4}{}$4qJLcKAN!a2eioXoM{&Gn81ha=dn8$y(FW`<&@>*w}?A^Vs#>{{wmXB zIl}b+g7Q=FtSks;8ZjVTaiFSnA%@fcR${HOR>j(VBkU{J7g4*7bOT>cE7cEFR!%6V zUAqbr7ODWfXI5d9?j}0nNKs0{{x>QIdzZ5?-j|6cbOZR<<6&hE;ZL`=Hpb5{-94pE z_bi`!*~>V5QmJakc!2Gcmh*mZ_OwQBQvP!5TX?Wy1=9n()0M@VNI>|1S*4FVl^@8k z>t2o^hG`E@B6MBAV{?-ea}$n{IN3CIyYj2b#xpxp_|5|~n`+mRcaYE&fA&aBSDXtX zZd)o!Yqs?yon)=qAkAMu_(D*%R^-Q0yT*h#Bs<3QID_Bcmox}494_lv*d8lDJFBrU z0cwHlfBLKLY|gv!&%IC>Ck^HxQ5Q&gr1-M9aghLVZeF9c0rspmBkE}Y!{bGf%0C|I zDocC?hIh@0qxBz@<_c&D)Kr{^qu(Vu0{roQokTC!j$4%|g)d7GkK`fPqXQLY*Wb<) z#6YURg9@;jXmInmHI)84$=;1USK$Ri9hi|1jPYGZ()FYNTSaiy?$CTC@e0I$nUDQY zjf4EPL}Zh~V1gIw;F(94GffF2>B?ZAeyy#TZTae*0N~-4GdiRnsQnX4hL;#_wdo^? zEk&$+%L=qw2`EUsjTkB4ruI)RIZ9{8w-Cgqv2=%)&_i^oie$TwQngh_Br82WUP_Ht z4Fj%1jBMs~YYG|0g|gpF7e;}H#O}{3`J@PCUbmd{s6D^u7ENci=%ofw3jUJp!}kCp z$3EeW8hLYXVE0%v8 z9hhD!R=X1yE#(|}^WwvRcXtR_>J2IQtISEawV)YwPEi_AC5-}&(;@E{jJhRicHl}G zZ5O;tdp}DM$UPxhMa1O&*TPfhI{o%#w>Z_LGQ|sv@kUm4cP)H=bII}X=gzgLa}9H@ ztQ<5nCX3og)ux?7r8$4Kl;?gRIQxs_P7RU@J+1YJ1U4#8UdALDEp=dLCRvs8*QY`BK7s)zk`Sxb~wark-W<5xoTZy(h)5n$4tZ8?~%lxzpJ zCR``iXJ|S37;#!G1zV%|1)F;uWcnY>IC7%A>3ASKPWFnPJ$wP;gGp6h|I7K*SHdF-<~Z;-LU@Owa=SDUBctKCg#XNqqZ zO#$=mB@8xx{5xb!H`|Kzta9FL8~!+h_bjRk3J9cE+W{QuV)kz$6f-I?+bmki^S2nnnh5$#w25-;Y>{n zBwqQ^aak4-T>t>G&lb~UA0P5v=#~pjPvtF z(MSwfq%ADWY?jDqDhtq*e_nbTYz){f)p+_%%{4J|Al_fp%niS%Hm6P&we9C}+%R&~ zi*VVuiVEn_xhi{SS$z|#Mq0d7Sua~~woEVTxJIlu*`;I0O z_`s6AbwI%X~rp~~iooeO|?O&xTrB*KnCwO3ueKi8AI-fqwqA11xGD9%X#vD-; z-!5>TK+Sz@q5!OgcV^adr_x1X?@XAiVF0X(udH#7)KJBz1XqYb>$P~TsXoH=t z;A+H0G?X_P>+E((*zzybn5i#F?8cxhbA2hG65ftU)B}UV%D35CmIu? zo=sVDlDb8YP<6X!UPu$cR`Jh@Yrz~=^iB#6vaB)7_ii{@E*CQA}0_+_bX%ic81!w+LR&d5X@f9v*h11^4ZO|e11 z+8vzQt?agFW1lY1VD2{(7(DM4(${<#S7gWA@cnT8TIcMQWvR8)rn~vnazEoFS1D;9 zr&8&K+k@F)g;06%4T0Tk8=ovjP0wHvOEVC6VO)z$1HqHY6V~mMUNo%`eAIasdK+0T zq{=UHtL)!*KYGfg+qtZ4w8FgXV!PYeB)3G>nt;B$uk_!-5S*RXmrrkx6L6WHUywqav8`ghDE(`=uX?w*IJ zo4wplh}})#`;EDC#Afc&$>Mc87MlxP0f>%(Bpb2@^e{8W($0upX9A`*sVR}6fDrLz zn(*dX^O|@C+}@^;N3SE`ApX8eGJ_L>#Cn7S-XfND$vK2`pEy|~pmJ-r>W9#0_9M;& zSd^8wf>Pd9L~th-6=|{KV&lT8lu2waJAmxgt~Ebb!tYYpQz)60ce^JVN%E?ud#euyD>Zvw87=7MdIbkyruVAe z;pBrPiYaELd%0rQtZCbKv5GTVX%6%4Nh@48QT4wdfOXoc2Fq*^((%Rx? zXdg~OM|xi_FVVS;jz+F-SU5C$XOB5uISp+`HUY3m>Q+^{4{xW(sV^7VsLLUn<|3}xKf-HFDqVTX&hp$LOI zTwhZYeOC=L&NmAC`H^00*r^OEJqcc_Bd_3j1df}o@6b$~GDrhH!oQnGj`>T-CVdaS zPeT!Mi*)+cHx-oYB;iA43_-X`N3le<4+Hy#TT65|vPu{~RIO!Ps^@1GL{w>xfUm{* z4E1`(9Zulo^yZg4{Cp?%QN7Hx$G(@$>G@h$4%kM^t_ALi=Vr_YOz4JHf9dn|&lO|9odm^H@D63Z!QIlb8C z+tn026TT!xgD2HVnPN+Z5{xjL&{X#{uT=2m!zxi` z0k2tY&N-G364U|mjir|WQf;80Nv9hr+bcppA4VPZw$({e<2M|24IN9KLqsyBOS5&8 zv>a6$*ai;`$HgyK;q_!6TBXOY#?ji9f-=o~^662vlJt&u(ad;-y!UyLTA$ILf0z2GxIMrRs-&T+W2^qbZ4e~_(}xV2T+<^=pdeS(;Si4NMcNg z#$J9nFqM}OruH7apiYjXz?Q8Xxw5CqIPbWwUM~bFuy+=@5{Vg@+r8AUadXv^4aJhk zP(`7_>iNW+dbdG3lun?l7TZD})P<&Vt!vSHSIjgt>{AlM~Z(2m`kNKq)`tysS{1E<$dtJ&qcC7eLyj{Ul*F1 zdfE&G%zxm|751j4J6BGAL?zTLJ~i7>?-`O8j!e`IFg?hqb!)7NK|nX;t(mJ7*!H7bfS5=CA-f_B} zKY$CKxGCD`?LVrSM#R>0RwJQH^<5ksGb(v=ImJI~!B*iGPg~C+cAzGIrf3WJE~KYh zZpS^pVa=i&^mCt=TLM)0IV}X3kF|-T3JvVdtTbi%j{B0*Wc-)fIfBQQKej2eN@ru= zhg2YQHrl1c*+WG9xMbkyQu)Q0fc1+tOc_E+SiP}X!wF7fdXJpQQM&9w_#B+1zK-R> ziCYcT+!|sU&-m_-X1T^y(r1Ah=OvctIbX^6RbVYd0QG0am;ez3vz5=~r2D?QQk{t3pk;3lX;X&{^;btBlrXHEJ{ z!MZvAQ*^jo*kysVJHRd-zTv_^)sw%(NJQFCRf~+mZ)T2177IjF_X(p{CsEA{7SRoQO!U;@7Btk#S7+X2*2zs zXW(UwQ%fnQl(;%|&Q)D$iyKdc+Np6q0@p$2JoQr!<@%}bzPcPI9$k!`6 z*OMsT=i^EzEbHQA&`0sZYH9J>^8DzX?-sms`{iXDQV51~oq9yZZX*i@drEcjA{GkTiP8zGy~BZY;aR{p@PU%%4dbfhoOC(i{vLSa z?@;SB1Z6fC2z5mYCFt9OGKCXQ_a#lDjtj2(4l0h&w-$syTx`);Dl(?`X0 z2Mr>T=aHXXp%^i@j$&uCV|y8?=-xll-gCsxi#hPbjiQHTQ4VG9;K>=`D<%|kp&=Ji z+xC`N%zkX}N|aj}Eq@g!>pekAeA=td-1lHf;>|INQ41A7N=)g&39hnrJc?J8S(f^x zOarlZbABj4669)hAf2qqkh1oHyE~{skD1=1I8)2Xnb5}q(y+IQO*l2cR4b^h0Iq@H z>wx#ZvSDgGPtT=3l#z$P;`4oAU_8IGibdwi*o(Lqmx2{VS3Pyn;qy&uarVK%n zO4S997P-B-v@YYQ<5k_&R4fA3fqRlhh_Jnoy*eb=gDiVA%xW1 znCv!qpJ~DOadXz3*gco8aAO6cP;*L{I|TY^_7A4)C$y`g6$eEkr(+R|=tNWv#ZzRL z(V51mMhqhZB}=7&7G={~UAYf=dv|rIqM>%%JzJ)-hm4*lrX9FheirdE%^F`A&h@Hp zWEPT~5iGr`zHmJb2~0M3VZVY~N^(>c)|Y$k z8RhPOA*n_*5lEe4c!~ge6m8CZFrJ07Rrr`=i}gfuFMqj-Mn1Hi;Nvm6;X~I2Th%*+ zdTxWpB6F#nI5_5LHgT&sTHjAyuoakVXuRSRT%Q_haMLxzZF$e0KT9jM{}3YUp$P)a z5+M#OJK8%$HCD!s@7JozhYxkrbvj=e131R89A=QqJM)lye4t$UbUCISieo|E)%_DA zq$xlsjiU}f&N1v`{m#<#Nbvb*fR$2R0!NEPWcV9aGyGmbkfQh8z-(^OsV4Z>e-T14 z{H_@I&wEjYx>Zt(8mUOIYw-d3f1nsd*=8KLr*F8pe#{~LBxWHzh#ygoNaJj+Z&hC9 z_HOWRq^^j&65g0xLYtHvI4s@k)-eECYq9uMJ$jcgNsh??=5lgXLPcwm#On-B!%u?0 zU|{f}Zv~TVy;)lGBPfYe;V*fX92p=yib_$}Ez?S$gvp?C5n|+zpy1qY-6KXP4=fp2 zV*FO%@O*Kj@|)&&kbFC))r#ek!uxDPWYr}bM#xsUQD~cG8uQP+ zxDSNnl=;mu$)e6f6UI+DAKB;@ygBRtR|o>!eUg38%5k=Tm>OHBx1QTn^2lbRY) zgA0t7CKC_jM+=dl4VuzrMoI|E@}!hx;5Ce;cXt>@CwVIRO+y=WmUFHRGweKhHD0xz zjmqjul480nC1Qqs${=6B#2kz~skJ^B3=ln=gr=nu{<4ax>LJaBkK6Ci1K#-)Ni938 z?nQ7PB=mL$U#I+MW(`*+SUqi-KHV!(#z0EZ51hB~E_W05)q< zmla32aAJTclA!Hw>=kzvGqw)vkA|;yrjq{lg-=Nwb&nNjL=-b~h48!%0e-<|&homI z=V*4LUwi#u1rXX9M4as6up`8dwMdU}9UWfOZul~E5*Mm2G5!akg9Aqa_}7$WZ0g$4 zN_32V!zJ{;DZD_Y;4bOHL9~G-d^z@GS=qKecXWaV<0=KC5*jV>-eKfuxV$Zv@0Zz( z*|!NEuA#m&RiFI8c(8b3NLh4;bverYTkvq-BDrJ=)8x$Z2z!lK9~1!dGqcMpWqyDL%GhqtkUj1!nb5b@VZ_VfQ@h(BTl*Y4%g zdu}0lMx1iH-$&NMT}%4pjErq;!C~F?B>1U=!2-u2#>x?{kKq50_=STyDgO_|pV!%| z_ZRVZizTZ2+HLw9)#aGv+1H|CH4(6ugvR1%HN>P1yUmA)x4{@S9$asdJ%!8{Jz$T@C+N*NGoSQ}Ty-;|{|sPaZkk~t@n)7!t-tQPeD0Db zB$#kSL3S8v>MxLU1e@J<)wK7@q{^Nk6zeUyuht1#Tn@sr1= z=-5{Ull&qLU4)6p2v)IyLzT)${vQlIBk>PI8y}LrC!xZ^XZ2@Li}N-nQnurbuc#i+ zjJ_QavvItrB)mDXIxh!c7jYjujX(swzTh zDB2ApgAG3=_*hGEV8BXTc&YdFhvMjZHXzb_aX@gr=pRc>uc9Nk2SjU+RTO|}x7~62 zrTB${;|=hFOe_M9Hk9Ytnw~`M*1wpGS0-cGPma8D<`C|m zJ)Y*|M3#m_Cpvx&jB=SwjA|P+Q&D~(Iy<`!Rom;@;A_x~kDQX5wd<%uX_% zv=5|XCshAdFzpE+kV7#erHgFa82j$W-qVMq=jBh?I=D^g{YicCo7*FJESGrynK2rE z=`7=>nP9?C9|K+SH?UO5P? zq!#O>mK^DK%+|B3B4Xc*7v6NeobPA>P8`vFD8uAp7txjJ%ikSfI4Jz7gV0*rt@^0& zDD!X~kC5*Cv?6dBK7~wbeTb8p6ES=0d>LLo1m$8F9mkgGuBQJ=yb57~FltDXt_~?q zHA+gh{si2p+QmDPyqWpkNdi^xc@GeC+_KWIra!)d?N1*+Jy?qxWX{yS`|Ne~b=-kJ z>2vx-FVM^T$+mr&Zd5WhOvAV?RAWO1 zd5CYn_K*2j|uc z8~1-MpU_VHstzSagUS#;##@92XMb>N<8L&EJMw=6HA&-(VPh&IwiBP>#;1b&uS)-` zvuponf^Fk_rB`pS#nBZVT>f! zVo#gXjBP3n!!Win+urf`3*P&4f9~J!`?{~s_qsmU^}W6)u!gt1iJt?)<*+A4%~};> ze%KByf2Bp<*l24|HwXkFl78hou9}+aHi!27b<_5cT8FQc=gQ-Ojgmxj!3QN-h3p5B zPj&ZYKytgFVn5Y^@+EnLobo(Zq&twP{9{u2q#-)}6o!Oy^C9nqT2;RuTomSqUTB7o zR(YrR>B&Fc!ok+s+$Mc<3F54F`faH37w(fch1Gqqolcm>{vvRL9&!m^zQ{cX3i>s?Of1vZG@@=LU~LUT{{$)6zRD^w`LsUrx7T?v=N=O+Bss9j%~@f`Fn0CtPdq zgJNTsl^ix&*b)*9AZ+0@XQ3WyJv^&Kr({o9&hrhS1%f!GzQi3?g|x4YmQ|F~TUZ*T zExQ%Om(XAPtjTy$6#cV5l7gc&4t5rSjlDW>?E62X{AOgE&Z!2or^?PUkq(}ePd!1_ z&yHT1+l>6KleroMw59aIb{HgklTlYsj;R#X({cuTSYtEBijrOIHAUgL)H&4@*v$MW z`C{yZ!SG&ybSX0;^^(_HC5UiTfx%k^hj>5Bl=_^Ws5*wo~V--N2IMT<%KxfA$xnt}!|=XWlBvYFHY zTGY{ZRxF|AYU>Ev%vjcfZ%grg1zkrwXCHCFys6TzNO&X9e}d)NH~sm+2@i^`a#=Aa zK0H>7l!+M3kde^Tkg~O2`>F@a+rPDKylwl{U&ZQExQ!DeI1&kg3OF+r&4yE`EYJ+u zyMt>H$7Qlukfn*py)y@3@{5Z&5<1@brfXSZkF&E|#+OmJ-ohDcJC|U{^l}K~6Oozx zB&29k{ULlZTsDUM0Csu|W=cwsGO1ApQ(N5*RJH&=)Zc(tJPr~|xptUSKDD8B3ywsr z4X-RPZVU%WcDdH#j=hZCF15rBZ8R(qXM51a_7^-xgLqnCOHta@e(%Yml6$Tg)YuGpH@nMP>Xu-I^s{ zgJi;E-t35&0bxBcVk)|iuBn_f70Jdz5?@ayR=Vzqx>FaR%S=l8GF-i}Hw{XwgSuN` zX!mLiD`M7#{JQ5WTZ;w1%sess~ zhegnAda3;W#>N%*XHQ}+b(pXpGdC5b^e)x~2zjt6&z=KsV#cZ#Ls9b75o$q@(5ms| z{78;or32q;ekOgM~^sTXY#4@BaGWvwIhhd46n>PE(WlvEFBLW>4DRg%0S(rp4m& zsmq2Pj$u3uy~t-B32KFN=2Dql=W$?TqVzQa1g`cyqogJ9<0`5CgR*R#N)b)KT7D)Y zAzH%jj=2yLhck!^7OdHP)9@wIYdMGRkSCU!U$d`DFi!bDSGjGp-F&-Yqt2dL~R`>hOF< zIS&13vANkWAOP7bkE@5d4SdV&-$Z%%nBMIP*G6q>d9wE*@ca5TBR(?aZ2X4t_CV+Y z+IJ8ZuV|NHlo*y4N`zWb9vVr@`gi6B4W+B+5Rd!9?I6k;bY z7FOF2DJsve^>@MLK_0P^fbTh~^+7e}Q84h@>YX666vkzUMFRj3^J=i`?NwgU8)6<& zb36kMUl!5j>nJBX>P72vViWpxst!9h4O@t@WTYrrGLX(dC@$<+OH?y4<(}PixZf2o z)Rp^p`|Az@bT9Ju+lDQ=I%SKY@@&=3$=i*JB$wc~=uEXdOc03AcgGp zHcc<|z%a!`S)`JVw=E6x8_Ocf)P*>#4-nx<7iLD0=Gl?*WEVXMuGz9f8}+^w{j)B7 z;qA&yUrL1P85;&fLl~_L`hN;l;Kh(-P%$n7&(^JjymMTEc&{cmNif%=t0~W}y_^=2 zGM&d0^Ksxf8Mm38dMRN=_?Q3`j+Tw)^twfU|LAlT5R6)~cPXv3oLV2LOs&Zzt0A&q zCnE(p0WnG&LnphbI6cMBmUhn0>p5_z$)UO_|n z-12?!S8a<8wsZCfiXs*U-K4fWH?$*VTG#;o@$ba>4cNpaP}+YELP?Eyk-+TYB~_sU z24V#!eL><}|JIm9{v7{kg|x>G$5Ytepi%8d()0klUBoZwxBl^W3Xi;DLMNR!#LDmA zG*t2u6}4zIP0 zDZ#Z+F_O5YEAIaoi2{_tvH5z_AJ!KV0nx>Sd0fSZ=Tqi#`3G+(hnL>U!+bZ%-SU5n z6%9HeYxQjx_t{494~4Y)c)C&^3;jt6bV;FfYBwIKLcI!cblhk+^#Ob33S*Kb_pOK? zrr_=1C0%6b)S);Fw1CLw1+g;^Fnd@VtBI)_SkY$h1-tgH@-T0gU3~Ub{eoAKt)u1G zq>LN3JD;5SS8Yy+R8^bHyj80#`n%)g}S|j@#dT@SKv8a&Sy_^w_a0#~$3f^V~&$ei#wTsuVG5SCTJy z#o&rXFi5q~G~RA9$3FDR%&%f4X}OcE7!hY$GxlUY+l6_d^-xwFOz zjZ{3TPac`x(=a7$60+vjFH(;8fx^SXp*(8F-0Gg|3%D7<4GI#nvvSA&>yK|Sxs0~t z)y@CoYv7iRD{{v_&-~}jy8{|rAYtr`%a5S_uhDm}je@1>qQTz(P?Az!@j3RNf8gXJ z)x?hJ*Q9Qu>=(}c_e4p^v`KdyNVSi%vUm-CeyZp0v0cDuv_CCTIu{(L{%Cu)OZ-!^ zv%lzE+_u<)J-xinG{f%@<4$T-^4-e?P%OlF!mR4${A{BNWH8}l-ErUD5%TSQ@pycK z=6%;A3!T^99U|e`D_4i&wy`{CL^Lb;+cR|0tIDzFtQv^Ep=i9DHG5%UHISf)X?QLGONs=N_ zLSJca>BP$zS)INxdPYMQhE)PsHZ64B1T*90Vf0J~%s3F8)U&RzVqT1!;j3Y>8OjT; zkGNl(YF~o3ta_?imU|`uubt)vMtVU8lOrrMg4zFL!hGk%)s9z;WQdMhR&5l9_F3nM zK=@>sY$n&E3#A-M8t+FT%yeHAh6ENr$Y&M&4B1K}S{mXg^a{9bUdPGQMoU*77k5Lv z-)Q*}^Qq@out+}z2iZu6PU1as)Q=R6UAjTHV(z{r5J*NCGRiPvg9rh6@ zxi!$)HXg*ONg3Tv?I%eQ`1;jKmk_=N0v2eEZ8S^Ey$J1zgbfOh*dyGQjc>|$YaxcI zsu`P%TTDD)8&3nf9O3+@r*87LM@egfI{0L=t!~M*Hi-X+`qoPMJ`+itIag$65|Y1` z(+a+v1($J^0R4XA)gn4@I)N`y*r2GRqbSm_BDX{`Pu)-OHI2xl1a zuHw>p@g875w~8jriH|3kT9~5e0*$z}GLSK9dG5a9IUSZBP7k7<4Ad+e-P$qVHctQ% zyOu16`BX2VDXn$0o>7ytV#;A+t^Z+lwz|GXP#=NMo=$T=!!R?h6nEj80rUN|8)Lb z&+z}=*qC^-4I>X!62X|FUTPdbV zWJ~PniND5X$z2PL6#L2{LK<63)9CMWBudKLC>-XPQPJcnXM>0_` zz3J`|tNpj5iX@Hqfm~zT_cgn-8ocO&Z_lXMjD~s+YIJ_%%aK)ezj(xmnUxKSFLZ{5 zc}w+nWYQhFM&FWJ0>M+#0GDlp=FD>jd!}(Gt-gglw(Nrky#AOdQlbIrWz>~FcvL^M z_7(N7z(7Q&-w2wJ#`b@>@zGbL(JthHx6;$3J&XAZZyZrmu8Z7f_%PQe#?>zoTW^e5 zZqZyXU0Fp~r6gpOy(|f5^EN_oRbwrw_U9-bQTv(NT$)U8^)pWEmh>-nY1;1h2cpY| z_;_bGwMOUNDmWbg#b$Zs$K)`hg`#9~w#~)DnU)B8M%U95nzu%=ZgWj`l0>t3@LE3{ z-4N3~*~O)MIt$4m#SXJ5Q`3~6|dGho-RXbQ&&pi0$mOVE2*8PHU~l0ZHg92j%Zp3&lRN@99a>>1R~YNcSic=cXj9$ zYvIr)GgR;Q3FmNF&S!w#2P;UoIdlgSOo@&1il6bNkg^j1)* zzMCR(itBS&F`D7tqdWWC1SoG;Z;!I3wzp;V+07&R-XG^veN20x(}}S{FV)-3LGlL} z>+K%U!5D(%$z17XM-9EupSzMItzzj3%ID#{!u(hAS**iu{FQT;-)gYB#xA$y+= zuhwq>>~>zwNXcng^Tm5Har)?657_lda|(D_w=*Z*suG$te?XggEa&?cli$*;OUAxG zc69KeYIj08|H`51ZEtKC(V-U}n!ztA{ausqOJ7Z!@ffkZaXZCxa)~;lv$H`r);2e9 zGb2whd8>4!-w+@jV=%*5;L}BQ|L!3f5L$1`8B+Sf$i2AfT-LWFnLt=Fhq9ArWCHn4 zAB}HvoN__8BMq4kR4%!;g!&iz`zOK&3k7yz{)tc1>KU6;99Q=vO(bcY_l5eFQ~&8O z|AYeZv}0U}s;)XH+Jlj5Pm!*#udAx6@?LXWx?6Mm{qz`zW422K+5XezM!89EC5FAu z_;TU!(ic(>i-nYt+jENhdwf=lua;Mw?n1=Dh5rpcSI#GrT+X}^aG*gxwQi)#1Jv&1 z&2FHUBOs%egv^D!nZ)`$$Dvf1HRW`WRa={7&L6%XDo_-qEvef(7W>01XJgKO=oRKl zfBpW92j>%s+P@lJ+{50V+)KNl6yjLs{}zn^{xZQAHAS<3Ao=>>`N zy*xFivo{2ty*9HeuiPWGl%gT7?`%2FL2I^f_Tmc^9M30$?mQlQHv9HXkKY}X87mJn zX988xhntUn&}_2G5`MBm6}F^4=L=)C$&LL(Li4m#r$2;*MM*HC;S0ak_-kUsC6T|< zt#9a~owhbMnkN=KSC=jgXUfb8waVNQ+K7R;JRW2~X^GWL?hIqu-!Jjy#Jm6pc(Zh7 zyS41xt_y2c*GSP&l?>CT-sxn!?k=hh82I`@G;Yoao__5IQW=+Rox3!xe;JS7{{P_Yr|M|gRaiFH_w zJ0hZVbqp*P970`z!1_?wPwB3zOTynN@e>K^?OLJN_ajto3t1E5WKz7FJ_cHhJbN+F zwvWetSSniZwL@ct-4J`?O;i zURg>=VrYzpc0q4Qk7*IXawGwn=~ykMM- zouQ-Sgu2qC86iU)s}rUiL;ZcGVkKLX;6le5iZLj&E@RKm_;OEZ!^`fes-9ESP?&{^ zY_f7&>-5SS?}AnDci7FGlMTrNYOE-7K z%ytk)gD&h|pWIe!x=YoX<)Q!NLbAg`5SLq(t-3Q?K@6>Z1MVlVgS}>f4HaAsg|hs{ z6JtC=F#zs#gb=^ES)<>2zkknwO0%re@AT5^R;}U8Y{p!ZWS*t(H(bj8xVnaAYF}BN z;wA90I^3%%LJFyQ#KL48{t=g==Zg(8Evhz5VL^V6ksk4;tgxn5t#O<@J|UQwy^#4N z`Vt%Vf%55nFJde1yL6(+($1B}4UcGqIdXY`dt~Tg>P&*4^FgY;uUg09OF3!0OGH-m z3NFMnXf_A&b{)LM|7&I{WZHY6K@bc`lkWt!)lxUoKeJxJ16dGW%{xlmWVqwa3 zy@v0VK)qu2fRxu43ag%OZ_y9uHZxdQhvlM@WWA#di_VX0Ui{|8&pO}N`r-msNXNSz z-c@A_!dzEo!M41z2Q`~~t%)!FfOEzAiR8_20V#GYU+W=d@jxu9=B%0eq&;b&kY#0iVBi(Jfd#8cX(|rIYARX5&~Ud zx(Ry5iI;q3#z03mT{`I|63n3YMYyIwqijmiW9I$&-un@H-<#n0n2Q&zd8m5T+@nfP zN7gBjL@p^4NUgRSmm`aJRlw8Mx(y*k+-@LSVbuWx6lzpGfF z$NMU{IwDx2N-!nctOYKWvJ_!v{{1vkow<;ZE3VJ%RMHl0DUeroSxxV8BAlXn19=Qa zog3rPBU}1L1hdG)L{KzDnDijJde{wJ@yx5bfg|5~tJcy(Tl`I#2Z~J#I7&x~9Mf<= z$SYb1i|wHsnrb;%WpYOiFk8G8PD>G8>bc*gP$Hv8dn z@jxc(t`Nk^(4nf}9zMdd*X#b|6MK(m&^7RNI3gS4;%?29TEm!Y;#Z+@=~>2S=^v5a zg%OZz!aPNeIQbaEksl#%8hJh=WJgq?IkLPmOwp65v=>rT@MX^9cd@R1TcysM?k>IW zqKXo{m7$uoitug1u=%@|qUfry$i{iLD;8(*);3SgMbk!9*@apcV)en@UEaho3TMH7h#Z)p56r<0V1t7=rfJs)gea9MW5MO{IZ@It)s z1JR;mN29EW0BCokjfv|A%RUxY*W+ zuD}Y$qgE&5C*hPGaxobe9H0bLnTA?4{ep6&dH#+rSLTHQzU-gg`5U%+ zOgRlj!HRO>`P?}wTJ)pQF~K%{&r}5?@XPf`k>Z9#%y+BzLV6X|G4VTAOpi@(fyxS{8?5PLW8ln2ztI}_CmsI-|5}v+A z!ZkV=Oy=#s@8AKeF*AHF!ju1!-l!;sVW|(cQ=h#vErw`$TeSE=Z`gdBb!VelG4{bA z`)|a~qW9{XbMPmlvO5=N{NK0T^jn*}w_Z=)^=ADh<{gyK~%naFly@gw@%sjq)9^H7ve%|8umQj51%uYqQ+kG4orP@7vq{g4#Z86QLT zaJdfUJ+4G>FpD1XRnYfu@Aj_plc+cqIg|y|vi%`LHYp@XF83m@{Eby&R{g>DxS@7F#Ds(i6lg%Y9N+Edj&bM9 z5C}vjp%8SC29S_x(BDUscsj4&)j=-%djB_xxtk$ey7-oDQ%o>{4>axkqPG>Sr2nE55BeBUmep1 z?LlirEANBGe+xE0Q*Ov7iX#ngY4xk6)OJG=-o{^ZTC8*7ao{uK&h>B+PNssUG!ZfW zAElC3S6Qbu^Hv5cK;exWlNG&V*aw&B&@ugBgyNZUYV{HB!f-POM2PzT#a`3X-Dm3T zb%4yvC^x=-0}2zyfTq{pnGPGOwpmhP(!9Q4;WlP7<;Cyht#R)rTy-s`x_X$Z=Uu06 z9#BC?#0Kn)DOD=%}x=v%7EYP5VaHmw~;cc^aCkHX2djkmiXwCoMEB>2T z-t!I9xITbrN2$n{3J-pC(y}X_@yHfUa7-?KhEey>@^pNis`p@GuHKQ0D(-UrS6Bf$ z$!$&wci*gjSi-qi_0m2L+tq`jQ&QG#uU*n%a~rExohx7*_bNwc#- zjCyY~bA7?R#N_|do@c(5&Uvz>fMI+yxB+I)>1)j9WK3jsxeU7hzO zP*%BJ=v$svm+IgqrF5W@lJ-2;By;FqIQAc4z)M1mulmI2dd{43`-v_16f#zYWfovM{M^Dhpo5w&g!bemk6%+o;!qQnel~*MJ`TTR|-<6 zKF-DHdn!$TGaN>>n><%kL|~)cM0R zebEEP)KYOcmD1}5)%hLiF*bzNN3-wBA8)qK58&+u1&q8})EU={>|R!$D)kzfTxSKn zsN8D#!+qqdlIYhJAn|wA4~GFh)ykfvCu4v{uN9n7yaZ zu9maXI|UxdD4&-^TG^cIR5Ww!YV((}pni4bH|*4xZxa$((4U*;xEQ^PhWD@iJ3 zJ4C9aNCnB$wRA(({h^bH!?$I#31aVtqA?vbM3wG3i9GT_*G1|+_1~^%2zq@Ih}=PM zw1|8{^KW@qhVJWQ>Wi^s__#p^*7VO~%+oKsjl>laQYKIpf2%3FiU!3eifRb4nzp*r zB?YIBXgymZj2kO|u|2Pi%W{a;9;*zMRsPi+~LStIj^7{`{%-ZA<#&i?DB@9*c zaLN4gv-wxri>Q%v*(JopC359zhiT4<3Xz0yXspD$;9!Zs2M!8|C_zDHn{L)7GmOxm zqMGeZ-yX)!0k>(g)y4&8sspx;^2(E2!JJI0Y*DzXTRj!2H4L<(@S;nw?_Sn@A9@(3 zZBoB4Z(Ij;B)9ZvnT(~)n~MA}6$!*!ZRCB*cse(kmVY7}lo5#M5uzE+WAoN9*3zN# z@LggFY!Px;gqMm=>e5E`u!asw|F{^PBl+^~pY5#WAYD&Q=VVe$GkiH|md{18qgPn$n}-;TrBw<2g=;IBIT@Tt?U^7O?7Qw{JKJrZL!dc`YUPi?F=muWb_mAkdXD-H z+k6O10Dsk@VTapnOL+0}BY|#)8``Ru7Adgy`1KGr)tC}{s=v?WquteLuM2ni-$vgN zth)JVF97e7fQd@AD%&9}grBCd9RS9b`GlD4Tb91EDOFKn4J)uNTThNbT~4E#WFTd5 zq_ku7LwPCnweM!JsT@lvePh^z70Y484ha8-BbNpDc{*i`x_drOJ{+#kHmIq(ByA0X zSW0Dc4QkSXM`;l={c|TGro!(hNaCHd_G^OqOPr&D2LAGQx+`M~T-i$sKji8?Gn^1`HTn&&Ubb)Z#!6#RWX>%4sGp$RPj{ULKTI?w@>R*G6_nnAqajIaiHLKSJ&RP-X z=HcI)rsnF^2OQ__u0&}G_4$}yi@lk)?sYcMO(+?=-wSZykp30r2B|zg@-GnyNX>b9 zI@ivn=4M$H4FT#WSl@xbmg^w7{>|bO+4?<#@Y5knu8soe=PB399e!;~hbg(^L$Y~{ zWX0IZ_qpf3i@U(gngo+8An5yES+)SWSwo}v8EY?Q*{ zK!7sLh1*{$q}mH1Wx+l;HmKE86wau z0g&QC%}2%)1j*uW5Fyi4lYJMct9-?aJWv*w164%@u-siA9!oYdk1kwF@~1ly=okRQ zy4im5vhWzpVKYnsmuh^~VeuA;!`RKmI{+4LagQt(JtT4#kzbB7caUi?8yY zHN#MIl?C#=B}_Sl%t8sz6Rbd(x?e3AblUlqUt^C10ybD)+~OF>SZ27C@aa(izymd0 z(^f|+ZXIQTYC+^4KdlV&VCEw)RpH-mm){#ExwV=6APY?QSl&CXeq&z7)X{Vzf@^3o z*v;Xgo>mOvscOOwAIS;x}JZ{6Sz^?UhT$lZLhQe zAT(O)ugMf*&ogW`w%%2IeZoU6An`86RdK@8AEmEo6ea#fB<7tywUI<7yjR z9)U8H55okrnLUcFrsv$NeaOjQv62&;o?nTm&NM2lVhDVIS*hw>Db6j*t(<)@*aGWu zR8hH3TId-vefy5b4qjlo`XBkXTRZlRC#6uNdokYf>L}A&d2AK&g~sgo#p1`J50obG zrNV-*8{N8}k!uw2I~6H(xSG?)q#+Rk4$qhnKK#mm!&`oGZ2?o~nuhtgC8#FJ_cNq; zJ4qqk^#u5ALNQ3yetF0Oc~?55qjf&}S06oNh=h9y>Sd){|D<%5rln-}5&lyI-Cb2m8nshX!eY@e!E;FbZ!$oBs9O!D=ZKSMq*Fv7A_;G`qBF5$`csXLh#6d~H zA3KZ9jvnH(_#5|aCrFiE$>9Mepp>6EzA95XFp+@guAiRI6Y*Gsd8{qDwK%82iY2;T z=VvX~xUO%{b)X7dZ{Hyf(g^R$%N~hdK|jS>J53MPS($2MYZ*N zb#m5~#clcQektGVeEyl~C%o`ywa^=%u16}I<3cP{SkwJQ#v8uFCE!ap9+WwV zo0HSC26(th2}5&;40!5iCa#49(+NYC%g=dtKs+tWZxran{WH=dpjmyWoBxGAgUE>+WcbO(jZ@D6m z=Tly>_1Z0rH8o}wl2WHovaEC3EY4cu`F z$OJrMbQF@z&Tl03w^jC`QQyK<{E*eK9`M6T(34&)j(Eq?%B3)G7+?@3Llb_fti9B77 z+5btzd+8Eet4g@{+QGEIqbay&5$6u7in|H=(X7yyUeH`_h4u*0I!iT#MmN)6I$G`X zl>y3=o+|f_K*M}xlFcRy?P$+_lUUlroY6J~D8<^WHo*@|jsJe4fdw#>4prnX{jxU^ zUl`eHH+DplKe?~^fks0PUk>9_w7DWcs8nK~;tOpZ3oa*i; z11?~R&ac_Ij~`z5y-n&ysckwCcpDV6uiL#2y2&dn2s^j?YHT|LmAi^SgC%#U`!L-Vwf3VbMwYl>L(ETzWycouJ z{-L_Ga$7cn%U)I5%enW<<+=VdJ$lt!B{=GbEkbfQ0CProJGP_C6Ku@Wf!LG80W5sH3bEa@WqxzDy^4^1?^Sc z#bbROA{~|13}5>>vwG+KivR{TFG=qA8S|E3s?wMl>7t~x)}axsv7Gx>PaYi(>?}Fp zr*Q#u4}WJOiz2AQ@$zUUY$*X2@mO%&^SItJJy_J*IzcwayRf6_yzbrqHKko`7!@T4 z!g5L6`cJng_mpN|&Dj=fw{27?KYiNPM4NOa<$88=S_4Pnwqd>uCP}`_op5F6CvOs5 zxXZDx%$ltA?W59fZtrtnD4SQ&F`N(^$AFt6F>|yQ@@6<4X~Trq>NFzjtg0gtHR%TX z?GGJXi5U>WRrA{e9yL~l><1g{J8=p?Kd7*34H$iq6_nhnrcV*+z^ zEsijGJtR!2ejJnEqqIt3Aq-=CsIFYy)9?9bas%x@JbTmOp8aN-&yTo;IAFsEM#>8U zqiu}W6)TIRDc-|R4~EcRHpWEYFPe2jtwh_vc|9X}PMU=9F;{{Hc9Tx&-Jr64F@GX# z=69!?JKMg0939#<*>!@6^MT2pB>Qn<_2gQVmEmd+1qhO~lw&9LG4-4jyLZD?N#`N% z#4_V*%N>~z;;d3IGxm(lW$%0Pul5>4e4hdq6ZZbxVc(Fgp>LQ#)yTQ%xb+dgrtrz8 zGq?Xg6#ibP|iU`ovC4{u~t?NZ$w$^#TDBbs_3R-(OA=MPN$Us=E^74rmHk%%g; zA^2yF|NV*IHdcM!YCQ-ic0AuE7lw5>58Mh6T8V?JXNc7d1GPPCc1toL$)y(H?|~Bf znq5mY{)AN+rPce8zG+dlnbLR#h+;yBBW7D6KWg~P6G5EpwUu#`s1d#dnMK+6j~4#f z?-LSF_VMhiBihKCql@yrzn?St!f@cO3}{H57xlfZ9X&R2T(X{Cl3FNDzoj>e+O^s( zmAMh)e;l)Z^*ic)V*FoCdmbL!H!HvHcVAg@0CaU}1cx9gwJ+;?3t(6?cA$Js;+zug zc7_wpO+9$nfOFJW`JVGNs`|&sSm`gD{x<;G{v`F@8TJmu?+l}N_?gYl9!&X|FP2Q4 zq&k5=_W$ycadZHiwt|heVKb9eS-Yoe3NC!_jBmgrW~0}VG^}*9{7NsCyOHQvz8a3d zOUq+2F*av-!d;3{v%yld3!D(08onbJom;b>P5%VV$D8>Dwqcb8`L^4A`D8aD_W70G zW%r=k!MpIN^V&o67o2*nJ5Rp#^lXB)X|0 zL>W|)w4}Zq#<__ZUTk0kU`^Rgalx?pc9_kWZ< zpJBKhj-D*VTiF&}8VAD~C&L@%ht*>4V>60%1?`hw=cA=?Cvdn}qhGae(=mHmqiE8mUi4wkZ~x6Kzv=qBr5#bC=Q#@;E#BPl)<8~r zVYaO*TVg-?H<7(x`4%6Q)okOV9w=GdVW2i+yhop;XGm1*aYp zE6dsZ#kVbQZLXoDSOxvo(oXcj(q0Uy(yZX)8)vfCXpW;QQgC6paZ||U;EI=CgRgH6 zH@VAf#HkHgSKf-1%|>MSKw|iS{2df5hI~=DG{4z04zH%om*Mm1fNz7O8_S?9)9aj# zGqzNyiu26V$T?O$F+N3#26Q0P(w{Xsag6FTFyY=7S3X{M)m%?_aR8x?pX~I3lz|2s ztSB})i+6O}z6+R4h`-zAB3HAhLg5{q$FPo41iO- zw!B*z7L6ZUHY~`Ehv_c&trdl}G^Kf2shXXnw|>I($WYbRaN1s#^>wytOh*HVH!Nqw zobQ!n@fmIn8=EA^NGLKQY`R5*p#ltwXxaeM4CO`akEdQiBXLYP0CGXd1#C&ROH|ig zk;r4k2Z&XY9#HEOV%$=fna}h_7BlhIImWTKehAT7Zt%)mh)IrfN;EwZo@9EZcf>6$g%HUCzW0nq6W_BSiDj`Hr# zC^fiSIQZ$3OIu<$}&R6~?9j zB^cVR1)Q^J{mEag!;SMUdZJ$C-?d&+kgk4qw!OeTMn}GJd4q0I!u@~3Qd#0&diA^( zsK#u>`n@!c-&`mx)wwu|pdOk_FhlR7jf41}|Iab>8(Z>wYDcoCBd8wuT{mg7xm>HH z1Wu`%L~-9%0Xg;G9|cG3h7rhTX7dm)tTp3!0+p^<9`wyypPg=(-uF+Pb z!Q)=sSb)&!)@GhOXjulpCOygQ(-F_@Y7Z0E{M#WuLm+F+%LPuRzdso@B@eYD-7>YI zVLHlt`s&bivaIq`RtQ`m9g70k7P?Htk}nD>M3gJGfBL1*Am?e^-RHIbe9gY6Cq!Ak;Ewl z<{y4h3H?mB3-xXu`U9U@e#(vBR8dxB0p^9^nhCPZX4-Tqc~Wo_sbCL6bVD&hjGKYh zUybcuizr~(U{A0-CD~pj4WOk$yM2Iq1qgcgNWt=ks$3kb_ci}k(#q=v+g@Yu-oBh_ zZKj*?FTlGeK#Ukdkjq8Q_{I5xwVn}?13byR%XnZ;nup3lCt8f?_r-sVw3U3juAj&< zwd_dq@v6nQ03Ghr?w?!bHyad8^7X%E=y{pt73WzZ_D|B(K0e8uN#glTEZ|SMYJVJ` zQ>PsbFs8XTb3bua3d(PKMkvy|J9Q|A18r|ZeFjw_pW#vmLs*?+P9cHic|}QnU78v6 zcyt76$Z;BY)(VS|;fzbIP>Wsb(G7O8Vy!=GSE+lGqxGo5rjfnvO?`7RVvmex8Sq&d z#ySRv4cdywKi3GW@*M8&)NURsfZ*oMlItCxXIQ^!=y5Zq5#y3@H3&=?=}|6??so%Q zhi3{UH%B$uFf%-Izqi2lQg06Gn>Fn==R>ycO7TKT0_cfQY6TPD5l`A@swZ*WWXvD7Pn9xZJ&{98t)BnuwW zhjJ>;tB~4vG^29Bh%^px?u!SW`IxWw`40j~9xz!b^f3o(k6SX? zX19>$9y3nw^OJuL-+!IsDI!|GPZ*R{0Mtn~n)K9>_+^mpBilKE)`>6gmr19#f>SQM zTs95qOBx2-4JWH060TO*BKih2wNCG!5wWEOHeRXxS@*v-o>kw<&~q{$6z>=dx4YRc z0Y`0gTAAm>O~qDQQzmD(b)dL~DnEwc8H3ZF+D8TMb9>@Zz>NN@q|9Vq8^qjD~N(!f1|9e=>yk&dKD}W=fQ=2DfiIZ4>B12o z2^hm^s?IWIPNRLE`HHQTZ&x~Wv+5f>3{=f|b z1>eC}ayk<(jc1nYbMEZ-odk)|jX*6yA^cB7O;{L#E zOm!j37M})UmGt!rp)WWsSa^!h4VB#j9Vgkd(EMb8k@AIMTsBxHH)?c>sX)4}I< zJ**bE=zyk+Ji2!&FbXEAp?%?Qx-5_zVIi2GCfT>(DZU3hnFk8 zy{cF_L|<@5$#U4Kf3nA^()D!(&4=d>(7>;h2iV4mdsOHBz`a4kaN&o`?geYflh@bG z!J(n_FfG|(vCp1U3J(UFtcn~O09iHmzR&lnZJBri>3Qex{=Xh+fCy7?coVSX+b=QmIp-=!lpo(ormUHz6`7{h^=CbJn% zSf|alyb5phZu!}$``0Eq_-*Hb(JyLSN9-_o`g zuSa6rjcZTD)U7HUpJU}K=@F^Nd1GM_IVUTx?RFkr-}k zhZzmq^|pE7>x$pB>=V{Az@ck5iS-lfRg&oCRvG*joNlbsg>EPy%dCez>T1>V(*ZZJ zj02ix*H)huA~V)mt%gb$N%vq>8E98onby-?u!_T3XJ)JiR;V?H2Cq;+*PWD(P``k; zS>wEsVuvW@e?s$j}w|NupPr!TCnG@CNzO8wCUtVG&D!T(LNzCOZO6U-_JRnQO8Sy9Oi#c?9Zh5xQ6-ujw;>)R4 z%pdrhJ3 z)`TWqWIqWUyI9020yKnMq33Q(HXUp4ECv~6^cUayKJq!?!1=GE-R|@sG{QfA`3rMK zN@;sEZ{g<7T2X#4Q{39P`au!m)-}a0U(64-{={Vr`^@B8uMDymWwt>#+dgcvK$N}r zd2zB5I3B&(&~94iM!}>x7_Hd1j0)%+FtPmur$$czB^&hUW3$#nt>8M>DG;;v$vRd3 zqe$23JJzr;29uU$-T8M6Sc9$lxpHN0(c$GzPvC64o%;#eYwo6QqT;tozhu;0#=Q^W z@2+cvUp`@ZQ(SwJPUOR%lZ>OCyGH=!(K7FkWN@QSzhAPHlG8 zutIPt*T{>2_m;-JSUi-!%nTpWCLb*n*nwAjgycasrFA!Xc7qK4#}d(HG@Vk74N>G` zt5HK+$)ibIOdJ=Z9Wn?@_x=X(Cseki6{3dw1%};xzk%}pab=5Qn!xLxRh`M`ne~NA z8*&AH;m^$x7z$`-##|29kh7lgB~cqdk-v7{$!c%Hsa0RTsIO(~;_dh(fnWx^sw>vU zzEo2s0psMGHpNpn!)N%jm6Ul-{cMZ+F4+C;LT`%XxY=K4Z3-MtggkD(cK~O6BY_4? z=>(5J$q|U?DGaGnGv>5}&_|%Oo%%if%>)%5hGMaj(sg*Parw#QOYhq@jx8W_W8j!k zf>@e>Ocm7_4{pd7&ZuGbMd{R>qMXx%COjs3YEBAuUs_-GQt)4|-w80t!)s&v>CWGa z`7j?R8mkb`lI?BKz!Wpq5}Vzsi4@ZH8g|sw71K#ioxpoM92Bwts}Ky$zDsGM{mA-ZDcl$bX}Yae!*p8Wlo(yZX-A?n=!W)I*PJOtVk5Z*g0 z&+#`t&7JOJW!?EA+mX+(_Qy5+`IG->mrnqZnmV6+R3l~}lxPu4en(UAN{*+i`ijt_ zj?bqlOYIBpTo=r_!Ca-K&iA*SsP-K>P&OGJrg|yeO0$_C=$$H9<;YO@wV?sL+T3gL zr$c@Z?)$2f%XUH*^ftH_|Pa)SrlpP_DmEJq6+7$J6dIkPtM$5Np(ie@| z2wsl)+mT*-{ZBm%;@Ci3xqh<$@KEn=mUrI(6Mc?VdZ!asCCBSzfg}=Mt%D{6^OuPA zZ~sCdfYG))uB{?HSZB7gsZHm$UG62T=suK%m6GN+gG#dJP1PQ0K&5mckrv>Q9SmgO zdPDti__-%buc*#^_c(!e@u#rBZTPlRW=G1W?gy~5EBDWRJVRIJCKRU@P3 zyrQ~dLzWHp=7?A41i&T;n}YV>3-Ae$?Zz=XDZVgN3hC^6E@FoE@T95npi(o zi<8jNTz&&|Zvw4*ER9aZ8a>8ZQ9Ih}y!P49*qqnnPVSa zl&^^*eQ*Q<|Dx|yj4K{|@1^SvkyZ-0JeX_{)I~RE&R)z_!(~d<)S6Q{9tjV`J2DjU zTZ`TPoAZdL$qo3(v|HoRS$EbqZ;2&@9N-@@w#0TBt5R6Myi_ve>iwlv)*wl(cuIk{ zxyzHbh|rDf;Lm@@h~YYmIPRvO8`S=J{yHZ6_O)Qha-;RBqiv-5{&K@oFwKHXFD*n> zM%PlaA~D~Jr%iFELwJLBQ0aLio(sMewS-fi+wiV(yOTdz4e##v2qh|w-r!0kI8)BM z_9&`8BDqy@FHG>}q0gnXt-jn>7LU)orSzKQ|Vn*`llr{ARyWNaJ|re!GF%IlGpm6MleuLtZ^ug`GV#+)en z79c%kYr9rAb7OGKn&$`^k;C?Tb+zarOGMk*-$36|PrI7P*1NGZ;vLXaKduZ~5=Rf| z7lS7SA9J?dl4x#Fkes12l=^o@gfW1QoL;uuo_)*Mxwy!9;U8t^ynpH}XcF>gx98A#A#S-i^!BVS z-cam_cnl?4VGv!u8mFeo$JTwd6nL)_Vt{`f(D>6H# zG-xWV3D}uCHovNL%8hfcBJT?seV<$Z_~yUH7UTHc^%yHUm~(5oNX(42@LS7LKKsiM z+3GB?s2aauO1MX9bn0-<<(cf~+>6wujx*9UI_P5g$@O5(@-V)?W&_ZTz$t$Wrn6Du|JH(bRTOqaHB zk=W2ZehTOK3@}>f%3Mh{5lrJZ;W_K`GyX zUSDtlCrDYfYRhfzc>R`J$P14VNwIlP`lxomPL(Fex!R1(2_}KAg>II*^gK23RVHIA zXNg>I+-!f{E@&!k=Q-cIS-inEBG$=~xKUVumhjfvF-}08uW5xg?!CeYaD9ina>Z5% zC_nFb$Oq}i1#71~X0#uO`x$QOf#Yfrz@||0+&ggeOMbqyk9a`Lp6p>hd2Bw5(QAMxMxEW?1X+|!4?3+pg zc-*PLZ6nr>)(IM~Ef(D_2YAWomwPEoW_pb|)mmc2EzNb$SC|$R6+u&6v$LYMPC0i= zZ?RN#CPF~coy8+H40ylI*M%BsS-MR2#;@a9{Ms|fUYPUqdnJAC>GTsIX_Uk4!&VN$ z?Vy7k+mzBAtc8xa`cv%H1!e6KGUafchn+tNf++ZIYaj%FP5#oAK&IT8QSd=H-3NO zq{xD*?YZG70PXbpAqR`!5qO%Hle7=DG@I&L-e_lq^@QZv0fw1z(~YI^`#E84>4H_o zIG}?=%Bsj3`_}HMoT!YpSvZ&FY`|^8Jq!83@UhYPEhU{1X|d;vDq0!T`qP^)HGh;1 zz}weO1m->j-g9!go$b|r!o zR$3bM9q7-KSC^6@iqj%AextqD(S})Q0s&pW)Rc&e#*`{!(%h=TL-ri6?p>g61il+9 za+Vu7`B&q$dvqs>guokG^y52Z5duluSs@})4$ZwAqi$pU!7dD!TgLr zLB}ifooA5pdcUoe3UgPjsnTx%ufnKh&7rZ+=3aFn?e!6Oh^RZ^;IvZWKCD#2wwIZ& zp87pAsegU^tk%=S9-ryUort@O<#;~zInc!Kd%rsy3U{KGI-NNE%qp?lt}#O!UZmn1 zRZ#ak9Dlt=4o7X>O!2jYq#fvS2gq_e>!mnZLf)izNzHc_C-p}=8r8n@%9r#V zk2|}ND|6~dBb34VZox|j?dmk+ysT&$jf$$NJs&xEZU4zY&B~Z`jc{zU$%m^FZh6oU z22?@wM#Y;wjgJ&x{xzlA1u~$RrHu+uIXl=YV4Ul1rzD>1H-^7i?~<(~>riv)UVT7G zer2HlKZgr;;=GapNn;~`WQRQeA7%d?Pj&x4j^h^*T_r`4?aE3-!`@vLNhr#mrI2;X zJl3H>Wo2ibqlC;OdsC7S$2`YA_Q5gFG0$=MJ`Xwc{#>uy@Amu0?ctpBe2n|!zSnb; z)cWKTCj}b*2`oULGb4`DXBC1R)i+=$h@nKvN^`d4z|S%9mBQ$va7DunP32&FZi>Ba zaR*3k0uol*fP#nZgGbM@agvvUoP}9i}`wj093weC*s4 zNfdYoG6U<&$)^v5nZa!4VHFAy^Y~}iF7L|)4?rs5754uJy3h$$ANf$d^|6$BB>9IG zkUF1)e%N%tM`H2Duj*m>&C7DtpAJt_7NY08N507%eE-JiQAH7llJAcMB-A*YRVxUf*?ntfh7e{I$+2_-9W^`w!-75YfnU> zHou`^AhtN8nK;II=0>vlPQRh`-F`znJdj^v4su&-Xp;N;ZFk6a3CR0dTQ$P+ zq^z7$*@@qt;A~zXlSp5MpO!j>TKN<+xuKw{0-1i$+S$lfe9RviE(~O0`xXx&k^^aKG@`{Grnk!A7&lrYDmOT%@aV(QYUF%@U>kSg zru_ytP%FK-7)v`1bdEZ3!3T)S`vf^(%K2TAXUtQ1mK+#1AfG4JCFV)~huK3`F*%Y1 z`&-SW?=M%p$j+F$8a6~tNnP;IqMypJhR_=wmoZOeV%fh372Z?8lp_-y$jf5!&Hp}D z9ECbQQ&hZOa@DwmOA74jR#Da}nlYt>68&v(1F$2WdgoT)0xO!f=UFv8f1i@BK|hd2 zhS}O+s|@y9n==y_yihu&C$3$JWUhQ5@0AnrbI)r@&wJu;5Vf3@<$=^5WgwEXJ#DrQ zxRDbiz`T8Fc3KWk!nng7Q5O38-n43%w+H;lvsZyUVXxAoU6iW?bZp=t+UV1H|3sTM zc{cTWytf`%!n9v$-r`^+INdx~WGL<>VO=V|XQk}(x1G^&46Rc6L<@F^M3nX{|G^^u z^+sh4f8$(Kq4`JduDbU6ki3}XZ$mC_kRm!$7ubdO^Ujk$_7duN^Q5VWxi1vazIV)e zKPayjAD!99B$=$8bP+?l6UOd6hwC^N>fZXz>c|Wkhy^nS#VMXsoO&RJ_611^?sfPX zjv_~}fHE)bNP?B|WBGjqK%E|@S%z=1PHmX2J=V@`{fbdPyBCc7Kj%p{2OMlQr+)_m z_L&l8bO?YqT^{`bRQPj-DOQ;u^2rl}-^&GtPPRkr-u#Ym04&nr9FQjrsYa=YOIRL^ zuU^}gBykH7(k|{t4$$=pk00a_=NMO;C+r(@+p{yU*h(_ptLo963&72$A&?aVa+5b0LsVJH-n4_h%RdnJAJ8`1FZCU)7=VTqV2P zsPKKV{68zxn;7uQ0JX{l%g?VHSe1|0Gw<7Ge?o;9XhGJXMCEhT{k7u%+92KOWGbIF z4z=F>RT9W4mQK|_lvv0uy|x;@eU#|_#vj=^#}RF3wKOsAasV^M_*4!NF1KdydYE>& zTdfxDCv%f~dSSCU%Y=&X=90h&|MhEy31m4cxQV?wppSfK~b|u%fD?ME^e8$|TeRN%Afl;^x&I2;5b*aSfs< zUwtgwb&o~E!#NA~Ks_W6k}#p*s?AZZR3J(h$I-Z=gS>;h)^_8t>Dh$KWh{H~PXK;L z0bjB>jnzT)*Jo}MD_Dt@=V%FehIMXJ^s$278Ld-wE49(CkWK7SKI4{3OlRJYqvzf6 zbN|0{(9aqTQ#;I?F2F|j!leAh28DNn~s6o*UgD{`g6nZ${`2?12@BIH%Unmpj&xJQZ=14;FI z4zL&kyk%3e+zELbY!3Op8yVpB1)a!4m}$u?}VZ=5Cs6aL?MLm?j+ieK>(F6+Zv_MPKeoN+AvQh%wEgIF9MZ-bhF&2$jnCD5 zc&j{ZKPG1ryXi6J%wirU4Go^{DhTfXVnZ^Kjv|$cT4o4xG%2Y(?llULOC^fxPhXXO z{lB_l93?xF`HO!0Uwv}D8v5Q#Q4{lD!z7w;&DU$Le=!NyE3e&AJ(z5FRgbDFT0rxd zgexg*zM6MdX8j)m42Z5dsPxNAyRG9wW@(m#NrAe6xhs2Y^@ZCHLpmVTjhNzMeROJY zX6t3g3+TYf1C)CZl76-Jzy&JGkG&E)F8NwqFF~1M(98v=QmBba?CauIJUf^Rs7@57 z^2ex_g__49eA!5L?orDPs^0Z-*IT-!+sjX@T%;5^;8%X$giztO7)U244`?}m3NWDF zW|p~)F#lNhoLSOc2wsK9ymAdJo0@bd=ANQZluu>uJeqlq*a}py87wEyX>8nq6;EE^ zY+jIO+oz+0BVOj69zGSTK$p?YmE5-o>oTXb_ENb1 zABfg!(gRi82|dyY`veT^)A>j??z{q#`@9!%o87Cr<+k=Ox3vL?bUb&J<^sy*5<|>s zT}eIJ&eXeK+Sjibeqja3bXb)JX$xdozQTrWT}FUH+s#rTzZHjX4dUIPhsI(2KJx> zs7)ZP%&*Cpz0e81*I}rgg8QIQz9yGoyEaUu%Ls+7%>~)qnH~S~=@_t+S{fvnu5dYN zC6J`b_=mP(qN3te-LV?YgM3q6TTV3mRzhEdPvs+KES5$~lq2#pz^$y+=D3@M6^Q)s zhl)~td}7?0tw{WMN~s>S@s-@6Pl$_sR&9ZL9H)}9PV5x==M~k;2_={0Mr+!gB$o;@6Z}UD?B923eYoJ|cCZ7zu`aKFnov*IDhSuC8Hc?SK+k~ z3y6KMAR$)&lz2Y<*rUOAeJd-lXteiK?@c~!sjAYak!xpzP}kZ>PDIWSm}u}$p^g6p zwVhP*+;-^nwtF3p;T;u)HKx06u-+o~M~?}*72uclU~rOe|WFHvI?ezG86 zuf@L2BS_ieL>;(zLT!TXh<7Vy=N;jgjJwp+`F5oR&89JRsz!~?Hd{m=d%AADx4wrd z2C3Agfy%O4%d%M^2UENf%QO2v7^Hsw(0U@b=gL$?;SIQKBDB7_bb&U|nPd@@-cHPQ zMz$de?d|ZEkTjPMa4SL}J5x~mM zk7b-%vGX9HO{x#~Hu%}75mnz1j?~r|pg$O7&#K#ieSKm45PMFcoVM2>;mN~6IRjPI zu{!>_tN7R=!3lx;ZZ&#OJNb*>!oBm0zddm=XGr~!`(+$R$c<^mExWw)X%*Ej&JAXL zA9Axwf}KaO3wb5y&56LPWD?JoG(;FWa zX{Wh*Hs~0BG&Nms*-8z6+tI4R?+1FJP=6{Gic-v8&`08{{ESOqfk0iAN>{VlmSqY^ z_O=DRU6_kwNEagXpDEvuZ5JGYF~bSH>y9@fxaPZIj%Jw-nf64rMEWVS8@2z?6#Cnx2BasDRDo8cP@fou$c=uRk!8@uUk~YoCNiZA`W>$G zz>oM;7TFYa5-i#7Heql{PkjZ{C0APf5V ze9gCfH=Zs;hS=Tqsp-oK)c4{}06oAYL!LS>F<}Vn1>?U)bMRR_v-h4!K_@a|+d`Jd z+sl$T%(`z>IMEc@h#?k7do2TP@e zJ*$yhI}JSwU7CEWjv>*xyan=}#@57DUZGfr5sj!Cx_h7(fK$n9$HfbDXIf_XnEwda z7e$8l+~k!HA2gePt!dobeziGwp?(UO0ef2fN{1oFVKu(axJ~%6v)WkQjos|Lo&vx< zp@`;sazG0kR;Bd}RrsUi?YIEYSVp01?xVrM#rBs~8I~azGqzTJ)S>*qzxi*#7y`W6 zrfx0Hxq6kI;AfHbcHyjBJcM`$OQxK-g=!`6!hQ?wzX4TNf$1Ql2J%S>3AnlOif;9_ zjgb*?`G}9F_NE`E2W<5)Q0(UeHR1jo%x#C7tJxgr;vCu-#0#W=14h{l>pKfuvHo6bW%qO1+izKeqR2hB z#nW2;{rxCN5CEHXdBW@gqtHK^c1GH^kd^EK2ykpDfv;97*1;lj&Hox~Rd$JX`d8-r z4T{#0OkMf>h0s332T)0eYEBS4(Wv`t)sZ1&7SgR+RauQKgH(pvhxPO)g0oaP-cl66 zT5@*UQL4CuhFBx*Oto`+^s0|F zFZGqJ*s?cei%rZg-+X_2=;mI+y`xETuV3|3j^h2YWWY%6Ke`Dhj4Nliw%69EBFAnN zUo<-`@Jp-+PrOJP2ra34Nz8OV&$n{P8vT@e<|?bAxpAw}XQWp5P4sog5<`xqYFYWX znsA}-OG|{_Z|vAx!L-H(XL!VB=<2v#WIh*sBXTPRGAYYeZ)3_zKSV-z8+r~Tt91Cq z6d(v;zQdf$3`*?w`<29h`HLK}dC|jCBJTCWM@>8^_SaU#wuwaSiUz4nEF~Or(-G(A ztj%a-{IT3|koj0b-nAQ2&x5M-R9P+s}^e?{n*lvcY<8@wJ6}twYwc5Z>-CBws4uHMqi;Bl5i7fr+K~U2(aTCkV zb#Lm2ZM?Nx{c4 zy2V$m+xYEQnOdospx5fmO}5@`yh&|9g~g6rdH4ry#Np+TtURARK1#l2nOC)U3gAlN za<&(vl$(*hv})FCy+Wu~r(%z$IxL+px87qtav@x=G`CJ(-ChO9rB26^h4VbVb z#J=rNo)&MLy$TG_b^qX8p@MfR-Dc zL3gekK{sDImO{V0(}D4@@*f=-d$ekee)M1Xa^o>Nvv{+wVCX^Fge-Q5fJdUxa) zDKPS(L);TaJ|M8P6O_FT=l}^|a+r67W;t$N!i(ZmAu||b)*k21;-^l>^;bv!b{s}j zL>&Pwf}uhM*CjvvB6-%oT~iI*BcH;|Wk!Mh>C+T*lJeT8XjXh`J^Uzna!f7k`2 zkw`j=p>^{Q>`zZCz9G*K_Tw|aI|v#*?)F199A=;Yj%V|NOvx{>!m@z)vbF(+*e9QK z%to6LeyjCDM}VD!L}{fjSbyP;L&wifGpNs*`1u!WNOx#exBxRpFmjCL^S37(KH^Wz z6i1jMC!Dc!pFdnCJYL2X{Up1`fChYd4u{GMUiVsd+^;0AdlB1vt!9te=}X36e|wDi z@t7sZ3-tNZ2Mbg8A5rDQsu4d@0bAg@&gned#M_Pjtlb339S~XQ`j>x|cvx05MDqQS z9jO`azOuTOoFj0aei^3+W;*`8v`M`De%s9*4hy>OZ|qIm@KzCRI*7|zb6wu7C^w`O zq5w^Kjs1p}?8`krt|nW1qh#b`>_Tkijg3t5Zv;hFx?ZZE?rH=YZ?C+S)6q8z#>N(4 z5y#$T(bjBwJD zh!V}n#|Y(t*bqCaVr=%7dfIXS^ z`+srp<^RFKXNdDWKM4hpTKhrJo!kIK!tnszZ|nBWM#k3MJZUi2t@3l+!Pz%=qXNYz z4AQB%R*8iJd8~%ZRHWD&x;i!43Ga(;SUcTiB0$q7*fMn^UsfE)!n2a3J3#bn(X!Sn ztYh^))9%e~u{k13*H$#VJ!^qpeO4G%WaG@z%w)Q$G> z%mkmgX-oBL=*61jMh2v>=O(+afNRUr>2faI&G|Gx@k+BtE2}Z@qj>hxjUc2nM2Pt@ zm2VhRr-6PhQY2u>68gaLOKf>j(rE=*?kyvmZ9;w`OIpp;j5T}-D<&eEt@d1e5&P|3 zomcGXUpvt>pqV6atsJQ@O^x0#R>XxoHU2n%?@6Zp2UUkH9hpI7N0#O7aOHk|Yc`T) z(KSVPRZ^s2{dq+}SIcI#+@g70Q{xWi@BF#QpWFgpJr&e0731`{+qA&#yzRT9LvrxP z*w9IykOfgVeI#E;q~?>jj}E$aVNHSMM~tmnJ@XBdTq{F+{#8nD*U83Pi98+)ajg>$ zS=SOcdIiHhl6QvV7xWs>Fzib^26#Q$P61x!Y)R==2kG4&ciAF&T8GN#%e7YN>ih`b z=f@p`gd>+ysEn+w!@N{UDJ#K07`v6@yfs{W$3o4dC!Ph^P3?e{e&%|*Gi5KRe}lr}c4 z5thK4NUVUrO849z4vHcoT<2<9Bz!}xes?=<6DNZdq8QS3B~z-oyxg}EQ($$DkY@>~ zG5Hpxw&O8|KrK{iBhVPSVCmPDD~-5dBrGRkV!{3AYnOU*b$#|dF4UW?ttO_e=2e#i zy$DV8dF|GXoNp<=G<}>=X(NI1!9W$N1LQCYFnja(oLuK1cJB$&pmp3MO|y-GPN|ip z^3TfPCl`!1lE(}yc~FQL%}Iiulf1TDvnP(|dh~Bn{u$BHf>f_F1!caKzPXzPUB0<9 zW$|UctF!tc0@)8eGjks1vu8&beGuyr=j;`W_n<$;gn_UF0(>AEGqn(bHpk66x-(fF*0y^n)KKh5U=J;afC<-+;n5L2s&Yui+h>HUYXM}b@?Ec(!SAVq`c ztzwNAxwSgk`W||@b)nO4zU1tISfu|^`j$;FL;t6ZU@rhkq!D!6p){HfsA%p1;E7R-HKsE+83zhM@t{*~D( zm*gz6mh8TXEW#N$+TM<})=0RP+B&H|RJ}Dh-Bqt(JVrc?ziAG>0#zO~g%monYs+@d zOd}rP)Fb-2ge-#8^d_2CSu8+7Dxq3h2 zQffopaJCeMvQm*Z0ivY>-n+$X{Csayet-EhP?w#I5$Z}R@>Jlu0CVI*432jUXQiV; zCd)_DJ}Hzm>NYUque&0>Y|WhR5N8YO=@L=P$<7)Q@cuPifM$>84Xx)1)u2#&qe54_ zd&Cpp&qEL2CT1suu8JHV3k1iPwU`l*tCjZVtaWMLFZA98VjRV0&yj(W$Pb_lQ+DAF zcy{5$$=l}g4D0LCX!hWixko9etLp6!EWh1@fS_DX1$l@VpW@l7o=;lgWe!GtDbqL5 zFCA7-uUMtbtu28+iQc2TZS>X$YEy6Hoj(t1;`dF}IU>?(_cGyux9Ks27Q4OHol}n! z$w$u>23FQ|usg|1xgSutO4sL>u`)%jdXsz*ei0{-(V8F+IT{*0h$*ie*f7;ye6UcY zo-eMn!jfP%hg7gzeWvp(?~)wnz+DxWmYQ$yAROW}ql?3=%vc)vy1xJzz=QS9h@rr` z7Rz)-372>T2#Hmmn=7_do{tcGMyj2@2tGd@u^E`y)!5mBtw69AUoX+(`GNUruS0j+ zGpJ4>{7F(x=$jw9zb&YjLjU|bzU>n6fj$_^hjLBK-E7^?UF(E-XQM9?;7+Wh zL#Lb)tBEq$$f$7o26xyfc4@QRO2Zkex1gZ?vRDvO<#;Vz+2L@)y3Mp?aQlGg0IHrn zRXunfPt))Z`9{w#PL2`9o2bow@nBEV{2)U5!p2D5f^_}S-;{9t#5$q>X@pY^cNm9& zibYXvb2B{P3rxv}Jyh+*d?a67AVI6OQ4l5=t z)Leba1ab=G?%EhSqpKMHi4L}~XeIpSFMpKaVkD~Ak|;pI2o=9tLI05iC;KMe_qF&V z>*XHR&QH{US!L8~ncIZE-qN7ZwqNb-64@$Q@e;TVw+`nOG3aMP1 zwliSvMD)q!uT>DCjAbIH+&GtiV9nN2@lR6qt#ldNPs%|dV@Hho z`bL)@6Y-u%1BV=lrq-n$xLKS9^%#T3p*LsxC$&1Q8Xq#6T9#>Q*#F&nN}BVfONA?_scx|1C05jyY!&7%@Z>AUMlg;x7f<#^DP%(0uf&#nBwYvNJhXIba>F~Z^_zD$@8r`k zz6of(3ysJub4($rx=!9;%&^v(S-ewa<1DAM2{M;OG~tVgatpEZ*xs2k53~3i&06>% zf?Ti}N}#(p-x*f3j;cuLiW6B1-(fGDB6&rYsAVgSOR2An`Z8qm9nT{Avm@8bWh>r8 zBCK!fH(*c#_W6!)g}(~epHVg`Y!WXEJP+qnjruVKi_fg;0{4fRD`#zC+#-A}daM z%b-QFjZ)6Eey_m59i9GZhE?wS1&nxOg2QT%BNrP>T;wr0=G^voNd9$L&XL?h0V~(B zsu=fYT*vY0)KcUHaL4d!D^8{*u@e98kYoULotOdoU#`}c7iI8R^R-?;OsN-Z2F6!| zOts~rVsjH}5+YDvSGYUZtp;5R&7Yo8{?+-*!80wPRig=JRIz4TEaP8#L}~8x)EFPW&8zYJG7I0QYM=! z$&)3hG^6)Ya7IIlLi@lu%hpKixdi!NiF6vM$7)+qmYRxo4+KxoD;2s94q~#-%!{}h zU{1Gcn9k(|2wJSUmWDlgip-nja%=~iiw-wY-A%d{C|Q(K=IHL>Se~o1I8Q*$K4o&w zaupJgy-q$g;{3aQ%Zw!bzEXTr%sOZBV?DG;qIi1)Dyc!D*JgpOsYD!B9$WiAD$eTOYT_USlLq2H{9DGtk$Sdk)(|9vo9h2+H8}` z8Dj(4MfvY7UmYe>8JRcM0fqSw&psh!XNW!leM)TqVA;oMQnDRjSwV+rt*OYf)3VoU z62Oo?=P%4v4j3zjEFlQ+)j0N#fcZw#Z~ft6M&7IN1i`Ft>>KL$C#qU%eu9Bz7B`Q4_o-Nd8 z6s7zYtyrDkYN=TFbufDTjj;7rlU%Wn_stBu2$RvW2uDq}yO|o3sb626ayvJq z&D*v-rsuXq2uW8+g$(kTa`wvkoH1v;Ll-6GV*cZzHcyP&M+Vg~TjY+Qms6Wp5^h%w z7^_Qv9~ce>oiS&OL1jdnKpp3qWAdVxd;(VZP|GIf_j%%mNPOGVjBLBCwSxAz7qzFs z)y~8VbDwg?WH<6S?x!35QWm|;Af8uga61obGk6YSu5~5THZQv`hK_b^bZ%J+>_&CUk`y|D=j7t4deq&znYOqzsVJ{b;(q`!9;GoFZ-H3ug zPnd~%p`!$MH&2-+Zd^Fzc9f8jG&{N+-B_-fX;WWj?W`VUy5_1HIJqITqDG6+jZ}&` zopN(mLR*~8tCsHL7iN5xwj}9LEvJ;8*`J*nynHrHZE3~B*ECwbEenPnYGo3xu_~Y95Q*JqX1Gdn3E@geT<|sPZ%T zchsZBHfkrO>g|vA!_7`g+HiCgaU<=5&-x-!b4mXBilkd|+`q$DY2$%+VQ(=$saK+I34di+`^*ws#;dnU#EU7I&?NcU*=B4)(Wu zCZrZi605pZi=nZ`-Qb~ewIdSgzH92}c%e*XMPl}f_s}Cg{C(O1A@k_|2Q>M$3+%7M z7HY)#FiKQ&#{jyr$f^w$0+opUvjw6;DDh~W|A)+W`TcW9r8!IP&o@Djluf<{!lqJa zfQo-3{k4rnjT`@x6~3R;VeNj%K2Q~t$9?MRar5r!V#5Q9;EqIlLz_#`pojIeBgy~n zYcG2mYVt{sV4>tBsr~0rtRBSGx#s5P6;u`Mbf?$1$p@P?8*9!w_jWlgpn8Y`CPjms zdFH<21XA?xa9{0j%nfuD_&rsv^AV4up%^)s+b$XIY{!UtVq?ngf(_T+dj1W}Kx^u!8S#5(^{XwS3D8zauXlyPT@PpPZOg-gN?S#Fu~AoUN8FKT z_ZOib7fOcby%X4*jRvX>Xn+WJt-06x(@j%PaLFpr`IHyY?)?r37gLdbT5Hekh=m<^ zx1KVXy^K|YHHu6BUSOCFFce@TqJ4+C78e({{^XOKco!n;sXs;67q7F!bjhpb z@&QMIkJ+EzhY^DBgk&1bI?!$JQg}h{0Qg*e*YTg%>{ko>+eW40?A|u_cSrtY_!QLw zmCxT)cxdw1!^Klz+ zMY(3rX(_;@4<N*fBM_MAv*F-Ds`wAj`AFN0M{CRf7s=F47#(7 z^McQeXC)YonYN zdVhFgVxqMKc&`;k=q8u~wT4PM3AO(QJ8v63fDinXDgGD$_`oowYap;s-v^-d7ZrmI zYX4;ZU&z_y4Rpa|_5Jt9d0K!~niP5NznAVg2Q(KudHdjbDLV#uF2E1WF#K=#S2zF{ z8YuvaooJYM{4EC_`VA&5KvpQ(H+vj|0=%7pe+V^u8^_m|`|M+X!+>rh2OQZ|>*F7A z*gKDkMG8P$suxH8-zCYP{NLmHql6Z5I2=LBp<(}l&{J)GN;dz%@Krw&_Y z;cU6qu(2w^5JT)@Y8xmMQ_2C9EG*lMNcNTG35QJU(RcdOc+65-W<3VQXutV1*j0*WAk{h`W1022(cxAv+G_Jv;n#9$|{x z{Y>Iv-4Yj%hkeSE@A#S-?2epcBM8_1x+@SHyjJmARjM22^UShkp0HV$*O(^g7>Q>R zm|fb_rpT8NM4d6FPzev6M)3C;F=b!~#}E$-aXX%T;u>wK=h1@CY0AnXYP5_#G}v72E?}GK|cbcq2r)N|RxsK&xLqn5grElWEEmo$5Hr zOOex&*QO_n25MrY9C^$Gqf_i0y@~g*YRK`LPg^PHZl41;R_P+zX0A%Xs& zHQF3`QXox%t(~)JY{kOYR45D?@`L!TcoJ;{sTqwYV3Wk;jt%0@SJi#m0%wF$7$K*? zi9uNUX3GUnv!~C{zW3C9pwHBdlu7TLG$Gjq1g?uM7NO!VWSt@AM(`<98BYk`Z*~F5 z(aMeWt)TplShP3Ptj22%uW1L_QCku0CTQtcMX8c{M7cYWd^!Fc^xmYgelvzG;wtuQ z@;}rx#q^bxn@`ScO?Re`ct}hs$wtJ5F%p~c!tL$Lvjs@!mk-GvQEic7r43u{oRGnS z+J*R0KuxX=27E8eiVIs$il!yaL;C*R=UzzqDJA$_rahHO=Kk>O!^_HBB~RbfY*s9l z?qT*0$7k>eOsdWldCsjr{PVEOj1pE}c|JAy+1_*hb+ot8 zy-s!V1A05k@{U>;cw&)-lksW%DfI)Ki?S;~7nRAUO5%oyIelI7U^>6=$p?k{HNX&0 zqoXN2jf&>D*U``c&sAVPP-`?rH2~5WmQ^`7T9T`nmt9A4RQJ{ad68}BvXxnfWGBe2 z`+F$P3$mt$`c)S&f8?ZW9D9UwgN3skfs%L9+owKGe;m#7SX8#o-tVZW8|XQE{gl$q z8dAvWkHN6;&K^JA-DdjN=`iV3Y@jBESWWofp$W37i}MIp+4$Rgn_SGJR+CC*eqN2Q zGduD(g$`|K&CJaDK7_!q@&{$_qCKG4jrF#ECQ+t6I2hKV3H6{R#6sMK67C+R0Qrx> zKv?ZbhACoii)^&_nmA;s+k6ZAs!TT`@q_yPAD!(-_2W=+tbHZl1)PKWL;2;?Og!Rm zQ?_+Ld=IPmqlhe=6Rh8EOXwbwSW{^ZQeWI`%flP9XK&bSxj?NwT!8l^I6X1A&E`F| zX(tx8u_~liaC<}MjmvL+zBGrd$2b}LbyF~lUZK2J#6cx}b#zqxrSRa0OQ^X033%D9 z4yHQ6mC*qaRG9(7ndVdT*e7n6ZNxFsPv7jhQSs9t&8|pR}8?5*^;`cO9RMuUz<6Z5gksqkM@ytVzbr z+sxWUqWex?>0QzzNAO*DU;Gf{`*vM_v_$XBCPd-UHh1UuHTEFvq;L?~wRSIp=ee1; zj#B*^;ubvazEsab6;GpveYp#fU-b_}+eM$<&8YLc$7w*ok5V@NZR*g~0zZRaT$96l z*cKxPdo)E_IkK_3H(ZfyGw&6+Lzao>!|Re_zE0Y}YO*Vy*#%)8p9~)lGB<2|&=c>5 zzo?%3q(v3!UKe8NFUd>sC_62)|C@1qbd)gqC~k$dqJ)@Iu#@GnHeO9bIt_o-MBM;(0Ff z4mGoE?@P&W=cl)rPt4ibL4{tMZ~L+mho1ADT-o+pqH!#y(DMEunO?!|Pv zfe&Hk5XEKZ5Xx2W4@#qJ`KoBuNW{ZI`s}|TQb#5OlQFtY1+~K2wyl%l5~Yt7;5tTG zk=%CXi1^mYc)64B-Tf%;x!uzRlaNUm;~h3;>cRU zDC03lUyc#=n#7Am^a($v{Eg3~^#N}w^G&K7Wte9^v(`4KJ4PzD~d~Kbx3$KJS5kk&A-kpBb~gtsP&UtKjaTNa6#~E317doy?3@*Tf{i z&d~0Ev5#0PfIpJEnu;_fq(Y*cm}cHEx$UiE8!y)d&JF zjn@RBdlJI0R)DNZS6LB4%cf)FXWzSaMUBAKLfM;)SryMCkgmFb&tD$*o?^e}T?33J zx_IIJB@Yr=p6KuenV694`x`FR_QzGTlvQp!It=@9+io^s^Z7Ego+(ZL|!uVf!Qoy;l0T#sXfwb}Et9r!ecX06sKTPXJr8r!_&#I2@!6lU zdadr?T?>;Y&;aN{;qACUNi|fBmnyVWQt5meqBHEeGx0)pT!rA{iMQVQ{-F&20U|XCOrWF_Nzz| zvchIGkl1pGR2~FmNbYeKz_@8?STm1uIS_1J{Qoc5y0N~dkc;S4KY_$pz`T=e&~9D^tZ071ohHh{S8$QLBL$LmP=Trps&iXEv-Zv zY_+TJ%q|8jc2WBt8PSCenI>>-DTfK@%E>Xl&jvRRX#6c>HP~3aZ&^@|2lztIEgoZ- zS`PY#hm9Oh`O3)Sdy?zP50sh|v;Nj$yw3_;wE8#)Qg@ z<$$sdOfJ`|M=>NyulWOSWmp@;9Co=hd|X?_R7ZE)p+vU?+U(_cjMz@Hc!Q|G9-1*l zg)CTlOH^fKjyI~uYYU}UBY+pQeQ9lEI==75qQJ%@iJGy~uhog?=}qm`E}=V3{U#Er zNGHgb$E#4g-}c9_ zEUv9P!^|uA_R#-5c@LhYAp@zVc$Fm(A>)N6U71TG_J(^GkAvI$8gskqi2>6b9=0Kr zQ;Q-x(b_x!oCIIAaa3gFF@n5eFW%L3xixChV_~k<_bAbPP&QdAtQiiBL;Qo;8;Hei zh`w;h=m|yl0XRoUl$jgJm;LUdbFkCgaTwP&XQtaO9YC*HV9`Xx7rP3|HSxZq@%`8G zt_7ONeVbby%4sC}ShM!xHYl$(My@`{N%hN_cT7ldyjo_7m~`)KWkh4UWPf5d_QW37 zq;MW!fZfqcIuu&O-4y2fU*G25nwvu^f6=L#Q(cyuFwcfyeX&I|o004|DZ4;|&y{C~ zq)Wnp_xIU&R!np*e+)xpMY)fB)+HHW%(v{|gTc!i8m-Shn@fw|)6qoE;ibQdU`-(5 zs7cdhOk|K0&8!mp-5`*}1amt}a1cF5a%`$h(CKbBIi?7dD(^Z4p6 zcE_mE-7Fmk5ZGcLZS|I$=;D9IP}+Q+@$XolfZ2Brus^SxWE{B;i1g#Y%sy;OES%we zST1wC1rFQ&#d#n3DAI~X@2HImJdKHBZ|$MR?gbdraE~b_m(1Q2JpfPC37PSn{&@4* zyE9^Aw%@lux`%YyGW?#rRu-_owo>c}sh|mjY?pMiXOd6q{TZ&USe`BYt-{KiFeIF) z&FUXN9kirIJMd&sXS?NS4}VVMPs^yv8q_w1A`y&o4SUy2f~ibePRc z0F1FahAWPrG1jt0x~Ge)m0=Y3U`;BpWg=05t4R&}*rl*7K0l=_XbhY%G4`PHue}5N zp9O*ld^gICHEZH4H)U5QSDIMOtBUZe(fVaLWI(g&2lryzH=19;YCw|3solhk96sR< z<*E(Dw06NQU}F>7olFz;$cfh1%H7s#UlV3}Pi@kmj0tHzLnTFALd|4{pF&`5Jh8lk zOUu{{%{w%w+t-Fah9On8mDCW!!44EZhFJmN{2ZQ5Pe6U0s=szOg`d5~fp) z@G3&LWV!2tlH=BZPpga%BjH}*p-9+VO;J|?7RcHN$8=Z2RcX%vHd}2L(`SzF?UCQw z*AHFInSFth$E~#=N&?z2?Oq+`hzH{oZ9gIRiv$S&;-87OQ4b?NI?S7u2>cSKp>(Kp z_my7Ywf7m$6Xmkm=zHwqra6*xIRmAlj7~`1fEU6L(kJUTh!=Ijb?a{s7u5|*&|Tz2 z4R&p|0FCjlN|36F-E56eLW-P_3x>9^>k4|$F^C6zhF%_; zU3&vBdC>E4GH@^+Vj5Hpmzq@qoZ+?1^xkWOC16%_pA}c%B(w4GWm|BPYP@zz72^W- z(k$6C*V~B zYU$D|Y_}%06O8)_T8f@rgA9xf3C7zvTM6juMU#?&l}XK*s_Wq5Q1KoIS(Eq|wmMzs zvhk9dKQ?XWV^%L@%7K%V&*urJ8I~;n-RuFRRiv}Q(=pe4EU&?oM8mFQ(^Bd-?~+39 zGqdCtLlXd4zf z0@FdT<=1{G$ZS^2y>TaRoAJWCieITUmHF-7BQYAyzF8pblq7ckX-W8m;+9Dc9OP3|%hq-Z=hH z(7me8XJS1fXFT`^Ml>O`Ro+DSe&-uIf=iH{Lx}j1K(5-yWlWLoUrJ+r2@god&SHWl zYX%~2oo2%HWXHU3*maN5<;riyl%^ft z$#=@rNuI7lYGRhl=WoTnw2Lp6Err;4KDxigGP#iACC@kWTb?@t#Hjdi0Y(qKu{~mN z<9JIJ66^oFvQYiZ+m@!us@s_oFzyR1UN3m{$|mOx5*mTu>I;W`Qdj&5FOYOlHg!irDI25- zdZQkV37OTBOqA)>IAtCN@BJ395XPhn3tg7mW;46jF#kEb0_Q1yU-Rl-F2I@G>Pf4d zUp0c=c>zMDLpb;0f*NO4&mX{1Ktj|TrXO^%8^O{O=MXZ|2{{cyPWMl0)(Dzb59TO~ zCE=Y$ng(V*)s9Z5bGlv5Xnc*_tn(xQ zsk>2-mTKI;brDS#Q4k#nkgzs(SJ609dq&&~?kpuF5FK7ujAYK?2vH->VB^dgBR9$E z3*apmm-p%LD-}1E!ZCnT_baJ=Oa2L0!8%ul_6}agqS)#g;F!G?sYj%gq3w$E|-%N!9iw@jcGE5Ih8t?E%LTq{_ohM)h_b{M~_TllRi%^ z1=?(SMjO5X5Djs5SbnA;QJn~ciFN!pSvUontJ6QQ1{rLeT6_SdV|Z_mMyoM;YDH`A zng)sg0C~2bGC5j&;wK>EXz4*wYC9T}$97TQ$);=Vl_QB=F1~_iz4o=`=YHn@deB;F z?3=dx+LX$$G zYg)$G#N@mVOtxndstmi*1Gx#Pr_i~G-?a3n9-+_b@#%130}Sp9HhA}+zaY|bXHX># zz2;jnIc}0~mm+Yadq9xwtLTEN`G*oIdy!Wl7Nc+A&2cpB|(D#ohbI zrv0yw7xPJbG4Mau$OG1(RRiM4%TjH0hi+2h*OV1@1{it;)k8+;jl=?Rkv;XO_BIeX ze&p73X;1Y4zKEfzq-}_Jx0|X%0omRlPXn;eSrgR%dnDw_QgbJ4q(h=8Wg;>f%rxfD34YjT|WP-{HT=d9K{= zZoEY(X)v@5bc%|*WgSasmIcd2g&|3uBmYKTp1Idzv^^2HE%|G8>IJDffBe=jHFPy$ zcMJXnfM){15(A^$V=dPavKZITP5b|7`|^0G+V}s`UY2%|XhRapzVkd44?>}kEtQZp zOAN-)Vv?dPS%#UiP1ccpA0=epXOev#jD3vR`JPds&*S;y_s8$Q^Ww}o_kEx1zSj5k zzH{B$iw(zZ-$^gthZ&x`mrd1XCz_4H!YzLlGzO0B1?0rgv&|uT{nw2E*z_e#J)-`D z4ZR}1{We8zycoWXpz2cuB*I()d>wHHPGh2ICc>Zt}KqJpe%%y9+@tO8x!w+ zW8lw~^Xcvpq|nHvw^Wq-4H@HJU4Kt1Be9fWFSfToKTCz(Q9$7Alxa)riIMLtOs{2< z1sMW5KI*O%#U7yxBUd%5Q}D5kcx7u}3_0Y;@}S}FF##t6>%xrSTP?ei(sL=)OphE* z{mY0C{Y^1%J0>$qIvbJTfWxLW;n-I4*_9YY@Yc$=PxN(+z23sXOWPOU)Svt*T>JOf zsH%%I4Z<%mRE$!cxPmOkLt{dIsKudu%c4{Yz<8ZU?N4>MVldAA3c{B0 z!#ZT6B7~*2y``1>>0!!KIiJ`fUNMJp-w7Kpz9QR^nAc0D;RQd)Z+4S zN^|nw9lvT`C}Tj1QIx4}g|&U=x_5-+DC%w3C!eqwSMPZOVZle<;tqNv-Kh^O!o2B2 z$II%&a>szjYtAzGDz2y8=(&0HdM=TgV+rK>T@%D8HH-Nr>zKXuTv4*}s9<6n| z`oIF$a8gZNsBYh$`(&Q^@_+@dlESmrX9utx=J5|j0YugzXM~TEo$!HkfTg&wS}OEp z`SE-``Quk1wOydG$L<2!>ve>?vN{U%@!%fMInXz&U+ObzAK=~>g#e(sHF?-@k);nT zn8KhyJ3GZB$CgYf*VX1;l(PMvEL?8qaA(l5kPL_{vx>tW?k*Q@zW3Vmi$&^xTYe?P%)UjDY1 zibpn~;?SlM5_5o|ZT3C!7b~vdA(4d%Qes;t_I>89T1dX+Vl;rgEBGdQUj= z-V}w#&u_ESv>O$a=Pc>8ZPn8~@TyAM}if?Ylu%RWXdaL!53aK4FuI0)3d26CKqd5TZ zj26*MNs$D6abT=bJyM$M8~JY?YmOQ7{`&sbtZls1f46xVWu-#APclKn+&j zkMQ}}SAW;}(Dkt;g=zdXSDbR*2u4%W$8kyx!QP&{)2`RM@e(L&LKAs8j86QpP+V&& z-)oMle0B`h$n6~sf026vT`qvNJfCM8+j#Q_u9^1mivY~b z)PsC;Kffv3vFYNJrh4sbreXo+QCA`;wer}rkvskBZ+omdAi(r@9BtwS--jGbCty*W z1f1iIUZJKlMS@aO*%`BH+T12VW~qa1LEUoF?^8bXk-y^JaBIrQ6NLG>?zE+pR02Ix`mnn~s0SFp_(MUKO$xAUrkS}DLA z|6zg6n7mlejd}|BBNW>o)WM`=U@xmfdc9}DmgMmBV{e<~Wn=mTdnB__v{-A0+voQ3 z&ZIS=$0hy>I_gkef3Gwp?lyd>@lugs$*f$z%`M6D>>pKG85HO|ao{Ub^g$FdMcVx; z0a0bfD)#CDCI$~|q*^)b1(_6(dF{yfbG(iwH}CCPl~~7r`G(Gg757xO#`TvJy_a-~ zYh78WkS_=PvU)3mer(Z^2$iuWTP3(*bOYFMbq>bsxhf{YVbXH`2`+ z^+lTyBA+$33G)d&jA^2z^eKKv>y(JbVd(L`dkb}LRzoSApRv2HP!i+;xgrdM$V|TSIM^D?bOK~>0s8Ea3glj>QBieC5CjedB*=!~2i=r%q z4tl+Ew&*fb(>SNAQj_BjQI~x#q}#8m9_B|4IlbE2rJ1o_ZMeCzT5V7P&+&^43lD@J zH7diz^qJ~+-vDK1S!tQ7-|0K@`e7jf75Q;Wfu|n_fgZonw$mQ~Rquk(h@L$_6JQ#E zCh(Pd#aNtZcUI(fL-72TA;G%MiS@J>xbt1t#+b9b$~^C9?hl?S*k}LXZD?~?UxM~{)Q*# z4A9T!j>L+YtO3Y=yDQ9RRaEZgY4=;6YhH<6QEVtb^2^}>{&h!#X%n2|Jq=wKlql^6QZm78^sLtu5P+e6?0*mL=lhtSw(c5k9HW0D6N;g!Hpbo-`=9Me8(#5qUhkN& zTfBmpR?ylCdrATo-$->~6P5Kze4_G7J(1$@a1OsMm`f4drlJl#C0g3NG52DbNM4Q{=_Y*)V| zr{T8fTP@Qmx3wo6mpH9kr(j#PubtOqE*Zb{k>G9s-^Z`b)PeqnqCnENPHao}nnsj0 ze7~4=OFE~IK2F1-eQ*Blya6fsQ=m>O^%sn(X+Mru2yBc zZfDB1Hhc9U+$}iVl7yIeCDIw^uVSZ45xpPR{0hGcr9C6oTk6C7C_>-BJ1`-K-Zklj zp?MLdqA4*^zKRj@z=6-td*`)F9(uqHy6?}1)zv?MvbTPj2u+BmQtZZ7f4Q^2S4 z!Bgtyg-rA-&aSk`rS`9al@()wM0jPK_-{8K*o3cC9Y2SwU2BJMIo@k~dc4{~{JZ2A zszH*%yEAyQvTVqr8S-2@no_SUiWd1TO|ZS(e4R%}ja`E$V}W<|+W0)` zmBPtr`VLYu>`Zd{<&}S6V-&3v!tjr?M+ZM>%~fmhrRKO!vY->d0UH$hQ*>AE_T7alKXt9j(#&V$fWgsN9LUD=;5+ITF=veeMi0vqXqOJuRl; zUEAXWN@SgA&eGuK52T*;qb%@Ho?a;a zbQN!0SOn~6C*Qq?*Yx9D14|N@%v7EBW4MAB=vF&DL%$O`nWu?OZjvx|^t8#Ix$7h0 zp-;bomzJ$Pd+10kJ-#sMG)$;6yZ#OWEoWdo6DDJo=$8!t*i_~2%4J2A0=a-pNZl<` z?SN68hkvwBw70a2bN9)0RPxnWE;5Hm%8Rv_3JOfzwFnWk6T@29B5Em{OX`RH*xB&i;ZV7l*n<$yTQg$zcMRvaG=f| zpA{jsSfxG|#vSio@0puoQ<`G=XyODYUg&LiR^qwZi-mmlm^QGvVk--v7oAs_FumpG zw|mz%*Q&Oxusa-;n`3bXssxy@flEwj`lDHfsX9)*zv{_{`|HT6q8-oy?wMWudM;>9 zu~ukrFQ-MK|ACBYp#r0=W#`7tj<^n#C;rZ3DMEoA;~J~JULTb;XWGpg!MF!>;2ch5 z&53opJ9mc#D)k;_;YPc|TK69YJ7I^fm)qZz4eGuyN!54TYt-_Bh3kRQ_OYw-KfZRH zA@*cPALMgDfo6ky=a~^wN`Ai~yAjaF7rClOnHI>umx;nA3^x-mw-78Y-#D$+E7vCr z_0e!1xDL_xPRCo43ho_ZuGxU`j!BPBG817Z-99kqE*E1F9<}X}9)}Fcl3*eTxNntS zZG#R&`WpO&XRkeVClsVhrK1t%LqKu^?Wn)P*P2YL>ukbv9kpF^7zh6LzsKE@q7?2` zlcXUztBib^uw|Iy<^sIivV@lx?^QSl%bsAa``mpN_XIhI42oM}7s$y=)~bDgE;P^< z(j*7-1xk=!7}%}Zr&>w?ZaL&anv7TgLalu@Tv+``04OhN=OPB^Wh&LEU-z5~1694A z$pMZk!pfX*g%A^kpW^pG;00r_=?YWEsA!|ROBu&S9)Xl@JXlWy+A#s%PGFWFCJy1q zDp=Tk#iz`rvQ@+ov&Ixqt2i;BoV5zJL3q;$g6SwU$vX{4Wx4hHBlp}|9gdQxmj8un z|I3WLVK2{Myd|Clu&FYq+QTx+kkmZxHNO=n<<$O(Hv_Wlv8?|-v>t*Z{(^-jy7 z)l=Um#rjx3N+AUCjg_D~bmF(L?U%j6g#J&&UDeLLToS#|r?-%{ zy+;sr_u!QQBmaJ>iJw?(Aer`wzkUrt40xlaciQaWx}^)P>M4Y}3-5lre5D5gufWhH z-eJAvB5$9~cJz{P#q#exU$CECD{?eX;J0#G+rq0C0KQJ}Jz|B&0s>_=b`j%8NqPX) z$>&C~TzfT_{Bt1DlY9;sZKweHyleK%MO_u61=0?Vv^b8j*BxosUZR(a@=vcm+0*eC zD*;b-4Ui3eODF~Ur7s`UT+b8%z548Ss*&a<{b=vqyRY&JUzgFqYu%MK|M3-XlExt3 zDtJUwWPB@UEP=-G?1s^j$xP>Zgz)Qr##l9yWk9ytml`lrSY57hpy;GH(c~eM_tX zl_mg$A=hKj?nqk94kP@j?LK_oFWM8CZ3R8R-k}!(De}1RMAqN75J-_7kR20|b*U6p zi7HCA#vN$F$Mzo23|$dA&3PR$V?&?Pt1lKtl~F@8?Yd)-eX}!4?*cq$ixiN7k^9n1 z--%lMv)cXx&^oMw%0qm4*CA|Ky3Cfbnd}Jj?ik+1a+gN0!~P#-IHev#Y3DyPj^yQi zI^HQ^>LFez1@yw^jTOHVXvT;OD7gHpG0U2qEP$7>Ep|06tVJt8KN-*fQk?dkw3ik~ zij!r_GoLV4pG>nN@rFE{(%I{*t>et2$xz6i4_a zrtj7#!622yU5xtzjgKcG-;4<%^355K5G4fglyY;>dXw5uo$X(85a^UQ^STrx?&1Dq zHb1I(ml^zQqu8X@_{UrBgsjP#w8b}yApOyfj;e6F(wB?Nj?JjwNUPiLm-(OdvHr;W z4MRIQm&%kT^m!>*+`cQ*y$?!#xT|nCsR=jW5%VTNDhW-#`YEY{cXCrGqCCG$3i|<1 z5J>Kkg0sjgKs7wLdUe)<(p$#LJ9-0?F-{R~WM^_a^(7u`8ZRJg8mzHo;CiU0=%ITYl%Kmlt0A*R3ZDsJz&w>69ecBjMhBG| z#+)r0X~}Bx&ElKjd<_s8%3rSSzmWT&*AiZ?cm}_Hp(M_iJQw6X=)sjo%Lp>_GP0ObQlS~KgM2Yebw0+9I8LBz<>A2nNd8KaI z=yk)W+{EU#gRP@vdEslX597D)@!wJ)nfR>+I8z0A)_is55E$-k0#Gy+y&<&S5Efc zFI2Q_c2#TgiufkTJu6(=^sS2>+f`+omWUxV`VM2v;$l8Vc`3dWvc~PTz#`4|t*QQm_3RNBb6yc?p_t&R$7PU3n>>y!epg5HI?GY~RBE>G&*F3BaT z`<>I7Ilyxe$v#8RxVH(DLLgCH*@lZeJ%70wz)xtYd<{R|r_$e<68I7z-H39Dhxt{J zp@n}h8{K!aC?=YFwZjYg5bE==qh<$s5aV8Y@;^q#)dPY?ax3eTdAM~-owNFNL}j`A zKbdMs_4Ecm6Gqwt1W=34HKQw1oI|5~R^EJ=z0Ma-{w9ePbHTqB=qep}Obx`O01;M1)$l|ejJme;lmf1An5CkeDEIaRLZkkP|;l}&k^XjhH zn7OD8=R8?v80BJOiahqWLsX{J(Gn-{m%;UhqZ9Mx7APn;hPBNw$&MMm7AD)Ain27G3%h3>2&*NhB5 zUoh_TMBhzERp z>Z37nIXp25}3Nx!HoFUeyOO$o59@jb$q{r2)qm==F45%f>W z@vub?FHj;{>~(ef6MosU8K(oZ63jVYS)c^sC2)M&u;Hq53wU8py=I}^1x;FTwO`pb zeeq`42Y4S!4`=Grd(CMwg>%y5z|Dwim|~DC%?}`c3FVPaca{C#f%(dc?66woA7!CZ zy<_D;;W>P>hOaP_xOd;Yn8n2&BR0XYX&DMFF0;SNIfD2!j5Y}CQL#ptLRP#y%@^*R zVNQ9JIb}UpFc(kBIJMoY^IPRCf+MB8h@+^9F;`s&OUs4#@=dCbrH$G-$qI9nzYKD9 z%pDzH+UZv3J5@$}=Yryc&3+&hkGi+t78!PTj%o9WQJd`UQoeIZx1&v@;73stHK5%1 z({@%;C#Pu*CtV(5T)ZL_|J7n~=nY?>79JNuOX-sNhfJR+2PMc);2rYbK@=$3?ljNtF6ZTIgQK{PPPRUzQ0;S^zeS0$XYj2Qh zL!80vvx2>Xk^80rPkb=st1VG#So@kv62~`Mor-;A0wd@k<_dd_>or1>u~=_pt8R*V z3^~JM5tht>x8I3p32+}H4<-l9vkha|L@{A3(x*nqp^t5{1*~(%W4HtCta4p!wBcH; z_@WS(6-Bq8rW@>z1|bJwBPrO7N=<&yMMzB0Y$a7Yf?V5HpqM+mOeuYd^Z3>Z_D)4E zrog<(_$3q#pI=UHz83~Mot3sVZei3LuQ7z@m7iU5)V6TfzBbaj7!0)xqHt~tu&o~$ zT1>uV2Dv?^)`+LH)vG7;H7+!qzuT4)$S$Sye$b`0N1psq*)aE3I664wZUC&<`C_Vs zW=XYNjCUa6ZXD%{u$Fy>s~!EWlHLB`pg>=TJB;n>xwj={UZQP$(oEzsOa#phtS-uN zwi~vEjBIb@T#!O%K0R@PRo!fOFt^9XZbnm3CZ&>WIVLGv51%=u?d>jw!GovCK^w!^-Z`8t4?rFm_JA9nr|&uf2~fcAU$3wt(4R4lAk~KU0%1G zOw**kRTQp?WNk!R8+h6_X_J~GMWy3ZinwNGD-(`S*!UDL+}Krr^U)v(?M%JV4GLED zKHg$Wg7%p3B}O^1VI^G!_a&bNbLR0y-~ax|W%?{)+m#~QKl zGSuqLUZBqc7YBs%UcMKChZ+&KbT$-}^P?)U%-bc5H`U+63y? z^r|50XfH!ir4Z$t_W|LPb9<=qXknRV!b3A=fFCrH-W+V-MLHAaexi=08?tAxtj_E^ zI5THq!0CRSw{E3G_1Tx)N)KDQXmM4Z&`cc$1Q^naEO0Ax^h2=Y5aa10~M^BAD zyG${YYIAUN6O6-{5+rN(B$>Svw-CXBRgCp}=U9v+=8f-Rda5v&?1q9m&&r;7xeRM~ znH(!$K3igHpx5*`4L^4aug2}6pj@O(!uXK2?}>f|FVr1!nFKFT5&n3-QrFS2BJf;Y zQ4fbMMjK5ACaV*#4BKXmU*`=u8gsa4?0W8aOp*JwFkEP^ou1X} zg-3RI&>9sy!*$w6_D9{bNK-B0NYBu$Vvt#X3vdpQgYL{TL1;jVqvSEOi;Y!0&xtqt zmuL-Cd0HLx3`l`;o#A!NXKG*vz#Jl$*rSGB5wnEDIf%mvBcqriNi~9*Q*eewi{57`uc?9AP zA!s4jR#Lg1hIG0ddj_^_P|G*kRqO~r6JM$q42KTM&x-|JIQ;mYF>VZaC?_mGMt4rUMkAOM}J{%-oGjIcz4+pO~cIy`iYc;JFVlC;c5*7M zXSyXZrlTDZuIN!Xsx=daN4&1rO{H9AJhdZ8SUc&3NUDTmKzc?5TV7jCU6%j|gAF_5 zx=eKkJa^0Jo0BOP9a0>B2@u8PC5hpA)L7z&s|5h{>gQ6^oOTU|%ZTJJgXXtpgzGE} zzoQ*Pu^QAe8QAnT9qtJy4=4AP9z=hU2Keto0B0S+3vMku1*}X9reSn%%0A_MGMJx1 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 diff --git a/www/images/tmux4.png b/www/images/tmux4.png deleted file mode 100644 index c2c279ef1ddf880d378f9645b0b7f916763e3541..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 328784 zcmZs@1yozzx;0D-Ed>g+rMR_Fqy>r=mjVTfyA^kLx1^L(iWheR#frO2ph$3c3lJbU z1Pu_9pZDJTopbK@f62~B#@;(CW9`T0oa@OBS5uKA#HYf?!onhy|NKb<3+qu178dsR zCy(#<=qe{q-oHIISCspNh56^1(_WNtzX#9tv%Uuw76HjWuLoG^nH2XsaXsagWpJT5 zG{g_zGpU-7U}3$&lK&*B<-2&0xx88Di~>vO~!;C;;f97pome{SDNWl5hu`S$96ZKc6)avuF(=V?p6 z82`Uc`&*Kuov0gVDMaqf{?5#SwXtm zldRqCwa}oA4b^0#OSX-X(c%>#zq_kvSmqscwX0rOBcQSnid`iGk zmU<6vsbS)AyQ=<0Dgi1f$LA-hw6GBz<9icvNl)IN=gtWB88Kd+(;6kPX`$e@kr+}- zGa2@JM5iLRakf2EZ`E5yJQ@q$|Mo!2`WyOuQhfiP<42S83d?B3k@hqa?Vk9u?rSoW#O!FNj*F zKQy{%;+-@E%~em)LXby!qG^Ll`>lx*;A{T~#=}eV{;dOv?fHKCjsvnI*2|E%+@PG4 zo#xHjjdiEi%5~ZcrkvQx+ZBjVe40WbYnNBxK_M-H&Vv6bB><+@c=vu&;B?H#vCAon z+r1*%GxO+kyispbTTy^Ky)Vx;5sH9P+q` z4z7r;+x3I--X6;+qpAP4M^nogHJc~qK<_A&l4KI*)ONj|ha;*v^NIKJn~Uz;>(yni zpMMvF0=-OxE$?G)=0Nrb7_cro%@L|P6~M3tthuP1EopL8fAGBm=nU!Bvi!)1R$Et z@~i7+d7PlHC13Y`chUFkyMN8zJn1>mIp=!-IQy~W=OrUf@fP5{DI^Z40rowC*1AYk zmJq>oUiPuv`kvT~of!W0M`);ZByk#Q_5nq`QaP4<+~wSUDKYm+bE3WhQ&+jo-vV78 zfjaESMWpFEb6lDtaY4Rzs~c}KPzO6c+;@koUc5dneLJ4It{!@@rLp!;b}*HSA)k}^ zNLBX|*MQkm@WI(x!IIA!1V@R82KXZuM3+YTtI}Wj0QP&qr6rMbWwj?ZJJ4KqmJ&qpqCNd)5pYwG5X)O^?d8;C2HpvASmpy2BEqo=mrI1fLQ%n;H8nUX zXyKeVG6tHvuHMO~g^GBXnws`hOz~VIau?VqVfPw+lAJ+)PM>_46%vHXuZ@L%G3lS( zdzY&65vp(W)H({XDCaceQUFG|iqS8%h8MQ&7S+oBC?XOYkW-CT+UJbKd_6!<6aokO zM*H@gih71j$0qy+Y_NKjPP)4}Pm4$2Ummw+Txn+lAqi_~9SflmFUsH{+J0~PRL*i| z&6ds%ly9$UdmSdl)6BAbaz#pUK$u>}O(^1>bLCr@F0 zV$AP-Ij7z8mjtkiN#-wdvSMcei_HMFvp^ga;Vt1iA1^xbm-sgcr79C4lfd0XiX`iAhJj}1hl=b zCtP~hai4$}<|>^SXK$Lnp}il8fk15-$#hl@~!s&oW*DN2J$|G?oVv~5?%nc&U!Nw)+jF6_R@ z%pVG9rZ~dUEU`;^7#lFAE)(3`N6dC@w0jmn2s=xM3%J)22VB>(Upf>>h+K{F5A4!@ zQ1+_oI?Q`bk^h?Fjms#TykuC%R zcS-$OqP}^xq5bmZhewypN!wH%dXeIBNC_u1ga%7s3yw|TsE#6XVmu;Jn|bs zxph+gj&#%^n=<05c zoF6BbFU`X<-+?hL8O@D_KYM)XiG7GRh!YXyjfV|0fMTd>KkbHC<8nW3ukoTB%f~as zqQj+o!jRcppeDx#=y(w_`YV#eMinuY?Kb%L1L|Vs*4opSNrLUpdI~hOWoESH@BH2~ zo-eb_8&hwX0)J&8{0HBkV~CVj2KeT$8iR5_tYBIIL;5w+iN4eYtfH_S?B`L;!1)up z-&LwN1RX_sRF4@h=!uf@83d4tD0KRxbS~|*vz<^nH?=VOn)to--ibF6jo*PSaH3W9 zq()1OMFG(DWR}`RZyKl<7a6p}D3+joBD1V^&=nPZs>WwkiaxmUILTfy zup9Jn{aqSDU)L}tN(tHZRATY1y(MPD*xI1KxH;)l3_rn~qjn!D@S>XT*xP>yd676* z-)?thG{zNu@;yQ1HNf@Ewp8f;5V;p#U_t5SrIM}1FQnMyHDMJa7iVZ=BSWQ7&E9nm zQ@Q@;e4*?N0O#RFNEXumgZR?nt-T*mI%%BU9locRHF)(souEMfb zl?{`Ad$S;nhEqYQ_AA$?<{a~<0@!EPQ5in|dSL zIqh(seArr2J9k_PRC@e|C|RfnnB9qu$30r~IJ zBEqT^gU{cX01<1$LQ}lx*Q=lzpx!kk_R?w&^EQ$NS@8kgHO5}d@Yh! z-meZ$xw+|@1T6N|E>$K0v%7IGT^LovPm4|JhW7-9E|TvI=A;aIO!KBBE%fpHN**Gc zEBJoy&i?GPNItEt0kU1A!0Q`U5Y8oTVRDQMDTciQV^S-8i+t&#gQ-tMb6CyZYJT8$Shd|z>XU8jF@NV9KZqODeJ|WC{Uqzph{^3>K%uVvq!GDzyDOhbf22@`a zE6WJE7<%V=q(@|MrEAk~Z&LW8UmU}PHl9TI)Nf^I1~!@jOI2bJ6Yl^93v&YL20M=I zW>Yth`Du<}iOwf6(KDuf$i-@7?+*v{#u%G$2_3|efb0v>z3gLvp%_X$To!Y<64nsGXaazT8AgLwM5H9Ls80?+i@8d-r(qS$BvSKS zQq9345&-v;-xQJ2fp=(O!S=_)IHSxMUA`Y*pF(aSHhcbVrcY9& zqtHW8dSwt}$yQm>KmmN$itg(xzY8gNI{bt_;2c-8lJaozqrSWt#=Uh=j2LXY3p|(y zC1Lve6y_n_S=gd#?b%LATc~(r982Fqi*fI}h4Z3RJR(Asn%ai1#Nh1@Pos$udi!qZ zt>GLFOm!=3E3hsd>@ELE*5scyidI#P)o=XR)OyK9Mt&f5hkatF zDKfxQtxiPr$xR7tUH=ReDZ&Z>u(+>>)M zHia?iH5YBG=aTMU!{iba|46g~Ft^QeLLaI^efVdp#kG2Z0$jFd(-YdZ5R45V6`QkI z?{k;XB@r*jnbT1#uM^y*eYa;YXm%~e?V~1MH8;?{!o_8|>#}d5rtxqoOlYAGCL!$F zWq2n1a&jU(^ToDeEodm{&Xm-IoZcim`f@A?`O}gfBskf6a`VaoLw*b;z=pNu)qHPd z;~UKs{_N3ZeN_BKHSM7URm{T<(ke{v(%1*ULK~r82WD)dW51*vk}?8ZNRybo2inb* zpyXCS134yz9_n6iBCm>l~;eKe=S#fF=fB+6E0t0^LO{yi=qLN61_afdGAI# zC?luqZ7L`yKihNq@=Y5n6A59r_}jT-o2wgU=MTA`Lc&xUI3xiJu-FTf5dc*c58^zw zPlc9JgE#>Ii|e~afqOvE`r@jwH2Z7cIL93FaxV=t)}cqG_V%Qh3*WveuC-f*izdf^@|K+$!<*pN83`Pnidr;%_BFL)DFw=KVX%8F zW%eag#7f&apFFFlc@a|J2%dQ)zLWkD&uPtT%b07UVUfP>53u0Sqj<-HF^b}`_6Ndc z&y$1Sm9O6O1D4P>aqdHi+nW8c4q(-;L)7bzS@A(RfvvG+Q>#*_0tVar4#IHR{^By1 zLRwTJF{hCIDJ34w&=IwNo>0Awf8$9o8Q@`&xaUwN+TgW)iLnD;t2EByK&EV^aF z4sm7{q#mha+APq~tfd=@H&o5yOz&Gd)GjZLqDd3B6yis*d&wtFyP=!IDvg`=m$SqK(a?rFb{=t& z!?C-QiEH9VWfhHkCO~$4L07iN96>>%ne4Fa-|jS)6sa~2do|}4GXRr!jmY{$n)8UV zvE&_kpDzpWb2&2dGZQ(dbIy#Q%@(xL>5X0Rh5q|}nkD?FgoYSZ$QPBYK-kqHMFs zaQ!TR<33vryOjB2$lo>^OB5?cg*aq>^}ba^t{oXQ@j2~XE4cq0qqj3LuaU7*G!i>z z=cS=;fUQ5w8 zI95n|?r6*6*1k*W8ET?CkT<>t>crcXW*d=lMsU40>rY2Dd=l{kKU?CQf!M0%YAnhV zG1)^_QMxh$-Er<%6jJa~Cv~x9U&mDy=dbOaQ1B9&tF&=^(_2n0P~BVVwyhLdu4_*x zGBbKh{dc+kShlZTI@2>5v9zS{ns+com1yFNo}(fl(yRdNsK~^o>k9CD%=*3X^ZK8=`!FWLSc9prpqMo+HyN{!U2NjoQ}1+wMr7|t<=84oE>=Vq7mu@w}J2U6J~(= zkfO=5%eeg;pSSx(GIKUUWuWp|3GX>b@r}ao@`$?@;YGQRxT2;a2l(BkoN&{3rc#0otX0BMC8cJ@oi8shVrxS0xbmOdkWdI{j|0sufVv*kNLicItgGz8e+3 z!MQei@yNp;2G4*9r;aOgDOXX9Y4Mv_UEL@jJpQ7pa@xM`+|ojHw$fp4Y(%zAsBHEk zj!t~8@GN7=hWPCm*6t&3Qx>oBu$a@N3inkF?M?@N@?YUZC-jGutIY+L*;pfKEOzf3 zk{%(qi{qFE)&d{~O|_P|w;A3wU_1MXX;HDbnhRpyt}x)`Qy>ryXqa&^Gq}^zXAoC% zSw<%uIC&{MmS*^W@+#REJPyhY8eOaDmzeDWiDp1dj$GC{f&`Q#lG8T9K=k?aRQ?0% z(;*FT5_;R1Y;Ghi>)dYYP4Rv{#~@rxnJ4$ppY%pDZI8b~qa<=SI>X{+O?sCOD&D6F zU9v}S-@=WSUU5&1saiZZV%2aQFln4T#$RChs`;p~p6f&HI3x zu|8sA+HJ270=30vfnqhF~C5oH=J z%68U2z3{%V+3WT!csg4o>&KJSrF7-?u0DLkKR#yn+5&QM%f68@@H0k z(pANRmM@X_3I=6uBOneLVAltoNp`6~tW1K-)c}_D8+o+X3;Lkc1Se7PnzVqw!ca%L zDi;Yd1cV0wr;uPj98xm5hq+0xv))k`t~dis`KgW0K1=Hv#6HLd*J|uYETz+inyipV z>vK~2=EB6z4QM)DhY|TDjHNXwr-zZ7T>|Kf7R%=4d;r&U3H^EmSm~kMX1Cq%Oc^{#hKidPg@t z1#_xHMiP~u0&CArVzB1Oe1BSL)PbO2t5HzF4&41b*848O=sL@Vp3NIm`*43M6Oz)~ zaWD+LPmRu~_MOv_3AD~TiJC76ZuleNML9mU~|Q zt{1L&&!QIZz%NfdQam5}H}g5Q#f=?4nOdGw9?BoRiN|js^QEA+-R^+Ij-cLyZLvYX z1#f(k_BL49SXn=A@~mYbbp_0$RY8Y?bv_B2$?kGWsq#Q(b`rEP~zztEcY1%y59 z2Dn;^wGvBkQ6_uTMTlBEq7{7k^V8$&Mw{&Q+=*%JyoAU@`d$eFhc5RyusKLqfP6)w z;`A=|6+#!aw3R_W(9v{pGFu+4B`kJOQTnFr)AjTfol$LG08X08U-lR_H#H71CsC6_ zs{lVjWm5c|w<@0eXW?+T#1_6UN+~9D3#QuPsV_8#-l6DrljydC{T@74XpCj62kN!} z9<96QmV%Q^ZUJo6k%J3#mfJr=BEz5W!aziOQPEFNdOn|*Obzs2#%_Q@>rI>YO{&LA zGa}0|GaZ=cZ_p9I*1$8y1unO$T5DR7AfgwD=N-s{srbN-!Rw=B$;U)R1j_nSLxm~~&soSKUnmxGkGl$K-EMvRDj(<`^?kkPur125 zTZwyGB5Lmm~MJ_t)V zOFTu%{%<4;BZ*BFIBd1UtdE6d;EyrNIjsD&ceAL7;px+;XOWQ9C}uamc7mHrt05q1 zl?50vp#GZ@m#*mjnd-miKKqN^r}uF+0zgC~JUr3x`rJ=Oorq3IcBpvjDbdx0cS%}z zmM1&9q7_qYty*PL|Ig@=2*Zyh$$!y{bWgEIml za<_i2!XbbG8H*B0Y75U8WKCP!O^stSOs3IOIF+68h%6rsVX$UZQOpE`g%o+T_ z=fQpRM)ie(j5Zc-O2V7k*s(YKJe{A+9$2xlV>W#S%Eg=;ju)d#_ESHqt8V1gux*#zC;j6a%f2`CjJzYUy_69{G$X)X%G1=Tiab`?oz!@wYVzV?J zvCm2**!g(?Me;At{r&2@Ri0*mpA$A|H@_;wVXj6z*BE_vEe?5?Xe0N!dsB^*MdVh! zEwrq%^z~+1{cX?Ylb{gkp7^y(d%+d6cSC3L(HC4QbVr+5PGb1V2Bn|R8>htoOjZ@d zh{{D&Tslvd;Qx7bN<+tJk;6JITa~-%o_=CGz8mXoOU6vcz8bQ$_c2G{I&AZ zN+RD-OLhhq-;cvA##h0XNiJcD#`{;N&ELhU&$CKhhiS#K=f>>eJx z-YU&`caU*KB1UTS%2~=h)zXRr`VAMb6Xi*7^Ng5Ufq+bqq^HF2S#o68d>YY@L4o*V zf-)(;hVY{C)xei8Ig>ujC}kgRc(#VoDsS0-G%t&^aJsX-w$e&z`AGz_VkOCc!Nr5u zA`+n9CiyK}K0P)n$2X*Dg!_eEh-g>@zk5XcN>9T}^0s(DJf^`r@3G>aw-<3y ztV+!9Y1?}sKX&P9(^)_b?OAF&(9a#VOs_nHzjTdeX`YTS19=n7 zJ?p}heuoWw`V}$A^P~0FR_M?)K$oNL*T+i>~k2h%Q_xQ$@So*6NS)EvT&kbsO}Iqz#Q(d2v5r^!ntIU zF0`5wJR7psDTutaHaSKB(Slh;w@>Eyp1bz{r_VOaXNnm3*eF~0Z$@>dXB_SV%V2Y zJ{+p5ZbzBw#(evjJhc_CMdczV)L}u#;QM0=@r2d&xDN?!*(&k;FAbgNGQ$*@e-!8Z zomQFgI~{OEdS*sFNZ%V276-avi{NXX(}+p7If`BMigPJ{%m4rw6CCJ$wFQ0T_-8i=JH}5ve~Vaf|WAxFQ$z+$x?# z39LiidGn3Xa5Pb0l12mQ%JtkH1pJqWf2&Gnmv~U_Ukt6M{;I?y<+F6}YC;RuwdN`X z@6c-NzqNa0h3yXM3hcXe6uyoAbDiBkLl{44K2DD}3!HqjL1FVZtBdPtX7;Q1jMG9JxLD5uBI*0#wMA#_g7z1XCyN z8yFKU73Bp#pz?&C&?cjxr5kX!fZbPLv7y4~elDSyOu;Buy*N6lEys zDGMxmI*)&Hi}R>x)HafcFCp14V!v&>Zn!spbX)Tj9u*;nmWZ~Z9BWg9^v|IR68`VH zzS~Lpu-Gs-KYb-8{uqZl+4P^nGrP)KA5AD|^`DtSrn{OUsR{cfDV*1nUuqC5ESfNP z_p=1cBJSH#sFGcFz`)kGDnpHl2P!@>qiD1Iu-V;_Fq!%cRvr(wtJa|8pmAr)WL4giSZu zJ|abluc#Ye=Ei;GX5oHHqGct2$i{yz0k0%`!S#L+RkMWYJ-1MyzkSL2zVOvfssfsf z>|spW%I@r?$lZFED{Ux1`9Eu~;U{*I@j=fYrp;j{6#kDj-MEeuAM(okI0yuvK7M!B zPGI?peelt1T$Iek!P|%9yAobM5pA;k$w)YgVeWK!oQCSxCjl(dVWq?lagw&k1KkhG zrSgHR#}7QBdRmtGF13iCeIdEVW#fPD9x(orwRqu^q?n|5Yu0 zWNgJ(DHFb6{Y{e}m`V5@#uX^QS|G@W+p&goH^IYyv>59AweVdcsjm6EJ*^JE7EE}e zWM^(LUBT4>4$hg59H#kq*}=u-4Y&zF4dKiy+5`dbJ3b*9Yw$0`lVSz8^l zXMlB5J}zyN5mP z^M*pe#x%Q~H%0rr%G)cZY@NQxlznji(#={wIjleCY5%;=g01bSy02+L@-GW@vc%!)Xrc zw%6myXk4-*+7O(Kyz#FG%38A5`{?WmQ8W!~IU8O~c z$G0hqzK9*5^?}0WL5X&A66&SQBi_-p*|$Pk0!KML$5m~s&KUB!SOv2!GL5t;&pNsy zi<>LI-VcH=*$;iniqhN)QV(Us)BYo=yW-^>EVSF`FNjQy59|mR+I`#WtnU{}^%oAW z5O%XG`D~!)cJBGY$Zf;j(C^Y}hw`ZwO(~y`PAgimPxkyNGk$vE!lGq*II-Iwzwd=k zgh(wCEw8S9C25wdS#>Sanf0c%ktoBhfaZOOGp2SIGF}whuEsZ7X+5slStcT*dmod7;iD`HuwuE{Xp&ROz?>M;CV2=~G*2)dX%W z!_jNd=HwLK1_Ea{>H(2J@K<`c#^l01=Zkxlq>!7p?tXg`K*OdBb|p1^0it1qJ$Z3% z><>u`o6|i-2+X=3AYg$8u3kW9gq(2YuSIg7WvZ*3U|yBJ5Xa6*ikSztLh3rz*V zIf~WJGe0K+cW~56v?|FN>2P!My-8-wOpY!HbnzbN6^(Ieb;KR6)}w7R6Rz&|yl0i4 zp5mGu8=jB{i_)9LMxH*rs;MwhuKUJ--WK3jU)LDO_o7jWruNzLI9Pk2viGAL0Y+RX zFE{2rJAcwn4+0t;T~u}e-XGp{>1*_vvjsJx84F=>y*EmpJ z-|CW9%4huB|6D)R-p_>zY){ZjCor(in@Q19wak5`F!jnV9cNTki`s3pA@dd0pMqRr z(bf(l^$LUWT?!Va!0IzXh8fP5Cto)1(BmP=4cc(n=6N<}Lcy>cebS(<@NE>G#eN;U=SIxLyDw0zZQ?Wn8)(m$U>1vVU?D+a4P08c8 zgiufrwlQ|+TIX2I+11zxoqF8e&C_FaQkEh4;7JP5$I`p8YGXYX+87d14FH^hmx`>_ zCGmh)Xj%M?guo46Il=hqMvppg8zyD(=wucZC6=%Uo}=eW%YOESe#kg7^!D}Zihv>G zBNOo7i=|hFCZo8=#RVO%{yxpw>u?F-zc$$!Rnuca#@B<)#*K_hO89Ivw(^Z!Eg}wl5L6Q{TW7B z@Tk^ArcNDP6?lYy-Nd_T!IQP#>zf|rXS`~XJa#*(!t~h~bX+ugxT(Z0?;|W-uoSA9 zr97|qU%t~nbDZzb@wI9PRj8f zFQLX~b^F8Gg_SWxL$h0x(y1w?@^X^q(B}klyrQT%FJsB~R~jnOMZ9$QM$n5c$om-< zSXw{yrhMG@wscR= zr8&P&g)^&Zbkc*Had;{7n&oUXLrFl2>*!k_gIN&aeHZ`kl~dtj@uYiQ zD(fClJBIA6NK>F`D{0B^3jMJEjZmQfH&=Z+ZNE!;uI)+i&#f36Tp45f4k3tG66q}h zm)94oaZJW3NW8v9MMJk4-ocTxZ#n*l-7C-AAlBsHQeXvhGG>dPx7$@tBoFSqP4YJ0 z4jYom`N%#yFnx9|ZuVj18<5#=a0oB<;~7H2@7vkOj(GsHG*&8~EkAi{9+dhRE7nTO z05yK#@~+#dD}!{0^rNZGqC9mXYtL8wjo%KobS_w;(@1pbsk7HG4D$T9qHUFe{jw<` zN1yz)!zxJ=Pa^#35gl}NNYwOm%66Cb9|A7yo7peOB|e8ZfBS8T9L3PAfxnm=Vj>Pv zNA&=Ha{EBINSM%zQP|w-^haCU)Umri=2_{l;p#6<@Jw#H3ken*TUG;` zA{z~LKTo0G*#r2cikwe=P!FnoK0OSx@lk7#nCojS&0!Vq(`}7!fPf2zqr(&CpmR9> z5t{9IdnNeD8K1*0)Ghh-gelcb-a3evR$Cw{c9%^D=zO`KKVEA71{SzaUF7$=oKwEz zXb=BriL8f$e*F$4G?-&{`lHdfJ7bm?&Kma?oIppt-6$UL)X9YeRLWYd4JHk6F3(>q zMoi{~UH8B=&+-m0T74lgT;PVGF&pDOxCMq1mQ*=E-0_zWg9esI*-=@GnjNDY3I0ho zKDe}Pcgg1C7O2AzTwmc2n7~c3#`3X6%vjcdIB-;!z;CG|aClyj{-(4$b2>T=#^o4F z*PEh=5kJ%DsUZxxcE4|uH65;vJr2BPt*>1ATaoiOMI88Z<#|;o`Z_pt$w}r7s(H>C5*$&Fq%!S?kT=6wvTYMR#k?#tPuz z$qft-?Q)YIzT5L9yX6K_rX}$G9bY=&x`j>;isL%vz zdqX8p@2nI@#j`D!26jJ`7)%q-l$Ub7>%Ji&v`z&vD~v{TP&qHsZOFwwb;I1v4IiCm zF)yr)ETOliBc?$l*553$xYN_Wq1MBkEiFx>ShAhqL!41d@h(ALbjB|MHwikAi+q+u zIMourNkwo5RfkG;e-iOa=Cr&K8c#4E=5pEvayMwbmVv&!qRrmww0~+Njr0t3}&GFDNTJ6(kb>H2zn6&RV z<`6UXPosB?@d?*&)4y@^_W(gLr+6t+)~>&f3EwmUMxU!M{I6m`%@#l8^}p1LfCJBb z5XoUk;*3in-&p&`1wkY%Eg9rYQP&f1o2i(Y>Zx>`j2iGwtoW3!OO7n|7HMM3gmjoj zNF35OvNvji3qLvr=YO##>^t^SqLukYm$CNQh6b**!K%EZ_c&9Ngz-hQt1e=hI9W*R zRoF<>LIn`HUc1~X5}S2f(!5}%@rM<40&i#ILHC>sK@qvtX)DzhA(}QTkGqYr0EkHb za|QuBYlz6QSey9;d=LZJVp2IS-BO;@RWpbmb2wfYh&C8KUFEFTA8ag~j+Xin9K0X} zp2O=i+}Grq!c$QKg_p(3AO{pLFKj}?e~gJ`R7Dk#*92N=Z(7)4I+kx;J^)|x(1>wo zyn6pcNY^uFDZzzbOX>mcFT+>1@A65C(||0RyN|F-lQ91ZiD%zS9}VHu@dUj)bJu+R zBfCqB#?~XD&pQ5S*7@NUq1JYQ41t4^?iUuT^eARc)NTJp>w{PpO`2oH^*Jp%R{V)I zH;u~TI%Sp^a*oCllZp3xNY$+LS3y2_L9};)+Zp7{UiIHRFezK|Va2>WW;NmmEIF+D zV@V%))0j+L`s93@J`85?lbr=Gb>9E)pgn<{x$a#F_NI&lU+nzY_XaOHI8$;F72iZ~ z^?Gss+CUz5W}UjObxSrf6TMW7p$YIwTBD|fhsSKjUm#H)w4xY8+-oK?^cro(yq-a) zta8?(c7{wx2nnSUA3nY|1(r(?lR%2dN8PvTq{3bf7o0K}@?mfs&UCO_>JD={$Hxl`1&DxV9C5{Wg=y3N?s+cmVj$^&!;( zGfI8T!17m1yCqjBqP68)>z?Q@c}^-Oa@p)e{C}b1hwSNJ`Q`g8c36ANVWutNedd3d zo7%)Pdv@s~5>h~6OS$nev2K?KtY6euhL3cycuCfg>tylvcuU?|ya~iyUXx#Ce+-0k zd!8AV5}lqtv@LAE3AHk(pouM7*gZR0GD`p{F|&FJH8VnorWl`hfX1&at;A_3a5#J7 zKM{s{K+_e|ng4vbbnhNE1{#`_NLH}!21~}oOjkQVWYMCBn*l7$#m2flVz&g>T|k;O zk?P(`uXnYrGOc}62gGnhhscm0((#AL75Uk+G>0fZUxw=_78CWLZ^Db5_U06uThFCA zAbgz;P;AEdKxTb9H#-f+FuGn`x$Uy59vkuob27(yGXK5ZC_B7C{0pJZE{p2p^=9~+WwD4bnY^t$^j`B?lD-84? z=HyJ1d$sLtY%FTjy92E!;NLm+L0OXSt`Bb?4NtPnk~qY6!)jP*%J#WtZeIW4E){Vn zml=^}f!UT3P8zu#a)T&8R=qg;9Pq;Hi=LOkyCz-EP3=alXKF8%sOjZq*y(13No6)- z|Hb`XXR1GUjG5-=g=Wt;Qy+<|@LxSpTdGT7h#mOkBVuG!E3k?FO~tQEHoA0XMRjvd zF#Nc1pe@=QpKCSXhuMS=xXMtegNtwAIOT`!6JNV0N{3RayTaI%v`PuN zQC+h+vE^M0R|r+5PrLj|YV3or1MZZ9^4V)YinG-A2uxo*`4plUR4z8cg5DAY`D3gb zj>;|eSBt^S%#d9WS0{Ippo&SA!Q`8ybfgUYTn$qfA461p+J6IdU*sv#16uZaeR#QAN6bq_N z`8uk^OsU{-u+SPK7BF^Al9fVtoa&=~K#6uyi%#ol zj4y_x?EKn((B5&{*5+Xco{EU0cRqY*JMvm8(tZI0f&(pAomV}r{onz5Ly&%FOs4?) z>Y1O*QWqPw6za58dde%`Pepqotmevu@fH>C<#n<$%V50nY%@vVv3zXw=?o*~YM^HD zA@@D9_2}W%l(d7ddL7_=q2;EtQ;nyi*hExN9sR*S+ctPDE;yL*oLE3xd8B$cbL9?i zWpkNidblnYI{+>H?^InumMGSG#l9ai&*#b05@VO1BkU?fCaxo?$}*wN@?O~}$>pl* z{#F71eP4i^&}G(1ty8Vge6#WLF@Zab z%{7!k7QK zQ{MgZD0bLXXd!#9l_pEvPm)NPaHc5=+a{X5!5L1&N2xt5kk1q<%PM}!3`)-{IN@VZ zjWB@q_si7Ru+~3899ycZHEBMp?J)rr<)$WhqKl$_>bLp61jv5u=;=2MG*q8CfsXrZ z*GpH91*55(&9(ArzWl`tUwXq7| z8xc)d_`jgyx`-N2w!J!A(w(sVp1S9GD}@(zTeg_WX@7W`y`Ug_H8^<3m=F*b2UfKK ziOEfJd%452ZTFweF?#<-Z!x1C0jeJcfwr`ifLI+-XBch%Z)xko*oAj)Et$KUqlx=rT4;n!6e zd%mKIRHt)9ZAOcXMK%)fI1klWO-l(Y33VbM($HACYl9FxlHeWn7y1;5GqP!fp z3b9y8J=BLj)Dpi$_jjhwPN8mMn9j93xhh^e{&naFMpyYfrC;bMKNZx8_Y=w`Z`dC> z9+Mr$Z=>?WTMUh3*z*S;?_KX)&-b}AC+^Fr#veV$_p*`;X3j{Jny6nj!ULY}Q=hKB z?AOsGX8ZbvjIXG~Hq3C?8Ck`y_?xU)BRALIylXbe=)Pqd;yD3=c~2&FJH~S_ZY5FP z`@a!J0W)$+!z~G$Z@jG`aqf8zDmvnYG&z~W*gVUWrl?m+#6n9%hv%)5)J(%kT0#+_ zz1&6=X@nHarusv5*`uETEQz{VS}BK{&tz2Mp*^PT!@c~yGiC-t$%duFC&@H%OIFx4 z=0ToMx3aAw? zjz)tYPic-F_FoZ|51hO5THAvj$<6cRIjguVEYh1Q6`FT>P@>5wp zg{O%FUZ-nQ$f4x0x@NSL5<0k$>gN111UH$(x~(8IB3{Rm8siqz7W(yZVQ2b9({_z{ z#o)Z_`n|k$Q(h8$V?+_&wyL0e*a<%tocXv$*9kfGr{3(JdaQh`qq0yBCZ;G+gI)4U zueasU0_k`yl~|!r>GwDsD35`6yMEVohXw|gy~ucW{Uw%DBq}``ai*X83D^G?i@6w^|4PNp4$FsTlE0O<*blgu-5<@2UC;Cg37E%$-@ zcxXKhKAwu)BJcdCNZF{I+eU~sA94_P|(8!RnZZe3~qawks`C%wqznD!_;x5gbxIU=et$OPoIW zOo0VHblF^P@oR0Ilfxt3WD~UPbNST73pu<0y6X9WKjuI{-a6Q6h`Q&V`*if&3VeM0 zb`MN6Ry5Xzej7ORT-ANs3;@#U9^WG*YBj{s(eZzC!q!G+WA~H-0hyqVThOx<*22eB zn7#dBbfbYrE$ex!dO&!BU3S4isfZ2V7Y~mu3$3lcg6T4QFy`e8_kX?P7s3YU3T~JI z*p4t9k8|;n^SRAp5hdOMSUm9EwJon&P`*1s1m5-;)n62^%!F(u7E&sjH`-CZ*|q8Q zejUJWiAS;VhsELWl?*qVS-qM41*te5;Uw@D05A77C6tA7-y0$s5eT= zGB+ASq`)b~ljX?x?O`i&*3)+}+{{Mb#2ny)Hm#RaGe zGM$k2rT-dFw5H7*{Mc^*Oiv$MRb~1FDv$>e_88Z05P@=x!6|Ha7eNw(av$3sM0o)= zL?37MJrSAk3|}^J?&YQ>^58@8ZN!MAtU&~ z46hS~Ubz}0_q@eYZ9jWGJ6%^9FI8xfQx0ce8zU}bx9-Dz3oGOebMb1W3yJAROiQ=; zSf;t?x${;fPa{M>{~a*n%k!>)w|;|F+P&V$_{LhRN^SOnZ%F!4h4zq(ways7ixSn0 zcSxMz+Fd9oq?b~uQnznF;r1J}3?w(eY5}k5pTy(kCxqlF&o}hCM)8>K7<#ukYx7VO zU`jM2J~pzEYa*3EXCW>?S4$FpgSg8jAmv;iBqk%h({edQHSVIkUBx;gWXo);a5@P- zB)54Q-+QL`_2^P|(Vr>^_wggvLy+GU#>?vbg~+2v*PW=(kUe(*_McW3yjV5PlTVo6 znV;PKk*-V_t0%?RX&AV!^qn>yU2;;1v%<`KKHRa+P=v`>d4+O3X6X##t4kFnL^W>Zi+EtJa0|jg8&XVerlMW!JPeH3#~e3@t1IUT;Jn?TCxzAkg-t zpI(DjYf33y8vSUs!qLR~CM$k-Z~Dr``T`w9dp=`m7Wcq%T!u(@gOh`)F5COG&4%gl zP=ai5f(-LbOVdUh{ci-%Gm!Zr7PmV<`7;BrF+{fCEYr&F(1vMvZ`g>2pcldOaE*OE{|42l_V@B@_4!*~>o3wR=L4wF8~H1fP5VNz~Zac%_aAeLgjx~M0A zozqm~c84C%b_$(aTd;a#1L>V4?PR@K2MgX(adj4ulk>tT zuoQ>sV!qbs%}$KpCB&BMsbRuNR2ig6m(SeQ53;%)_Z^*mt$@zS3Lw4i)BV;~z98qt zR3@;(!E$vg9y+9BF$&J)0siB~296Pa<53yWsldN^^|;TqAoi?7{CVWE(9rz-Vyb`@ zp0BS;3J(&ER$bgN&NBT4e(f(aWjhU=Dz$rOJtP&x8F0KRV;FoA`^(R?Cmhx^VkTl? z7fWJ3RvqVkQBXDD)}A28d5hnBZ{7@J!>6E1puKs6x;(Dws$2ATq5=3lzh0~^zj^ET zrGpW|ae>dV=oAi{Q*oyjHKkB?OiuU6t+bkRFE#_DPFyM14y|N~@g99hR|>F;Bn_bE=Z{5>P3K_F`99xi%?cO0TTh|W!QF61fUeVso8&z) z-F#*9bdw5sP{FDDZTOChWK!nfPW;=4O9bOvW*VLQu!(TN4&!CP!cE9q*$T0B`sM#l`-Vh^NCx|=)Kn?;TawFA9-b&;~hlsq~#fP3>%UY)l7b!LZW za$)~7h4Ui?2f)}3Dq~w%)4T%uBmRUq%}kt|8CWmx47S-F5iEiqN%R)ReSl z2YCB0Ow?Z-U6y5O4mMD~oT=syPfXxlT*K~8NFSk^kU_ZeALg9Dp>R6}p`#zmodI>? zCIa$b2b-hL+Ei>E1t-mC7BCj-KlQZ(KCue!zSDtLhO>2OY=%R*IlHPA-xKi{-vd(x z%Td|E+4H=nYtdi(Q_wM+B6!d}cxD7YOPe(9FQ6G? zb?8CI53meKce4(pwI|zlkqUV^02Ockw@H87EI|E|>hJf$5eME^kNM;d;RQYACvNQp zl&MwxzSzTV>-sFjAX5wCZ)5c7YR^13y(_>D?N|N`i}a;5`kS{2#kntNGhVIn_5 zepylRdTC0;iBm2P!=T~i;sM`v?NZ2>sk&^eY2e5sf>!%N@4SHGm;sYxR%Xso|(7wnvY359r!|U4x z9`_$XIk&c^F8 zo>x@o7j)@VPTt)MI@9E|m0ji3$ADG`{nqeg=4v@7XR@0t@iT}uWQ8Qd;W-tw@i^i+ zk$JZhw-q9mE!bFf$Xik#~8WQ`?c5)Ic$Q8>L zR!l11nZgjELAuo`)mA`V5x5D3m z#-DR^_05S{`XL8NQc;UuI@vR`(YjAINbl#{J+8j-%<3pd5!pAD!Q;$<*^gbBte0Ki zaehZ?S{c6$N!s`0_w+~&WAe^R8euFYgU*t#PwJiun15cJo@lJDaK$Q}PpBo53$B`o zxkJ%zY4gt?0akcqRU|VBxEX#<9cc8fgQW~hLi0-{)mz+;b-(8JI!1+SgThl-bTc8V zxi(wP6@k(_Is!L(XLo7Su4DQyK2%>e4p?WpUfUG&4?55(EXr*fT{i0oKYKlGTcxKm z4Sr#hhJVGtw-`4RG@`gr$B1ZU8Vw_l?XMrFfC!Giny#aE25EZ^mQ;@pcKUDhIdF_^ zukYU+lWtcEAfM6k@o;e^LLz3p!4>32H4$H1hJH~wPJ+2{!f7vIk@a}<%e^4H^pnTu z>JiWsmWO9QDb!tmi#NN0`F$-{5JNxrOz{gHe!e%2(5(<1t%$P?xbK%EAO zOq2I)PAj8^-hG|W%(PD3Kci(77VP1Y_)_ER(|oi2Z%(As?A`hlL++baxQ-0r*44lA zGrooqQ?ycz0*N;K_G`~UHfj<4v06?s|bD) zg5NX@V>ztA@%3&MYp=U#j5qIPG&w3(l)gE~q~ng%iYW^}0XbbL>#q9*ho6l)&Oo7T z5lmiX(cLoSH=|19+S__{_35&ZZ66H5ze1AM1m%=3!HT6WKjMTs!#1kG8D)U2jMr-d zz2lpqTC{TSN+zoc10KESKlptOo=(nDXs6GVNR%*jjLU>tPnh9)3gF%*x^)8RXx;9M zXK)G;C|6HbSDiz=t|N1=G z?LBNh{yNwHb?>F{*u)4_qYN8E`|Grja#W=9kw&l&C1`%7u=%N6wMXiI zPi}K-$|fKHniteIq^Do^fY!w+7{tvl^_;al1^oSFeNVMAfWddnE0U|e&T-qq=Hr7x z>0H};<*V0ld3jufq_2L)2=~X>E5y7FeoOq~5LD4?^Rxe;4+ZJPAfvjJe4xeYeHb?z z19w{EH8f`Kwv!KhbV9`k%uw-P-sw(F;3>UwFkyCGf8*tbtHirXWuuU{P&_K#pRnc6 z4&})<&$ormb;w_^ zX%#t)>5e8BHw{t`Qi^1Dv%bX(~A!-q&g>-3Z)mz5D1F`ffZ-|2(S4Lx%bh?;rZW-o@%Ib{SmHkV(*3FOq5S>6goJsG zm&~wRN*k@a&G&zZa$glzUS3#eW6wv%cNC6HJjenP6;zRzJ1<`$Njq2;73Et#tf-0| ztid*<$5UM0tT!o;1gcM2XS*iH9FWT&Ap~( z5^PWP&?Wvg#Ps*>u_H>Rge(8y5{~GM;gI4JSIol4B{73CJ-Z~{7cZ+9cvPf#B^I5L zg_`Z%gGksU(~7 z30S?tZ*HRvB`N7RQ*mr~msXho-LnkN@}mm*(hPAlK=z?DBk%3f3JH9CgO;vV4Il*R=kL>mz=tvlHSpB0^QAh zCgVLlWQFSX>v%PPhsOb)0vaA(y+RUKv7#Te`<1SbEQ(#Bn`=Ev1CVRp zh*7{R&@?SN%9%yZ&)10|DUtI0+jj*|hda&Dj8Q)`jA7HMp^BhbAkEQ``zewZD>2fG zD6USw%+$+k4fx8BB(k&PT6lVv_64G!QZdvXmyh1&uhDQ>icI#R>sA)iZntMvDNnN~ zin*cL?_lqm93*13#hg#xo`|k$5*u4^K4){9O;H^O_+G;tg=q0#W*KHa<7V3Ss9KO@1(4dTpL;yybu_+J`G)6`gzrS~WzDXHnfyRW<%)PO$O6!%5%~Ix&2mH7Dj}J5M+u3It{^7gYJh$% zoF0*r!KSqDL1kulzY*ilAl~Mk?rH|wmOn@#x~avqboP!OH_J8Cug@!PAf1`nc)sXN zeh#MT8f-B^elOGL%4gd*{qbYHx@Vw?w=$scFyLh5W))2cXkWCf&kriJMDTId4K*8J35%$y{VyL8| zuh^y17IJjf42S;1iPDd7;1o`(3XY!%nzS^a2lwRHn=SP0tES%X5cjg)|8tnhuu)MYSk$);jY{NKj(zWgvw#na z`>f09G}gYIp!{pAI`irYG@Wkl5LplYf^Aj)v1rS~gl-qbtC!NXE9cALyo1(Kv4uDT zZIXkU77^WZhoF_o^{^(57syE|&v-+4Zw$X7J?4gHygK(yexZr|XX~12B+;v)kd4a# z1DP~6Aod{@y3}oYx7s~#xq(GqeKwYJBT}{WaYTik?Y+>n;^{LS6}|a7T$8gm4}}V^DR$6 z3EgoNt8-*t5vA(!r1048#VbZ1#_4bkxu+0KGHHy;O;11VH1b9Yc01%b6k{8Qfy zApwhe(ywq$`SWz!ovV;bP$%H!Nsi^~Mn{LUEp0YNot^{iatU^xk%zJOfc5Kn4cF&- zJcU}eyC)#NqI`^KfVsG7D(2tb1jWuAc8qeMA|vcmzl87ou>?kjSCbO6Z>hq{Yh?X7eaqMImZ#4q8@4>@p~lvVWF|qw~8Xw=(C7a+p5i>sgFe5wgfRX_E!x6eP2kou7q+@-ZPiA@-}Xj7Sm%~fx%h#Wt4lOBg)p{FeX7WR#Q zaE<qYMttY=3k{t)?OlnnfyXMf2s-Pcr!$Z2b6EnASO08- zZ~jVA#P?XFSu4NEtm15Qw%2{-q5U_0x+0|ESj*d0lP1E(sV3JOC3AM**DFt9*607& z7Id)4=@pU&(LM%A2ntz2HG~MQi=HrmpDDZPCs;VOc4Z?ZSdU9q*nL%Oabe7#LucWF?feZ5{TO3 zY}v#p;W%7@>5&0J*x|I%n?$xY#>ygq@f4g2U9J1iY0W4a5u9dcOPRLrwL1W=83&rM zTBIT`V^G;)$sd5XcF;-kL?}b^*Kpulrqqe+w})q2R5nK|o2t?*u9xSYqB6a<_VCoK!S_E=F5>N25uw3j z*xdoPH2|A6%wa(x?Q1&G`fM)(_=pqul$)#fz#gv*gN5eTzaB%z$MH2y<7X%Si9HK( zjb?|HYc*WViFo9-f$Y*%YT`8|RD7~9P=`k>USgzkfnV*0Pe1VbTi_8PQQGR;t-2{( zu2H>*6wHCOP=kU4e65iPO+jJXvyXfawI{W-sTBc}xWwYM3k0yY4oFAl$S)Ile}610 zqeiUEnV!K*1FPWPiwxz_qWMn3jrk4GFa5AS*lYD>0XmoX^uHG8Q~kfVOkR}e2MvwJ^0@R8N^e*DU2y@qE@#fZZWgt*w4xG!-FC~{C|Qg3W}87fg`lrPRh`k|~^4ihjCVK%hURt`<9>wxZr=-LQw zaNpE@SYgCmtD8~J&*b&%MhMy;Da~3s$k5Xu*0d=x?iORp1e9Ixi~rIuWaY%wjKsdV zll88~jqgWq!^!vetpGGt8~C%o#x$j87}^|&q34*ZKTrOiYNYTN(+&%L+mNNPedZ3< zpd2T;)_kEo`_+6ef@FXmU+E$&BnWr44a|@p0ISDlf4!ZC2J$R#@kHKzM#Q%mk<^KiZgj}+xtvrOpThdFv(B8`5 zJ9Pa4+*bLR%AstpE{#OP5g@nl?wEKp6?1NSLwv7e8LwvV-k4rFAWioWN!tMEaP4UW z>_Q!Jyx)<$7mH6E*A8lW92zIcS8YCt9pRoQ-qAe+N#_cXeNrV^X|E4_%C;7G4l|yq z`yX^3a?`BMXAOu?XJ1ZB1RB zG(b3nr4jpO6dE%|mMGAhN~#iJOpK8|+TVw8TaZ+(UB%D8bO)vE(*I}Ofq@1O0_o#^ zzmS>ClA-V0L0ze{vgJ-8CnK!xx%uK{2NaX>Kfu(o+8Ixb9oKa6qCIjvI_p0F!Z)RB zIT)0(r9<0WdEl>j5Nuqp7FGZwYB}AyG+im`?p^Q-N~YvwB?*W=dYnHBHz`bsgA2%= zPSg&j!LSe@{G$V*iA_<8m_cPn(q@@qj~Bz`8pphDC#ysQkfwdS;^xgzaj}-m znqg}zt-V_E&O0uGE~Pe47YkhF19A3av%Sj4??W`B8yUQR4EYDe^g+*OBi_`NGQ$`} z5-#K4ks>>0hMEkY@|%i>)4e$}p`JDID0qxqf~u4S|FNn5&v2`$0M#k#KXUcY>DaIZ znf~0ISmWrMC^zEleckmD?hbPBeq5_5CDal98f$j|0#nXekrZ6z-r8>*TG~(H ztUZkJ*BPqVuU*goICKBCm*I}xZ~UFb9r%#DCjxN=h3y|C+F!1c^PWC{r*5CYx8W7{ z^dc5az~>bK&w`pQ{ifLpYsK@4e%vS zj^(2=LlpZ_$4b7M&}&In#%0(PpJ?OnFs!OC>$8ZdP_en!n3+Fphll5xEpU|9|`Z9ygQhb_oh zCFypE@F2$Peyv3kbMoCPN#UP7ZV>d{g z6vHR-S{~<;t}k@bq<%Pz8WPT-R(SVe*h58SeFZ+{uGsS+hZlHVhf+vv;j1r_3?xAw zIuMrnh+eomF;I)UANmSLaRIF8~pg{1sqb) zXE^rhys`y9?0~%tvlJA9X%jxjq|7GcR%X;eD)O?PN__7+10ThDH#MW$ht$$4rJ=NflIDgAq{g_GvM}G zTnGOSJ#4-o^6$2fxEW7oUa8W%`3^;9u$4B1OLe7pHm5FYVA*zxB15S-G~Z60-LeTA^s;^tuxR=v^cgM!K=yheSjF;H?66 zFV9UX()N$?Qc6=(48IY^YvAYh)P)+Ot>pKEW68Qodc-V{G1+r{zZuDM-jr7h%xRq(8 zjo}Ma`(Lsd2*y^^p#2-Yt(J40p;&ub(_`--M2gsM=JL zK*fIOH}EfWV{w6?&U<}_n~4x-f=pCcZuYWz$E-}VHM(`q#AkQQ1wcH&+OaHs5aU_F zWrGEa4pAC&evI3~a9UX~w8;$@)O1Ndek-MWCdo7m4C#Rc3MHrg?P=beu}pu9!1xok zp^B0Ici2`X@fU0hZulb)c}Fa@>gEH6lAQANozINW7Aws**~O?qHv4dLQwuwwnU?pX zg7P&t<`Q#}&mM+V8Na{`To~AnCkwLUA|W!1zw{=i0a;JG4D=6OWAyMi2CyNBqPgO? zXR_f7m4Ycxv>&l$Y#N?0gWMU7?B?L=CyrB=J-opqpbAvoRJQq}M`A+O7$ z4Ty5tfs&klXj4-)>f%7RkwVG4e2=-3jM3<8#t?Z1d5UFhH(5vfSUbEt>6)zN4Ia`F{sAc&Qjo z*E6^1e>8hd?DtQ04}kJ!pdoWmp50ut_e>15RC9ShG$-=%w$XO^BLfwW9|bz;Co@WE zE6CQNGwX0?9c%1WR#c)!q<7MWw1eHyy^)qitgmnq6aq=*Tcy*q{RfA&&5!cHG4xp` zC6K6Sxg(x_!ptvK*<{76eGYS(q9HGojEN{ZAnLE~=eD~rCdqR*N}TvZ zqkE+mCE?uMrzaN=nbLJOpE=$x+00^-Ca)w_4dE%1|FC1d8hDL=9`|3c(dOYFTm$6d`Sx30yrxymS*?)4&sw8OUHO28#pw+e3F!xO>G%EIt!HMt_--^V#BH#e zrkPI7G)9h!(Ja3C07aI4%d@S-N(@R*t+v_(6=R1RLLJtaasuCXOwaWZ^AFbs<27~% zA|gsgpRiC&>*}sAA31~az%tnfyX0T4LxmY>T@ZLYES6nM4pwi4M#5`IP9J`-^dE5K zV5L_SICnffzCLjM_W;jmSaO{ID+tBj(CwYjY(07*CT_>dL-dj&Ae3S!?-LCS%e^cD zw7%{{#Vf@ANbvGqt09XBVMXr5>@)jm3Dr7J0fz98e-dnLQWjgeiK_NS(meYDEgkBT zxz=!51v?jm+PDjHUCiA%T{WI_>661JnB<`zu#s9W-g{oB*u#BD5sDe>;if}#y9v?o zRy!g;+meE8MfPt~sRx_$sH6_J;1(ZyS=f^VFI<`&Nzc*~9PC_1)sNFYJ6>Y0_h70z zjFiAC;&H|`Vt|Cu*08e^0(bm~UM8H;& z(*lPBxzyn$z?bp=UvM_Py3FYz#bt7dQ^GsYLLyqdur*(gUM^kihi2Pek+qnLF&768 zGN{Gl^Nushk4<)rf{OB)puoFpw(H}PlU#})KAY>MP!MfGLY`uF!*7DKrkBhhc@0@6 zkh`J(0D`w?K}c;p)dLD-*=*~w0}B? z86~;y#bL|7*xB@k70TBdrEj!S$Aaz1naoDdlTcC+tXLx?jX<*}i=g zFR(Q$3A^=3`$U75J4uasi=kK;fw&%1PTOcwAZOkvomWJ90S5JU`$qytb*s7EBK(%TkiQFStCTeE+S zS69Vgxy%igWt0C<{Nv*t{;l}OMr7^zI#Yk8s3tE}GV-Fuac;;ld%j<^GR#(K@9d+H zmI__wuyi_?x0^X@zB6$UydH$(cu!8O*I53o02v!Vq&v4HEp^v89VlcX_R%z{?7W4# zDlIhrlxL!INwQKe#xlZzzrTC6k&#kT-W5E7eDHZRnKX02Ew!ZC%Iih4vtK(h5<$4n z{8oKKg5b?r{`#MP zMJy=8YD?k*4pfMaT3k=B8DC?FirGsE;DkDP`QSv6x;S&V=(b0Driyx*prnok($NS{ zXUUlBye8-G6|nQ3i}6%3<|a5nA7z2|4FqjoT^v~8UB(lup8p2oWgQAYjuH(tMbt<* zaM*7gHJcxAX8E3WK0p1i_>9j~MZx!TZ4naotCKLr;Sop&x}LAx1X=3}XUzWO=L|Yq zbHmf^G{e1Jsg4WvA13#JESBL(@ef)s5H%co3RDkELO0~urP5S(>dweDonPOs)u+7b ze=T*a<#Hm?;c#hw+MMg&=A}O&_`g*TFap~CeY&+BshOltg-?N|_?kD_aNd^@sug;M z$T{UHG*{tBJ8h-xx;%=xpa;2Qgbt5@WnLzJs5|2gPEi(>sfqX^moq^XpJSg56z3w^ z2}?I9MdfvjWiVsp;IZT*4vjId_c6Oz$;xcZ;dzskB6Pe3zQ80VaSMS2K(M@z&=q&= zt3J~OgnptUeScDCR zA%QwSp^vf4PuDazjf$MxQZ@1|y!Z({Ex!joDX>nee;`cfpM=0vrgZ|cpDvHn@l zn>53cDP;|hEz6JD3t}!B%soQh&{l-DY*PMCSXGZD;>AMSeBeX#Wk-8aDV^IpL1G`P z31@q^{tt8I4C~ns2yj}}jK~3SQP5_<_&rt6W|g9YDBc?QL>=dINC_KBTQzVZMvIq{ zgR!&b?G?&nt8g4%f!%aODRU=u_YQ4wx)Hj$BRGE(rP^V&>|D*;+dHNjzjbo7=Udn+ zXNmXD6V_mJG6(4gvuTRw?vjG#12xD!&#%&}B4!3-&rGnhF2nlt-pxlEU)4^fWk43N z+!tMnErSbAw zgGE33f5v7jf(18K8f6P*cn8hggLNfb3}|X)o~_IXrSONYS>s0XaIt||qWP->42TC9 zX7W13s8wSw+t&}UT8#gc%k$apRHq4ecT{b`L6JJ+q>YYbnRBb4=kYlc=hj8Z*>=R-S*|VRM~m>5Jq+5=PyP^ zXu5Y7JV@?=t_QQm-uDBx`t!(Ngx>e`Vt)Q@kyI36^~AL#ZDsOuY=8GU1F?)@e3Q=u zoymhuJ=P?*up^z5Jsr_uR1oXo2PP+=9NFg(S7gO*2wuR`2=|f5d5W74?*~V(0OX|A z1{_!rTVEFkZL3MTRrYXunD8oeg1xax*CNy45l91)^YBS;M|#GJ-4;tyy}`_waWkpa zV9u4F)2d2hQ!H=00a9wb!Y_gU`n)j>@XoHi?RqntOJtJ&eQ}yu_-LlfGwr^x2yH-) zypwBvPg5Lia!se<)?Y1m*yYXNdzM#SR)W%*Gg-a(s{THhAX>~*?b1ncenrw+9haNz zQC@KE+0Vd3i-Yqx`Qq&3@v;Mf<{F{WUUUB5ZWqg%V2j@GHz>I>$8A@Pzd#hqyrt#!s*D6Cc>=iPVn3|K?DC zz&JLO6O~G5)BVI~#S({3+4xQGCGv<1sJEd-Zx-$$~`tpa|C4o^Eg#xDHmGR9RFg zt@#bu=#C4NHC{RtAE(YMtggA|W`=cEAPSX^+`-Z3Nsg^xHEYvvMZ!emWEbeuuB1|DaF*q$DUKb6wz6)f+Xy;`?p- z8*<>obD^HlrorKH6hU(Sm z%x0(O?H0T9mV2-U!DxSTQfJ1nRIhYTHyjVzAB2qalP??|c1#+h<;#Op^iA>WAB`Hn ze}1j2f0bjLAXDZI)6EIooS46}95Pmy#=^FlPvs%XdTZoph_cFOuS^GP*f*QQ`uUq# zHFZ{z8a6c+dk4<_5M?rAIE-F^_wR9uZXIi;#d?aV_eI|} zdga0Ixn^624#Wb?H-6Y3Z@q#nj0mTC{Dh;|Y>Xz>&MnhU<*Zg^@tK<*$?#QBnNd1d z9vir6kq^Z)thqmS%y>{|Scae8vr^ndF12LxT<-t37GSaY>AXRK2y|%p{rQbRX~f5) zuU}EMpknXX@cJ>%XHKzB)zSm8UGL+^{f`pGAk{~SW1U(F{iT(T=Mok^`E>YI&7&bK zJcF0%Si%E(M51BpD$zibxu^?Mzk!qa1U-u3qt-2CF3b!(WK&% z$$3Pa`QMniuXTbQEa2yxfW(9FT_qnz1bJIVEyoP+81hJo@}!vAWNOtXq^V0O3q8oc z(4YQ|8ntJVLQ)?s`696OH4EQ$jRdXoXX`ZJ^FV_SZMxG%(o>U7+{BJ6jSDZ0OH{dV z)IJZtd^!}kmiZkhpND{N_*4bymJw5T?e9ITQ^P-G7@?id7HZ@cq|8I2rgC!tmH)%s)1i6AVW*tSodr$}39F$YU7X zi6WS8cr+_%`LwkdJ%DUwxgL@R!~q8p3(w- z?D<0tPq0oRSn@6GfNJqG(dXi_AtULA$2{$hQ0#}LhkI=ke*tMDr&VE_&X(rM*)^gBe2Q8Y=<|1M#kPtK2ziY(C;HJdgw?ksnw-c zAJ7ir0;oMmBmK9|#y%o=UgDEJT{=;$Tujzu`;u*jke{Vifs$>=xgJ>h6}Ouv@4)z} zQ`Tyn+z;qhmLG!tX~omC7+B*zd#K*dYemGv@`sMWmP_0|qeZyP0?RQd?33~hV`hk9 z>1zd2G_2k%Nx?PT7*e!*8D#4$UgO_3qsJ}Ej}E>SBENC*#Z4xIXwnbJ_{t7+m)g_!hs)@Q;#ZFxu7IO0o)ad8MyBq`5!Z89}NW9-j+UWa=` z3a=gZcF68*{ta-mLo*ANarkaJ_uVc{V?uyYXB!#g@aWMBH*aYko|T=#cNn?Em@nFhN@|L%qK z=S5H3J49HU+kcwk!mjmnqT&Dhy+r|t0lE>l$bl8v2U#V+`4T4vDQ0HTcW*wG5prq& zp%8cHaO&BBQDHtmzV&{g7&=H&2~2uX^7U}1$4=&(sbg@}M$gGxQ1EwgX)N8AmZa=~ z7q3OId2h7A#^b=t}R zhTd_lNcQ^5#BF~_$czngKUVN@aXCSNX)Q+fO;KQ4i$s;^7{gpNtCx$7xxPNAuVKw= zap2!II->TgcWgmbFn`xMkPCrOSyAAT{E%Nysmh6$&odnc`w6ne2L!0c74o`SNHy98 zg)ubF*Vw{zO9Wit(9|bQ9>U2(@vh4>uDS#!Q);5+BAg{efEpg+%n{MPH5$ zQ}Z4MFFwgfZwO?+h^IiGrj`kT3)=HEG#3?+^=x^Ik48Yrg`=i280fInyjSg4IMn$c zYj!88mjYiWa|GR#u1fWSvo$iX>&#_Q51PGs^;_DN_0So788?dxmwyg)>A^H zMRwa)VV2%8mtJ=pp^&tbs$e*GU~NIdq(nud&#(at3qH|p605xIMM6VLF>`P!$x%>R z&;KILa}e{V7{kMNvko8{?c-^6&9I^j5{5MIov)VK7tFe8da=#Va%Ns=jQRu%2nJ|* znFeRJH$|mid(|PnlH>E-fi|Y3Oi9tsa-=vKK%OQ4F+TrJf&!W^ykblm5|KdZJm|Yu zUR1g+GVRms!AX=5Z1h>t;K%_ac!&aRXl(&bf->J4jYskrLAMb0_lB@yhL?}L`y?{C z%$&m;RRk8+@^`i^QJoUkF?rPC1NwY(Y*Kz4I-U~FB`dM%!pX}{$B_@&ED#4S!z1^V z8(d^vMzLHY`tS;##ppxs{^!e}lwV_CU6}N5>B_lE!kUUkF3F+-4BREeRa$eg0-nYllHA!*NT>w1Pk=fWZp(`nw7MAz911Pbnx_9=g3?J z_4;N5acOfI?JIn=-e&~Qy~+0Dw3=4OtpVB!oeYss$@e5I!QrpKOi{1x`b1*a zy9go4dminQ@|(im3a`xrstgE#U!U?5NS;+of&^OHWdTPp1Jz>!t#S}GmRnR5Z`hPf zbJbnGyXF6Xn0wD~INN^hJ0heIk{~(}f{5PxL_|dN-ic21-iC>Y=p{sS6C`?w-isinH`PsJlI-4=)G3R+4`~Kg5yZ_J6AhlR}`<&}OG4aVcvpuweGryfcMv|0QO#HBUGvnIjWwaW4 zsX}C|F3m#r)4ClK)z2vM@v#|AZM&OW@`=j}8Lv}@CV^(3H`wn@q9$M708b8Z9Zx2j zLpbF!gd1YRJ8yuPC_DtqSG>6EEBjq7wO{J%k3tV?*rvPle!Tc8Xq>TO6J~?+7m+d3 z&}nnyZ>o-o9ZYs|XXN}UZS3#)_)%$iHo3s#Tw6DYjTGbkfK5rJr=EdxKI$~S6-`pt zI3G>N10>SWZbr}ZRBd2+g8XS@upSLOBcM7vUVrNv#rg37Hdm6IvC=2viS3H7TMFsCTD?pfOhG=*7&ZTS@3X1FapgJhw~noj9B_Zb zfqD0=o8<@=T%6~%%6k!YvyPT`qgog*gf;hYk{Ze`{RZ?~NQ`7g8~s@dRH@K=x<&4S zJp`8Zlr3e`<9%IRhBVT9No$>IS4(zpqL;Bi@k6XLo@f-}zb8M6fXA`BnrsvjH8mE+@k;y=Dd}JCaB}n&Zzy@$; zf1j0@`~@4|v~uQRSjku99^bT{1Lt^SOA3A&@XA7)$qwV_^;-)?$5cTTXWmiOR*s^495 zmD4~CZ9RU?(|ggq7hQQs<}&lV%K`J_IG|==EEF8|gdQ3r{gS27AhJ zHe9Q?cF=pvIHlsVz`5j=!z^rs=-=1yCAoYqe!9! z-|&&0W9_KJVO8QUjXzJM1s}5E5L?8p-$!_Vm0u=uLF_Zj+Z~B|`BV2Qch?vz6A+c3 zoehpYt{X2^31?hyNE~2#`0ss-$m=r_$H=s!)Uqp4N`0GeoUCCiv_po}8ctIRUM3s0 z>esF>IPEihb@aU5^f*OXia-V#ch2M;KDkSvbUAX`zhq~KU-gmnlw!7Sy7tqrG=TY* zWHzvdo2=;ScF(9hSdmDLL6JeRr3dYrQ_Nr1RbQP~C)fDlt0QVc6l^mW%wcZ@Rei0EU1XaR7}*`8odNcm;M z1U|`XG#dE%@zh?rI8ndPOTH&7^}>S6*Bj9?!Sq>&ZL2XCIIA>9H?!VvN){&mOuyR^ z33KhxK}b19moQMHkG|9P2Q?n8;B)c&TP$TLH4THL!^_rC^fHYz@X zcu6%WqYWR7Ej~Mb3${xQ)Q!pLa@m^l$uAsu%`phNtp>?*cjp-V#jc&Kf8F1{NSm;k zsE~Hd;AYYcUM>w$l2a62iP}N+FXzRIcq4CfgsfhH; z<-cZ!KBdN4YA5Wq*NMEfAVV$qw(N;KLBHQSaks<0U(cHO)Eg~qzq__ts2lO|jGx8r z=&+Y^1GxpA8k6ued!Gt2dNZ0epge1LZ|`737vP+Q!rsT(_2cXv-=<{}n^|Ub!z{@O zwBqmT{rzL6iwgnHri@!n#=w1cRl=a|7adu#r+(KgEXM_ z4wtgczn`8|2Y-o69eRR_*|2u*=I$k{nXeU3fnkbs{YmrRc6~F{ziUdjHgSB6%>Po% za(`WFeu3)h>-i5#OB;-K*vqRE_O0Hg1>QzBt_D1&{7-&=OQ1{pK4P_4$q=ZXSP2Ow zL-j$&a@(=6bK?W?cn7eKDHa-v@mtIKG^ud9aCM0-8jtIIwI*Fk$#Vn5mPQZsK(<&tjiGR*n)a2FtSo(50u=Eg_43qawFQE2PvJn zpaE|}EIN|+2pra%To^*su>kZ0TSa+sPbK#*%+v9xF>VQngYOGx{*voqM1P?3ZkB8Y zNVzB?us8eqok6l)-^(VOT)g>gAa4rgq*7Y=e_nNsE5b|i9!LJ;@+UcMhmaDx#xvbw zSw8iDUl;;+c>mep`-4Rr;fd*24v4jij*gS-rFNWL_l*>B?7ZistkfvQ}Q`hLB%Wri$HO~pmGnDI*W54ktY3p~vd5*bBOFu*Qw7VW14Z{BPV7MC#MXAd_8m)S{O z&G}keg>zI@C+B=Z8CG!ep^b`&gxH!fwUpO3j8HL$2>c0&@)(;ATg z9U>D6wib@Wkw<}|#R|V%TRzS{-BZ3kvgY&gkj z*28IZDx2w#X!hq4zcxjsnWLTMlmm`m(x=@|$84wO4a_mcj}?`aSD60XZZ4?aask!F z`!TYH)oz1=P$Hg>ZxP%Y7;vVa7FYPbtg<5Kj`+_Mz`Ph2YaaD~-%8&@L_bZJ89=v} zy(Y(^xBiqGb*m&FPtjPL$9|D9<0z^2N2zg``X+5_MbVL(Um`1#cC~)`OM4J63x$W; z*w21n0F(Lfy{l+^nb}RL%WrJhi)9_&me# zn0t;BQ3obEg6}3N;HCRuK-;=CYl-5RN|KF9bojJ-U z{Ga)clD=k$`d=HBT>N)GQP+G|xu&cZ6AypnX}#_A`sa}$2f>hL1(+z;y=&wt#N%6u zPkuDJP2i7|AyJ|yP&H^j@LDEqO)??VY3kKTj%NRSL?p5QZL?PcLdvsi(`wJjTZ6Clb zi#W=uDScOo)n|xT{|m$LWGIhEAUf>=w8I`SX&!WNhZC`O zf4q=+g)MP=(cFxhRnBr~mC;QHp>H@mh= zp5v0|Vd*d6`{FD?&FcSD&5pGx()aE%{X;kU>o4D-lu9@L_u=HP)s#;A^~(RO`5h+H z`9ErYH%RJ!d^s#b;yT8}M49zYFHGtxB5soQ-MIGdy%=%PlbiY(3eZ9gNv4N%54g|G|M9y-}k+sXC@<@n$wFLvvouOx`+I2(GcuZ+gGLYv0p+LEH7W*V_<&h(&*8$ zi;h?w`CaCptEdtX$1ImgcXaL5o0yA<4JETy*+j~UQO+s(m%)c8phJQGP|=ETSx2%s zWZxV4%gzWF>B=Orro+K@?Xp^Bb$y0duU2YJtYz~nM8`aWlGdW?Q>D4C+QF!WbpDam zi)c`%8}1HXT4q;QN4F=fg5SQ_^R43eIm|)TUbwyd=xV!{4gF;&k%@zl0GJ;StXw@% zYk^>Ai9D$Nh5#P-6qPk z`CNtH8YLB2+|N%E;6E#WMMtMKF5$ydl{_yelE*6IrDHFWLSz_jr2jq)5_-mV#Y;6! zD|&#BW!O3H{(xXYw^*}5Cr*R>xS7E8oGXXaf`{OmmjDBl} z%{irYgSXiU8$pE+=ufleO(@Lb_tdZ4($^{CUKa+c_?bFf7h{hEwN~Au?MLuL2;q?6 zZKnxn#Y4By!o;;Tjw2mrL21M9&2wJCd}PW&s3qM!hdf$M0sK-Ae!c>H4lqE$htDcS zBtn0Yp)W*qjg8Wk0-IAB`cBTlm7}kLflBnegF_GeKOY}$9YIm?keb<0?*a%a0V?8; zt6cYy(}-DF`Ky zUB;;z#ARA}E6u~U^|Y^u+<%R)coFpB-Pbiv)AMbQnjfX4Y5j6l&L%~bzFd1D1=RK^ zt??~a*WWK6L~buUY}FtU9KTKY$rjzzMSiS%kVYL66ik6dW&!buH4qTL7B;DzTco*v ztfS+(vR`a&O#S8k{Ze-}{F3*@zP^-WA&O;SeAxGNWYR*wBsH7wKTv-~x60etpf z-y$SR_C5fVJ&yAw?S}?ifnU!(?F@i!Zh=w(3* zdjK|k3Q8_4QaRos*Zr%%CLo@OSQmMGU}>zPV8{Px+iJl#aR1&lg+DUY)!Oir{BLUk zOV2MUC&v7bd=XPJ>fupNtAHvk*Y}%_E)RYSe9M<~euXsZh3gkp6yK!Cl&tPQ=h*(J zy%0$rQ+A7!aF4rHs(3ec-kezSND^;@@0cjH)_X8!4poJrISG8#ql&dYzAQ)7NJ^uj zdtoUrt%&$D_{{Zyy;C{vfk0bZKF*t3*;`-jQdcyPuU}l>lJmJnc*_UAEKdgMbRi0P zyqE>dFTiW>J_489O~ijO)$sV(m(WCwAO79*Zdq$+?gG@V_Uhaq_i|i7Gd4lAb?J0m z-^}3i)#g5D0g1yy#&9$5Wv`hMqJ*#Zp1-c9B68p4Z?5*159*+>A~Glo?2?Pfa${9Y zS(?MgRh=8k_68 zn-^;`HAH&AKCe?#_#T(AHY{v|P?-@+-YR#EbCEf>y4kxiv!MNz({ZG^K7EC5f7ZWr z*|mnmeOqVfg|ly(d+Ex-{VX6#<~9|X8g0X}(TJVCQ==B$KI?Cp#C^M>_+&g;Z#|_h;`afI@gG7b= zY9DwfW9=7lE&aftGgNmjXtWYEA`e)|08hoKwF8|WO=q|fIL$SY2eJ!3ytx}saO%BK zEZBH(GfN$aJ{6L0u%r2A(*``My8pSn?DwQEe>(WsVIWqS&fVXWy#hv$2;e(9x5|%Nkm64OGBk z2qR^VkKO>uz#9fDC(#IvTa_l6V}xl|Tuc#uEZLgzoJ$;l_cT2alKPx&`ZQtn^~qrp zHJF6Z-WdJr=&`m(@RH<}?ri%`>o??-k z2vm0X;w;b>z&$iXNBc7>EIAtXO~j)y1v_$mQ*uf3Ic@m7-H04s%~fA0@2tUbF655; zuUFzBtNYP)8p15@oI?_zccl@=C-tIKy1FHoJ3B~1xR3xZn#E&tzDl!|tXWq;4orbR z?lA2AJqvJ=VhPU-2B>}@1PyHn+?G3ZWN_61jz@gQjqUJd@Bke%b9X>>TGiRaw#|8? zbLAHfu8fzKi%#R3J?|ID?t2?&fY0i!o|UGM@$)%k==ScZ=KBm>o!MxK+SCv?Z}^nD zrC}{;oFpMpnd0`ph|9#1|CF^82O9QQbzbSEVZ!7Iz&hW14N3rGQ(5 zI@MF$?(NG1;LOOEuT%=TM@D5xHLBa>fw~Ec15g!CsorphRXptCl@a!4+ zDs*ryw!v@?SU8+NCHdbX;5Z(ezQrfksY)7ICL6+r=rZ#r?fcCswI8s;_h z26vkKjarhq@%Z@Stj+miVXv$SPbDUP$_T+;TngPgbC)^lj|DwJKdNAD^swRcxBT(e zrJ*yI_`d~eD|$dQIYM(v`+lOJFB zCS+lbct1<&8yMJqtk^uE7a~Jv)3GX^kR|}$JS62~@G+;P1%>h8bN6O5)1h zOSD1>Ke#{B@#&rxJ-1 zRZ80Z_;*rIyDNyO-NluR<%6V6x3(yBbWP(-+ZE|xqA1ovdn<;O`tO*N6|O6E=!=5v zmg0m#wd?5oI7q8KX~52PaACVsdNm{@X=ul5U`Q+A9C1GR*}ONRVJQlISk{Wk^4BtR zqb~4Y`XyI+v=eEe1jPvYFP-~yox1yiV3qjDabNJa0Ib-B<*%W1r|L^$L>`@7D>iGd zNL}-mP%hRuJ}zPibF?LT;Y!@bj-V z|BJYM)VWL`E@!z*r|r-xq$3Tw+hOY?^s+wLU!fQyBdCW z-@N)yX}+GG9v_lLYMW6!u-1g!-!t7p#9zZMz6zawh|BupAv`K&?M)jQah?zg?*?wc z{3gCK_A~q2&;TGRQy|`58-dV%M2KrKIGA-I+{CcAJE94G3EH|dtH*EOeMutQd~oH; ztlLjTwx0r^S+ED%z-XPa3<+%j?cKJ0^ojNRM$t5F(__C97mytnEWj72Y&|6Q1oiWX zci$*uIQ+CKFc#DsHzx0>mA&@W%ZW5psk3LODYmV#jiT=Z`{%Q0TGo0@&Fo!&pz^t1 z<1`&4V3#@9CHe=dP};5dq?#K#u$U8MhOgPiEBX5^VXVaD7k8qZ)+!qgD`Ac|Hvq8& zDwSMCh@oZeg9?y5aLgK)d5E9ih0SfeaS*@h;)q z_{;tO>TVp}Y_Tc2$K+VlE4EhH$Qj|4fNx-Li?Uk}lnmSTVLfd*7h&RmFpHob z>SZe{y_I&gA~}A^3;(CpPJ({;h8y%^p_AyO6-T_C^?~RHIfV_6@rCRi>8Yn*WYh$M z*hf78%xD~No8*g3v~Es}wJ(GLD{{I>>D{?)S1t#Lr>&o@YMfJijTd1O)_~F{mR{Xk zG!4l21on6SKD^M)u+I$8Be5w?WM#c^7|#)5 zwd89PGxvrdq?C1Kbb4#a$3oZ=pAvieNF}b;e}RXSYyE3=$KHcEF~PFc8k$}jrvU8- zkhiSA(!!}c0<+&*1xMa{+r5|0sjx+`LZ9yy_hk;#wJ<7c?39HVT&hmBi}bE%=UHZ2 zoz-41DSfqaY`M_!6ndw!ZGKSm7Ymd`QBI-$oZ&=SJ-Og6t`+2=vDh%7nU8Z-#swfP zE$&&iKR?y|+kRP0kn_-#+wT(&t-Xfr?~*@THB?|5v`G}7AdCvDGh-gisAKcEGsj0v z<;aXJle;7K#Vs_4?9Ibsb3<}kzi;uX{lLCIj_kcaEJE&$B*ye&YHQ~bpT+fIQz~r^ zq$Np*bO(kH-_A=UA}c2)3RjGhh9icU9SaoRg$jsGOI@2_Vw#tD;{<*W?=B4SZ=Ly> z!UwOMf-KAS-A>JY>~NuR0P6Kcq8kR?4d69HK1Wb~&>R$GqKUbvude(CVyQI@&2|la z=@VK}`f@nbZ`N{h3(6x;H6q|}iE)|+<)a9#psIK~-U<+pJxAVYygtTs3$TK*62ajv zyVt2DX)J=?KzpjJ|HvLmjzmFk*xdm0Qd=j!ad*W!oZ$3Ej5~#Sb!nKRH%B^}0ZMOH z#R~Nx`1H34(TYW4eBy@I5K#Q~QNqrfFBu7Ecc-GS>-I9`xzD}V^WeV2vLn?ChkqjZ zA&qiImxmTadEU@gb(KsGN=g;UF%H z?kU3D@ia@9Z@EY1xUgs%$$`kR+C|fk1K`5--veM*L`SAhg{9$3CpT%ltwBk@|>s_~W;M%?ZJxdTcx6ejpu@L9w+GacQiimq7mJX9hjBV0GCDtOjbNLIwIRXr`==+d%Y z3I7OTf=CR2*L(*%t!Z+A=7vB>_#%Y?&PfdPD4BJ`I#gpS)Vuma4yQcg4c34Oc(& zp52ShH~U?dTAlBF4ph#IRalZW$MYCHTFk7)$bvGn#sH1)^EPNNx&?koq9}C|8T>Dg z<7h;F&K0W|xY6@JaJwlkEeS_6D+<&&;OnwYJ&DrH$u~UYg0nQw*gDC2WpQwsT9UvI4Tx zZyH=?k)cOPCg%~{?yk=8@2;+25%$QYMBcn=Q5!GW!qJYXf&6k?!Zr<6RYEUljyAY- znx#5ED5$Ud3==B5aG6zk35byV*=JK(2_4QMCnb*9#o0<1M>A;xSz~@L_7%0PCbc`!v$Y| zM$r8Po!nS{^Rb7k-m3PzSK$A^-U<91-u(v1wLPo{bg3A+lJ9e}`5EUhjpvEQ+^wa9 zskw&cW6%YJN01CkzyfGGKf3_Pso$vl>v87rNC7<(fb}iK&~V8kI3U@b#|Sm%*9Is+ z0?_Z+Sr&QE0@f3a{}F*BzPUl!vJJwFHw1J#j1_=$8%+M3rE1!*xZb--GOm4tG_uo& z>ISK2deYkI%ERaWmQhnDdP^GDO&p^LY5A#LVcCQdDm-1VzxX}RwQ83@q5chLU1efD z>$n7RPoGd>&*aQ)W#7x6qaI^3)4f`+=u6yE!bUkCzLE~jV!d^#+UEkwsOY5B_WD(X z`jQK+_@HRZ`>SK{n6?HR41VpOecDlVyc|ae-m~-ae%~NAG#hQN;CM4r`DF`XS>*fL ztJ5VtsebK^K{I-D`4r~EBJK*q?~_4_{E+rbx0r%yb~lr$b$aWU+S{ZSU{v?lsX5RdiK{Z(LvA*Oa; zW;jcE_Ve{5@tcGs#cu#+N z^#hjG_z=Z7DRU;QvHMY+diL%u;q^vj{(zMzh95=EmsVjm5 z!bUqprQ8Ysdp%a-da(R(ayHSn<&sQU)wa!@ml>#V74k9bOO>H!$EoKz z)$HQsSxndn4$|kR1Hoz|>5W6Z$=aKBggL0+niri3w5h9Po=qNFwY0SQC7; z+@><1FU9>x;o^on%VYS&)fCG%VbKC40J}z^<8Cq97bA+>S9GEhDybR~qJK>=1jj0h zO@H3{GEcwe|NU-lu7$-O@5)3zuUq4dXKmq_gY-yBYW4Qg^Z%HBziFO8F)1fpyOpsAU|4%!!oa%=iTKpuX7}TF?nOKPz z&pH}00Ce_==Q@vWDAGK3SxR~*F8UMd-`luCoIyo7Mi^*^#|eP!)cKvOmmRq-B39qG z*N2AAI@y7`g+oWuoluu2|1p0b$85Ni9((4H1=zjB15t+gC?R_xz!rg^e{9TZd69Lm zqP^Mg*}6h5W`IA_huE$i4sv|IAEmJJliS@{+8*)1^=|ml55EZs77aV_7M#8NGrq?f z+23HBpPEVYZK|A(FrNOwp#5-Z%?)yaqlMt>D=tvoK|rKb^N?2%s_y+! zq`M)$=8v#Z)W|*JX_K#$y>s>P(?33oy!vne^-Ho}Da+u;w^67rARrd(Kf0^d++__2jCm1)-W{#}4|8cAj#ckFj;?XKJ?1?6wp&ihxUN;(37UxCj!82L@DN%{O2iHX^zb2rFHB5TJT74k0SeMm3o3YXM_GSQXg$_G!g;gdI&K`kJN@ZG+&2$ zy#La#9?kV?=S%0Fe=d|pNxR(--?E!|^ja?KqUqGed0uZ@YQ1j!%uoKTJG_1m07IfJ zHmk9sdNTza_7 zz++}1shH?d%EuP811u3$Y_29KDr6-J3|U1ZfahRGv^SgO!yN$`q!cY`T`dcs>5l_j zv|r$4EO-1{P|ng=CxY3lj!Xy#{!d`yK#XAadPob=uzq_YzyT#2CtyD=;Q~3Sy$)AErCA>< z+;FGk8? z5z}|#9YpHLo`SY`a)=i!)`H)T~NFx!FIz^5ARu)VDhUvG~lx(=m$9}*sqY-65W0E?8rka!1VqQWT zaS0x|fIY#72w?uvmb*dq|ALVll9u_goA8mNW8oqCR&gLqQBdXyB(72sts7z+n9Tjt z1H4-LkHm&NPP_iwX?5`C4EcHsD})e2xXAs2{Mg*nWHfAgt(ry}IVRMM?euM+kw1)09%~xZK9N z`4zjsy<2)kF;~;8uAu$JCPb(?9!bnM8%AbD=!-SV?nj$XN{4)xaXMi9CO8xsqv7sM ztbA2UZ}$F*FVEX(w{#t#yn1>}VmV3E*UiaA@6ANm8XA<1#d?QrN7^c7jLTjAzPlgz zCGBHWS|!Myq&Iwd3Hy?4z#<|SlC&!F^uJ;om-+vd^vDFC!Bk-h^xSj=XEt~9FWVMz zf-(_waUb?}0zHYj@9^93_iQ4h8^nAAT81AEz#Q)pN_>M&FgERb!3>x`{_?+GlELYP zSN0yXIX;$Rk@$!U7d^=VE=!$iOg^yAywK#;V`P(NV7zgmTEab=sUvHE3@G}P{I6KY z*+s6NobW4jl7gpx=W`_eQw3jcYoRQHjX#Q?1N^8hrK7pwGkZL7-maoK_P+H=ljjhp zN$4@h{&ZFe&rok%{8-~DkwtI_4cTWC z*qm|UP*D7A19NV7257 z%^{!>sKRqgn^cc>y_aX-@zV#l5gj~y_B-6B9u6+UQH@vDfs7DoJ(u8iELo+|U{2Yj zBd{z3bm=N^rD3`Coob9Z<^lj)@73PmkpFv?-FbgbIZ6G6W+@lhm1L!H{M4CX_a)pD zdC24jcs^zLa(o;v!Z}vJ7W9nsJvy5AXP5t=O^`2X^VvjfWN#on-0mtn=_}6Aw9$?g zcJeqS5A@`~$VeBT=@-|kja*P+J#T!~S;HpXz(#gvs>@~1GUgD*qJZRpBkik!$6Os# zTdq!=ZRPJuA?>+)?!X8;f4Poo5~>PqFQHkOKeEwq^E>98Rvwxp)#nJJRH?A_I`URg z8M?+vHij@>f{hCeC!Y=(1v`lewANm~;=f1pX^q3%R&$ZQ(zHP!dg93Em2Xbe{_9C! zRfXB;yy^$g+i_lS<dcdcn5vP<~-8Rgt(umvJ)~Mjqok#=UX0L zSiZVO{qVDHqFZFi^Mn*kFeB_+X!|MgbG72O*GSK^w%-$8xF37we!V_J$ZubpkqCPa z*zKnDj}Hh;D&EIFD_N3PxBlc^+Qf;i@A|ROu{{B#=IaFmejCSEj=x-4G%bDXC*`=b ztdhu;O36D4#|7ss;W}9Qc>U;Gyp97rR7rSVUzLE>Za1D$8g0TffE>c18(;9xPl7u> zfQ;7Do?p68#5e9iE+ZAqbkkJ!nqRs766s>JvvVnbSy0)OLmMO}?}Z4w+5hjM&_i68 z6#AjTpD>DzjL}WyXh+h~KP3}#TY7vvin`cI>OUnD@!@i$y)-mm)u}aW9jS+j82oO3 z38;BIcr-pa|4P$8DRaSk>4>@OqHq8&C`LAUqrr8(h!Q~5G1t$!zNF-ZRYlm5P z6X`GoT!@~kcYng}g_~KVsakyKY5_pN^}9U$t?MtAA}!a~VXfh-UIlb*@U#8ER7=1K zPtZnW6TBTFQvxbOqeHQPxC%6H?@nbW{>Y+FEoGWjlCpPmWe?^8Fs&V~0nDp!O{*Gb zBCf6Ot>l0`COjmA`ig)v0j~AQLE6yuysR_K*WqwNP63Tbo4H)r9{@nn-V5)9hN+|^JJfrgCRh+mOsUuN`?9#;DSE$uFD7AXnzOdG2B`V`LP2mdu_RKHeD z$lk}&YVGRG;|4X_k=qvuOaB>3>2)PQ@HcS%;V!NDnPM3X_|sVX4|+5r)GV{$2)t3R zvho9wJmk*JGj|%W!KFSQ#>V}@6Sq*QX5!Eptizf~E#N0Dt|dnq@5To5Hf3ha;USEr zyJN~-HQ@i=9|hnA(N_^yU+(@r3sBpwiW)@bI9L6oGv`y^U4&bsV!9s3OTW|@%C3+8 z{ZwYeJe-=Zf=!}V?C~dW41{@0iT4S#`=)IXx)|05UhyDmk}opir!dynZ>07}by@q2 z7U(0AY#Vs}!Xx+!71LAb4&Y0>6W!a6--3n75m4eh#oO1KyRQ{UtdgHkRvkc#c9lK3 zJWF-8%l8cnK!u@bD}JN4xH?1;vg=IxK5g)gUC;-`4}pCQ&~1mmcvyzy<2x(3Q{YKp$sYMJGlj30 z*xCoY6^do?Ib)*B_tUdcw~t$nXD?b3ll_i2>QUBLvW9Ec;-8;7ud^DU9*yJnYFP;Z z|Bn*7bJgOp90c@(3S5YBR3G=>JD@wi0K+PeX1C5#a#A!b`{}fav&G-o{v#o?EuU8} zj7!SeK4+yG6Y|`wE$rQRwR>KvZT`#C{I=-ixcob5Z94toyO$r-Uy?*#U8%}V+0SXF zoz8n#0<}m8I6nvped1fTpTA2ip_`E#PMPB$ir+Q9Cwj{yz$?v)qm1e4S4}yPF8KzfNYBN%7ZU*sG2<{ zBi+H%om-Db)Dhi;^SG7M^>f)7>p*vv+&Wyj)*r88t?E|BW4O5Y$nWsa(VGMfe1R4G zt|S75pKCe3Xxt7$;wwcQIBRU{ZL1}MQ^Z{-eIj&Bc<$eNr>79}_J#$u!grh*%j}L< z8^2-Y(DVU`(!uOHmthZy|Cgv6KmCaEB1oGbsW;oY&e&%SsM(KZd$)r)Tqhriq^RlK zY6mAqQmT#u_3feXe>oc4*$x%YP2KE*&azoF?S_DG3b3@|te z0j-?S1|*7AglzjsbiaSc8Ps)D<~;XhvNCa#oRLiK0_pR8-bF)KN07_dR(L z8ROlcOZEV)#jXLFiEY%`nZi`|EvC-{68mgtdkdOV$^8>{kC+k-)Bo^dxFm(&yRpcW z(NxaHzw!N;VwYD}lftG+V$m}2RGCVK@S1j{i83xSy!-SC@gq=OJP}i#Ye;JSCzmsQM}X3MYvF1(!F&iu zp$xW_i$+05x0a8NX^%1*XusU)Mp+6R&sw=I%rag-D!tWc;W;@m*Q>hR|IUYjxtqcH zsztpU&l>VfGHYh78oPp(oo3a10y4EA0yG!AD>O_zdA9Ek z(bIzHSIf;0yOc%6%byf2eDm5&^|OfTg}%XNmxN66S*p5Ud;4t%;a&FRXR~j_#iy;% z2uWX5Zb(IzsR3%f_FKYO_eiasy*K*96-PGQ;nHC_#dOIIX!|lN=Lm|yIL%h(O5$yg zhma2r$)R|?WDMSeBc;;iF{#`zCGNmkl%E$4s(7;U;e6)s$p1{urB_G-uJ|{mlW~%o z$T2)@2FrAlKnM4ooSM7+)Of_pi;0sxhtJ0+`^J>r#;584i!&AHdNoBZ>5l|V;>0s; z61siqVU?9zz0g%Vx9pgR>>1q-!_)htW$(n{ zG}6r!U$cL?VEH10EA6%Subd|CWoa9q-iZq;q!l#4hSs~3)4>qb9S1f;oUEH{^>{3| zdeACnHN`+Jaf`FRT_KuS$o#r!I}#oO+$o4HF*jG(+m4>cp?ZQbGuf~? zHet0TmAF=14Y_&{KixsvPrDAa3B$D})?Qktmq&8%ng; z%3_;EGIV#GYth)!q<7nQx}22YZPyr0i~?BVCMrwHP0UA2r~5aEb8?`yxt;>Qb?auh zDk0AkzE6BNw|KF4gk+GN8|!jl-IjJfO(_o1eGI0TC|dgUpcRrDndu#%To?IdHhAm< zDQaY2Qo`($*!TLZQeY5B00tcEb$}TUip@^NLyXA0@dEyuJ8a%2OX;3?H9gQu!u(^r z8Wik?&F2~dATh~jsAz(^7}8*nQj{*+?h3mgetyohiV$OQGtTTSg2^Q&Vw*$$d0VFV zs}Z$!X;Rc@wK>5l^L16SmE7^5t$A#|sf%r~NE31@S_z)cZRAQ{>~$dR+J2-o7dGd? zt{5(2I-jLqbv05-4K&17JFjLLO4Z;N!u7H3+!GJ2AK6^+;ftx5`}rd`E2p5Cg9?&= zhvI`1y{LxN2B+nCv3nNkjNW~@HT!$nGG?Dk=0aWBM`KXSc(t==Q?xs9umI# z_I8W)M5yD*1@XB~<>JXU6khvrOY6pMC#O{BifyjtqmdW*S7!2zsv9yt$J86wB&LP>8Zq@CUr2)|@`HmC<%&H8m;Zge1q_wq`uRQIM>y-RoeRzAE z+jJ4M>3~ETfSzi^UEAPb9|ngXNZdL9%Aj>B-j!k`=zC!h)?M#gT`$mT2q?}!ggDZd zdcHvFM8-HUX{OsXh9gSox#yn6TFkqqDq--2mF3GLm3HgPQ;C$btdt!!D-0>mF$xnO ziz*r#ng=S}MIbs;;yb;8p&4yQkUaECRx8kZ8US)yeekn!?L98Ki0F3yhB0Ssnc_`9>GApDi>Qz zA%9^F-|@LifYne=S~k9wE|OW-dt<;4hIkT*YwVZZVw1-ooQiIhEZHv1U4}^yDqmil z94u(C=&{I%k%YRSH0DI4?I_$TW8boeck!Y{cP#9*n-cG7Qg3EjEq?=HMMm(x2hH{5 zzN==E)vVpyKU6jFgGLz$2j~9j_c0?wp?&sh z&HXG8{0uCCMiBqCYY>VAKZ0T$a&n0)$M5>U^{L+#@w?Vb8XGh)O_%0hu?K!4ZaJTn zq3%mb%fOW1msuC9n`1hQ)}NHd^0hebQ0h>7 z$~;UDd-rj|c_d)Fl4i0Jl2Hp=Iw|OZH#G8)HCzSZP){(>&Z-)D`N<4R`vQ!+&&?r! z3aER@E5dg}+=Ne)X#~}agb<(q<7@FRyz$iZkXC?g)`~0WMDRAYh_yjO!-?U!I@Nhe z*cF?yAhy?k#dws>L|&w&hfutQZxNi3^yWb8vbDl$t09qqszw11&aA2$2=5 zo$gM^X9X}3f*$g3&!3Lz6V;+9f#aF<+7&EpyQTPc6TEsJ*|EaSnCV#?a`I{cB3+odjN;YLN`aEH^%f_Btz;Ls*w$zB5fkIC>TZh_T_fEVMaE0W>wfK>EYE)5$ESqLd2hGM zQhwwP(==;O`Gi5j_WAeAJxLgvbv>7Zxk)OH$3-GlWCSiO9WKwHbmtg^z=qan*GsN& zYh%dH*LM)e-FHsX`n85+Szb{a1`JB_4e#3{V|9jFXB}5r@_7Qwl_!%Q+GR|pnah$o z5og7e?LvyX+xPObezr|wSyey$7j+~}Z+Zx2ywbsxtE0NhQgja%ZgCItYySB{{#MsX zB~Meu>mTEy^c5HfoE#RZ*wHwVRY((PnQk;9Lnw}2f%*ZzkJI$-r-uOv(3BHMFQvX_Yp}Rc{a6WFA8Ti}_J;bDrsAS}GD_63A2t4TB0#i}LtIjM;-OVbk1MV&inGn#PwOvhZNp>i;O-5X=z@zyqVYs|1WqQttM2=f|2}Uuv0mbU}Ce{)J7H&#fsP7HSNw#5NUl){?Zqr(wV*-tikYRM zsZ<0%(;s@%!x`Fy%0|@{{_-fTSnPzVJD0OD+V>q9-r)wc{qV3WOW~}*6&bE~j%18= z=e_E4j{T`DVN#2(4&UB?O)|1S&<{w_XoT(x9Tkab&of8_r`Y(ZV?N%r4-Kb|MV)H* zB_yO^xL#LVzc*ctvw>bepVfh_&eT<(dmJ}hR^6>&dL~bDL`k_2;4ele32H*am#|gr z8jAw&7YT0oT_y04 zr>{?2Eq8bxU@PP7gG>CY7x@|fQ&4HGODUis9R;AWXXlHl-(II$&+*YrnEqtcwUMNG^P0_cm;0=xc30xP;aMAOlbNhH(yRBON&(!)?aR0f0=8G zVAB~OWMfBQ?fSw_*?VSE)iNYoDZjJJtoHjH`swpL;POUV)bByoc?>{FQ?w2rdr3gQ z@VxC(JFz6wZv-~~4ohgnO2z)BYz1_AEdR?fH+dGpg^B-{V{}~|hldSPmquiPnL)-C zhrsM+zL_6V>Om&e9R~HgQnC|XhQhKw{_{=wh~5seEW=xb@7$qV0TFA_1YX`+% zZH?7PD)=LuZvC!mEG2nd;yVeg*`09!x-EFOMilyC(rS-aLbj$E;q^#8twg) z|Mao)+0H5(I)+Z7#o8b_VtDElw|;rMSK9AwTm0}$ogRclUB&1ks#TZh{9 zSI;*t=}*?+|NPkfH9zCMPW?;}r$~p?>5@3muxcmovcXR@GWUl+)pz1t+lefi?x8?h z%?5lVTzasoV5KnYw~(9?uS1c~HcZ06m0tc#Vtq%~JzY2zQ-x4pIHb&a*3f1v_>r@_+hk)24Y>bbs=To|^5zAswf;T-S|w zW$bZ{!AEMyJ~oOxx<#%MEd0SfCg;uOJ$L-f`Y?I;bDO~-;0YSPFT?rWte5xQ!Fach zXRp!N_}m?EouB)6QF3hM&zmIR;XSS9W1cqD<+45rkM;y;IYv3yCtr0zYYW7C0QUZh z6J(q-t(1o_f$y4cXD-$AGS}`t%>mSHWklLemxw=W_&YDQ;ja7tR)7|kvoAti=f`ly zp{3#GFh0Lb<#JZdN6{Jcox})dF>GhB|J>z=qwS~tC(9N995`^>0%!WiSA0c3skLK^ z`h9a}Eiig&@?4qrzzMLP2j}4I#`p`TZb1N-(0`#Xy_i3_**o9Q6S&VU!p6j9c}{98 zZ9FG`%dna}ROOFQiA+d3gWvS3)^2;+(6bE*LG2^jlXDa9HNKLyM?AbkA);WW88>|j zwX2C+>9`vwe8Pu7GiWswE0#*K_8Lmjj0upOYa(DL#G7nsfy74}^Wv}OWNc!q-f(0> zD_6^>cwbeFBugszGw_V8OSzWKVpM^QMc^WVsq+Lu(KlmlnVTSg?_BqnyXLP3$gtT9 z(Ohbdwoo$OB1DJuzdy&V$aq|5!?+GvTr_v!F^eOPxY+vIIUQtxRs&}xd>{MOw=u9{ zU^Si>ezgS6rbCDxUJpbYz5M6LUvyCHB^c|~0*J{rqEC;7&z)xk+n_^A>~eUS9wql4 z9m{4JdQj9Z?W<58mP@Wij}#VgXk&(BiWOU$#} z!_4)qHj~(6V!*VtC%>2BJ%a3z|{hnnj6Tj<^UrOPi&u0IR> z>B!f=DHXLhTCS@M1Fo-IkK;(@$J2x(@4PCa8v9Z)lE-!Qrr^o^b z6{_T~gAcS!3e3J2p%8R5F3xBvE9e5h*t&WlU-I)}!PY>|FXQ`qtXMX$G@pdw0eC_~ zZmG+7CJZ=t9%vjptt7Gje%!WNE4d1y_6F~J9xuoEPd^By!p}08hCuS zc25!-vc(AOoawVjUYJ&*L_1#?JEsdH?)kV1JIo*xX^%W)XUS$6AD>N2s`ir> zrfj>N@QCD;exg5UFT4VNyiOW9nmM&m7+d`7C}PflC}-%KiTKkH1NQ6w{Mo1efNz<` zQlK8vwaKv@#&P}q$QYFe^NV}e-RmT~$2>J?&wNsgkL3t^*;T(gm-C|NV1JCQ9Q?MW z1PJ*rP4)-dvl~r&0SN37*<2_*>dZ+l{U1R3f3Y(oEMp1VL2b09+Lq!m#!PB6gJPE4 zpUs`=Pr9sj?=$jq6}idipG2;SgbnH!p_`w<*j?-Z`Y2n ztOJ5sl8}%)Uuz8PAd(WPp{b(x&lx0`G-esU=vD9K4VL4eqF+Uzh)QO0^gd@oqmEb8 zBgc|NpihZc<4*Dv0r7us@|%*qIUJe^mtPqhr8$NVP(a?(*-4}rT{~LsaD$XrA#xdO z@IXLqdUt-oyZPbE`yP7hec%F{QCWGrJxz4@XSF&cuTD+D7|dX`3D#{*yrnCZOF(to z*OzeSM$IQQ8noZR-lN2Ng3?EEn{9wuJT`ooM|4~r+7pwp`*4L zaF;@fU~ZLgRS#IJkVKzu$$H za(%zmH-EW}F=;){>^^xBVig^-H;?h%xkdT*mS>F;+~TW54sgD@f0J+yr&#G(lXj~& zx{!V{sAWre-y56TcwPFVT`Q|>^$C;YIc}`@v4#Js!+J=2;Y4}71x;i$oKeHb%b0W^ zia;fnBjlsHx{^A3Xk92Js2RXHJ`O4n#1~+Rw_o{iv|I};ppD5L{wx6XT>Rr-;Hz82iDa)lQ{2BAo@jG$RAUlj&?p7Fj4YjLB~W%M2|w|aJwbx zZ<_pMR`g4cZGYhBWHqD*De8dW`ceU4x3(7h78F+up|PrZDpI)~;?8tvd_XfZ;Ly@rnl$*}7+iRMld)}cRn`9UA z5QEi&5d5XQ=&c^b?o=((pH#;F-|qzS$6$rf9>nIaV=rIHq78f}@aR9_Z$_9_bVaH!C&kjyLB*C)XX?pKJ+q)};u zs6S<6WBpP|DYJW*cTwb``T@j0#hpt#2uq+jzSUw$qFhxJn=Y2}_~jV(W45od{HZU% zJbnhS)>88@US@Kg^!up>@30;M3HI4NMq=o3fX)v?Ts6|%bmiS4ayj^58^6jOd>Wf| zmtmkCv~ZN28wfDV_r)Etw~V$Q{%A^@+O9-S$sOzBlh3$%BU=yIPh`J`yxzKhZfK{m zy)z6f0lCY&W;b&SmiKkX`&%S?z|!s?K|1yFQ|F)z&^QWXBxNtg<-o}+(JCQy`ZGv` zRNka$VQae()0eyQTU8_4A#CXQun4acpZ`1RKg8-eHhusfk;UqG>{%sgrd=vYGa^H= zrd)Kl$?xjm=xAL2k12w=ESX8ZQ`yg^C+Pj&TXA@T5$K?TE3V1$qHN551nkZr6_)Lv zb2#BV4i$#;J)GDH=)-bo4ls~pR{!1BH2cMqthI(#jhG92W@}G`d!v>@dcF_#^^bLG{-ds%;R55FCJWqIc_+U+2{UO)6jV9%OCLMD;x z3)X%6{Hc?cefC#>noIS8n%9quRh33QKzQhB3~QQCR_FrsHV4k^?%eLhAD6I~8eKP8 zsd2X<4T(k|U4>s{)TIv55C{v}uYOCP@~Qi|JDnBcS?Imtv0;30g$4qABp4O8Q4;?pG+Q9BZ&l62V_h9hPMv&}^YY3dVZ0=W2yS_P>kz!0DKxx=4#n~s_ z_7}%uV-jtINp68$8+}{_hrgOxC%4yrL_M|N`L#>7rg*$LbWKt7OU$oYcnp)!G*_j( zU+vc{8+(r1y+h5Dbfh6=1H2r5 zX@4xU;&*QF(@)lP_P|N4RFqHu@W?06)u()c=DIvi4<@bWAHK{!`@qXZW^sO{Haa>BiL3FQX7O*j&lhSRae;mk^;5E5F9g?XXo0l9I6^=3iN~wjY>S`DM3HZCw;z`BUgu0xrk5)n6<}xb0l-IZwr{bEu1r}(s$}{U1psU z-1LX1a(=vjQ+s}SwI1!z%|xjg`^MjgmZ$VaI@eEx?Xc&$KK_xpsp((6gq}2DmDNrW z@o|$iiIC%;EQJb5f z&}o@8*-F}>FF`rnLVhx}fcJITlN@*=mdsdqD8tWE1Tca^I)BB-jJ}5V)0p|VO3L-Y z1uW+q$}XwP)YA`)$BjmL^ZF`Ve|5wn+T^Z{jR$>Dh*PDVANMRh^Sylc6YYmcHS(im zLO)o1N~>CtpiQ~M4|d>?8QwU8jqoc%2Zz9y!VFjo70h(Up^`BG1*X{txFAEhu-#<{ z`)qD>c(G||^wh5^Xx-)Gx(bLpW&8N?VHbFECrr3RpK<<9^`1~yqZ=-v-R#sq-u^`a zx8W0&Iu+iJI&HLCUu5>T+Ij3inZKy(B7!|oUgskf#MhSh?i<{NN`+}_?Ba#nGsbp2uZ!>Ml|J8T?ZQqAz0q-_!MKN*sQ>-_6ie|8~q~Y{cT+kO8cVTV5;+dBEhcKZ9}qYT;2~suezi zKMZEEq9;t9@eTrhH~QL#z@EtWxfrsPE`}5)BTPp@1uX)?!aR!nhy1-;8^`+6{~zC8 zjamBd`K3BPbQAtgL544f5meOo|GNg=OmwtFJXh|2X)#5< zSzgXz%vVm=^tlg0XCy~I*D9wYm;Jl-*kY)eFKU8AagwuXfb!XlbC_T^hXTMeRNPF8 zc;=D*BIuQZ2#bbN6g_n?L(Qwa^IBv{*$ROSqS%)Ro~98Ds10;_+>+oS_d3>=od@;`8`+$LrO_A5`faofB zTe4HMz;)Kh?I3W}pc3SXg&!s_Oj$oK(Ge?VnCR;jG-aHh*Ac_Ij_NI$?~xEyN?DNO z^E*Z!RwgS{@}Ch>-vsDoUj@wV3!agXi}CA8y!AMkGK+{ebsbhLWkNIPlM<}(2DFMB zig>0Bj$2hK>nTT!8Ui(VOQ+YXBI=-TBoGOignU^sU1D~t8p??BRS{)ex_@q5Y8o-I zJ-fU(+f%LwPdKk{_m7W?JlQC${wQRqPBpiFdTUn>_}J-*eh*b`w8#_e_DO9lYx7*% z$H2spad}@t0~RD*H4e2HC%&(0f0gnL1`^*RWh9b)%$cOhn2HBF7i=mJ){iW!0h5`Z!Ef$G9xWmjT zj4*NLXV$Zvch@4&Ap0p&zPGUBl?GStP>M#%$0F7lm2Ja5sfG4qxlCU__RnUbnqQf? z5PA>#2@5(-91Z%A_l2i0@9n`MrP>v6M>9t(*bcKud+Xs{BW~{LF^Oq+f17M@alGyN z14$5Wr>=PE#*jahDl^j-a$@c;!tCnuKXqgBVo?05ZIo~8i^~c@OH@(KKn$m0)!Y&1 z$9-B=oo1#Ome7O(0(uHkkUL;~6oea5 z%A39dm1ka5*@WjWEYg!SJO+!gV3C^o-Fj^3)+%Kl_w1-~=8#~%Q{0FAbq@UXv6ap# zfuPlSLbT4;!Ew#q-t&LbS0EsG&%c#r0;=1`aB=0v`EKO?I5>=MkVHGdCUedM$Gg{AdLE^#bv zk0h705$a6Z?;X6>We<2H4jIAe%xN`Q;2LBd=mrfV%e}~A{}k8O>YFTam{_$t)hqIF zr-yLls;-clCVjg$SFHp{t1sC|%+(vQ__m5dJ;Hp8J5UAs#M#Dd+`5<@N^h~Rsk;JQ*thrXo@u%^PnJzL zV_Wb{W=Sh_koB@^bLw;E8kC9IUawcR7kAkUZ~O8%1}q=Bgad@#dZoIEBM1C*|7n<~ zPenS{)b8A&sr4Cs%gTN574o zCE(0})v$ML@vEL*##R^A%@fOTE03`ub;*g+xODk$cJ&q!=V9VqI=??JdKD{AAJO_s zCz)x#yr`J(Gzb1^Tg0#$I2M?Dc3|K3*;)>haNURcrp(-9D^^{>#tkpWzNOV0xH-P= zi~R1<%IgvfKira|}kQi)o-}E!0gp>EP5`TgAmjQ1b%$nbY$1?A=3* z(`}9}10On0!bbH!-f8}b2!gGnQlk+q!v77WnUMJ9je>HJETZ;)!U|+IN&}&o8+U}k z4aFc3Y0no*Kf{mtoI^+;vhan2;2WpkUnes;yDCX5qseny&wIHur7F!G@^lsBa@CF5 zmD;N#FpCqb%t~XV+{^NcdOG*Mq6iozJL>@wX!X(=M9ww1eR09nvdv%q2M*9J`*xYt zoXmiC*!heRO<3RN9R-J|Ycsa$*Sd_*277;^fKEe9gi#*i1I+5`@ePX+8g7D}-seG6 z`~N&WE!C(R)tb}NbpTJtZ;g;Olr?^p{LQrKJr1JKQ!s&Dn`88|OpT^3Wh2JtOxrz4 zXLrfUi@%yJElVoU-|VWvRlcFJXMqx5D-}*HE$QtWj1&!v$TgH=8J^nOZH%vnmJ1v9 zT3OD(TOYs)`4l3|*UJB9&1Spcv>DO4<#x7#l`+q^KJ)_YU=JIxi&8&4QPK<2UhPvi zjcms7(*s~$jPwI({2!+gSt3tdu|dimEQRw*AkRo5gX||?jtigr7F&r~=DMdA32(R9 zF0q^i$`M~$jn?bdQQ5sDcB6uO#Z2fEdVYI{cZuV%a(y!~9Uc$rwyMGNnve96m~z6c zrhBM26b*@&Mf??()XCk~N7i%WpP#OxdN>sahw9$53owH#e@&n@1&c&{SY}KzlvBOK zs}JM^(WBJi5 z|A6?Q>m3>K2!k6HOFML(bC;E(X==|G8s_* zI&a4{G5JyD9OO2j{4$<%IWx=ojQpqG87kX6`ogNix00gk5)JL%F-o6r?@)~r`mNMC zy@-gSmGtV8Bgz^g`^GS==2`Ri`StTnH+rhH|92WvmR^Pbfpb8)yew^5`6W++`d`U` zX(4s8fnuf&uU^?N#)Ryyhc#D{2pUa(qV-LICEDs?^3uo56wBxHmM$39C8!>YO@%-D zmq9jK@xwQvx0fpZI>=xDvAyYKUi~MG4m04Fjao(cIm<%dZ^$*Z_(7^aJj~Ecjxhpk z3al}M>+Z5~?^EdLS##6^Hp{v(dkW*%7QKHvG`ISGvFW_MADNUmW#KQg13BMqz4W`^cL$kLlz9(ztSY)rq^bc% z7!yuf@`(Dg+5+WhOOfh(dqO{)ysEG3$xd2RB^sa40P5@|O=rb94pQ{Cn`)sr@+D`^ zmMJl#*1nPC$Kd?({gpaShbeIc>hp8C=KsKzcOD2!k!g&!{=d?GD5s7mPWh0`aIm{# zVlTB`^O55@8LOaZPDDwHlhmmiVgXX=n^IKzQN|jc$Y}IMDz$XEzNdI)Z z1{07{+XVsTW15$sK$fy%>=)U#j81Xe6~9x%eVTt9A+p^}s&B^VlpPi8wu~+^a7aZu zxZCf#04it1S(ftj<*YT;7Gj9#3UA6P{pFe0pTvt@aQ-d-hEq3rRLog_pQe9dnMKuq zs1f1JX8I3=|E@TI@HWj-RF`p-392TBC1bt%o#DPaw)XS+@sYz?*SLVRh81C+W1iCZwwzQ`7 zAusEoc}-?CQejvx+phm&cN{c^J*#B+VsVWkL-Ap}-CI<>1Oo@of3n)NFfOV7Q;WEi zoy1G*%P{hO=a6J1Siu9%E+~hY- zFkcr>+gwKhDnULj^=rg8z2X=0Bc3%;)WsU7;x>@}j$2?(3+1fn4C*Ps= z^?7i&HJ1TcX%(u%WLV>@;JEAeCK9`23fess4#aKD|_Sk^p6vlKZxzE zIm+J*8y_U{%X7b;OzIHKg|)8J6%egp+SVe12ZuvOpB{T$sKj5K7;N=}jw52VFkaet2gv6(ivQ+J^+@&9{$ zoab>!AS?y8d!ROA2~XyPTJ8a!8eE+|PrtEBZIH)QA$_%U%g=GZmeWMT!RS%4u&VpqV0sV+`y9e_{0H5*C2EigIfU(XYU&&HcfWQWQC*-y(B!pvVp^o zM^O4)RBA%E`)NU+B|Os&&>C}UkDa-<`Y-fhp#4wuL0|WsqrVHIN7g6EaOo;OsP^m7 z2dJqtal#^QvG=vWIG`!5xh{sLwas0(J`#Oav8ff2Ejj;U;xv_EO`~@xU`QGJ=xtl} z_u4iAeb|~^YIKJBe-TV2`xos@|NrE4(YCclpgB^)>cYNI%;3$O=RT*IzDKDqd+3?` ztKduz^zYr<(`XEbpMk5K6nC5>jB0MFB;7$`TarJ^>Zp5kB=9JgMU4X~A|^;V4UZTm zJrQ-xSMt=&vz#e?1qz17yPoYylKr$<;mi11W{3Pi@yHI~>4|t$^+Jdi?0;uHqk8wU z_BhLzCN#K~c>xkDe7EoJ7q&IfvpHh6m>3gKIPG3Q>@UH~Yu}ui!R`~DTfaUy<%z4C z<3c^}ZwK-ypU}QLHV1V7Ag1!DNy5;kYee$&@LB8#N4P?go~p0MuC`cJn$u}YmZDro z4`@9>`|QlUzqTxNn*gU&u_XsoY=45A2QC-j(<2jU|H3U_#{+%Zp3WiRim$dVVS4^l zd)CqC{ry@R2B?`%&l0rDiVXbff}b#TYl5?~yQD&>YsakJO%>Gk^JV744+!}5>tDEJ z2ryk<*xDQtfu^d+*1R40^dZZ4_2Okn_a!5)4tEX$mT-d29VaYRq5c;dhov<+UH!;# z$QNc$uSfW$aJX{UW~!r^GN_;qXhv3w+!bJ>sy;fd61CdK*Cx7>V7427(kPgIxh){VDRsqyQ2UDbBJUTCO~r=W z(DKTQIe1sXx8*Dz%T6avb~M%KGms)nmVcqD3?7+_Eh74Z2W+wmPCs;o2TUF}}1*u7x>sX$`2!H9+{9{_m|ahQy8iUOQc-Oat}32ub#( z0&bMIEsM4u52G@AO4|`*^-JuU$rZs*j;gGl6wBceRnf5JEp{_YQ&F`y{ioS}s zXoJq0xjV04&deAncc^*M^833|6LcJmyx{)6ikNJG=mmtK!UvHNBC+63qKG#51OCfq0e2sC1b$;}P6>_n&`7@5W zD3EhzoEQC7~;^Rw@{k8P<7`kyCJUtyHtQdXQ*d-ovjo}p^sb%!kxdo=uyVmK@aE=48xbs*Sk9<`cq?}r|*7tWvNW` z8TG@2Nv7qzM?o?5&c~nGhsW6sgJx;874L>Gb*t4xnQ_o0#yv(X$@QuKRydsgQ8-v8 z`SVTv@X0zvvgdH?+oz z*E;f|46x>_%kQrjtvVxthO-rg|2cv9GG$snrPbJ*S-7r#YaIP-|CZHAOp^dzW!Q4r zgU0>AfP?zE$ePH|HM-M1j4zOw!bDryg(O%m*T)=6w-0w#sTsc9vpe1_1GwetLb>N) zC3C>P3qOz6KaeF{vTKjwTX(m?5;10IW%7?r7end~^8PQ%*CSS~#3C>z*F?iomA@J* zjSNMGF4S8C6u(e>NlN(P$P#UKU=i>+ePaNp$@8d0twoQ&XjVclF?eoFE<^<1)!Iya>*9s`pqL7JF_!B)XuBl#^KQkV zVFKq#{G`c+6tUx`9iFG%!y1hAmk1Xg3Un~GQ$@CB+s zZ>e?{%ZmkmsH=8de@{#7W79hjC3rEvM>MB_dsgMD{5Gb(f3b!x=@|PI3~xOz>nvQL z-A$6?^R#+>BP0oZscB30xMadIdZuaJKa7A+OeVaAwDwuk#jx zPeSbQ*m(Rs5_2N!#+TG6TTHRfL(U5=bZJt4YU6yY!aEnhLMXWEpZD`3FT8v*$Z`cp zb@e~fTIJz&d#<2XolX`y_{kidoSgod-oLx6snYO(CAn?$)byGk%74<|ou|smz5{!Q zR|N3U1#@{nQel$WEbuyxi0^b&#Xnow8o|p6m1={n+uU!5GHAO$H$CiwQIOi0m>8jB zb-&ORh9?67Yr@>dbFh_tmkEm48l#xrb0S*^mgWbPAF!sYBiZ&bySR{l5^PZ6*DOKQHy6tZ)?yKL>XuQY0-Q>^IY=TiKuv9xhWZfY)()x zTBPx{n=G$=M`5c}ATO;ALT9)7AV=kW3W5S{(8weWN+V+}K!#}xlPqLx>SA3A=T zC1B89mRv(glCg`nvu)!k0dcX$k9}Uc;;4V9$xiuRQJT(A0**4KxhzJk?!is}Tn{U0 zjtE*|DIBc#B@GW+E5v$CYNb7)tl;E%zgvutMCgv{#!xhy=o@{=mk+6rDj24`T zFSGwdpV6vC@6%=Twz6${d$)z(@_7W9`<)}9ipqz=r42P#OqTwxmRc*2XW-ESz`K zZ++XB1;O5JU~e8(F-5V&esGa$Z_GS5JO;eC+=57!b6|=_a!wWEN2yniV*g%DoUYHq z0VCtb>kqR$HQiA0IL=QW90Lu7&oOT{=Vg**Cjnnlg!=~rRdAzpJ1fgNwx|dTD@kmh z4y0KVj;3TzRs=$8cNo)KXNZjq{ zhmSqRK}4fLQoQ{Tyf#lwPJIF4B+UH0Y8GO>ViJ)}$Sr7$e$iiExc*x+`$)F4IsIs7 z{5Tc?%fNOX`mWs!BswWOH`eO=VhCnnN_zjP7~kD&|-eduDKCmq(;{(6rui|6*Ho|Go4Z zoR=@2eKimwqF?*Hd3^f2stt*eE&jrjaUds6#79LDDPYyk)L{Z8E~6{%*%7CrNhmM7 zsxZ^}om^-tocJYU-4 zYVI_Lx%3BDG3!CAXsa6q*hEXf_|opgs}?-~-yHvZRyCaGw7}4N`fI#4Zypg@9T5uw zcV(4ihy-%ide7qPtwCA$_*0%L*S+me9m`wBj@m|_oKvV_H#9oyR~(L)wWixxE*G`= zsd=pJpwe~_Rp+*fpOflaH|g9qKZI37+A$X359p&KI3C;{`bjp7mW?gsr^j;)ism~G zT;mnDZYniX4Kd8gnTa5I*qw+-vJ)iEWEU(nP=yjOsBb+`_R-Jv&}Su1?mcfs3k~rl zd;LXy^v2+(eq3-fbp3-92A72p5lv#k?`k)9a@gs3sfAx~9Yyd|I!)Q2^w!9bq@91& z=8KwBkhOmQA%y0yxBi}koNi#dsAm2+%XC7rg@)6^!?L;)J{14KBq#F4-dk)}{8^VP z&r14!35r<*y7~gV_+Aku-qNYriuk-Tv8mO{0c7SK^dSL{Vk%;c7mwP7HhW~sw5$S| za3dwR#ep-5_3c=jAOTA!+VdC@#qlawy8L;nk6PPef3uT_zD7y_#U2=HzAW#x%44@a zjJbRp=QSka=cxDkGamnwUVF$lf?0d{bk{kj^P8mtKX0xwdoTWbt`tkPsqJLq*!A--}e%hyiUr&El@|b`B**31W=Y>E|=)M!^IzltS@Yg}N5cIHdE+#`S z54ZgT$%~mnS}!o`r#{ww5yy3c%=p)yTXoN-xMet^UR-frv+HiA_^VAfHm6y|gs?5% z0qxnTN;y(&?h$0${MsH5u@P|Rbt(g{FP$4s<8bO>jXU3}PC>T+_1%7YPwHTn_``$~ zcW+I~bUt=D%J_DM_gPc)@!7}P-JHgz*Dr^IMczE142hu~QSzb?c6D|<6)z}DvO&dACynYI@h1{*5R`!-V4Ngr9aOcI)ISW~@ zaE;q6N)ELImpG7kK6UfoL)Rz}MC%QRlXh~qIHX_TkptT^U|z1wuDci%?{dNj*H=a; zA4Bw?g!659^@k?zVv^Q!&h)&0z( z#6Nx4o!l@AF%8gHR|6e@V{%&<%$``gEYz19F*^jr+pZ z;*3CGvGe@%gq;4ckM~tNc0$y+^`;H43;eTy>(F((H42Q;fjc(kyB zb0%a1C;p!4G#Hs%>s-Juf%xf5n=Gc5%g^|?t=u+gClDT}vpCj0DmAvi432%fM(2|@ zvg8eWM-ifh4eXY}uhP^Z?vUGY)U-EUbGzf)M}K#0Y>}$a4C+~Zu&zR%#6B8!8{dE` z*fidd`<0RpWOJS}Y6#-7U(YN0wgiGffGa4sd=^B!_{lCXK_e}aLZQG0`{Pf2o(E!f zeQHG7j|_X@!hR-?N@Y~$q!bdsYq*-xQLK@)S-OjI;w8rQd247<_L+Q&N>Xt3Za?*_ z-%@#k1AvRY#?2ydgN;vzfYU%FHn#8dzYhwS z_Mp&i8~!Rky6=EfCZ&Zt97d>Q_f~Ch$#BhtX03bR_T|j6b<;UL3DvUv_T~6DsOQf! z=gr_;pR$EcXvSj$sPV}VysNC@Vv-0<=?%P6iF~o347ewdRK6~^I0Cdh6|~gmI=lS7 z&;K1uLrf$x;jUq$c6WSQzuDr6RQ%bgk*7KyQ2SBO{P(5)@5AO#^Q-PFI&yvmG0T)? zhIRmRR z#t1yVfuzrLf3@d_o|HX5Dl2$PlqQ!a?6x95lNEynjLt7k>-aYB%Ji<(d#~q%-uFWW zT%~=Rf;|1ta6d;n9=@&w<~avNH!3KUPtWdvW-M{YeVz^Z4PD;{yFRixn&g)jbYuE{ zOw3zR-(09u+?Wx-?7G9O5}FN|h)hdO#9RY3eVUb@UmH*@+H{+V{6vtBsmC?1HAtXo zYq-GhbG>nLl0lY#&%)1pUmHNA}u9oOpnAcc`I>e^D(4pedH1HAJl zK9BoGlje|InqKR{2+muy+&k)`PSMcS>Cw4g5Haw00=mv1sk}z7y4Mk7={#{eA#ZG) zqeZ!0(~)uffHB*w(a`eXiNV1R6MEl)j-;zb8vZje+F={ zlNH4i7DTXCdQ9C%A(baCN(TbgwMCN7oBXhtThLvX$~Si^zmqqIBFD#i6sK^DD);Qe z(~)^^K=Dx}I%(o{HV7tVLhLbusFGGp*k?m?&u4Ct!z&|2YNk}1ynL@Pg2Q5}f7tNL zd-*2m>W)27(PutiwOLz@7|rOYFJmQIwTt05{vx*8jBTC5D`+DuruWt8^Dbf{z-v-I zwyo41Mgf}O)$6CbC@xesXBDX7XSq%2a$*(Ra$s}{Gn@WMOquo&{4u17`Wlau6@94; z?2f-k$?)h?q(|MWI~d>^h$v;HoX?{2?!K`{{BcmKdntjDO3v+(aRbky@Bk~5?p|6J zClXXc8tPuM;HU92nB06@oQ~R2qCi<|S^6w7K`OS#4;cbJ9gQBPcrr(b#T!iSV}QvXO3&Xb zTU`;ujU_8ljukPmd#P=dv&S{--$TV*`8@GeFT&@6i=&6nU))uXN@X0DXIfK*eXY&Umvpx!>uk$8Zx0{W^|SI;O}Av!;X3`X7XNulGu_0n zVp#a;FyiG6J{0o1y1G|BeR>c-pEmLYS39|Diw3<-ue`zY^`DCGy@O7Q7q{uVkM73L zTTz%1AY#wH^OZSi)(J4C#)ncZhh=QE8DzD_`jV4qA|VMx$E}>-)J)CfbJp|VPu|~e z%$i^k<}RPz&Q_oA#2{Gw7~6apQDJkYXwjIxTV6hmYqCIMwmBQHPGi(=44L}5hIy!E{_pNJxe623*NW2uk6q4kYy%_J6avbuICM4 z2|CHA`dwwAhj`D)MS0>EVShwyXQ^20!CF5e4ZdmdII(-Kit-z0yycRq-Gp&vWd~ znKr$)O9yr(8?G3?R*R{_c-_JDG|Gr_TfjVk2lD;!>#)3`vf(q*IAckskD*WR_YZgH zfTxmrT0KGggZ(n1n^(n*n>)8h)xw1i{m!eoNCHhwo@c$!KhZtMAu0>|{-*s?zx@r3 z48yvG-lkfuOC zhI1${w$~6y+4YC=J}^5gMfyx+hmZrN?suTJ;_!ntWV9SB#<{_cx)jzZXS{F+io~O& z`Lb|FdjG=M&@L`=_jm7vC(~CF(_V{w(?#8&hip0^T$m*rBF3qETLUt9Uu9L$l;jTdv1hNkE z={eMulFxqBey~*LS()2AL7>WjC6+`T`5VT-F8tV7(uWUJiA9uDu{$J+m4BRFTotcQ zN6W_^F;Cq~`!@y;tI;Ubh{PZH-%k9tve8NS{xQH@??miBF1c-)_oOA;>?CCkm`EWO ztJ>nTcDOs})BR*K@6)LbyTm=bQwwa5X`SsD2i>M)2@8hac#{e_nveItqSmq}qhTI= z2l4vF9n@2pLIG-vaDiB2Uv=1SS!`EljTg9_q1$9}J?=-B%o995e$HLoy&$ zu6IW5%ZDg~;ma;ZS1T2@DNU}7Cf9C}-nG>N(_-^)l{>j`6TJ=J9`R2mo+Cf6j-XAB7vdm!m z>B#OvNyP+w2v-#4=fsSj@}KpfJb}c2EIP~}5Lo(ZB4L$$`jOgafAXp8WipH8ds1ar zmbb6AC#8z^XI!>Z&7k}eHoxF6a#x98`#OtsNes2odnW! zHE=(gG8|IkQSzzfLh0DzLqAU;cgE1kgXj=4ej@F+1=V*PC$nc~H#yV1zYPY$Ks8R! zhq4IEM?YISzNQHMT3591FTvJyEo;**kw5m$|C_YK;A<^?!uImpvml~y1#|jw_d88N zCuq0{too!qdVg_V%+023cd)e^9EEKax}ah~9FLIU0dXD++ki#N2c$%TOerj%Vz=b= zrM}(am_nkym0Rx>pw8|u&AW{2E1FGG_rb!q)On!9N^eEk)2uJvQyy*hq1=#JfpJ7H zE@zp+n~8X9Ysbc>;qlA%>D@8)pB4Q-I+#J2BcBex794@oFE6FJKes?;PAY7bX+C)1 z4Y>5in*5-m+oxW1@3~~(#GUvoC9Ey%q?wS>W4SB2=0;P+HTqFX6`hiSsgc{om-GCd z6LK#-^?3hygIK1&wSA!sx`Q^;shRe3Ow2gVc>HTkzjtt=$?B)*v7zQ1lvcmQ5yQ;B zm&@Xskuw#iFTC&l-9vdS?k4n<;raq&5RYPR2^23^B9dN$?dg4A&>b}SCQ4@tx$xo} z1Hy;5>wBlqhwhr%MMCjkPKtW$N%q?DeLbOc&jHo+;FOt7&`5I_Rwf(jpkv1n2z8wy zKebWcppk{z%M?l#PEEyz^HQd^tD}-viiVE9d|_rPSN*Q-W0-D2s9lE+)--2E`zs<6 zA*T}*TmgBgBkA?9Q-h1Ckl>))*m}i}S=!N+tGzYL@^LFNKYWdS(yZ*kP|F(k)~>a- ziM>}Yz4AK)qb>g*ZEqPB*S16p2M7>?yF(J(2_BpTcMtCF?$EfqyE_DTcXxtYaCe7> zew};Hy(jm{ct5`P{puc!-94JxwQJRywdS15?0pg|(gZKUvBcqhx|U|wVGh8hhAfkL zcGM**Bhs%94g_i*>2q(@OlGv~SUxR98D|BBU7>Tdck%bOI(*{I~exMd98ih^>Guo+`Pep7AoY`*VWe;G7<(zI+aSKwSb+kZ(QZ zwQ0P^CN9JWK+s02Sj0LA>N&RDInr)TI&1-cUWy>*3^9>QAQ+N=Dc#9Fv8(N(tG(}4 zJ9Dq_^I@RhPETh!(?F*DX2F*&sqTe(8%MbGnpd5Lk%aqG;8pc7%%*}x=d(anFIUN( z(B_Fd=PN8VU}brDWHR4=&x|7QK~Yc66|6k>xuYZbA!l@yf!%(3%LR*mR4+n~LLe6G z&Ia2z9>@PXDSWr3``*^E%p|y$-jb!_eN74vH~&!#Yx2XgKT(-KaF(+>(yL|J@#K(R zk2I&a`lo{v`V{lQiN#KeqLm}mm~<`3iVQ>|AKB^zam`mkic=n3NnR;``lu^xD$) zfzYBe~4bDAR$T2mvj3%xjk<(_c`& ziVP&^cq3Kq8CQ{yzbo}I@Dq&VY=1x*_KJ1$I2cNvD*17$Tei0qUMCpte{hoG0lmEW zihuh={?yYe`8bGyP8M?lT70b36`s$RsJ75Jg7jr^E=ENrUIG;v16n9NfFW$Wg7| z35Hc`dwfNV6OL|?0bWI@5_hlp%eb|3tnD)z7Du=Vk&MCGm^WxLYUY(>X;MGRVz*x< z0*Tc_e(4~bqTirvJFRb!tatI=;RRV|V_q3=zCgCACYp9wG`rQWNZqg2?q5}AjJJbG ztK!ziR2L0oO?R-am01ZlRu1@Am-+!k(gzN0iW4FW8xyXwk=TKms1n-`>GQmbkkI?;Bjf zp~sn;hH$qCpPJcR#x>Jj;fJHK!@6dk8_P;{8x|ik*lsh>a=E^(dLH3V8id8+FrH=t z!hK8UVsPIH+qA{#$9FZ~;baAK@g`|ZH56k-+5~*7+26Nhd1ihvPlp2g;7+cZm zADq{-^pn~2;;~bH1K$4dLO9#h@^mFT)W|2dSvNiGZP&^xpNP5nxL$ExW9r4d(cB(h zq-8Bs-)}1(ld80TGjOSBR&`;b3=sm4Gmw~+R)r_XTn|>Yo*FR-qy1MA z7hsFxtiU3cRC2h4+e4p-mH2c&qm(x=oE&N1m{2Q<7&3Oh(5}QFFX!mZd1DFcLXm?* zO&LX$&~AW-`sYiJ-4CkGAr>l`UGSYNyly04c7Oi-{PTQzhrLEz?Flonb-Vd-j77@| z$#(CGv;o-ercpVbH_R||S!B)^bAt`;91B}1-ielTRR+seBstQ^SAY6AocNi?tw@^m z*|lLV2$`_#C@)tmhix&aW+X!FQhtr6*`Q|{=-hJ5mHGOSM5qpw*nBx?=~YO1sv=To zp~SiV^4?r|S0051T=MpcOI1h)22GbYW`AsMPga3s2#D92NQDUjQIX=~UJT~385-5} zKCQ2FtizAU$CI&H{gV|FgQDKruQz_v$frX10Pp#1KP@go+>za2u#Jt7!^bNxqBbZZ=xReLT7Q6kWUYv0ETa$qt1YI*9FJe zwe$*m)!NqE&5XS6uSgC*XCeNd-bLd;E z%xZmoA?oC?{{nv{p%Tq!XM29U=PhG+(%9o!inM$oRF-@ERxu$lagBQ+vCmjx&4Si$ zn|OKB&t0iQK2ew-*0U-kiF3dDoH;pN^T^U1Q>@rBkUA!`)(L7ZO2!W2Yyh5i zu$SNCYdgBS_xX--F5v>`V4%zv#=_6x5xLfS3Yux(nnh@7S-$)A!)m8_S&&y<$N0sJ zOI(`)Q2MU=P@zJf3E(PtHi9}Q%-NkSZNkW(^r_aslwKl{C>)!5W!;2e;TR<_D}>aj zsOmFUfcVnrvn7vQxy&YP=X!6H6;+{wDNzB5{sZi@DKQSXsLd3uc5JB}u%`o&uC~m_ z%O^5NXOy664V#1}iTpn1g4cnK6{Z*u^jzs*QpP~h-xxeiSbF)WZIx~S6u=p~b1BDe zP3=CPSH94{4lWLV^Mrzhh2Nc@#8W3483VJuAZfr`xIKss127h*?Rq^OOgr@ z<>A@zB6uf-PQ@!p{wm!UHu=-uY4~gCcdUygO{gF;oP^?&Q6h6eBHT6Z2F_PjRjF&+eXXXhy3$zRI??fr!|SWs z2I@-DHZd6I_}!5x2&vV$49C|vw-lnuD>-Pw`o$!}q4k%d@v|le$MlQ5HTFXYLT?o4 z!9FOOFO#y{c*}$cv3`ClY5FYiKHUn9G+?Bo+x7=XG;MtAc9jWULc8a7<2VZV^9hoD zIL5sllJR5JD0v4z+1w+GCB-LeVm?t>Sf{!XoA}N3^n~L8yHTXZJ9-C_*Dj;(fh3zY zYC{fbj1rvV?mTfvAe_^unc6KIakO;-2E=w|5Z=+{&vSvR+7k}agk_R}EIi>NXbHfZ z%6`cPGSP391N94)zgEXSIB#4}UwkV>_fCn`e)w#(;AN!m88^LgQepjc2jJe}0~n&- zr)Lb+0u~4CPv4sXOhUadH9gwyo-otnuy)ipR=fm#X=k2#2eU3Nbz3gkOIU(n87BHg zgPk|lN4Kr(r#I&)SV1$nf@mJ!EUa#1hP|R1K!YD&amulHdH9fc-dfVNE12alDuF`h z!OMRX^U?rS2^zyWH&gYAO~2jx;bXe?neeAB(92eD6)|=C@}sre0tLm>V|RS|gtb*> zSY<^FKCw0Tk#%Iz-;4c&0n_f=y5F~q*EPOevuiEONmg@Kz%xo*OAAei%E;}W&9*o1 z+27m$m3JPal4`GWQuJ2|Xl*1*&4{;|QdQIA_&Vk0jJPNLz@UHC1tv}HVaxhRhEvrL z%kY92|7xgeSQO!M9JJ0`Lam_NnZ^CgPGG^fl3I9X+8rE|Sm?eugSE;Sw3?YEj!cWT*D8@Sd&r5gthoYha0L3cu z|A&z}1>}6}X!8zD*a(`I2GE3bwl0hq%^8x%1jx;zFyE#pDYx9*MYy|;2Vd!-IvDF_L8AE z-}s^5P@b0o+SMU0D2lrDP1VW|1f(ba8U+=3Zyxf{ld8P}GZ8W(LIh=+)Ja%=y9tk) z)#_~5rO$T2-r>~m(st=`CRT~lh*arL*MQBVC+tb!kf8iJ9s$Pp z<%c4hLu5%T));vUMi>_fZYyUs@ALZ8$4)Qvqvy%DHHcgJRPlWhf6t9c*i7G>9286D zxp9nLBBOEq%F$l1fqqdG7NvH7!9&9ftq_^lq8Aci1twR{!7z<(KxV%M%qlzO_1y6bKQ~B}=^FUL1-QM=*Ek*qM|W9htWv z5$9%5W4(rJ-)thrL>~r$3B{P=pC?U8p15K~ssG2+F>ja)JZV=ROA$hn6uoi2zvv=y zU;L6k5ezi=4(Y6^Bx>u6J7sEp_{&GctQc3-IRA9B>0--Hd4yso$?l-SR%o8*4*yjeQ(}$fBLb*=~~Y&kh6Jrx_wMrwaZdMYV|Qp+>M~3>i2Av ziC+A>%SF8ac$$@lkQNfXwT;~Q8obXH^(hWs;!&g8?}h8$cF_LWcBcaVe5|}K@FtTMxI-fLU6$=m(@)82q-ZHKB5wSX#&&;+2Nz;x=*%I!S1yg4r z&fQ3q#8a&J59AswNo^F+PIk-ADyPDtrF?G*M%3!t{8?*Lj$r@{9Q^o1~UteSls{`S0SOS znLL5*9ZF&hU{d=_6xXCQbpPNpu|wX{whG6BaN#!?8G{#@@IcmiL9Y<_7tNF{Qsz4Y zcKGlJT1va^c(=o;?Pi9ar_)O#i(&e+#rSZd3;B9&L$4Oo)zqDsLC4>NeZvB2Umi{1 zB;T^p+N*PEgX!V<2UTeL*a#TX)`%@d5=^?I%^X0y!O8{H=m1vMyR$z_Rl0#>drA#a zZfmt}oi8RJ+B-L*Oe2v_I&^O*XQ%-#c9L;weW(K|qTCmfK;)44^whMVL`L7rlsfm? zKE70pCu>Fn_H|(YvDYsOit(mEgu`$`{yjQ^A~dd7v(|mpEx=meU5d{&X?0RnB2LU! z%;ToW-9(ahtB!`D*W(wWmh0v>(l}hPV}g0-L44r$zI8Tv`lO??$7}E<$?3*zROjcW zt1^QhEhsBA?l?1!i5^Faq(A(SwHDcquUzkzG8Dmw2+qLCuerwkumwRH2k}le&nz$D z3GHL%ORp1Akz~EpruLXM0y-NzuemIyB~# zh<$%=aLasU@)mgx(&pEd!lGhwSKvi%M*E7_ml)f36=$j&=}NwR7Wb?zIwlX+J;WBL80yx zumD00nW=7>g<`oiGw8>mqP5kHBXuZGXIcndGSeyM>x!MtQw7;*C_S|_-|n}@Bw=0Z zxLq|>Lkc9^9*+vgO*Zm01xZU~IX<&vPWZoO>yBz=4*Lsg69dfP2uwSb(VO9HY1K1& z10*yKd?2(cG2l%8+ROZvJko51nX{}a!`gK?-m*Hu^X;sPr)+W^jGkixh<#ts$hrL+ zi$a#?wV3u3V=dSTt2MvFEUiq1aJlnF;!L}dsI3YKC6h85R_>3u&+EMjpXZo@N~ZHh zg2~=K1sQ@9=1u0hm&34BIkd+!&1K#5B@9FF!FgfxQF~q_Ns2xrK>;QE%&Xl6PlGjA zS1K92N=Hw3*h(T3`>1cx8Sb6=Q~797W$$Q%bi`SJCo|ZmBb>+jwq|jC&4|Sqa$A9Kflm_e#`^>ApGg)((pB*m-5K;FeN!vCPFQQwfQ9K@V?0nF;POrN9b?D z1Vst>vM}u~mdfH`aTgp2bxMfxu?YnIPh_ON+)tvc8L5#PvtyvZ<)se3cgb)iC|OD4 ztghL}cQ)Hl?zt7*nH4W0GuEUpgkLv9iprwsg$T>Vf=X)>x&??{HT(0`JpR*${=SnO zpOa&9_41a4^clL@#5Av z>PI>P=%A;+Oub=uVM!v!ECiSvBzR)1m--%Ws zFVv@!!j;)RfM`6S6j>e_Bgm~V@9qMO1id(!s`tLZ=~kkp{=B;okobB@TsnjFHX%=y z7%2_)o#Yor>3opr$>RQIF7T~11LM?%6`@q~mvp(*Na|jRAG|~1MlWbBb_d(>W*aCB zmiTKdTjd`yR0j+~a>htyk|ncDpR+1na|IGVEY6NsYA?$GDubeyA^$sqUqX_-6NOut zBoqkv6}}zBqPkkht07ORt%TD3@J1+d^d)?8s6Squ)$9}fDi@cN*8p>S@^~}XG1LjV znuy^G<}22??sHpcM?gGkio0fn5O0F;YmZysn7V)8dQ86+@5`FrjCJ#RdQzekl@aZb zkO`6T<&483mPO$9U^*+`0m*+V#+D2x8>;*fw_J?b*X+a6hv|G_cOaYr1M0{Rc4_CeyBHg9%M43_a$nn8z9%=Dd!M0`8y14;_U z1qghCR{=kRS12Z@p#+TraZ9&99{dI;SHOxDLxFj|=BULANb-n*XH>XtP(o=k z<{AmX)g`Y+<{Cq1Tq{{dakCL39akE1l^!3Dn;I)#%{Cn^F} zEtS-5mZt8`EAd3ZdfS3EA#~)WX|KX)I<(p1c{gbZlN_6K8Kh)hRK&+`CH*;z^PaX; zO`lS;K01}pGb`}(@FywQK_I+}!C7wY)?^*QRNgu$4_mv);!Jo9?PTMLfcHg3Tr8(R zTeq1jGi_3Gdv_1Tm|jM2bfavEfIP@3mbAs)*SoLfI`rMHn`+}HOd7OU3tjM@kR==q{ViE3Pf996XQha|22e>FLeY;W?cPGZ;x-h~}%=2$u_uPbE?80c*U32BZii z{ab%R{mCNB1)@(V)(fsnEPR>gt6BpJm5sT}TO0*L6Xt*);l&`)o4Tiq9DOBEMHZ@` zFwe5e&7Y~XN?X7E6p5CpOV$tfxRJ(;DoATK^NF+GQ0$B7EDyiZ9sGF;xJDS?_!ISp z(jw+4$8Mxuric6Y9vNJmE^KwcyC`89eb(o8Sae*=pNjPm(F%3Zf?*T<=1_EWL1=`w zAxjv>q~T>Lv#gFg&@PCY#u_=fZ*Pbf2b&xMcJiB610BcCC52T@Ok+jWxEO$2-!0l+ zBzDS|1t&^3J7L|+aBxh!a}KkqUwiKmQ1Pw{pDVCI(%p{uDeeP~W6-2Of&xisp$Y8y z#;NRc#Ga!Qp&aGTE7@ODJPG%i`AULL8BSAX)aE{3EEXj;dtKHWDH~539+pBTLZS)W z;UHvxeJ2}Py!R6h#u2aad^bTzdVZHN{J}0WYQfU+CpvJXR?_ zQhMEF*hsT5O{&PRvNnl5h}MJpT2|-rIF2j0GdKMf8i^OAIw&eBzlav_N-|Mt?S1Dh z{M7XUJf3%xV2FT-@aXLEk`jr~dvOy*Ug;ovTTEi-^2)#^8p#)6Zx{Q}T`16THMpl* z4{%I<;tJ-O=eX*U=&?u~5W-Y~)x2^46Ylw??^9=R8B1k&?jS{$UwY7j1Vi!}oitR5 z%-f6+m85hj5Z zvr~diizd@yBOhJ94KkMdSkI~rsPY`tf z(>H+U*@xWy)wBk0%xkjqF>vun<~kv{{8a#<*lE9 zqJiF6b)RZ@&7#D275;aRN14Kj~MIiraQzuh$ZPhqI%Hod_A2XlTS*;0$ zA}Nl5d1U5VX!}VNIs!e%x3#xxMhy;SwG^{TNUuOaO!c6jam)Kr34nCq6*2GRqKDVI z`Ayht{eSMrgCIJ&dC`ASQNjxWtjNSliV!-D>bL|qJ_bcq`ET6AFs&+Y72EHlwVb3ri8OOTJk$BF2a6?TPWGOBUcFDxlOHw>KhMgi*-WuS3 z8&dZ>SS_#dlGyh08l>t5I#+Ned25n#rRWNwm`0zwjl!b(Ekizz8}e3btYG-{V)^y! zkt@#+CG-C)G@JiA2Kp0@Uv@h9Gwu+Q>*AH(zQFO`p84ny;lYl%46Bs(0^H?JVe7Cn zo-ri^XYF;`*d;hzW5t!@O+X6UOgSEpWA~L3Iab@Xe`g;UF0Yo1_Et8nToD+}j&Kpv zljWr?v8Vz|M-x=jCTO6@iSIPCx-OB+gDVH?37q~Mds-8-$@lq%X0YgmB0A(4Lgsb zj9Eum*ko#NY{28H%F1XjiCDr|Gra{GaJw4$(efn*(;}t;u=+)Ap)!btlm$aGYvwf- z5|5L*HjUA2Rco|=#axD?o$bGzWQ`oG@XMxw_#U zWGQ1TG^TcA;o*n3GLz&{I)VG_Dci&}ATaG&<5fmS z7A6@l5AlK)cG2Bhgd)P_qP$V$?`SDz+Iil1Sambek4p6G#=fKEUm%BNgWX4SKcM@KHU|Z2x!AV4ZUnXa*;f9@hvg67Gm@kN#|1?% zFN3rJeb=T$ZiAv2)W8e-_@63-vs=Qs$}NtlWb}WuYy?EjYGnyxp5Luf3Lu4tm#Dxj-SA|rBDJF1;{cMrd*Wh_ zcn+fNPVGm}Z;DrlPEQB&EUTr|72p5Uv2{~q!nLqTB-0roco)jpT<3#<$(yM)NAERE z*0=hz!vv;TwkWIzdA&v3heza$x)9OgGRH)m)zL{?cp0)9U-6KhO4jRxQjd6U#KjCq zm;DEXo9z1W=trdcHZ}%Er84@KI_=oS5WrV*X5r;_$FxNTC&sqPsQ$(L zQhq|Jc6MqhK!mBfr75`i>NAjj4`)`o0g-Rn#NzV*#1zN_6Ao5+(bNBAMtrkIA-JsO z80#Hu989Ywqp%+={F0xBQF{0MIYqKBvaYN7@X0q~^fXQq z1PfZie$>H(oY4u8|u1GRPj~D(A6pj_qdbj*X8SH<7p=_H^J;SgcvcD~* zHm9c;=ap0wXIU7Gw|HRT1l?~`*`hAc%5;CEI;l7xw2l|ENvTH3Zus7xlGsCbjnqyU ztr$B2KG5%{sWtT{LxUKEC1CJ~3kE+U}$K`#D$FF)>DY1y}Fy4Mf=;?4sd@Mnrr^9Yoa2zM@Xpe2-6PY>DA~ z)|#%nXkH!)aHhggdp|MG+8uG7wOjq&J2W`AHez=9)&b84H{sU0XjU&VJJ_yXvvJxx ze`j-l~{g*xA9U74_X+)%6Ur z!>!a0jDfStzyA*dp_h)}F9HGt-^I!3b90pqec5-z3sEJWH`qU<1$stjE1mMFsW)7k zu&LNmL^`z``j!*ZiL|_s&!A2jL5hlmQyf3v8M<8|27%p~onBL!PBvm$l)e7Vx={NT zqPq+7DDrt*20{00+Zym=AIfl#Mig*)fkdlKZR~1Bo1|s;K5Knt1i+^yQ&f2UZ#sp1 zoH)zB&jS2U)CzuzHem0gHOuSc*SC*Csl>5{YFA-KFPlJpnYW z-+)~1vu$5asMA{(L=%G@EFB!Pg68@_qwSn146oQ>aXaKrqSLW$hOE(^VE<`@w_^Q9 z@fY}{4wDdGbG%(IgPJ)cjJGQqe z&-RNIC?6>@RpB)<{)Ao5iV@?OGK^T@W@W@kp!Cpr@z zTYk7;IuhjrX>w%ufK<&+q1A1Rr<0Kvpqy5sp+HkC@#E&RSNp>t^Ys|>#(oOw^O|wz zb*%Qj_;?pO;-!Vmef}v8(;9#L)7j=Fz`fo09W~QMVV{|Q+50;7^SxF=-ZxKBA7y7Q zmnX&=5nA9T$_dV1O{8gkSU5+J0d7Ewg=Zv0Irh@(WpqR9?V*Rsqru3?T2HIeB*QIb z45zkLruD42(T|jrf-b*ah20R*>q{btMOv4ZaC1x&@G0K!>|Mr98tFn?AkpYZs?|4} ztCJuTMZV;})K^D%=!`<3sI`r_ zZET1cn-w~Yg#M6DTs>`b@1r9{Ue7@o7`MR1J8|Yt56g!eo;PDC+Mrh&T20N7AO`AB zjcwl6t7e;l=j@+&c|P{G9&z|d}>?`exh^5e7(Cj@f(F)QnDou5nO0XH?g z>eit#@uJFck-j(PkY5DH7o@(~Gc@nA?&N1&eB$EaH{O4j*%PqirtwQ`fW>Kc-3`N9 zxIezEWP?6-PNjwy4`(NWh);R6dSnUp?m15pa@xek160NK2yJU%>5O0bbp5o@qz8(ZtZ zW)3ArRDI{Z%bJ!L##93Lz4v=J0WFgZGkHMeh4 zT;)s}8X_Jz2+8w{mO)296Vm~H2`fOEFw){Y`w>V`I~k!Kz%;7h z6gvp*RQuCqn#A)pSY?}tZ)M;3(|sk_7_J1$tD<6L3uFn&%s7-MwVBetfN*K84G7q= z!T4K!&ZRZ&6NDui63mp`ySIVeb=$~qtCFL4BnO0}&pJMs9-97kH2Q^NY3Wy-Vi6vdIG7MNDq2e!j{I{m=$Pd79sVqjM`=U)pj)1Xe9GjVX65x_ zw~4s{$opkkR&Zpj@ap^HR+~?>66> z7*sW8gX8fvoCBEUr!8(+GqSm`GT^1h%f6C%z7?2os8Lmqw4o1cB6VpqD$HQQ>jeU) zJf=L=EnrjdBEuG1EfiATAPc?;xMui4u7>vuSl|3F%Oe*=%YKB5hfJ^|4>c6UMbBtP zkp;th1BRhpvworL*Sxlwg29Nae*CgsjRuY^8b{=$%(y5CIYvZ#qAimgBPpW0Y_aoh ztJfKV$Q97xBG|hT=%ILh(W^dc;+7YOeUH3ZyUfwx<+yeJnP6AQ@R1GII|z?}2Y?;0w(^vdSxHORhxso)tCcW})d^Ng)m4@NXq10>`) zn#!(Snj=qE{kO_Ez*rV~*zkLVi)r`c!KlAQT!S0)sL-Zhpy(nTU?M$rj&e-_*l)<@ zMu!}d!yQ;=*S&yHN?^+hR_IJI^IXYq#Lqr8KBi2jxb=_-#5_Iu4)L+l6^IQLWtti2_< z^HF1#>S>X7f954R9NAiH)@o38hkN8~KChbS{JD$D)=gl!JG>rXF+8z7K!$>MxOeCs zcFjxy3z~isrXSK8Nj)+wFNwT0?-cUkhtF~+i(vRsvQs*Dtj~3v2iyw4UP7PyNR`+$ z6CTHmyNOHRHRt(DCgTv+9ct|@^Mp7*9cSZdA=ZkOp8oV#3V;`D^;?JR>HrZWgR8R< zMK)_uA$+_YcK2M3YotjR$dT~W*?8EghpVWg$D>o$pB2=3PIHSu9Ku5Pod<#XQSo9; ztmDm`KqKDkp)qBAX~&l*9b|1+j+Du^qU2GAO8k{CU4EV1TKeu!uao1|O7jZZ+UXA} zgL}p|eZwYbA<1)b2nj7 zR~ljZ<;`j%5Ba3{5_GtY&G0&HlFTb*xLYc(T)}yF|J8V~mM+dLqKI65P%F4dCu^D* z2L8*^p`u25w;?vMx%T!FAG=iP^h+PJr3Ix{SLtm+`6uPA;&nZNFmzDK`9}CG6KLy} zfiqPFxCOS3yLlwiCdt`f7|Y74exPsznek}0%_t2#x{^onFtfa#hYT!iRmH~S>nIQ! zP`+;$g;e`XO6&5jpj@NcPB|-7@*73zadIIX7LAzl?N2x?x^S!N7?!ZP!a`KC+(U1$ z+_b-03w;)yBNR8WGr5kOsObFX}bpgA+$&@1mv99^(b4YgS7A>OYxK91@ zW3Swe?Rn?Cek)8Hl9dh2#hAei=rZx%>s|z^)K#Rm(vR$HYM=g!u>$+`aQ4fb?2im= zy@wS`9rj@ZacAh8l}O`LAwh3Mv+Xv^U}p8{AAb^=UR=V47t=i0zYw+k?Hm7}&2+UHldnZfA` z1AN+MPI|0TS9dE(XBpRC&o2U%z#D$h_1f+sE*0} zy%0SNG9ACIL(y|v4+U==J)uiTpjN#mJN(i4!`SIIC!AYMEsi#)yrorDvF5%w!5}x< zjY5g+tyZQ+umbm8GjYlK15mbzvm(mauwya|j2~-gXA)30fR%(r1uQXt_cU!8s$_4! zjv;?f&4`Hv)Av9=xIcI;mRGtf0-UgvcI5*g(9S@W*~*2`$uEc zA4N~=KN65A<|jTdu1fUgIK{_*O{5>w<$f`LaMhpmdLyrp5grL6AXZ>zOI9K# z4y?t4xH~uNvi9vi?0W|TF+{H_7{iY1ZDJCwc^f2PtHL+8d z*!Ag0Mw<3Ih}^M9=VA)=>8iBYzGY%n^&6fh6ka`<>Z(d?KQtnp2e8{HkRdQ<1_VHHJV|ya=bV-g zLUI!)^{_#YTTxn27GSwvG7Bxmp2l_j-EGhcJ^EiBRsX61!ElA5{OY0%8j^{<(oLXu zh@KSIjndpREbr3DE6~yBgrgeP*dT zOe{2<>c}Vt?R+BQs7!3Xy*+SE(1_iKA?dkxZ?reoU%-Xe-Rsdi>uA6@=I6_LkmhL* zboB@{a(MtED*bdh@fLG-N8ae@3?W}jp7I?40rg-H8-EzSY{^&9pX{=q>ZYXq@hpU= z74%dm{i*4+_ROCp&^^W}k-I)zF)Jrr_rsG-$H zH42auos}paI_sk#_t4dH{je@%PFby5)g1P?z6bv}@R;^65OA!br}UXF1$fajM{7?p zJ>?W##1%Ari*+3lrW{vDNm+{6xHcR>qiidtMLpi``GAR!Z&s?Bo!NY^XSAQcC6AG= z1&fQWESDCV)@ogrGY}`Zz3!CB#UU=Oi{$KC2eUw>tPVJjQU0W|=wMYLDfxGa{9VP! zlV8$S#wcIt#t~BW%Y?t7_N4ka_N2X265*V~G&Cs<+o8G&C0U~*IQ&dmTHeQ#<5kz6 z7|x}n>q0tYf-(a?W2<7&^+uF_bHoEXC+C}m=MmzY)P%6+X2vA**{Fm*#)oZqhL~0l zI&)1M5c5j0dSlFTxgz5rqI>CS!O5M=)F^O<;Bh znJcr1cdj>Rfu01+aro}U;#ca6t0$vq{MaKt5~rxOanGGqat@;d3YA*bikTc7ooT9t zcR-iT<-Xa{ey|FPa;!h!nJ~k4q^E%mA_1Q*d2S@$oK(eY@YxijN&B9u&L1OXILO@k zcvb87hM+_5%enT^Zs9J~YHcrjNrvn*qh_klJwnGjxj#fOJ7H@llU38>8KQnPMFe^} z5LoGfc=08Yc4bU{ehpn=HbnRFQATn=kZ;OPJP~8SX2Ct=6+8|W5HKaf(uTDpl&VvW z>de15EvFpCFLO`V8aQ89@W+HM8-E`fIa-QRLz(=V=TYhI?{4va&xEU!=zWb*xuAFg zyAv?Zef`&g%2G%^F6n)cM{nECLbE*ch2S^tm02Fs=TJ#@l!6X-7vP50Ij(OgLK{^z z-exJ}>3Hj-l?ZLh3h_Ii*vJK^y$(Z0Ux?S|dnt;SXRr+AdpSJhr$LbABq}kjRBzno z8*++l%-jc7$}!Q8qq&mQyMLrg-^!AyYK>|A|1YQvR;q(X$_kv|ArX(37TG_j%m)@j zK8sLDg+%3QN$Sl__FM|7wrSam{Jb9PnAIO4;x#WWASgA5R8Kk%3zS1R(W^gPD1za( z$g>lwOUFiNWpobYB>PA5XF80MBcTYJuBfR*oiiQ6iRLcSq7(KA&I{R@v+lZ4aX)95 zG-g$)@>^TOYp;6kG~>M1NL9u(i{@f0{bstDZ;}J-qDPR2Hv4! z+(Xh&+WVrWGcx4f{0DLv+YVVE$rI=j8}XkTBP%I+^Y$i$wSNO>jiyXDN$#qXY)Ln$ zO|;01;%f&xj4s}TvD>G-4x2}@AlO(~6r*4J3)zh6%O7iO^6#}wj01;XggZ-j(+vfWT0zXo*$I@)iE_;f6%V_f7)7Oc;jvb^Ysik zAZL}*?N_f;CMoXKdM+GuJ5{Bu(6nnbxk2?F(WhE8dsv7e3Bo=aDiD(vNG^fR%m_AS z=HMT1TJ7Y5e*yQ*fvy~h{Oflf*zDz!`@TOMABed4%|nCK+1v!KFQ4~=WX$$!!mey{ ztBP!8$&3DO&VE0%=#nwN&2dcgnQdq{7lmeD>EDQ#CdC{w;vSk5w!gT1Wg@FhPKBJz zXZ-2bQ`p?#5Kj+FfOL5)Qu}8+hLgU zkr&jM^f&=+t|H%&OtxDMK!Se$g7Ja!rTQ<+ z)^Z=FdiJ%%wk6D|o6T;Y7AKx4J6$$~?gSl0eJQhXDJsd4j+x;D&Y%ECCnH+PYUN(R zArV6G8NkOm6-YI()(Z_X#dXR(hW+ENV|7OT8{}8J$^0ZtNP{`Bjzz9wc#c*_r>Ejx z;h%A%r1@T+u5>p6b_{BXG)>j1jx|t{Y0jz$0K1gNDvr`l{6E{G7_)zwSgQ$NQEq-X zjcUA3ySso>x1_DGiOx?UFuC4Nn*ulsGbLrJPZ&zHQ~E!ipSMk*x6Awf*-nKRWq4z4 zoo6)WdRJ;`3mgr-Y22t~*u;LfTyy>SXqiy#T?q)z0yz;+5=4me|M5U^_$Yn-uwJ2I z>LkqB^0@p0izdwrWzr4>oQ!+MwIbfzO^kjk|3~qs8hUT?M?v&Qx&B&8{f}xp_{wDL zSnvN^qh9>z`ly|31^aDb%0YH+xYPAR23MJhv5=|!?|YlnD#%J7oKDpfM=SLH1Y55n`8q^PaPle~J%3#kJiF&l^AXx(0`w3SuL z3c8)h(J&k{f^25Hr_o#)iw!lUE<$yh#X6RtFl8e&3&MO>opgtAaKNlRixZjj{|9Yn z84zXHu5Cg=S_x^8hm@8MVL$|=rBk}Q8%6}Agpo!%q`SMjySp2NA!Znu`S5*wzVCha z-rvvtpC9+!T=!b*I?v-;XPoA8m&MyAupb<1+g>M>!gdU9G&IAtVbOc5HH@aai-YSqe*Ih5T;3M1WTsBAh#rW2NQ$)_2e+ktJPr zbBAmApDc@>qQmNb+j3U4K5? zJ^)gJmS2}!c?r&!4(@jpcjsHdl~#c^MQKMXuC{`T3&+6W(b;-$#Glr2>(uf;+ao2t z-=RP?_N4y`1uDo0^dUPW#r&R}hL0$DDV#f7yFhVMsXi^__t*!WlGCrJdZAUoKT)}HBAq~C3dOv{Q?$7Hi%}?Y&_y*-k`1|IC zRkwVmZfpPIX0dzOoffi&f2%q3xuOiQ`WDaJ8F&!*$rqyA`YkBU_i%fdiR@%=j+%m@ zw{U`+CX7t<$YOzKAT-GGS6ZW1(j>HX9{DlD7o56NC{ryk*O>@t4wn^v!s$eYmL)KL z1o{*M5)|r4q&k=6E_KO3Okb`4&MMk3ZS=k`T?71C2|R_99~Ez!R>wEmxWb!oTAr)4 zmQ|sP57Vz~lN%J})yTz{$jE;+%NDR+Dt@3S7Dzqn`GyoAP9V2e$?JkyG#Wf9EdO!1 zCs&GulE;adPVQ1rJdZx4mKs7BQ5^9R8F;Ihes`vMfRXea%wE=NhZRB%d=48bRZBE8 zk&?Fbz0$SyF`|~0#U)*PN{1ZIOIcr!?Nfxx&Go^-f_SGT!p43%e0HMqoAZv6$7NS> zzoxUIhwr#DfE=-FKNKVEc~xv+>d1;=T3}`ipC6B1dSGgQIHVr?@NEp>fUH?I!q9Cb z-j5Xz z#9@9F&v547+0W>pD;`#!xJ)fDlI<4nYa1Ua+%_#%ZWKwd{-YNW&SLGQ6qz!%Dh)ZI zO>+r%Xn`(9@5wN=UF@B^ehqQ`Xj~lOw%Xxv1VLr&5h65vWxYj({cW8aNmH*j&aEh; zjEBR5xZv&OG6N<*n_;l#ZaLoRUbC?DzUzaD1!HpDNWXodiJv&8GFwju)za{IpT5(KI-^9r!2g zRbR_^^tO?xmd1If(lx7%R7gp9pyZm%N9Mz)2Kei5D{q$6^$gxToTadT>Og4@20Uyc ziB6|Z-=kLu*kW88bPuv2TE1Wnsff=n+_<9Nwvw>)x*qPj>HR2oTIbGLakP5#l$~6e zZJ^x$3JrAgevfcHHltjlODE2fuEzmb7m}ZS0sAMzW^3fJz)KB1dhn^g7Aiq$ygVJ_ z3SR74+A#iSfBo#5fc;mD?+{n35Gv*M)s~^SVjo2Jh3Z?c2H_OT6k_9Bs*z z9B_0wd+19!%)UIS3x+cIN!~)qL<)3$+&7tKy5ACrCsRjQWpmd7bq3?mwtNq&b)JBl@LeRp6vc7Eavt`n1h}5sWJmnoG)Er5zz{hv*Go8D0&D1|-d7NSoU{3G-8yIffa#H#75iaMW)v9t;>%FgrVb*yi{;q3y{r^J=@R zRJXEL)>Zsml567nP;S&0>G0QuW`CP~^Hx!=o^FbwX(s3#VKBe4O>W`OfWIy{y8iwL z4g8lWw|c@LhMA8uKK<@t+<}nMYF9$>%fK=M$=p}_J&gk3V|30+=j&^?1@*wW9REwO z%7U{b?{%a@g~-v?*|IfLmA2)|vLc8!(t~wivkasI-Kf8L(IcDp81Rmcx62_e6c*>AS?T0h1HqWR9&sfBRB z0DlA?vJt@%AA1rHkT2a}Y+c_kP((RxhDW&U1Y6WcfzMo^1Fo1R3FV84C6wE0M3uAyH--9jUqJLqLM}E=ge6ve^amSRY!gT6OYMLAGE~}oGP?Ou)X`7@>fjvS4A7ywAm|Nd>`9A%zI)5y{t%aAB9tgKL4bF0R z9vw6#b90gtTsx;-tF4RwGQoz{#1@edG{<>G`4V;S_mTekeXjT$#K$6_Z4tePx;QSWon z8THwf#0;E{A(#D^>xb#(1j9x0>;Iepo?A?v5KO#e+#S|LdX57-5LoGf`|%~P4`)mi z1QXU<3^AafmQoy${B#s&Ta49bx8$1g3!Qrr_+?6#wGDSkDA}@_3MiA6lw99LmV#2Z zHL3@kXJGw2`vb+Cq?tB>yaGvt<31_vG2aV<9`c5WV-WY5JKx4R?u$^p2;Lc`Wq6{J z7gcY1&@Lg?)fQ#Bl)m|XRF17)V-T;+>f1^aBCK!<2%hNse4{ZR-~R_Pr9V&B!p$j| ziX?dI42BF^R9B$zg%ELmP*h>!knIlZnpUC;rN>52;k~#aXzM*mcpR89E`8u`}la( zJnr7=f_C$G6@Q~w@o}D~pFuW76qb2|fAlUYrMWY~t-xF>Pc8~a(>pNU^^=83n+fVS zGGRep(2e^zzX5IiF)uHfnf3+{G413u4}#zrF$iYVxZL(c(GsWWLcQgG{N@T2?Cu4Y zM#sjU5ohq%Ll`c7Bmb==VyHQor|&#$Gayr)@f{U& zVo~r*z9UT|6|rZ`o5bE>Z*@8?zUi+!c@NDqfq*6+5iF~ zZ`Jh8L98TbPpoi^$(4MqD5%0!d=vhLi8K#$1?&vp6Idf$R~tmv2d_G$ksi<-2VUcJnvFvoCqd{wAA+&%YfWhOuqxPlX&>L zT`3Ma;x%Q&o4`L!t_r@c2bJ*bd-AvbfY#te4TREX?u!xJNk)(e{dxj~twu1Oe|NWa zJXHlXlXmc1Z;i5dqsgbBgkz6EJclQyPlP?`E!6FIz9<;tp8te*{D4~3WcOKj%xMTc zR_GTt;Y7TW!40juhmFn`H4#;Z)`xrP01A;q2obHayc*Zrmj}oeUdZI&l1$x!I;JOE zHNd~P75zuQDaP}NUh}`XxC5xFrU7BaY`>YGD_f2XU&LQUvRnl!Y~1YJoYTiC4y~x- z@@lq86at(b?zDm6ORh@%T#r{nodJj2HW9?Y-mmJx(Q~+GHqO5sh0u40m557nm;!0)4UDcjA@1E)$_Sw#{}nh4~#Wd{}*+dti%h-TzPXik0L2pX|VPr z=MIuF+Jz!~a%;jdwBN;r{=QabUX;^VWmBOum#JT~e*B{=t9vIw6gBW-qa0LzDO!qu z1WbjwgpZ!Od~?_WtF{&X5l%wu!ylOqhD}2x%3v`=5_w^`7*Yg=%$TX|lfyHtn1uAN zj{$L5xRY~yp~>2Og^Jar*UIh9(G~P5^l}j-h!(5g@o;gI(KJ^k?Qi>Kb&|1Wt-En+ z+^#p7hq}za9-baP>5V8F$m|`ZtPxe{-)Md<3Nz9BxnGr^75nWexmKGyvTz&h#f@TY zIZ64sl`A(VMPAL%uO52NG%(?qf=}(=^45LB_5!vsIJ=+RW)ExIpXYdtBc}XkiPfyYbJBOZ=e>DPEl)t%hZkGq7CsgyZ*S;W z99kMOGIp06jbwxn7n|1h5B8{=C8>hF^?xKZWS@lX(2sk0f2-}yf?MFy~Jr*h}%JKw6uxFdbxOvgoGER4d5 zn)=xJF8$wUQOn~UV!ZS+D0Mkv`Koa>V|kBcy>Ub-|^ zf+*@YL4;iTn;@b|`I{gr%99o1mHQ+xOUOI~E&!~Itp(gv+9^H~L>0sP{SAaUDgEtW zkvji-iMW#XuQAt^KoItsg=xu?5Th?;X-O@i_-Wq}E3h6DwY8e~r~9itD8IAxM6L2Z z`*aEm+921El~4O$8Fuxjupx${(}%Ufyz^Li1vHJD=b^0j`<_rC?)8DZSec5WZBk&F?LD%et=n-<`?93_ zlD3{+OmAX2=`KQKCq>m)*If|+*zV<@NME>53r|iTn^Q2W6k0$!w6D|OKwIrE=D42z zsRTfq#9+yjXd0|^V9F7DKfmw$&{=t0_DSR2Y1M)owYB9QH>gLW_&89+cX!iN6r66% z-rizwi}KX-SE7$`@wkt-S6yoo7^_v!h5d;Sy4TqL24!$8LO|p~o}4_KxWd|xz8ER^ z*>%Dbja_CwcES1}mF3EE)HFR$ML{H-ybQMp;_pT8A$#M?duH(d({?HZoF`{5Zddqm zqbkfOPrrBq)=Q`U$Kh54nC*tgSKqxiClgZs)X$?(p(RsQln<{c{O?*kb9+JB8;`@k zPQ*+-0TO}LuwQA)brd0j@&;g|nrScJL$?*rWuIqfFhM-{)ui^d!hF|3cqHBN2&1%r zbOeQ@iG_`4vY*NaO+u6$L0f2?%)ojmOCeSc_`J~oXd3P-#xh(y@w;wjCNSdy;C=LH2)3k;6H<41o2|4#grrvxMOUS zQ_?@jG-2km)fh~Skl_y6ICxIe{dJWO+!&pHYV75NDXFa(UKR5U@BOnDdgknZ7x7=; zUTP5jD#e>sG5>aHt?Rk;GQtL{ar^SGuVm;4-;yxwGGt&5FCDyaGn(tZ0^17W*ev#| zg~-zv0e2oE#Y7fp1`S>`y)@L#-Zk9LW1iq`>WBOMYG7||@JeOv7X>+&21_%B&N*=% z)IL(6=m>NzdG`UZ4!#|20F0U5|06(C|L-XLHyW7&ke;tNtW!&k(A@_I6-I0{JjyJN z@^GPin?C7w*i^6WYo{J|^t8XZIKZbc6!Ln%im0h*eNpK09BH41nQ~!D_6(<^>(Hv6 z)u1P(%#PW@Hs9gpPvzwE+_$Sk?iuM5H$Q3+<`s|I>@|7O2X$9mO0rH@VVTNeAOde! zO?bBPaKSmXVjd_pM(Xg`%VZ#2Z7XD_K%-v2n{IAJ?YS=H! zb$in{FKT%o}Cs&tubKZ)5HhK(3kfI(l|V(WDosjSF?6bR!EQcTex=$%k%aRLyj0EMwL9;t7FqT|m%taB(AMR~>dND9!4uC8zoB$@4u)a#(#HBY zuJg$YJ54!9k@M+keeZh)LPJdXY zWvo}La^w_qqVlGj3}>}k*T}DzsJ11)PhtOzcI84Z$j70y#|nw$5nPmYc&z+F`Zj8+(pzLC68U~$r3|dL&WL?|FYAcnAl{7+0o|-RnuGlsi;#N`Z|JS=OL;E@mRYCc(`XmRvnq(eHab>%PQ$=>Y%5b?$6|1w;?(&6&kE)|6*QFo#m;Z>( zrvDO~-8M+%KCd}ikrGj{DBwtYdBc=(ail&t_q0(p!tdgJPrD-qt92^&_Z)AfHC^`6 zHJ#_BtqYSouM15K`t_V!edFm_I(LzOPSb1A}k6L=c1>IyrecC>5RE%m;_b2Mo zPTo2lQQpxg7U7g}eO2)`1rwmfvy@Rj(Y=d1;J*wBAxgxgf88m0&+hdv2kH`e2-$Bp zBGSg$k%jDWSdpNr$dl{D1dP?iFk`3J@zkvB0%HHRJ(XUAHw)Zv16aFHl0#UC0?hs4 zhkcA|5JoP*-coy`h=Ji`Q}D_~8Geg(kfy%PelDs6U2fjT1C4C}b)MOVd|joh6Ix!c zfrWOTKPlHG7$ACIw*@MEx?VRu0Y5n(hg=-;jvQuCJEdoYJx1t0 zWcb5P%>-oJe*WrANvO)|5MNdXgpYP6uoP*V#g_m2Q_>C^d3JSwqlZ*5FDIsVTHF8DJB@iuW>OdjU$C8Lp`@se>?$d=s>< zjoZh~j|tcp&PZXl^V>BJmX#N(aZGp!Yes+rcEwqYkf@V8CNEVyU%SQ7jgqt=N@;#d zv;Rv&Fg5u6;YKQwr zbPmxt0(##=o)N#V2!Jn7<*)mn`}e2b@z3u^6w|NC3NC)M{b{zRP*3vXcHfI%7)tHWO4-5eZwL+#d#Q zFPqo0(vgI{aqegtUPu4>J;avbvEGm_$|hE?W$% zN!Pgx?!Kd|$KG0_ok)ADOUSKzqjZ|&hrplNC}qqjIn8Ou=gVRH-#1DJt%XjI*wa$J zswyvB>7R;q_#C(+O-|*cAq-)&c1zs^@qW5x4Z^5m_=?40Q!xu;0CzHBcMr~H;em^? z$LnUB(dyWcn1($vpJzMIZ*1=^-*_7;;+GMUZy*yAqP)HNKwsJvprbx6k!8-u|CC9D z#u*BhOI=WY#gY=D`S^LU@^NS4>Bf^NI{D+f6Y2wNy z6w^)qS~%@JuHbf&5)x4)fUz{?L?N{H>OVa2AKeZMj04&(NNpa1G5~$LK;>cTZCokc zIrYn#^tR7X^jRwQbZcSrTdhA6Z}iF_$(>-lsV+tLyeY1QGGSLc`VfyT7KloO zCwtrN_D+go7hrw{QV^gt-}vRj2e?jDI1z389-%mr#Yy`EtTAF{r7dFMXFI;Zn{23e zJ~;L6i8@gPTh!u9UR%y1N5Xfu9MsqqxdLoCvLLgqU!GEPqn$|op5xTV>R(MV@f|4T zZ_y-Zqjcm|x!>GY{?yi-kV^d9VQBA9{}+U$Py zKhew;b_E@)D$;6E|>IC08_B5oi9Cyk@%sa*} zGpW!@kf)*fXYc2tIaElbo{p{!bg-|Lc1rrhwGey#)~TKUS<;6v{s40M!f658S(K(9 zwzEBDk9a|TkAwtqxC`dK#j^MzpLm$oXetQlp3J%Tvc@x(M@%^4ip6 zze@QecJ9nPp$Ny{KI)-CRShm{k~%A|aylmcvEmArU%IPD6^MI*8EI*c%FQizQeuT+ zI5@C19HAlr!LPMEoE)PreUNg22ZOmn9nMWZl_ElGEhKz62$#Q>x{r*X_&9eFPi4Ee zTkfvc7DsH5lAe^??WOPEL^k=BexkuvQ_*9vMD}rFAFdkB{4v1!8kK_vTeHIwIu3I7 zKWAD-d`L7PwSZ4Xf#%?oQ~kPaFSZ#+!e<@9a+$Ldx*r)i*sL5yN&D|PvmciHND8z# z3(D**a?-mjs;4;#vTH>Mmmwf4fAao&(I_y;$G1-h@J4^l@|Z&hIvc0emVpMV%j&6i zHo|sq8+P-W^gT_XRi(W|NmXA8e8VceT= zss0LLT*j>K1wUmty7IKwO29nuMD?4uI5IfVQcLI#PL6O<96Maby4r9@CDpz^yZft1 z)Y{lj5}m3#IPjPjeLe!+>Hxcni8o9=-wdqd7drhiw|a9|cFi`zE;pNZY()B)#q(|d z5K(muv{O^A5gu@9)EWN#`y2X*2tssTBA6$zT}nZ29q(y%g5q8g#6E#_uuqV#fr`zq zpgRqKACcYT3zz9v+HSV6$a>!4KaYN2&F%Sq$zxR*)@Urwm0y|~xxZ_Dgs-Pp&r0-# zD%~%i`{-hFh}zc5%*W>icu#&8T^!41sCh6Wt}k4YUEj- zq3Dx?M~&t_+F#f7YBm7(i=0a3lU6mVXT1XO?s13Xnt@a5l*->Lkqgf?!Kl{J`6$z_ z0l~Kw;N4x?&F{xLg@vxBs| zbEfrmWxTuuYwd)-dH~dkcGc7prWcEm(LP1;o~*P5k5b#2`|Rp}U^A2|vsu8v`E(2v zQ`?{LF@AcmQlD7#sd=%r`Mh1kCmDm}b8^SdmTk=qTAjn<$X=AD%_jf#~S8XY4nMND)JY-~bms9?D=040U za#+efH%^E-?@R|Q?40H(O5v>o-pgqYvQ$}GA~RGE#tHMMo*z>3iFgW9%vs^Dt0XY7?7uwdBmpKo64a3s8E zWO-?kLm|-7?ZAWgZPN0ZobBN3`{ei30o&YHA9CWk7MNXP{)IUwkhoAZhuQ&P1%$=L7 z!}mN1ePa^)d+C(*zi!tTzCGM;lV?=UDE)+*++Pzn-dA2xfBxZu)woykC`J^Wu$ONN zbKn>#q>(tB0kd+xsKbru|JIa~42QlzvHEI`%d%{OcyPKoJ4_kIN~qvytOwHE+M%in zCIhtZUlkx@n#)lX$&fu>-8*KgqJSw1WSrIai|cP1;D&mwuX5*I1kTkAQM5+k`;@=3`sVHFv&k?1JiYHyy6-#z2R@bgFSOrYzvg9AJ}W^ zcEH<|uZvE!?kpmiIX@)zq*=}i2NO6klg(bYDLeGOp?X|t*!)UH2Kj8${9E`~p4K7p z+NTwle|pScLH2irNuvVZA*EIsvlqV<^4!ace7m{<=;AfZvaO12$P{<;QyOstBwz0D zFn%v#@@#IMX}G3c60J<5^@S6x!l~ykD(jUV4>PHv%G)M9`}~V`BcqWy{Liz`n8v9q zlMkSFZ}+LkA#YzEIk0Z~+=bITQ5Pc{}>xV zWG+aLW9+#3woq38D^kXIb+!}xc_wDre4goWA0^6p5xxcsucIA3eGiJ~iHT&}2 zH4B>%zp-f3+~Ud~2*00qNf@G_y!&@c(xD~_Ha`4k>7GIlm&W3M9<5SBGkII&BitQq;eQ{3NAF1TFHZdO&{>pxYIbn6I$7$?@np}xr-D)6)q_D z0AkZ~{&Mq}DAuje`RPE>X%+AzSCJuqd64?fph{N4=VKY-nM_*h@T$QNndr6Auin6s z{?|8z{+_2inBF3`R#3>2Ny#okX%I2Zo^YqcHnx)>XZ|afo1tJ1K|Kv5*%M8P;cUBCB!*SXlYXK|T$uT>8Dw~31@of`X+C0=CcKYV z2DQX2nlkA8oQ*>1jvE{-hKIz}0WZ4@<|uBs=i+=RE@T8$WBOh1A%_hf=ecY?%+;q+ zN?JI&T5i|23~sfTrxr(#DNlv34_BNA)mX8qhcT(_p%>;wJP&}TbL!G#54zr1>j+xm zUZEx#;YnF54Xy-vVcT=)!ri>qFwpf9(a=9%DclCqfa!8oHGsnl>6poyJv@vAPU*B} zbNJxdyFfZvd3Suz&s4SW*iqXn%S*garH8+Kc}Gh2vDM> z*OAG;4jy5bm5|J-HD|dVwV%T0{+PSUkebyKo17w^{jyC&cnvDp4zxJwr^YBk#V6d2 z)$9zH)vrslANA%B6uh*0oXS5tR zdQ>z1>yNhkOR)T#YYGt}pf#>OQl1}AWNPAq94$%$b+!p~H{|S15gfOZkBdQ3yl^7X zH6Zn@e7Pge-ElPbi?vf`z55nc-sVSC*Jhm5#tVGksA!`h(~HD*32iG;=85T+AM;cY zK{kC{4bB=&QlQPu0B^VrE*HK~8K> z##X-iF&;gCPnkPsCe!cSS zX~w@EU_(|F1%wR3cPRTsKV;a2FK~&vseiUKo+}&j_V(VECkV7Zpso z&cyMYwfb`{uNQRk(1KLMDKejGBfOt_lsiyUn7t-l@#yAJ@m*bU8t1DEHa7S%znB$( ze}whSN=W3Z6v)Gs*Z0UG-*NK`wu-I!W8!0$7{~bAw#fN+W1-io`G+r-(O=aHq@0&9 z{al!B_g28VI9sw?sm@}_y9#T8TR!Y}lCj+C8+`eMKCyT|r|93BZA+5>5qyDL~EOeM7M_#JM2lsPBUkuktc~ zOdFiK4IVXf9YVhDru-$datAmDt#0IG9C&bWb?Fs+x+BCI^hFeY4=deCRqZpj2+dfn zw{xsL550py-WLNu4$4+K-CfZUqSO?>3cII#*-j4xvpeAVs==~uWI0VmO z_hfed;XH``n+Kko3!+lrLvAc+Z@4S`N{hF8{I!vcJxZ1#2g395PDnhjzi6i*80!eY z5kir~2bvSvZg~piInw;Q7sC+4`}(|c>CXPGEA0rNAo5k(nf<$_3_mx8CtEH9$Lpfo zuh=$ndM(7xk?$J|d#K%0^doQj(rS!NWX*>LFEt+`^khJ!WCk_#uo zmyD_fRuEnS7EuGVWT|KIEAlK8qv7%324fBUuj~DFtD??~^s&jty>oz)sgsKyyu~O6 z7LkCUp67fUm&3%4iu#C1KMjWVmN(VFnQIYa{X)jeVRi0-`lN!DHL(b`u-H6PXIP|p zS(^{Kf5Z>E=J@gSPnaY#v+gnaNj+i1SY9^|Q2|KW{xX=P<^;0Yfu!2zb-Wa|4`&MPL{*tGaH3WLdSLESkV!6)rZQ$Ai`l10ouq|Arb*Us@wKed; z=(?Ab#Givmfo$4^wWpud*6v_;ri-V|^RXjF6razpLkfS4DAiYbl;3V6oWoQQ3E$+; zwAH_0l5`MrqyUcYs6m#{^C%U6Rc!F_uZzi9fT*z_KI7%RDbrXe7|6P=v`uE9Jd~Jj z%s83QF*`rfS#&gWdQ8J896&@IS5T*|niC;s4XGVDq3~#9Ea^=QGJS&Rg?6f$rE;Dn8Q1q9<7^6Xc9ZOGTk^Rmh z5oAwIx6fb_dppuQ$D_Uf#w8|pZHf0D3TH(6<-7YLDy8LnAv*yP@*UM0zxM)ULczA| z=;r~}t>ax6HkT@)sP~<_^|Y)Y^irB1^ zg`*_aC@K#CAurgFPLs!;V@Wf&n`OKghuh$smUd>7fD`7vzL3#12 zPvgw9`FuCZhdrr3-bJO+Tp#83{OKr?B*w*720!?HZX&^W>L|bB2g9#&xn;E+=gBxe zo-}wVl8qib`2Xo;<(n9E})GrlIyK1lxzUv&vDz62etaD5r z0T!l4PC-6`$0MW^=a`rM?dsE6MI>!&IaNU4nfshL13am8gJT~(qkd^y0@EF+Rp;yI zeZ(FUI3n4YY#tJ>(H3ZfUt%5FE^o_jfNgrx7WY5{9dnj=WW z6kOi*0rOvn@r&2IH%K*uiWe@@}l5~n;2zgx4 z&mQE(hMVy!8;b}&Q+Y6cjgf7^@m1Kp;T*U%Mx>Rj)_i{r<@0uL6^j+U@Uh%W6sXJ*)8NA^SM0~uvNacD6jc;gCc28^ zgV!{N2yYR-D=jUtI^6Anvzdp3V=Hog-4)N)+$7F(g-6d!ZkeeTj|F@df7dbY)4b9> zHGE0Q6jZRn3lRX zD;=3JQ`U+pp9$n@#nH}*lC+)_fK_yo2Rs~fWgl;|KVB=}9K;iX<%5_G30QRyq)C*e zdEJT~l{4!DH|NVsuPjNFVe`?}?T)*6bv)ITX83ZLQ-X^+!_8wniX(LsDw7@P;*m0w zOZ;=?nVeA2?H!}H@9i!AfA>8;VgooF&bF4G4(hqTLjk}5SaO(K2Sql9ey?0de8+=| zffEar&4G)n;!z&K3;K&hZp$&WEOqzYiGa&{9UZPVDBIFKp&i^IjH`X;ap0*}0BEnR zIjHJ$=f7~DxzydOYWD;uaO;cK@4Crocl6x_x!s4@0L`+G|K7Aim0p8i2!_py>v(h7 zT~nb5C7EayQe5$<>QBpRTwrs>;(9#_0J(*QSek1*ReHFNy(R~`Rt3w_);#vN>Td#YUo?5w!*ezI;&dK!Il89DWNzhv}no_ZNVj~X5Q@m)aU7C#m{a+&V8SN$?79RAuRc+_s7Zob>JclU%=a1dWRMcD6y z7A8%d1Nll_SNuX^zNE4-1z-~fTzZjA-6sSGN&WL>TM_aim0q0>y`9q){Dfu1WImH6 zZt`y)25mQS)+-UTpNxy>;XjfSBm$e|bXd{YX4Ne% zi>DnbqmcfOUI5Pxkey>5lSoAC@#Nn+fR;s5YiL>HK`O7P(2>Oy$YFy@w&atwz-P;= z?r*+61T^?S&Sjf}z>keRe^uBoCkZ1t>%ocq2ox|?GomKK9vNY+tj?qVOivrZ!6Caj zk?0y+{P@3fndrl*wO#or=Pbp%-sUW%ilv%Vl#Hf9FC~{zaLg!+AdOaF;>o*c|SV9<MXN&!u8dO@Otv^L{?+$<;wE!WvfhtIw`hIhVhl?Vl(+#3#AefqM5@4P< zPYrf?WR#hD%d0*ie|Zy_e^l*xj}ScPc0H`n;BT2k)UNAECeFPm2N|Zm10#uscZHk*qfWb_C|jU;eB~I7v~WRcpXU; zq1SSGuov22#6)3Ed$;E8ri|UmxM{h*4JKJ$i4TQoO%TvozvvhTULnI=4xF* zXG`)+YNUU2D+;k!*F}CGgEM%=iQAjoLW@AjS_YE={kEGMcis9OG0{YC{IbgdkE=fO z6I1!e3ASEwgWG5_77;w0AGZ6H5*UaO@xO30Sx&l-tLpHoe`${#_DadF%=LU zb++vQvW0V% zUC3aARcoM#puy<<#EYnM%U!ulhv1w4Y9v&PKF@Yc-QFJyLyfw;yB9ePCUZm1Xoxij$`FGihU)QG^cmqkqt$-_J=>_x(Mx z(Cw+&LfIEtF77>niee0T_A&GQbhNs9{;T}O*U8Bd>L?SF=j+$z`p|0!EibSplb-XN zV7WUum80gwZ2Qv?`_C^=D4m3<$f^eHVM~dx8_nreMSXBokt7}R&EG2_@5hZht`HVX z1AZE|NcfS9kx`zt?*A zW;+IWwvEd*s=xd5y(!~yXY=Xlk7nrsPUAkEN>lrD+&B!3Np?%F`HROkV>t%r8pG=N zHE0|DHu0^ro#NM7PG6eNjtkq~QkNDt8>&N%3w*o4O=IW2r}HEsAdOs5W&arw8L~0> z{tqocFa)10WnIHt-xouEAYY^3)kOBHo-vcWKyx&$tmFxkw0*B7cc8F-)p7X6?h7e4 zc3B=Cx1}R1T{7;?i4)@2W8)nQxk-NO`%Y*Ti*HBES{ftLvliYJAHLNlQDPq3UtKp~ z;AU_C%%x`eCBJ=(7!0a|ReejJ(Hjs9zlRkqYd5b{ctE}SZHDUcQB3i1qUyy;SW|?O z4w;oky}hloQ^4~1jnPx+k=|5q9N@<#>GfP^T9dnfJhmR!gwKAs2PoPSBsmBudu3K0 z^1TEU%wuvT#Ksfn`iXVv+g%@n*%VjH>l2T5d-pugtQ;XqnYf) znl8R!DmRNx?NYgIKL8AVJ(zfJ(P=)Q36ilp*^;sC-Oi4jUJ`WNO?RXW$Zx*@jCVu7 z^n*8+gxI@p=zfF$y@i}=&H66^EDThv&e2~B4DjGmMtf6ddw}b(&*qfK4kZ>S9zh5& z9Kzk!;bMvH*^*;C7QAZjCN)Gty{zd5lp=Nw*mx~U7sbv$kq8~IR%p5~b`Z;-|H`xK ze+z;5IE9Vuuv_gClL3N>!-2JHOeSa)95y00rWN6we;7#cRdDcqFDIVYvgS5;D2Qwl zZ_^8lT5{yoUw3Ku2TqqPpGb|jS>SzsEenfe4QaW;tFG7jT9gP1w0|4BFh{x&p6~m< zK9+4!Lbn9A)KHM|vUREYTQw7t$m-lSpm0_>%K$Fs~TRf+fRN4k8Cp)af_ zRqcC&X5~dP+1nq4rqGY$yQ%+Z!3)H1%ofOAtk@SN+7wXI7lF*pK z@mb0u*fmmBc(EeKq=3c$XTS~zyAljR|`EkTE!nZO*QobU2qZpny2Y5X_u{(QdAxRen8BNlG@{R{=Eq|bkCDV!-K zL=1KEgwuYvU;3O!B7`QsPwxfIB>$mESpLdX*K2*R&K-4-KV4d_Tz`U2-aw}+ZaNnw zN&z>~>eO_#l}`ba_4DUUQKFobc!z?w2C{VqFe`POC!wRY&F-^Oqb+a}VeR1iH)pnZ z+QkG1_enki^iPjWG!b^jV@$pQ%gm205t_Xkz(>LGF)R1osl6CcRE?&?IQ$H?!`=YV zJ%86zFJme#0RmiaUo@%`fn#~8ognSRt_E)J(9K|mB%rVQmPqjUiDcu?Oa{xp`MX>zu33gknQm2dj*()1>!=EB|^99h-jKr8t8N-s-_wT zPF2NbAF6Op(p4!q6ALtpYCK}_Y&LW2V0fV|`HhZ+Ogc|2j$Uh4=$SW0Uy|S0sp0r= zrT_P-mcq2{TudqO(RP}rj}Xq!{ja?)|=_8j`kKA?2Q+O+y? z%Slmk^hJ~dzT_`%g_G70%c^-oA%>G#ckLuIt!K2obXoid1bZ~?45SzT!g|;#!Qa-B zQL(kl=}YSUtjyZ{EWxG@Y=`dblZjg$Hk)9CMLD$O`^+S>+54L8?8z72MH4u@4T{5N z9i>e82+vkCYHbz906|>t-3r+WS(_}2p9HQ^j`XnVk+ep(cyly>T@HFiE}ebJ&SSg> z5izEw%H*_117CcEavb}3#ARGli|B!pneY)28NG|!d;bnezYgaxqiBy&NdhvoUvnsB z`uSXbLa&ip6p2*T|1U_&_YX+Q_BpWc*Z&rha>_IRKZK+l5PvdK`w_(dUyOABL`YM} z*efE(6oR+CE0W39M9UI$URx!g_5|Xtay8VD1=9BwWx|d)4K)^7dm7QXwXHVhx=+Af zH95-B&W}p3zqIGz=TMWqE*4C)imn|uQ)?gk5fMl2i+|PM?&#DSpfhcYq*VqM5`tLf z?wd92`FS(bROd?H`nw85LE9=MD3U ztNWC8!!oQP?b?$$r5;=^I=s#CQp_F%#dc-6Yy9_dqHlC9Eok1t(!-iLJe-WMK$?0% z;rW(vc0piGUY?4*5^k8Bfz|NuKQQf}^^|!8#@gMXll4ypgnjzFLdPVl`we(UZ%3wL z)S=I+J!S-x7*hIj`{d?qH3t*C2?bhpb3SuXw)tNEQx}XyT9{LWf*l^?&g~?s73eKH}xJQ(|64w)swA&kpJF$P_a*W06k&t+)S)A1IicNml1 zL_JbFIfbd0X=*hQW?xf{iL;74EG!RyCUQm7Cqrvr;617l9NvCQW92!vw44Hxy!Fx} zZd6y+n-5+hWRxd#&KSeITI;4ifD z4`#(+%qSwwUElC;KQpvQE1P(+Yb%IaxYzzzn^iPLeK*aVp>oM|&~X zz2ET%W^lz;&Do=fO7p z;ItOeOwUldR~mcr4N>@yb>Ai)=I?x~EQ$sCzvWvoQ=%fCD4*nO(6QM#7((G1+w|$* z{vY{P_a}U~l{~?){O9LM+7QTF=n&lK8`od$IYvr26MK!(2ab(7nvdZm^0$rZ_E!1kIhp z{pDuHjx%Fy7OhTns!QuGuY7Cd6?1(s`*C?fSIAb+99&%Tiedg7vAqLyG7w8$XIHu3 z*Kb?{4Dtt#`izA$1W1qt)0VwIP*53gq$51?7FD|C72u#Q7cL@rEHu|+cG3iDI9=k0 z72*?!2vdA?Q-@#braEGY|L>KqC7<%djefPuo zre60Jk6J}a;wXBD=I{-A!j}BRw%a_e;*BEK5gqS&3IowfSD|`H=O3GwUKR=&2+hLw zhB(TzZofs}653m6g)h9x?CB!vMyRN$fspk!)N?fyACwb`QWx~gqV{F;6K3d5ABJB25b>C+~wKWh9QnI$pMqEcH z{0ffqja~P5!W>w1)cIfp)I}VF|2c|rB>o4Ad9t!|qZ-rU<%FAP~N>xVAEcQsfZKa>0!em`?@rdQ+!P%X76L;eb>!2&MJKV()WqDgdcSL;zgb=i77(y7RKoj{W zt~+o}jy`cu7oxgg-QdH#?HX3KZC7_UKXU|g7f>VbGU3g%D2vS_SP+C5_zlTqXb2qd)#Z?m8I#Fz zM3cp0_~*b;r!Y7a)!bbb@leSQAi?m`?c2xNggZ!0@mot!*>HHhf0Yz2ZBgm;tCo)U zHEtnhc{-1!3>Um&kBDIIs|8+3IbW!uh=9gr>1Zv|ooY1J#CPdOAR{TEB5t8Kg}I74 zXMDz`$bT}6pVonI7DuPqwFS|SEC)I`xxF9y)rGcBmxnknl^DL@1U?D~Kr``U|G8Bi zh+%=x3C2TyRfM%&G4{Sm{x}uz&`bEFan(*}5x4&R4}%QC06UB-!ETzfCb%Q*Wy%x~ zL!IihJr^#2xc|$hyca;N@sAoOqJtHGPpm*01oQXA3Ni?Y|C9k5=!q8f5l`$0J;EWH znylMJ7;CL9taj^z+)z6NIPg&vYGQC%6+i8=1iB$Hp__4u4i@`-am^ z;UL_bOwR|mrt@v3xKSO@=Am89Ye_r$EL+Bn6(=)8RkY8{-fa&cA^1xqb-}AqD-Yuv zOwlapw!2Lo&`V;!K_QRkjb0T9sl)NsdUF;7b$QNlug5Oh69PN4Hewc+@m;s&&q)_g zg0+puGXd6uZs#p|-_I3y0v%ViX7Dc!_s3VvXP&7pvozkPpnPkaX0B3kq1K8&{eob+|^G_4Vjpb1?%vcOEggJ0*%qyZzQSpwh z7uLhLYVEv-yQv?F{X_X-`S@U6VMkBIDCM zX*dH@`Ufp2SaA*Edx$zZI%HLrjp0woPpMkIp2)#ZSv!Yd508&&udR6suO3fa2?f`F zYT6RLf0hs>bjr`zY`<$T6G1qC(Rt=Uo+N){{=HS5oPs4fzVn{#ZFWO=scnbbyJz!X zU^BJnh@)hbB)Y9y^^4G4=ZfZ&)RYOk*UFlWdc5dZaBDGgpFVvUwHaq}aT7^E}q!~vy!31-T;HM4;RL%G?kqip7cvW#SfA2zk?nlV| z>f6pd)dszn-PRv2K7+Hs$CH6+z@J$}J|;l0`)w@tD!YL!7l7;|-XtE^5}4S`5U|8& zUvXN}Oawch)|56AFbK0ikawVV$ex7piz^-LC`@m`Pw)ur=L>e=a2|$^GrKj;HRPa4 z+PUhNXS6w?XPERmE1w4eNAe+>{0t1U-FBgs-u}QHE)&Ncrqco5ek9(N8%eC|>)FT{ zuFXA8fooYtA8PLwn~TQOw|1g5f}uNefXfgeZu;jKcsmL{-S1Wt%icQ;3+gAgFF>Y; zIsaF&W{%sjsGcjKkRX6HVs5BK*1u?njX>9Uc9tnOUHbl_9cW^?>rK*pW!V8L4~1Od z?!K{0ndZ5>SDEB{cu2N2tz``r;&&tu7pG!d25FDd*_$z?1t2awoYVSVR%WrF=YGBU z(Ba`o8MM^2bK8jyYTu9BQb4TUb;HqiG~*4rjpe&XIo}X#w0{s|Hkxf;Y50)3K7f5q zYz5{cK%sb}9sryE?Y#Q--@%N3Z8GIsUr%qzIFN&X%wCQ2+0~rh#nx_i2-%BXo?mZu zI`_VN+edIEmBZSr*`F9nl08O{)n|tm9N}m3Q0u4_{AEot-xBIa>rrZ-Cx+VxuaD`%?6jGib}Y-l-Y2YF{n=+bdUw z?i#Q6Y|_T%N!rq03wGxyKaFFRy^Ik;YeA%-KrQH@G!T)bRp?U_z1DR~q$lG>}Nja2k&2e-xF zL48jdF+)QS9PFO?*v;4{Mqa&IuL++?}^e2oBT9iWLOQG{QO*H-5lvRu&aXN4Ga z@7X_(Fz2(`XwvMB+e45ootV(`zLsiN?pr23K5KnBV<>Q4?W`IV3J!R=eN7aE*zugs zC6TZ2hiTu;km{UFUVt+@bx#gA%MM zaZPUe=>5hEEDv7;rQ2VMR+VF^atxHPQnb~i;_%;nw0hPh&}^Ob=a%GEF2vS<4uYlY zSJ4)?+o=W$4(rNN=@NS&adgA&AD7VWyhxJl4habjZ^f<*a1<_t_}H$Q6j686*sAVky$0GlZRyKg*>0m~jF)8bQc_$_ z_deBA`-xhLP);^NEphCzVN+@BpPY7gecXs+s#>uWnQGm?xY-^jkt!SZx#gv66%Wbr7$9LcJ*E9b5r}=ukmc(}#fKw64@7Dpq^LvJYJj~6^(6g9gz#^_sIO)p- z2R09O*XXpgy2F->y3_NR&~^|v+iow6O8oG4ynLblr>7mp=C6yUF7A5S%@H%mW#*@{ zD=&^fTFb`P$gC`k5~voSnA>C!6pUw!gIfz^)6RTpYLcngUBlGmW)F7q&t!n-a+e1& z8Xct;NphyT1d(rx4==STQFt}#BH^iTSbiT(5sU-rz62LdksAC3B}KteE2t3NQBwoX zR2kv!^`KS@V*H|b!WU?OFCWhTSLmhmv)Y0zR5tt@Iq!F(d*&i9lhrnt%u$D zWUN9zOrdbQ=Lvw*YQ6eZV!G{a5cd_!)SrI;V#qWoz?pM?YNh;p8q_b9gqbBP{r6!L zmC(8+ZYmC4gzqUz{W5;Jd#*%Vms)S@Un?O$B06)_mODRE$3H>vR!IzzxVMKeQgmw<+ncmlBaE+A9aPmC-a}{soihO@DXxI7_?sQ zscp+E#oAm3pq=;EsWZ6h=xR>`#{ zGD7$Ckdm$rk)8oab*gJF;rDJ_|N$>Y5VnC zHKN1W8PNb0PTd6G>H7^40Dc{IJU(buZ!M5r{t6;-eyl)Tr`gQH-;Y2(7fGOV)Mbb6ayHE~RVJmooM&Xtp@M=gUjbNY)#uwX!0dsI!5j zTI)Wi&I2!u6gX`~byijU6p}mi(M;dlJK~Ivk7rmf-)yF<*y+dTy1K(jd^t2MvsozDg{^#AGdTC#SpHu(01DhKAjUpEwgxH}dv5`lF7aWnp*w@5)pH8QU(jG$6N zMT(N9`c1F%k;t7npQa9(xXaTP&C8eA@weigf(IXhmi_zn&b>+o8Ju#^<$kzX@=rOe zen#T@KAWU)^3a|Ec-n7$KO=y{DAAkNr9PoGztSrjnqDVOy3?(^h3C2SNq4zz0VtkD z%+Gt((9=3z8`xUW+HnAU9&ZgyHM{92@`9RS1KMf^tdH;Q##yTt16vagYWGMxr%Gwp z$G58Ou2Q^$p-@}hEu>+c&?C@kg?mn6`ENZKA;e7>KUK49RnFtR9{%@w3crgpRV`ZD z*PHk<<`Gw{&jR>no9<3W9pMC!RG!;2lB^#MxL0i`pg00&=( z+46uDMAOp+4IWMr@EO)_n->jwPnqLCc>zZDKHcz@@wDYPu`_FIeV!}U5k%HeCU?Kg z*MOm(R7eVC;wDMwNCdw!TE?f5+Od8`=`HDt#46+N0hCm4xNmS=bb^nMRW5nxC!L$D z@IPQU#vtIpf$;I8NUDV35lPfBQYiN+R568%8jt4m(!!8{#xU#HWM)+|lRCf(L1`+qZ8oeC%m0Gq<(BJK|J4AhvfY_9HayQ&FIJ#QNtC_J zOHkXPGtsG?S~B)*u;NU-cHG7}J@N z>l$71>I~XnqJ_@RpI`pgXI(`fT424>86XYn;2QYaIRu3{h{DwOof%}euQ#uV5#E+s zUghgddFLQ^O@DzDx1)U51#XnQ8lZNb(-(zCB|`APV=f)G^L-(97=<)VJb$Lo zO=?kK|KvMe*&LDn{B%HA$?M%@>+LVi==t5DN%7x^048&ClG$_@aAB~eS6v&TYrS_j)?QAvHX#EI3X)o1G2LArK!#}c3U4i2ZReA zRSVp*5?poR#4XT5TW)OMD$RSIzh3WlJL97_e~9H7xqmINw@#-s61t4V1Ng|zGa_hY zljxsj1FRMWAkM(SZ;XOtQt#RTqbJ{IIl*qe!SIH%K_5!15Jf40(j^*@vXX7OCd!0+ zg#-}Tp3!oFvV_d&oxwB8^v8PlqBpxFGxtvE^8NX!UB=R;DT;uF8a$gsVAhLb9l;?# z0jHODq{t=E5%jwr`&HGjdC$BQfY3~?K%){|68+#NdH&ba`Tr*2oK zTUPHa#l+QxrQ5Q}z0`u@;d{Zk;T^Vts?DiB;*zb&gZVcW=S&wEteYe+EONjFU%9se z&j{Ijt$Zq->4i5UkW#)np4v<9TCNmPk?*bVQMGGtc+@lpoYdTq*YmBjE5|Ne=usl3 zuh&KNX8qyONk&zzKEmF53nb_20^rO2<#ivLXeJM505Z*d@m02kSDQdXjq@4ZxTjeC zFB8maf^Os!mDc+TnV#Ct&~CFwG}j*XZvZdr?<#)xL{q?Ml8`Ia=(^ z^$S~nM%PwmhUG*bJw$!mf{s7ZhnxA?GUZL%jko=TfU%d4EIOI;7}*DkUJSx5FcT|h zV%bNrWUi%dvM-Hr_aH5Azg@nshc3hf2Q&7w=AqV*KKlI6PfyDg?YbEPq=j%I?M%BZ z-*?IH65;OC;J0lGo^5`R^U`eO+)BTH{(5Uw>T8iKIGkfI&~)W~Ju5q4GC9ZinjT-A zaZ&>xFt)nlG=(urn_vs7TGX5?k7j~UCmdlQDh6k@88Oq62sV}PEZ*hG9s1`sKim>h z8MSIn9Xh3rU8)N3rLa696SQGu@PCbD;*EdP{$8;Sb$jo@EYE7KffW4Z=<@Ay>Y&>Z9l=29-0nkS2S6yDjz6b1Zi)^rvo%09y%aK^ zrpt?d3w^y<>(taMnDE*)BszOA?1g5boU<%qy5e&A>e18dg~{EoXp}|UdG6CsXy_`E zOV~*}mjB3`L=i3q-0T%p3j=aXWS3n^B688+lQWP6HpU#=q%X9zLc|LC{fhbpZAC+E zNmr|J^P%AG-C&VYiydw{r=5Yi9GPw)PTu2yB^`y8zFOor#!3eSb+8R($fInVBREhp zv3Z{PD;?ueBqG(auXksk2?k|`7tb3LI*+~OUF4O+ylHNeU+;1QWmUHAwgZDpV@uFT zuG}j;?s-vMj@)E*K3ep7gmQvU?7(n2#LuXNzg)of5Ud|^*uK?z=Te_k5uv1>CZ zmRKbk?;6nd)|?H(XTNr&ZV3o@XL{LlhG}G>>rh@{O4^(0VG`MfnVaaF9cD9PN3i@TNXAhTs=}N~vHI;fHsuRu&1-xn?aMS-j(n+e=EHRFY1L|YfNrC~YV=-7 zjihZc=p8Fie(H}Re}si!zxm>zoIMYJQ|bKGBHM%#Ax}86UB>8X@R|NFZ*tW3Cw6&`WkIiy{1HwL z;%whXnHsp89?PjMhq(=xq^p_EQ&V-LhkJs-)rMeRFRp3KpOZuGf9;PjxtUEAb4e4( z9SWBPdfr3i2Bj(e{-VH30j=VaVmST5o7hP%a{rL_(uEopLg`-+lt6nMWs=<-40;En z6z?H?7#fP>W%X!Fq+VlNy}n272YCs!qe$`6cXk7|{1SwaWZ&^1`mB4b5%Yn~TwlLo zOAMV7yF2?EXE+MF_H|}R-n}MKKVBePKvg|n?g;m6M6|L;nF}1gYayVvoh+hb=sAe* z{!^Owx?ssH?t{1lX2NIL=55}o_DR>8a-)%nAYt_AFc$D#^g54N=i1L^a z7(%ZE_N8Uk@8`mOBRQN>t1%Q2((KNytkg2p`FOGa&%ApGz)drOKqXWdF8(U z>DL<;l^pj)y0T?mSYaJ;oHWdkSrnF3u`24SqbCm)^A{9N-BYo?x7f6Ke+n61Ew)8C z(qM6;aXe2xg9Md(C)|yoF=l<5N^*VSnN07r3SylRC~~@H>C6kR!HrOpwNN$c{W6uu zbCot1bemXVq-F6$i}RKkMU!|oSaXK6b~eOy^4dfdQqtzaV1V-^clKK=S*t}W!vbUb z-v;`CVkG2#B59lKVbq9&jc4eN*L140rVyvkCDGGiG&k~`lJ)dP=0f~1vEJn>Xh(8X z+hR}onceIPFt5Vn_o>VPDZyXx3TgU1&Us2Tox-Y$UvVEuMIdJ(>^zB-@Vr))8lir<;^LpLfHR&8Pv)A;YS#Wk%Xb5pIFQcf4%jK3!xlGoZvGi zGcD`iEIPNgj{A(OWC9{elZ?;p@zW_uB%7U>@;Scn1y1#OHj|R(QU3BEGWT=ZG9HgB z!sDZ131Ra3B!8Bvv}s-MiECgkxkoP#3V~2?R2CwH3cZ&oFCXYx*-|aD$IQ+Xc@kp{QdG}iB)WxYt&95G<(^ll z(BP$)`uCV~t~7`eoh*vy;-j1Z6QWxnd?H+j*QS6YQk`odSnZf>1J9>EL7fMp>L$CN zV@BR&0N$pO$b>mV_)8NQmOK)2Mem8M&$#G4|+d@8gz+Y%teE)9b5~ufp(aFYO-p_ z-L6p={&+5o0p&8J0ZWjJ(g}V^yFVdDX15SFkEfs zfP8J>gm`S_@Z#>x(7WI4<{oS6mwmjiCb0z=vGxl10Og+?#%74N^4Dqy2>UT92t>% z&6vt-th%j0QgNXIaF}1o(+U@T9r3u*)guTI-2GL@CF$gNF9q3bk+M6KeO? zz*MU|by{J}Y-QUB7wKNT2k3j*_cvcuqH6+_!2HK4wGShDm0kN65F`zwozYB5yBvI` zgEvliIJNN)*~u&i)3pfb%v&zo)$5h&H66V|87;DH@oeM#-`+)%c#~L4b6p*XHCC6Z z3F`mmo{5fk5&a=RE!4?|+5M<{A9E!kvE_If!W!3@;JPq~``~8`S?>8kSH_3f!iYkG&&@xjmTH}49CdNu^mtG;Y}5Kc!G-a!fWWPeXaxmum0Lep=Egr zM86f{(9(OCc`frY!|6M zDnSnp4Y9o9wd&LxpN4!lYmPN>beUMF9VaBeCy6WtO`NHUx`^$aleD?khrBIF-{PDlTTh+Zr=F3Qs5I zx3xk7w!>K3t{XHkG{M&Qw@#ea3o$J0-@-7&E6{)QWueR^Nb5VLsA%*w)fpsa-CLKr zkP^@SPlYeg$9M|D9LyhX)=e1sfAI=%uM(8xVqm)8W8(R%;mn$wF5TP9n5*!#`$_q! z!d|cNu|T!z=C|bf6F(gm-P})V{VG7`E-!4!1a7%~He)d?i_mPLgcV>n7ZGz$* z#K4e{k%2N$!|B@v^YM309_%dqGz^OlC-42Ck9q?pwzL??&>QOxiJjk?>@w%(|5%D% z6`&?5Cx?RD!5_FR)EsZ6*OHQzI(8S0pBANqUQh+Isf-p&x8xPQvsP*9Fhe+l$H$Ss zQ&O~{eZ7Kh9g&=^rxlhLuP!QBfL{p^i_CxDgkNxn~=%>-^#@G(@!YodNzV=WqyS5PP)Y;!%9c{8aG&@q8;ERVZ z3=#>6XMPI2ZB#d=q3x30EN*kzXJ(9zvYi)M7x);avh`dlx=C2^i$z(7a@<`PhY_sw zjct_|I|V;Ly5T$FLciUe=jPTF;JT%+4B_v?%0SWf9V*>0zLPB%6rrVuVSRk@8#%Ss z_CLByM_EoMEIMZ&hW|nQm&Y-TMDK>fS^o4CPc^r)!UshW(>7Eb?x;7tuU5)KTWo$# zdgDwZ5R;p-)F^Em!?HHbZFEH5=BhPk-5m9^TI&M}v{AAmY|8Xz zF5QqjLgq&J*nNP9+HLAT6zX2g1Obod_>7NT1+laYyYq7wlc4TEn=!>XZ4-vr9`d>h zJ~!l9U4+;Z>7WvY&6(|h-?kuA5jf3^aM!F*1bXeQ@P3M`AZfefW#wgSU`MLRN0qs1 zci=a`W06PfsoA$PhQZx-Sd46fab?_IEb~U@xuQM3h z1gtf+`og|{)_$JL_rAL&_*DhN8uGE~c&c%6l{?sLafJhgh?yi0svA<&%IBo!Oqg0ZMFIe_cB6a#%5Dx{MNfCEqgfZa5?l%Jk}TICnsy0ol-dBIuB4k zQ98Ig&?R3MorM1q!vEYC*D0X*YXAq>KwWoP+H!wI=<28dv;Xd7WESimLXk}`jC>$l z=^G9L{XDUzNi^a4ZKguI9SWt;o*4;&M0E15-7%L@{W7jvq$Xe zA8p1#-zADsvOaHor`CkMD(L9SvII0gHW_H&nAT_^ zU_4W(hR%##pmp|q=Ex&-XT#MKlDJ)t5Wb7k26vfjry;e%Ty?bd0J_5Cfu2>4FFa}n z^u#B1r!dC$Y?5BXq|Uy{eH9T1TJz;sCOV(2{0NW!f64mBt2zOF$?jB2kZd z@1-Jjm^cYPQc=-9X;)dd$YuD#v=p`PD5rbzMxIFhxY$`5sb^I|Zm1%25QO{N-jAG3 zVlhSV9me^^7#GFsw*D!T^?h7Qi>I!Z-txiDfP~fomf6pe`-QfO-KxuFsozg)=905> zWQ>M8?4K(%**ZmzSbxZxL>DtbYvokqT&_SycwenYbSq5m()YB`-tuGSP*rQ@#p}cf zO_+3!S_&i2Eb6Ci-Skl}%4ovH(huCBWS5vf<+86aQ1nW2NOPaUDd{Ws90Ztj2>6Z5g^ulEoQOM3m}-}r;h zKahvYawmMiy z;`_l7E50#>YGVNUJ<$`~aI(36PImsw>l)mA{p`O7?QbrQ&RZLy%l8wt<*?g|{h!b9 zc{a7e5?As?G&)-(JNdo2J`x(E{=4OP2gzlBXJ;Q<|4Z25 zpU3hU|H$gg+hwlAOt{VY`#Juw+evAxf8S9qEq)+t;ghgxVBNoa>*O8ee{QDV(?V+j z^ZP?AefH)1^9cLvwf`n=@vm;*PuT~$^=_Y6T_t3~brp^n7sFOao zhy<>9{_GeQihpmP_!~^)0l)OLOK;cRB=2u*q4DOlwrU*(Fq3xf zq|V~$Um8nq;O~xs^A$vcfcY3%Z1txryHe2eW40%Lp)p17)vwyqbajoUf*PF%w~uFK zZx)Cb%NA57t-Yje?Cpi>W?vw}$*G)fi*!@`9uMEEd4niRFWbj%NjY~|C2k``_H6Yh z3OMNREqUstjMGnMG{Fst6>t=6hWgZ6oqEd&X41>A4H70)x}60X#7$hbdSaWUfA+cm zEB$+;To!Xwm2FV(l8IIN2__rJ&@;a;S3&+Xf@`s%{jmxJ9UuC}w4Bmw=2h>J#~Nb5 zdwsg9I=Xh!x~y||o6y;b)*TYFv%34xnCG*!_332h+ZA8hEl<*IV_&f0WPQf2!!IRu zV&StHOlpn#;GgZ|Lf1zR>%KqImrKr#V^t{|A6xAzk~3o;xy&~*~z)?%-ko*r%np6|t0FBf!rIY2*& z(S2@0Pr(Lkr@OmT200c@hf*GPw`}wadx9lxqL}ZS9P7 zIbu3xWsPU<(mi}VW*vlH6wKtL$fuNtgaAbbU);#~IA;~6@ggVTweaI|?3U&PI8qR{ z$S$H=Jm-inebZA3Wc&uYBQgT@*x+hdkB@~`6!(l**B%Rg{UEadog|o_KTtE!?v;aW z%~Z=9SNGm5J0V;YVEVpLv@YJ%->vKJOEp|t-DdA~&+Fj%aekY&!)R{}_HT@$Ku%U( zb{FZ38?~Ui5rJR7@-Go>#Kz3ysxnq>XzL*zA*^LgjWk`6LpLHp*RvLAh=6#w?l%vySux)26qVV1ozX3yH) zYr0nxH~I60wdZQDSiMa9w4nqj+kf!Q54pj21jJV6_n+RZ+2p*qXlK4Or&bRaP2~9E zQ0X)J3xGK}y*gxl9nL{)l7`gox*lkJ9bIN1=lxDLZCh|zbH|vcJ7D;?@IJ`==-!C+ zG`vQ8x}0kXa)$%?;3oB|*t`@x)E5?ONF6;`>&*eAscMdsPu;NkJRMusx9oO&)WN>c z;KAsUE=Y^D$Ez+R5BGLyz3|Wf&PN9p=4Z}?u$3Vm^~8qTj)0&U-Mk$DmoU$>lZKZN z`Ma>z5MKx1lQ0A;qfg|lCPJR)&)I~zW0(TLoXI)U52i{Ej>tgV&7qx-QJU)P4lZjP z?3kx%L0#6u>Xw)p;j1ldAkkL)TXiu=YhLZ;t;|y0VXHA|hFB>~4fu1N8~oTHudQXB z8=JK~+b~_%&|R!?!c)d{ME6ypz zVlcu2J$HG9qsRA)id#1=f&K#WnKHV3#r*Ek8~Ae$5NfC!T8UZadCLP>*}0m!!3tV^ zo3%#yj&X`Muj5zP?Dz0ZqFzxny@4R>E$+xHWF3Dy-t=DH$0HSmLpQ~fla7yOcr64r zf(Qf_5Y;d-gOv~>E?SJ~@U*H4vfS=|;D0ZeJtrBh_iV zePHKrSe@&dOL#3N))dWD*I@~0G{KG(l$aYrVH22U{S`uJ_(Aavl#aVcCZG7rg5rjh z$yL*|Do)lt@|W=!ImX=f_U!#~0xQx{Avw_E$p zz5>Q?>;z(Xw- z^sCr&N-tw22(kvw77A;$w_pV{D2rp3(^P8LrL{8o{S3z&(~a1;nL#$X?LN#Y!7)Or zQ_#;r3fa90U^{lM?z;1ZDzt%ESj2hJFGXSOz_$S%05|T;I6*@(`>&SAEN4OEp5Q2+ zmh1r1c*kRtn+JM&2ySB~eTb}_$IhP~9Hr|Z?By+2);Yza<%U0xObe+ z$NRLo@`^dWdfcU)@05~V>!oJoeZPm*m5i9aA0~ZQM|$qs7Zi67H1jx)OMl8y z4uByhkx-j~@$&H%8aJM>tpKKiHog85X|pkXVroX$ucTw*D#fz7Pq>sJv+^6B3$E;?0mf+RdZ1q=%g&4Aan8Hr-J3VE`?K+bmzZQd%2S*EpsSV# z|5Dj7SnCq=&bN84mK$a88_@qq_qU>Zva!spVI7T!bYu%?(KcIeI?I)h+kP{4`{KFS zn|W~TN6ApZV)ETd9|lHfSi#Pq?~Hfd(G5|9Z0|m_xuM{4S3_oifj>#x)a?5jN2&i8 zI4pLC=qpRJ{!m^p$M9u8^u>!M(vzQNZ(d-X4p$KYu1?ly= zNQm5W{+%Nqq&A81)X8w-vO2WGEA7X5#rXcw^j~YJnT#Y|g5_E|uG}QSY=M*Pdau)*tV{18aT|x|34H0$4)^iA2_gc41NH!agH-Z$Jdp$QjlnBTYJ<%BL@N-4*dwAf z|1@exoyS?}(_*dj!?I7JBbjEz=4na>gts;m_@X|nVDF$FSN|T~UKen4Yo6(G+qV^+ znK&j7Xio6w`&=T7p72V$H|^jqA|gBKU1I?m;#1_BxtLo2=ki(gwFyF&cBzt%NBNt zEao(!v1_=2uQ>U2cjr|hXwwwjJ>pY)iw9Sa(}+=ovzgBgUy;51Ay$<2hiZsz22s-X z_D*@q2_7MYjsH$hZV{{1yL)?^o1BNsAg%z&zMP}~+}b)CUl2u^A|WM*N0`Ns_m2lmIt z>t|UXDWvJeb6xD!ep_LA-j3Ng(vxzsv%-!H?&dSXHBH7){cqm~d;#0pXg0j(CC{A) z(=q*&wm2d0OZy4yeGg;cUE167&x;o~yyBnz zl3(Va1#kVH)A(}UPOqJa_>d~fYdyD|ldbWa0pKs`Qji>w5h7Z<%V?=z0iIC#a3gxm zVcz;rsjmC`_1_XOqSscyg6g#X-M6%U4<3@~PCWHYBLha+v9bUtj3{&pSt6fa?_e@V;nN7Y> z!Jb7vem_K7?2Y}!CD6WlTDZ(HxdS`%JH%<*^z@|bcfTD?ONudfjejPnycEctcb8ws9%;qNqC6-6` z>6QDONOE!v5y`ioI!=Xek4F)!JB%JuqZ3#RozomtX}aI2?tlC4fAb@Yr;V7DQ$Ws~ zkVqUB2yh~fLfWuB+O|vuR(q;nE2L${Au*TQV&$)VW4HRsIVk=(6!Wg=*OBY@C{o>0 zi3|CP@2A-ifzpj*h=@W;w&CA}X(o5LfZ1+X6st=Rd7TywBfj>2K13r{tizGEA|cSO zZfiPNaCv~s$Gn0p`y%hRgB*$$okzu&*;o--H|%D-CQQDr+WguGs9nxS&&UzYkQ=oA zKDEQnmh)caqrdg>rB=_LoP|3hc>zmFNx7CbjDcYHwmXwbw(WNWEgT(JjcNN+54|q# zYTW!ha!59N;PcSo_heL`Lq~^NUe*zRgQ@BbwejuU2SfJEp3c<9c2tvE>p?1fc4p#+ zEBVjpMyYfh8QK(OFMUYL&}o!Zk1S;TXK-5qc*!#32owWkMmZ z-9H9uPoI3boZtEoP-+V$YT*4;zDHkkXH~|OX=rFOn(Gl1Im1Rav@|I``44mYq5kYL zD%0kO@77`SvA4}`k;j&%u+e&O@Jw=QBxW|K@Fz9qv#$jfR#j*=z)v_B4i1!HpC&_V z&ITFA40=KXN~SOkRYbKCtVJr(6mkQfn)zRd-yMiy)_)aL;Pa% z!pwvBb>L;0l?saktUcPRw?obbd!b_+7qsOe=2}rWdhk-gvU4O?mZihMozSe;rS5gW zUCh=YXJrIJoiK<6DPtg@DBoC$wdHQ4k`P#Ys||gHHoCzKO-#g#)pJ%#(f(b`Wq``O z+dVix>#O2^0ZC`temA1kJw>aYKKd zm21naYC5S)INpd1Jckqc9 zj=M2G&&0pPAqn-y6qciks?Ok5eHheW{5l%QIdMUZ{RdTkD|OxG`_1MYd|EY?Moa6P0>;_kr2Nj317P36+D62?3RvVq^ z3f{Z_RczM+(k>KeMWmxE>4COA~Af?w$?JQ50_!`F)(3F5cb% zGa&-3iQbG7luJ8i)?LuSy#b4*0Y^m*1%bC(Xn~w;36Nn-`7CZq`qhMl;w_`Y#kz+o zEhIEEQt^_ucOHv*)H6m#eU^C9(oJ<-C6RF1Iq3WC38mIV#1_sHtR#x19P5I`fu?in1 z&t+5E6C)PT>dE7k*VgfIQeA`Yo0euo;aBLye`W(^^yc{F zJC7P&6R&WAK<#iGJvwkQwvwX!dkQOja&OvvqG7o8=$(u*o1Vh@}aJA!LL;-KUXZsR+01BC`V6JX&Y+@gt7>lv({w#Aw1#r zhTGE}A_{aSHk>Nc`y~v0qiTC_Dt-hV@hw_GUnu~CsDB1?*dQDj*X#rD`Fa)No|irO zXpp$aisy9&o5jV6P~}E1?N=VqvNQ&t+7Ey(D6;STWrVRd)lKLXNBk?kp!WUz<-!mK zBu>RfHQ%F6z-#_EINv-ubblX=EGx6qmp;w*c)-*O?p4na*pc5KnNR#AP#*8?wNG~r zOV9+VFOH(P&yUI~!H?HWoNoTFti&aH!WN5&nz*0*^Z+=yj%!ogDE!w)+;qK9Hh7oi zGk5ZON62`uNO`8c>2{R>;Jnp2y3&W4cM88Ej@YxLoRb=3v|x-FOF_aA(N(f7D|7!{ z(CgS5ERe^Mw>sHr1tl&j+dlS`wwLB}^wsBZ{N*Bw1NnV?ml#uPyu0cSDDgULVgHuG z*Tf~+$&CcpsJ`$9%pBB#^3rdjIWr{f^;DRSV%daba7`pEK|J8 znZ%wkXj)(%iyRK~IjiwP^IxZ~KH_&eZUGDdg{BwqDj)E=JhCAWJaswxS#g>0iv$57 zaK>9);VU*hgPQ@sdtDS3dz_ButgY>FXpH~|E4i3NwHyX25bKu&b?27b)YGsm>E8{4 zUs$ew)BEJD&eNZw#e8g^dL_V#ESR91=$oEl{eoHvNMVwMo2wC5%8L&Y*m@{C-?v%+ zkP=@I=GU!!pV%{aRim9#x#4yH*vLWc^Rx!9Sqza93 z7!ldyM3R5=B4H?!_iH1f!WsAdeNueCS?`v-RKL2~L*PQi;LMWWo&uXK7 zrpfafO(gOB)I5t}_>GrJUjg^G?pza*aAKqyB(vT2R`x+1Z7ENOE7lD=&v5wR7Dxjr z-Tt8SC81{5hv8g;aXjvlxC zmpc^RD)TJGG^rKXScH5Vs-^i;5!OHj+KEtq>xp-O1T2yoCe;=NzCsYAB>XM%eKv!6lvqPx zPikDF=Cn+^Q@E)4L|Efdy&sQH#-4c6di26h59Wh?D@&ali>cixH^0iN3~p|rzc-G; zpkDP$&x2}dBKwa`qc;6XCDpn<9EgEMi>yXkfGa#RiY@WUh`l5ChmMK@$J8^=tNxV0 zU_>h_y|2>)DSA~${;NX7-v(}f|CG%o-1G?-MG;>bdVCsGPF!iFjzf-u9|diGn^zux zyS%8j;=N@KGj0q= zu*>*;&XY{mOrT1bj#_zUH59WZ!16l*u|(Vn7Q_OS>E0_TTK8d|kOXAH*DOL0b(LyU z<^eN;kSFmg_r3D6Of~b+927;-(G7^d5}}{p#JQAHl7_E{-RcahK+&5s2SP3qCuEqO zC)pYc0ZXOgy^+15PEtd}&EJp8{rV-pv1CM4v*az*o>L#&<1JhCWVOer1Ga+y&gajk z^ZWw|MFAJolgS7!37==0?N{S8ES>}BbmFB__J#uxmNBZ`c26=0SH$>$0V$BFIhY<- zMF=SS_bht8&$~FUcJ)3&6^rZQX*kNnZ%8fd-QTVq>A&eDs*!Ls)|F3^efQeZMbGS6 z!Xyq^2|!9-K7u@#U-}O_5w?Qzo(B=b=x-Y@Ps91`97*RiL2oKQ=$gMzYY*Rl{K2cP zjOzTqT!6M64h&%}IiPGhz2?pBFzPt5|M;&B3hpkzpPiMu=N5^)8!}c;WNcN-EN-nC z!;jcApv|i$-$>R%4F&GM^WdUvzb!nFJ2nE1fJ;aazjd(k&|GaTTnz-dLH@gm?)($| zZ3W2?Y;YJ7=O09CQ%KQ4vtNq_w39_0YhwGI%XW}@r2!Y0;u&%>+yZ^NN@q0VHT_j3 z)X?@s;FJOW-+q zlc#@zU^e55xc?}P=GNvf?^UqGcYE@c_s$0VLbHdqbMi*DNY@I6EMzbi{#Bx8Sjmy4 zyh`y0X2~ZO_=9Q>vBxDV2$N$#>hWW~FwR#tx%*?4s$}*wQ(bkbUqyZG`r5^C=O1;# zBnQ@|p75Ib<6X5i>SaD%Rf--aX0Mjq#}UepF|=ITz%n|Cwvzp zPz`o=z2P}@|25y8yU1l7EuRn^g@*iTxIZGNRQgv=Itoo}53PVAA(5teHFdet{EyyZF?8LmXS=(&+k67i*4k3Av-fl`=*ujoH=8HZB$0rQnjswj1^Pk^ z$VLbYGuY*?9p302M3wo5v}sE27UrE&r!MwXEqyCPLRU zP3Kwk*nR1_7M_qYT?zd+q^1Nnc$eu9-wVKwsN8O2^H2dCdv$d{8EqdPyMz=Eead?n zhUU-CbLp*Wj#{SiU+~XPHxOkQ$x$%5pNqQ-8}?U_xQpUXId{zqN%O#sPnp)HlN^JCY8^ zF6JC{R8s?!os-tzWwmLZyA5He_4}|~(UK!KZKh5(m~d0hp#0WujoYCI@_|Gqqp_zx z?`NtMdY#un=|oD8m18=f3Ys* zLtx{|44oQOaCcuHKZ)}6aNDRC$7GVkVZH9b2+Co zqrcSZ(}VOzt=VVG8&~4a0x?I;3BGrSf-iX2k0=naE@Oh;KR8P+&Slzq#rMXW)f`W| zkLUayuxS1mnK6F-0u$8Dj-JYBtUydG_0&DQoZA>3sY*{p(JUes1FL)`#(7!Z zR-d)5{{9LnKCIUhz|8Rqr!V(48!`K zp>rtXrZ|A;im0POnY; zC~S%r^y>MwX_*OX@Uo41kbGRrRv8koxV z1rIvj#fzece1VuF=BYx1hG&bRa7y>Q;nR1dKJi;{A_`NM`zlCpBt|IkAc)c#X&P-pAiUV1#-V;X`mPa)kggIr|!pEK}n*@&#V^-rxIX{n1C zl()HYwPB&&D4gJ*ima) z`C6IBo>C~O%eVTYh@?3`U8bF@SruQS{gYBBuUukBf=JiJN& z_hY7m^WkpMt&QjbyK4C$#%`!M3f{JeA2cwPBKHFUOZv_Ze{k(KtX|I$<(Q}>=O}kp zGOIeL#LhE*+^#zYkGzlNB!)t^D+`PwbFc<$-=twoZ)+pQb%uXtRhD1dl2goglB^YL zrk%Z1v&fuKHp?47`4{XJ-%rbzt>P;{FDOsR*#NIB4$I=917r7w+Q?6g$5H)i_O`55 zS#?pPamJ%#Ta#DSkU88 zQW`p;0=`9+8eOZmrn)3FnKNdxA-H6-AY_vhSi#nNOc&KiM%QWg|H+-%=OLq@q{125Bh ze}yu#^W>`0E-7C|OYBFlbwoc?-~N^TO(~>JvBkh7E(S>!-%K zoZ)}dh&1R~pDni|02|}Yf)(L(NFMO;n5Apdy?D)5sed#eeh~e{I0*E<{ff~?NfCvE z_D94rm=)EpJv(^YZdiaWlz`~FxPuK%V0otrSS$SUOd(PU?dHaR`l{+Lr@cA2&Z_tk zmF(-thoyVeK0y}#@b`_DGRLT&y7vgVAl4M?FfmqOPT7TCjummhy;hS?dp0<87D;#3 z&u^s4$cg$~K|N-oKGoZ~Wp(GKb~$o*CFy5DSt=)6sDmdz zure;;XDW?U@{jG+aj7d3ldGDJy6a#)*-~e*=5pmRzH784^;2T5D_V%;6EFPDOll;f z@zEoS49Ii4yUSHyM=Qw^$pjJ($eJoW2m@wDgcNMB%$whTYTC7w_8l1z?XEdQ8|};F zc}zEBHQ#Nbjsre00fI@Z$+=}1iS}N}&pl9TO#ZP>twaCg4$*Wfx7i5;vS(|=4CnX0k3)sxXoYnU6kaM7o5CX2A>Pn1BY>$HCEAH|fk zhZMlg>|vG{zSkl&%F`&`T(Rr@I*||*f53DBBc%@__U-N{Mw3??i!~r!>K1t%-Ay+5Q%KGa z1_QaoS(@dgkIjFkWBL@yAzUNvqwB?K*+D%rH-d+~y?jEA4qdVy8U)=>vs(jO2@VVl;uw zCE7P)ex`;C5XEnDxM-_qQYyi(eF&k zED_D87widrx577qwFwT$hw;nV4+7KalUW51mv8Q(o|Z}OpX_*u;%>{Qqu{SmnbkSfuK9%aXX<|RQr90 zCpmR2B*n3x6x?!ncv`8O9(h10)CWP_)KQypTYygzFnd>=FIjH-yg=h3k>^Uy{!3Ia zVgsxJnByYc>$&rm(FSosBWjMoC;x-rs6@ zl_IbzbD0g1Cb9bJTylPjkvu`@4wpA;0r4Xx61oGl^t6z5)5(ZAY?CfvnuTUeu|p_d(N3N|o=d3myze48UD`qy=`I6|9XspReG0v-IlQy)OMAq^& zDBAJwUG^VxXRFhZ!ejRv1AupCI;@Ovy?HDUG<`7Nyu=LJc4jHCL#H(P_AdaJs0E@? zX`%<>8;3!ROWh>`WdBBs zJ{TE#diE;$ojf5W@knAYu&uGDEGgq76gIq{_0mBern)~E>?C?O7@SR7lShORJ)jRl zBf?h__p#p&eTvLkF+s^ppCoV}RL=5DCR~qGIy4elWd66n$<3$Qgo(94_w4C)d);5J z;V&`?o8(=;|5D;Cr~}*_2#mPzW^TD#G(U@Pfug+UO1rMV$&v8d85aVkX)c)jc(bKh2HO`M?vB7uSD55tsbJDVfI1QKTj#Vy*?-S+ zao{ic5>9P72PlK(cKNLH8)>j&#-kiBzF8$<`)#i??L(M9|fT7<*MId{qNaRe?7IQae4EHnZnxuvz?C2U6^Z*&%-0y!i0gG&;q=FVHD zwktTnE+9va4^JSqeR@xpC=+KXS8g--0jtCWHX`;8sL-{*j#0p0cVufgZJ98Pr~Rnp zWs8wNdm}_jML^=oY;n;tpN!wrTvM~RV%goXx~5}biGUbavW#-0<0iZx_Q%HOsatk- zDpTJtga+u(;%wl5<#|*zRK~ZOTi&x!=O?Js(j@$*##7s^n{$K^QWF>B!PClBkBrTY5iqhV#@@uiB?0|KE0$fRyHvfU6g*CAW^QOT65#}doZ z%wcodh*f)9Wx7C{SL!R7!HSt%yy{6JnPd*>(AN=`6})qXDLE&4Ct<}<&zeoFr*XCL z+Yw)R_^>%AUSoa3%IEV6=THI=`+cT7_QBby)A?b(opV~A-OD1n(m@QMJL@0S=5py-<&oDc=KT@VC#VMl<1x>M zK5+>9U4D7SJH7Lk0dTFFY44gt{>LOUn80)syal=KXIvg)jJi61r96u{b8{8Q&>mE1wEkz=?b^y-Ms?>4?u4ZHt7?Q%_DAH2VqctX{ zeCIXkb1kf=4;+4%%tvmq;Dv@TVO!t!C_kMrvx!s&aMA{R26Z2!f%kuz^nn}lz0^$c zyt}k}zB(uK%diaC7lP$x3i{1zs3lD~_I_~-jjv6uh$#Xq#FBM4rSMM}=liCQq~7dS%#W$L zkDVlyoU~LSp+1Epij_+sm1L<6bqgpLaC>x%;2}zV+k67|rfoeUVCbyXjqN?hLW|HM zJ^*d*C>~@&BB&5oRggu#G|#yDgWl+Lxq1V@mL<(}xtv$3CHO&7^l9HtzxnJnb? zH3TCX+D>g-N;YiKLb2)HB#S;{tmAQ?;+4kO_%p@e*kYmYOwMk&-T8~yaKoULwXJ`B zAEC4NE@QgqRcDr5m*yXwj~{>935=8E+n;Cm#>yKH?@>4m=zo|tx3<1t1XCvKoybds zQ!D(Ig#>=pr~17zojEl%a`+qVpLVhtV|B4Y3If6tj2sVep*e8W%4*r4YE+cL`v5yf zhc`DQLOq_jZWfRx_ERz#WW#Aey;bA(cJ9MPnh=FiknU#4o>Bnq)22QC2UjfrrxP96 z`G}4vjlcF!5mYeL^f|7<7F6r z#{;a6akPIg3OQ-5NS|KM&UE14@$wA37)9=a=$f+VMylwhLIQ?yt36 z!QIsOXQhl*eB7e@M?t;9VIeVV9ad9~_OpsFv2(Eg_c!xBw_)(;S}FsJHQz563ja~X zI?C%Ixt`ki9aPoRZCPJS+&iKE_FfZ&Tlk6>CSHnWbw>N)DewL9-3KmCq%c{j&1SfsUP+^tJ^wZ&Y7h7Ji8v z7tf*l4XZ&Zv!eA3rX90$hs;s2XMZ}5)zR$mQUeQm6hd-Cg{ z4C+?OB#0c!alWE9g~;S82)kgG&)^n1&SBp&p;gtAAg%lPt$yr0gJ)D{X}zmnq;JUF z{$ejFY17-%dK)2vpLZ##p-|K$c`ZEpo}5!iUh@dysAlAKex1orhCfd3-um@zg~^it zdGlqz{({e)Hi{8>=cG%2gibB|f-ioHR7^@)@VJRu{iFDwg@`-ue=YrzMQy78TOsJv z#w^%VyhHPcEED>jgDotVjH8$btE$j)n8`v3FZzT=+jFi8rDyxV2`t$Cy`+HAVLEFVq$cQS_hZo zC!f~3=gPa+li<#a*au3U8HmUjkm;s9xDej?r7=CQg%->mI9_liASCwz1_WPanC0f$udtG5M2EYih69azd(vTcL-94xt6gq8q5>h5YjUuF-Dkdp; z&QmLXz#JsZPs3#AiLNMg!R!SSx_9co=C8*~CpDIpvYCEMVEW}6sIxV-@JS21^7vDa zyN7a~`=j33qxnj3kET9~8Z+kaC5yUn_Iip3%w{T7$wUl3W~LB)=L%_2AZ&#_Jm$9- zJ)A~C;~z=3NUwT}%N_O$sqlQgSwFE9=({r3)gBIu%O3B8ou}FC42DV~!*^?5v&D(J z>cTqjy);oD_dmJyMo#PP^9CARWsSEutie)*t*tch34eQWKZs-XgS2R4vKCO343;=q zg|?xz(dmq_5=cg3eVdd8~x$R^u0}B#5MCE{1(>m!u$!YCKa|p!FcIbuQT|1i5- ziVhVOff%g^8ajX_D4cn78MQ<-``9^TMRj;|@?|fNB?XCIC=ME1ukc|c12vbbrVJ0R&=Nw)& zoYLy)54q^o)t(NP;DHoMqb4^faq*T((~g}w;W1GpD?ctembx?bX#}E)U|zgkd6amPnN)TgXWMetYE%_8|4%IciCk{~?1R0= z@lfLnbT6KpqOdL^*k73C0-ZmW&Ixug-Dw*o9e%t zLk^o2J;FQli0(|_05Z*vtpB3O+Aq`by%~AUN#YjqeZ%D1KYtYCet~a-6=|vbK({CiLhlWD zEmCjYa)Lc^=j7`pIoYuX$FAYYek(4uaHjtS-+p1i5UMZrhrVsiW=c_p3nU4)&mT z!qb-fSSpecSQ9p$b>$Jcs(&T=I58CT0W!T)zFFLBUV%VtV&RD$l6zk4NCxBTvYqQx z_-j0lFKAM=t?;w`@}$0(=ssajgX6kX-u-q?tiS7puKD%>5qMnFq$9&$B4cj%wKbk@ z$B!?D2~@8|aJKz9>iood#CGd0JDyJ}WM}7M=cW%5`Em@zoW4*uSMDTm`JdI=(3;Rq zGo?>th+iFt@wa1I+=2yq|J0c>wAvnb3IAU%z#9*P@zKrmw%1MXT%1JPDOITWvq1%5 z@N7BdW;RwQ%r)s(@$GwIs&TC*B*B}YcjWR?EZYGLL8CC<-)a|euRjWY%d)y&6!N{@VvlAA z1bn5U3?rpAO+Nb-9yy<1HBNr%QKSqSPk4_#+s1z5u>!Ui?fmB85d(S=DX*s&T6Y9 zuTw@!Ui7s>FD$hiJ=;Y~-1Zrq{KjZL7Y>n}xbJf#c9-z|Bhau#8Us#Wzk^azCgZ;c zCre08+-bM!&Y@rIEMvJ$S*U7}T+?Y}DuvVtl^h;U(UBF2Aa7AhLJIp-dr7e*KyBVH zI$ZFK^$2i>zASjn-akai_amwB7IH&;Yk23I{4H14z-$38#Y1aFcl`t3`g^wQQ-o}f zE;N4477T1t);6!UzxZKutI(3w%o&PEv@8jUguJOA>^~{-lX#j22#wN;(m@q#VRPm@ zciYaY)e3zm1ajdS$%g}CUP(teO07clVER&B7Z1_j`}4xTD^E^1iimT>6lV6$$eSdP z{Pbv5{z3fa@{!_z|}{9kdo9*iOSr-hD+;JuhRH{G)4Z` zjmvM8qlRAO8KKy>MNjIQkf<|RRzN02FYDpqv44EI>Ez?iv1fR1Hiv@r z4Rbvh)ADq|n*SS*H4Z6VyNvPikHe!=;gkFdGLFM6>V)LlJtJi~S( z@z2N6KC|dh%o&kSNT3&OFRo{KsUCRzP`NA z1Nvnbhg?VSgFBGav3kJ-G&c8}53{>>E}`j0*50ns6_oh$hOkxu!%*G3|9m zXJ|0!r2*kz(biDfODd~jINF2fDMR<$FwU_h{ksO#fh@bC3L zTAsr5w(s8%5Pg!E0D4FPV~d@G2YN+eum9*u#+uKB(;P>?9?aQRYSo7wtZOl0@{VmQ zg4(sar;1bU{DOT+xpuVbf|EOrUaC|9ONO9!CBUGRa8|?bd!UyOB#O zO%2`djyk2m7qfwlEnDuEjc}<)*wzdX{iDJ>vpHbxBnv|5XrPe|1QV?;kLFFj^xCKQuOt%=akO&m&ReIt>%FQF(NVY%(xa>#ABzk)b0yaArk zhx)7TcSPNHo=3<8W3I+ND@03Sxhtq9+8=Sv z?AnkQ;-M$_i7w?*T^{eKT@oB@*0bXiQeP;_OEN%sgv6F)$M(As@XaJoX}#@Cy7P3K zTJW5(c`5ceFVw_9dei3)^A=+th&o7orwPnfGoNoxqI786uN3K=={=`Wo_2B#AP@#` zFm0XQf?3t>68tuFp2f}iL?m$!Y_FEz$a-Hv>b@r^)={V9xm#wwolO1v&gr2(@56h> zFX9dz%H)hBA5iY{s{Q2#I%8W%MXQYhL{ovlhU=4a@(5x*PWM#hmVpu$mg@4Y``>OI z_(xj{NS{s{o`hyW34lBs{gru?WzUjD~Wtm};w{2r-svyE0Z(@LtILorXqc9IeMwmhCG#hbkC_*U;S z>NhB0=DOndeN`SY<|NT)YS!TpO2~|Ea`g8={95j*6gbLWb6f8HGQC|3IU`(jpP^h#(X; zP>kkhMdm&r+(sK#vIW7}+Ip5YvhppG4v%G+AYVQ#m^OOXk^g|_p z>}B_a4o%P-l_-8-^Gfh14|?xeV-y-c@TnIJl{;i<6Yuk$w6;SO7sD;AUew%4hh?$$bRHBOhFO=CM5pdi^VpIm+~z zB$7PfjVC^KcssoWt>6YjzPk4h%;aQX;}hwx4jC2IA(Cc{j{N+dFsKqc#r){W+{*)8 z>r>PHTW8W#BCmZm!b^#PXJ;>0v@)SgSS`UniYsVJ^8=$^B4+hiK{4r%3E8ODzjT1@ z%J5bf+@RNIrS=d0Zjg=b*kGKoh?A`8ye7+mo#|XPg7+|GF8^4Q>|hPCgUdPe7dX`d z`|lOseeOb2y>PgFBs5&T6ZUS4gdiZ!)5PRj)5749oW&trYUuy+EULae5rKV%uxVcDem6Gqg^zEiwRtYF|?WR=j+_?n8VO3z`E2w{m<@? z94_wGByE*Xi08h|9i_#5?D)JsHDZoCRe$B&K&kvM&q5_6mxfP2;9iXS>33w1g5bx= zoO^=jjJ&RIFH4^&kmas;FcRk$$z9;3uD?Ab^jDzDSXt&_OU%qf6KiSn*b8p~Ao{!l zypp46B0Kx_kHX3p?mKO%@~N|QMXko_k8AofM4lO;c1_=cFBWSbJn>soBezqFzR{~O zUe5aeZ^#_tpJ^KXKMXP;mrM(oa~)q)JCAh! zti9NU9uR+o6X%TBj83V+k8ttJcpHLOqzFp3>F%-kNj?Z>yt-0-OeS8Taf9i-_$nl= zjA>}0G@Pb|5H14PUU)lTj;A#Y*;;hPQq+y&gpV$Q6h?nGIoDkrvVV4GpejU*?{QoU1GUiN}iV97-$+83c&pdCEs%#H;xGsMjenHIR5rj-o{0hm`6&3NcImv~KRgw+6{Jx=c z4!UIIE1-7T611n_X=YZoN)jc+PDvA*-8-lW)McTVGBYhjV1Rj3qjNIT`4kNb1xwD? z0yz~puX;ekYLh25vYQ>w%D$fqE~sBMKC`zq-^qVB7gveyfP~__sg0rLe!Z22yoB5Zc?a(o5y2MAmP8s zm{xCHsC{+vF~`#qhdkzRLtoNyt9NzB754FBb(29oOZJwtQ$|AVIrf~JrdasqZS|Fh zV!9mzFORIE*P@E!SW2%g-Bme-%~Og7F&lSG_33?{J@11173MMr7k2c!Znj7I6+Od$ z#6|6b$9rMKWm9_zT~}(JCLhw`A9Tx44;`lVnz9eiIO{gwv{AUc_v{QU4g3=S_!JT~ z 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 - - - - tmux - - - -
    - -
    - -

    tmux is a terminal multiplexer

    - -

    What is a terminal multiplexer? It lets you switch easily between -several programs in one terminal, detach them (they keep running in the -background) and reattach them to a different terminal. And do a lot more. See - -the manual.

    - -

    -Download tmux %%RELEASE%% -(changelog) or - -get the development version. -tmux is hosted on -GitHub -and needs -libevent -and -ncurses.

    - -

    For support contact the -tmux-users@googlegroups.com -mailing list or IRC channel -#tmux on freenode.

    - -

    There are some programs to use with tmux - -on GitHub and a -book on tmux.

    - -
    - Screenshot - Screenshot - Screenshot - Screenshot -
    -
    -
    - - diff --git a/www/logo.png b/www/logo.png deleted file mode 100644 index a13e3d7f3b1618a98c8c513b33ff96af521a7698..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2701 zcmV;83Uc*{P)Pxdi^5MSZA3#036Qo)p;VO8CiY!O(-s*y0SORoQ4`dkJNxeJIBJZO zIQIE=r{Ao-d$W6M$1%S1-DyX%=Djy>{@#A~W_ISSDM4y(ZvKQMNe_c;qLfy0*=$mG zcQ@trR;*aTW>cTnVT=tUJErUUy*)iWZ_TzHuU`IGK$-;DQGjdJ2LaezO^J_zj|HGV zx_tTakIz-RUOV$=0X$3TVLa5p{0+gqXFewUJ>p*KNejsWJ`A=gj|Y75V|#(f!K%iT zIVUW?Bk|T~g~@w)?kym}Tg-Y7mhIu@^VqUI_hM>aDyGSJbIDH&cp%Hq=TcOo@<3Kp zk260fl`=KvCIal=W1}Nm3La$BPkjzcw-X<{=F?Q7I{~OE z4|h4}d_JkG`sD`2>|N<<*a9FO0ic(fGPfMce|D4LUwY^$pOabH|>4V4=Yst zwq2IVl}ZbCi}S;3kxHfX_1vp_Aj`B@rsReCvS4%L%lK7)IkV7BLW6-)dM`>Smbb^? zy>IIm@-&v=zlh!A2eLJIFvvZxYpM&Wo1kM`W+`FY2UhnT z;}vE&ao4u6bxz3a0+U7t&eh2+dfF0WkCd*HN|Fm-~wITJR5+%-M{;#o~-Um(Hf(My7k!r8{0Wik?pzFyy`rG>lh5izt{{~>ci98Ij85?Qj6#$(Huu*{Pws>3gw?aKm#<1Tx z5|q@xquR1`8T%CIc>~$tjeYOcb*bN%B)Y>K6YOw9UAS>Wq9wB5lo5D%nUd%EzSfdq zPseELR?SKU(Dc%zA6=hlR zWM=T9*ik{#I@ty<#DhAgO}fT*$uiv{Dly8wkyzdPswi{Px;Fm0Bn2!nqhzVY%olMd z8P`JECW%Qslir+zzox9L^1&f8ba${zy#)ZF^)1D`DTExM_a9XS_ zV)HZ(6^rdZ8$Hh7sw@GB`1{c(Mc$203?}|Fd3I+MW2rUvlWw}Xl=Zjvy$)bKF7^q2 zGv1cJKqQK{$#lYb-*N5a*ga`Un#zhN`yk=h>7h+f1?3g_Z9 z9>}IOtygGTfChX&#-_j8aV3BU8$8R{ zx44=ScSe{kssy@s)OM<$6`SF7QGyKd_HWoHN*__eN`qaNXB>VRGZ_&c^pL3MlSxLX zw^(k_2!d29)%^g0EwC^{PY?tzZvdF6t!;SGuMfBOS!ZSEMus=T(?x}9M3%@?ST>C- zC{tdXAS2{}IYFKq7tYLo7?A_`@WY;#99&Y%C3XZ67QYf@LpKi%Ax>@sHfiazGVUq;zI_Cq z)6^|Wknz|qO#DrAl03(r;R-|!9LCo={rD>9)nG_HhCCYZl_y|?eI=6IChF&$o}niL z6#wKHTa&M*j=Vd>||9G2-`GObxuWiHbXlRnfZ_>9L zzswS=7+*ytllom2FAxZU>c*ICQ7QoRN_fuinrcM}GGlTqwH`(lUjPg-)+BsgL{|cJ z*C4l+P#D69el-n~^L)mhEcI+AQ0P*4w3kXKiQ}7s^A+V)r)5e0GxuH`F_UvSimRa} z051Jj=rTz}#BMh1Bb1@P9XwiA5ZTE1ndlFo^O{Xa$o)#EdL5S)Ey&_+@#DJAn&Hv@ z;h4NDi$KAZt_67})B$f;}N5#q#B;~OBe_KN_jjFn?Q_o_t;GH!(a_UOCuqtUB%U0aKX@(XS-Je$*? zQ=kW7{CDw(xPAR=G^MSz87_~RSX=L2e9ZEB4AFY1uEXUQ+Pc0GVdEPK5A(i23Y18X zLWM7rJSNWggYE9ZSNA+#Y}%Aqoru7;cS6aki!Dk`MOo!eif;+u2U9t13-eS6APnP7 zrfQzL&--qx4ycPN{DePP>?i4!0Nz5lw*;l^U-&NmMUv9?#&3*|I40tJi=E0NI;zc{ z1<2GxaBZt*-$eFm!s8smCcY(mJ$NnW!U7V}Q~V>;@mzFvQGFn!7fTo)o0kgQ0#dv^ z{ua~5o3J;*Yk%h3Sgz;C$6$;_AHO!XI`($1`}g{rdkg#@KXEj>2Gf_l00000NkvXX Hu0mjf@);+v diff --git a/www/main.css b/www/main.css deleted file mode 100644 index cacdd438..00000000 --- a/www/main.css +++ /dev/null @@ -1,55 +0,0 @@ -html { - max-width: 1000px; - margin-left: auto; - margin-right: auto; -} -body { - font-family: Sans-Serif; - font-size: 10pt; - background-color: white; -} -#body-wrapper { - overflow: auto; -} -#upper-left-title { - font-size:xx-large; - margin-top: 0; -} -#left-menu li { - list-style: none; - margin-top: 1em; -} -.menu-headings { - border-top: 1px solid black; - border-bottom: 1px solid black; - font-weight: bold; - padding: 0.5em; -} -#left-menu-container { - padding-right: 0.5em; - margin-top: 0.5em; - margin-bottom: 0.5em; - margin-right: 0.5em; - border-right: 3px solid black; - text-align: right; - width: 12em; - float: left; -} -#main-content-wrapper { - margin-left: 2em; - margin-right: 4em; -} -#main-content-wrapper li { - list-style: disc; - margin-left: 12em -} -#screenshots { - text-align: center; - margin: 1em; - margin-left: 10em; -} -#screenshots img { - text-align: center; - margin: 0.5em; -} - From 3ed03df23f7d99f53f953784a0738ff16242cff4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 01:26:03 +0100 Subject: [PATCH 612/949] Remove this file. --- README.md | 63 ------------------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index eab63eaf..00000000 --- a/README.md +++ /dev/null @@ -1,63 +0,0 @@ -Welcome to tmux! - -[![Build Status](https://travis-ci.org/ThomasAdam/tmux.svg?branch=master)](https://travis-ci.org/ThomasAdam/tmux) - -tmux is a "terminal multiplexer", it enables a number of terminals (or windows) -to be accessed and controlled from a single terminal. tmux is intended to be a -simple, modern, BSD-licensed alternative to programs such as GNU screen. - -This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. - -tmux depends on libevent 2.x. Download it from: - - http://www.monkey.org/~provos/libevent/ - -To build tmux from a release tarball, do: - - $ ./configure && make - $ sudo make install - -To get and build the latest from version control: - - $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux - $ cd tmux - $ sh autogen.sh - $ ./configure && make - -For more information see https://sourceforge.net/scm/?type=git&group_id=200378 -and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@googlegroups.com. - -For documentation on using tmux, see the tmux.1 manpage. It can be viewed from -the source tree with: - - $ nroff -mdoc tmux.1|less - -Some common questions are answered in the FAQ file and a more extensive (but -slightly out of date) guide is available in the OpenBSD FAQ at -http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO -file and some example configurations and a Vim syntax file are in the examples -directory. - -For debugging, running tmux with -v or -vv will generate server and client log -files in the current directory. - -tmux mailing lists are available. For general discussion and bug reports: - - https://groups.google.com/forum/#!forum/tmux-users - -And for Git commit emails: - - https://groups.google.com/forum/#!forum/tmux-git - -Bug reports, feature suggestions and especially code contributions are most -welcome. Please send by email to: - - tmux-users@googlegroups.com - -This file and the CHANGES, FAQ and TODO files are licensed under the ISC -license. Files under examples/ remain copyright their authors unless otherwise -stated in the file but permission has been received to distribute them with -tmux. All other files have a license and copyright notice at their start. - --- Nicholas Marriott From dbc5d7b331027c3f44d1fe41ce0480cfd6cd1d72 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 01:27:47 +0100 Subject: [PATCH 613/949] Fix clone URL. --- README | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README b/README index a5898a65..7859ed71 100644 --- a/README +++ b/README @@ -17,14 +17,13 @@ To build tmux from a release tarball, do: To get and build the latest from version control: - $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux + $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make -For more information see https://sourceforge.net/scm/?type=git&group_id=200378 -and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@googlegroups.com. +For more information see http://git-scm.com. Patches should be sent by email to +the mailing list at tmux-users@googlegroups.com. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: From c0a790453c639847ae71bb344ba2d35fd8a00c7a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:23:40 +0100 Subject: [PATCH 614/949] Add a couple of presentations I wrote a few years ago. One for the ill-fated AsiaBSDCon in 2011 (canceled due to Fukushima) and the other for LinuxTag 11 in Berlin. --- presentations/tmux_asiabsdcon11.odt | Bin 0 -> 35714 bytes presentations/tmux_asiabsdcon11.pdf | Bin 0 -> 112246 bytes presentations/tmux_linuxtag_2011.odp | Bin 0 -> 15220 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 presentations/tmux_asiabsdcon11.odt create mode 100644 presentations/tmux_asiabsdcon11.pdf create mode 100644 presentations/tmux_linuxtag_2011.odp diff --git a/presentations/tmux_asiabsdcon11.odt b/presentations/tmux_asiabsdcon11.odt new file mode 100644 index 0000000000000000000000000000000000000000..ac9f0934e92bc045aef91c2c692924923d55e816 GIT binary patch literal 35714 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 literal 0 HcmV?d00001 diff --git a/presentations/tmux_asiabsdcon11.pdf b/presentations/tmux_asiabsdcon11.pdf new file mode 100644 index 0000000000000000000000000000000000000000..76cd9b25f09d51654bfe934ce67a942a2491cf73 GIT binary patch literal 112246 zcma&NQ;;r9u&&v*ZToB6wr$(CZQC|h+qP}nHdc49`S*^AxjA!jZYnae>LMemGNbB! zvd9%h#p#&nSz*YBONV=g+lC8Zn28vP9E`1DczGFQ&Fn2)Es0qE^C&ZjTiLprIWvgc z8o8Q@nwdD5n(_0)xVSo-8QH;j=H}_j#a#2I@9i5XU5Bq|CfiLSfjFr(W$F!wX~8vL z=LhMtzI0#q3oL!6pJ-NPz;>GHB$r8?|HbqAawYut`&yrHX)+Z+xBYY}uyn(}JWd$! z@6{mS^MjIq`rq$Izro*ogXwI-QS#HV{(o0fI^oT0m`7J7i_jJSZ#u<8aSJ;NA z2#-CR`}?PWL6*gb`E4_B-x`kdIWswefbzV*X_K=0+r+UO6KMtL>CHw4>$2$Ta31GP z8>)9bfwK-SXH5j{%o|K}Bnv7NU|wLZUUvCI_!YY8cf5k)GQA&GL0Mn1A+TP~_Vj!g z9-XIhb1^R46m`}Y1!j1@Qay_ay612De-G#GT}SU@PxCrMEGDLE9DVLyBHbYh$80jP zxdz)Z%D6l|5ga1cfcD&SGMCZyfzT9H1%r^RDyGpa5`{9z8B&Bmrz9rM;U~PcPGOM1 zw2Os|cqwR>6_X}nbwh1VFnil?KfmHh=@_Sbb~|@VTys+3%-)*($M4!)B7H{0k;Y$H zT9E#QE*RQ3(sFU$_vk%i@-eqRj0I5@Q+9QBw|OM(MQBJ>EjziS17yoqTC^hx1=oq0 zg6&^vujrnA&^!jTfw^qzpdiabV+6<&8E%B|3ktg{kwGLvvwij#XIJz*7f6ZJ55!9&b2jv_F!uA8LdARU&GscIyYV@SeI;w zxJMki)2MC5E>Dp!Z%N{GckkuQ6y1cb!Ps(a6)+5g^c~~fA@`te8Q*E(Dm0Pl?tF=4 zMv#n&=SWu2`fjfXfWRhoJdL#$`H*LY$HH@B+6;Q!t1Oq3(A=qd^@x@5jJnhuJA3nE zss-9b^N8)J9uOzkSV=TLazl2E*g-bC^^;EVN6GCUB2G{AM#-0acr;4w8hcdxGKKaq zxhyTDBv!+1+tc}SXbt38A)JAhDJsLG-UY*At*>2-kP!lLh|gC(cFK7xol-)5yc+_jn9_)YQ_pARmPS-oB_aZv^AvkLr@a5WHr6JBm< zf$-D8(#Q3BfnLr#+!^B>^VTu*BdwVTqXM5!Cd5D}--{MfG|~%#<*r>fZx9@GNOxJ{ zbyf!7f>+i12NaK5o~TT0+qJ?CT@ohVR7KwvV;f-mFc%-n^;#eR0OUZ>q(krJ99`;S zb0Sw}0d~qvdzy;-7f~HmVPCt_$op|x(MR zq(w$sz!+coTNGp&S~oZBU1nok&^k<$etFZe1Xai_mjg{0qLw~F6)|!%2%{>Om68r5 z+>8ID& zK3$!`!@nVr%+jw}52)D{U{$9_QNxaVeyEGNfRx>B!kGb+ZWxTxDUaP^gvb*tiwkd+9*B|WBsmHDy z8yOFq;KR*M@rHWcr7#YGPUs3#?yexhm6)&ZfkcUXxA!Am+8$;p97n?3D>;^AP==qL zqlmPuLw48aLI(P0!&nr>qsg(ZjfUq!mZEl>yBn_ytt^q;!u$5V?fmNV`MKl^=}NY# zXThu)om*W@P=Z`pBLJUINIqfSbbfw<7~=Nh8S)nGqXi+bx4vud{OZBkenNZI;-Y`Nk za`^I!%<7aB_0g<=A5AB=(gyYr3Jo`F2#oZ8 zFiYHpxPe+Gt9W3H*2LV{scertMU!ch8jpR_+S(r4D0wlwPl^a8P=gRj($Iw$OU*^S zCYW};kQ}%F)$LP|*b;LW0fY6SK92G26?jSbU6|ciit-9( za*RuVF2}S4-}b=<9KTUh!4CLXkTcoks&8dT5YLe&XrLCgL^%>nF-%@CIyx2W4`qLf z*oDtE6MolgX6f$I*0lYp?mCSZMmyQIc5}E*j&%DUULJ_#Wkzw-(&Jmao(|h|lZ_>} z)SRGlg=(JV@(PezNaF!$`aiu-i;h?F;*sUn7)EPTBnFXw?l$dEgp71S7M1#B50E%F z4~aI$I7pCWpv9aauQdNqHxt|@vti_jnETIUFf1YiD&xI5K3hHFXW#kVt|YPt!wQ&0 zISnI~(wv%a zk>ix>xX>)#SGMfu;Qq~_2BeX+^UyqqQWs>vqLqBEKO*TtpXKdFP!40xHRvAXhN{*Y z(;;NXZb~LmA)Gdml58N(&s^sCV4yR~H}GImh)?MyyevUSqbnO|>>DRj20U$lm7GQs zeb!zrC<5CwvIBee3Ma;r3ShLW9Yx?YN`wLRYOggb*f&(TY_`@P)@;#h1B&P{ig(G& z^vQweS|jlQvTpz>o~Mq)Ee=3Q zEc9nGZSeM#Z^lELY8kpUB6^J^P9~mGF_02hN~vBsmH0}JlV>E18MP>fN!UX|995iW z8n8YxBrJ*BhrZ1!IT`2k!XLMee@53Ti=<0$qFWCFXLZ+wTVcW5JMl8nm5zup%Jr2?e(CJ95;I{lOrUCXMXT5PoNQ`uA2h4H(S-d=HE*NaN(+Z zc>xrt@^Rd=((J!p%^BB0l`?K-oF_|L9l;Q3RHLQD=%#d3<5cxbL8C5gQquR>&yd zy?S@i+UuoHoUxi!XYA`4CH;kysM05=@~wors<0BnWSEvSS#ai-JvQQkE$koLf ziZ|iT=2U}O#ER4vsG7=Gs|Sytj5$X(S5WPR39(Z94M7BhL(L+8?o_MTTtuO9Rw7Wz zlqyezT8u7-%t7`6HX=h8OkBk9qD;6cdC|P&@N{T)2_sA@cv+GHP<2~(=a(|DVgI%G<~Xq_=usoV0aTdgb%+-zoE$*B-zorWHp$s>2Aya|EJJPHEa#h*kR8~^jW@a-cRt{gTKjG+jcv+GDVc@! zRbP74f&Z;QRLv#XIA#)Mr0b<%tY}PZs78Xtyg4hwea5aqoSiKk6ptLdF-$u7tMZeJ zeyWeC>41tgxJrOLE`qtL>*n}IKX=b%jSnHRQhZ$#gZm?uYN8OwU*`>8Ce;G^>BB_+ zJ#)^-&Shd6Ev(&sJB`qiMS?P&_5)~__HkfF)~WJ1XZd~A9X}^lQhj5D4_cjDrzHx!MHN*zt zn)f?1#w@$+yRNJWOOA9k#GUEm7wF?GEU8r_!hFK z@8mlhAb`c8OK;z#WG|zdOgl*gqSVZRanrEryFA5)I0{E-Jf!H_lO@DO2B;2ityO*7B8J9C$NrDDW>SzdwV@RBf?$`N;YHT3T+LID+4wR4c z$=>4lz(D>1`3qD1$;B>hEZTo?B**zX?V4tCZR}cQp8*o+!*%1Z@9s!7gmR_rWqvCg z#jL<|hm32S`-LZALvP8uMNv-1vA3Dimc7TeD_;z6zw7EsEM+uau~K8ZRz!Elsg@`# z@wD_g5gW6|fBlo_!pq|t?)e8&E9pxI28FmxH(ha<1O$F)e4HFphAxp(#jH^L;@TE* z%wOj7;k#s`{<5eER^X~Mv;mJ%PAgLBLlf$G0M6zE2fV78@?$y5IssJ9s_&!70yA@> znv~IZAuN|rz~i#3b(tUz&Uo3AKYe>vD0}P_)>(T!Ok@0R^^JE`m5;1M(r)694(7Cf zJD4QAd~i%q}rRBkjM%)x#-9X(49Jfu}XXrWglT z;-Lcb01BDpT)%x)omE*BS9{?7pmYNfbN9VGfVYk%ut&4Pp33C| zxqEYR2W+I>dC5n411fBFw=KHAE5{gmc#VX?HtGB%6PMSl*l9FX!KY#Sz?5}^V1i)g z#c+jsb2Qb7VCj0C2X8R7K*E?oy9`C|gLu1quR9+Fl?Xzc!4l zh1b={tGT+`TP*lAkRwRh9i9BUEpyHY{tk;|CCK@OsE1+o!&cK**R!fi$LyNRk8Xry zItN}TdTpvS9)&z$%N$EO`76a6RO4^0Ve}uXRDcqNZ}$$~SIvWaXHbWddfZ@*Z=J?3;fJ_flvIIE?h+QX

    jcUnnySwt&3icq@AQ_ZMalBk%tb}S!|q%i=zSz2pw@l!69 zQK8e1R#o3tqq6Z!TS{Axj6VMpx+;yZ0=sc{6y00f$x=vQ^0;n#9egM?Q#-71F0ZO* zB-B$e$wsS>Bp3;~ew)dd%^ad;BnffU$(vhj6ebN{YiDzZ+=sefAuI~`=c0+0KoTL> zkD%q}que}*7t>r&Et=loapOS~vVMP6D7cXDs1yv;XC`Gt%eQ;<)}~tbMPxQhU(r+q zY8h(j>ju{R6CL;{jVM#q+yM!SlWvBMB}Wm}eK%3jf>RYx3u23hW(@<5ls8))+wcD^ zKr#xZ3S(w(`u}Qm|5^Vd@4@_!s`ozz8zVE@|4jd{lkNWy|JeU8#J^s?X?rq`WDh^F zJjldw2Xl~Mpfqi5LwkLW21pZomc7vdeM7&jeS@(O(}$$VYWr3rO}$#O5L3oIfQlUn!!s%#lZqJJDh-CRy}kj4 z>!~42I=5m}7voj$v05hADfq}X%$4w#_i6^)kKQGa*?&tJMx~cu!Yc;Ew8&(nh+k%F zcD}i)A$RH$?RHYG6X9jlLxm{-r{Hu0cT4?I8T=Q7Au%He`T*T`HND4azfq|vzA%69 zE$*T;qyv-`W_3C|HzSrBwI2-4ECr}-L8{LguR7>7==i8mQ+-+|96NCNDV$~CqFJIt zCSoi7LjP0$s(z^%BhXwckk|liMMB1CGqBeUj0!G83#V)&jZ24!xxC2hsgW{VeiY83 z1P7~6FCNj;Vn_mU?Us1#o!mNRNO5D>oohVaVzMZbUfB3K)dH;wRPAu;!G$c2Qt#+F zWFCO)7-x!zprsaTX~NjE_YS=!8QngHI)qY|Wy6Hiu`T{kvWQ1ww5AeD2HBJ3RERr^ zd0`oV7uZSNd-jqBT5{wlhTS_B z)gAN?+=W(Q8gB7wXRWnC)eki%AJ(Yk%sEm1-1m3VmBqX5Eu)s<0iGUDH2|weMCZPV zs}O=N{7~Z^yV;EPOa$jyVO;vO-Y(cd0!TS@47w%`cR~;CwLT3UsK}$<8)!l&M8hAO z8gl}l%>`s#ipHwqVkL>Izp5>ZkJR!?rJx%kZlgA0s!GXBM%CBQq*2TJnCCpTW^R4N zcw9~z6g(6SWKHc;XdzkYpW+^9Q8Ol4BODunCv{-1`_9uny+AwH~-3{m(oA)6QxLt)iYnsRV&)qLI^f9fV$<>=Hergr+ITc{^om{lWgwm zm>J)|Uhoh& z7wO-|5T3c;w4>_SQLajnH&e_2bFpJseCOB`9zx7)SpeAarz+^>n~IpRe@hr9lCGvD z(##^1*Lwyn`^)D7zh0lO4EQwcKP?r1*o_)cRnotqp4!VQow0fbmfv=a6rcvw?y9!s z@kKU8_uu2%IuHzqI}iX*|wWk1F=xyTANDp9-N2g@-ov-IDE z2xGGV&gLXx#or{cE)XANj5!cSW`~*SA=d#?uW8RqQcNZQ67M zFeX?mIeatPVvb*|WxkVEiriO*V7sPt0mxDFnnOVXoKv6iG&O#^WAH*3YKp_*m#m0J zqRu$NJPD+><`o>`qaI;rmjd0c-06-1-}sv!P)-_FEW}g<5liXR-nx0#xc2fH=daN7 zEJiRpD2NJ-3eSpEo15!$)y=H1%?i&Z2u-;jg&#Qz`97%j#l>jnot{CU?}5iR)wwfp zp?)7ga0O(y+U zynYT_bM4%Yqedq+sJP)Ywsi~DkE5|pe@@o_AY)t__bSr?e3WM2o>qcM$UCpp{>C0d za^)5*Oa0-N8%wWmtA=wL!yk_HdWq#xG{|=u53EABw&Pt?M{YNg+uU1XoRyl)87-q} zXx>ggh4hEq9sOow6X{cg=akx?x-%Qdvl5LobuR(OEl_z4oJ7Bh!ymoIbd0Gtj>(Lk z#tI4m$F(qKS6@HpdgKy)!r2CC$Crui3JT0sA+%OnHk7UX=~GR(ZcS_tBn`XaT1Cjz zERw%1k?rThmGepiyp;204%y*$opS$OyOA$g*ot~0lKA}%O>vy6bIasynuJ`7$;bgWNn@8FsPOupJ+4s!3OpA=h!tCWRwoPhCP@QU z5`BW1SM}PzOI%{_>oh}IKN@(~DU#M`i|P=!UZKY?a;^3}uPpF!+0sIn{H4-rp#uhnpHyG?ue4}x4!4^5Z+o0Zn@49F{OjEWvIMq`iHj85uV$iD{I6*u z<}&Xlj2|#~(>C>axe2>gyjcK|wQI;Tb`fB|U=*Pn&QZx2Y39Oi%^%h@SaS;YnHFBo z)4(?3Rmy=<%K8mHX1_!KWn`8#3}}TB69YF zgr|y>N6)xsWluEnPpy#H!5e0` z@%A5G@jJ5T>ftCtq_OM-b6aD^j6+c4dPA|QAYl0?@-dckL_6MKbJZccSJ_UI2Qp+yrzy9jQ6Tuu z(T37?e$r%#X5ux%G#AZe~Q$-Y_T;i1N6oFH7=`{9&=eSTR>Y*mY zTk$ljXZ5CXMZ}0%em%2L20_h55sfTVS#&yoi0>KjG9apQamF>rCeFYt{Ip+dp;8j2 z_OkyShAcU$o-s$J#}vDsypT@TJ3w{dujI%|gW9aKWPnT+#qyl8?93alxt*$!HWGt_Xd)gzeZg&{|V77 zd(|N|O*lSmou_}!w?hw`;2%ae(9=e(-pHil1hp=UrO_ZK7AS%xv-vveWgw|hYTh}= z^tlieH>E`+`%-ExAY%-vXXAJ7`FxuGqof_FpJVO zhX})2iaH#+IFXCc-J@s`>h3Lxybr!YRig1Kw3Wzoo3GX4l`2(UI<<`7+&{d9VKYd_ zko#8@TYLD{b^XCrW2m%Dt_C(0flXP=Ie+!Rwta#N&|f<^y9J4kB}XMJ_dt|$T`s}W z-+|KJA)ID8t4e_JqT|ispn{gUaV!o&E?qZI#rU{7N$b1)6&l5rza(6Stk~C|6byz$ z!cU+qy`T-L7IV3>VNc0zXt=?~HtWUe1LfLthdpqCWoplWldxjPS^Y?*T_GUAL z`NdVyP=m~*XUAMlOY>i@YN42-qNb8OCwd&7y?~KMM3ZveFaNh88`@sf%UlHt@kTdwC64DL+sQPSLU*6vml?i z5ybF**^X$dJNZ9Rjck3MxOodn@RnNmoBW**7FtBp8Q6UsK`l*<-ozDZU2H?^t2C-Y z`}%^L(`qr!NyQ4zqCE*&Z|>4uH7nV9>j4&md70k-hAN6L}%7^Y@))$EWSn2n^RrgrKSMKkkS8cCh3B-W~)H`+rY^ z(w_?c1=W0ceEoB>wi zxQWDtlep~BTz+p}z6+8`)|8XyKHLs5WpBSWYb2`c{=96D{KV6wlj-(6jB(Vm-4JFc zPsc=z$Vru#5*!0qk+u-5GdzGxKJ6n<+i$J30R1mM=-KbWPw}gT$y~XH+$k>hAz+O4 zRc^xhDypY`5!CD(tSNxd1QW$UmcQAsj@Wb=lbwv8fQa87%^IAThH^SwYfj&GY7t{{ zt2vdg!eDYy&rrKb9oAHh5XB5uv~B^ph!>yjVP+A#!KBY~SO;Ie5XE`h=+D%LiuUJ; zS3K4P;Ai)yS?fepP+yy-Z$Dkz3V9>gnmbQhT&uP)jqdKh1{BFmLgsxiq5m+XT0|iL zGDdi8-UtNY?jOi`x`rGkmcfQNR3_IHvG9B%;-D2;rZj<_bG< zba14?5YE{Hs!c`jzi&kf;FnEV7pY#s48hb>;=(I;vEZJU>jW4Irw`#0=BRh7yfG_~ zO0J9WLZzHRQO-IIs#{Zq{OW}~C(RNoj}_K2s^46BZol%p@_Ar}A-1|D1wl@WvH1d< zQVtWMTkax^+IJK77D2kLvoX&p!P7!+S^{9*?)kbiC`Z^bR7PN`O#DqS9iwrTnjh~M z$}ymSU&5x-sV<{GCTF;v=+;-HOEZ;}2Av<-3HBfbA z(YFq>4hJjT-SE7-k*$aQ0vkX&C2J0t_K)tj2;9XsO7jp?TulKpzNAfuWyQdwg5LzD z+femAk*~9)`u_vSPHA*U_N^Orqdgr)bO6Jh!!74)J^vpCm@606zB z`2iLCfXNny@Q42M70bF)fmfNW#ps1!(bo_^7A)a3@gI!_fC;d_;KYu_XlNb0@Wm_8 zHm=6{YGQ{6`^*vnEYnN)k+ad_>)7fP0j$%&I?8BN=dj&yBD-;iLACwWh}O*+Op?&WP~{r-2Hxa zU0eyEL%SC{kzi67u^BjW*xaWfqT!Mop=k4n2=LgWDO>>7)U?>5F*+2iJ*{6p?gou% zV@lItEvY9fb@g};ltjpiIB4+V^&M_U0naO3?B?xwr#K#3JcE5CeU#Mq%-*DVt`Lji zpo1vw&^!$i*KkYVH;e(|)Drh&u{`nTlu9XJCm32$aB+`XMhv`R0(S7I_k%`p!Z*E! z%x{kzG5ja!a|lik+ckZ(Ioe)ATE>e@B}z|O>?gv`F%!{7ZFJ$C;s*tt>7=6Ke9_@L zO4uVuA({l&D*E0FuTdOF-!4R%MNF+48yrOs3Yx>jSKpPT>6I=J#mN+k%CpAlS=vNT zMP`q3(o#@`kK1h>|CK^CxpVq+M6Kz0__9I=?Q+IygdRlzD7eR-dmV3BXj5VIe^n?H zg!UxAmR^ARBaR6U)_ZvEJr0HutNlarpVyyG7`dsY5-J;zg5roUBUpK49r`A1VBF^a zJ@G>b-^9w3Kd*>e#U5rw+QWa2@xS-Z1WL|fSVG6tgJAA2U?$rjHSCM_BBIO+Avv}J7Y1OXLs8!^T~uAW#OAg) z9~QnG5g0<<-QXAHMs5$5SwyaaO_Dl`S8a+S_J+PTjs(@)9sc6n@fIzmK z-{feoCB38}dDS4gOizc`@6HFCElsLCl1%GXQfNuK@wWa8o*tDdRQ#2BM;enX*jC&3 zT1NK?BvQcxOhDB9z3d@IU6$TbHz6qjZLk+W5urKl)Oi}>4L;3sQVeNY>+K1oO7QKe z(rj}YmG2yuDQDCWR!*+2Cvj;g>}0JW5hfVOxjInWss3ad$PrsW@0UDQobv~R zKH%?sM!?U13Wp)9Rpn*iWEL!Mv_o28~dpysec;XPhC*p_At_pwUPF91^ z7#Md&{8N?h`?sJ383yE)FUcK3B-G!~I%-%ve41rmvDNx+BTmCm)?)Wp&gnCp4&8U@ z5vwTC#32vB#3ZkXQDmMc$3Lv)r9(eZhXFJWKZ8IX!&Z>>?Dk&Bbu@?WwGIZOXWSlYJCQ=k5(Gm-5dpIU#A)UFC;RbD>gAj4Q+Hj z9AAF0qRKkj`{n(C{E~2Rx0Yz%uoHCG7jPpIT%#{b`9Tk#?K^@L?PRMEr;k0ijo=)T z=qNSV9CB+xZFGCORcWF4WNPo;wysPRIloL_S@1fIkMkVw0wOxwo>{@E|41t_ypkQ5d0dJZGvW!RHRXz7!qtP-U84mYcso10^=Yd!gja5__G!YG0%&dZ2YBZ7qOipu za{&pNQ6-x|Xir>rqbOB6?}B#gg5ah8ARI6$$xzkJKiV?swF|tGC3o+vq}!qX^W(8B zcAU*Wmf!e-g*a%A+Jw6}KF@@RH;KNI`tXsoo>M_Wi;rlq#m1T>iU}U&Ib$Q@pn49a zyo=%a8{d{E=pg|$f7ieOaLAcp;Y#zYL2r%mOw7#`po9bgN5|t+J9vXyMc6MyKa0{$ zQo2_y+Y^PhbSf9|B>IyJrY0c29nG?+6$?>V4|Ws?BtLu0=o`TtzLq=p+SgT?giKmN z7sFI%xVWM`#Zds@(71F`#EkE*$L)}+NFHIxAWH&xCU2_@WF~I#msG{pRIyVxsDwlM zIQ8AjiZz945cR*zn{n|#U$n+7r1pS94vsO_4Xl-kHQ|lpxteH0U`0D&;@ipg>DsD4 zb{EG~K<)!HyRfU8@IQ*epknTNQG6q;y?OGF*u1G!!%Xe}k?Lz(6jac7z(F3)D@H@< zp&(?;P@pUwoS-I!k{w0?8Asp1dk7bL{DfKaC0-cfRE0+N|Ud`WtS0q4sQeOlI?)J>ch3D5+@G z)yG9FwCQAdkiald7EC0g$DFy?UEt_|7u^PUN4og^u|*}$CRlhc#`xInEr_7v&&!ml z(M)cA(y0Xy%w;~N5nYiRl8d}!4&%vpcbPNJ^ZQ!19B^M;8MpZi(IA~Dbw@MpV z)0!sDam51C&PfiOnzBpWzm`lAg@f#-z#p>Tw`viZ62Jb(*Q(KRg>{N=+gR2GnQk4a zdtrPqBx9~CW7V!V;^&st5L#UDq+7J^acov}dRR#%A0)vYR!ap~da?w2`F<2gYT~y@ z%y^nVpGZZEknxR?rPS=;VRI;bmTrPdH`fE~IwFQ+YeA-C)U0h5gaS%xzKK>e+%byM z$Godm)p3>9m4xRP++)QWWjO(JM||F}OUq2<8R0E7l|C@W#lesaKS@+l&C>NSuzBM& z!)TeVtAds#&4yplQX6L&?871VGg%|gXi%t95Q!mfe9B2Y1Qy7D_(om8EW<1}_I8kb z%x1GxVE|aMNYSC@n}pk4mDN}Nb`-H=_Y*_0wTGsIJkR;f%p_T)E!YHcMhQExmp*weMK_bq zl-f)Y4yEw6|cXVX>hEwclG-#)z>pMDM3~m%tt9OWXP_f?``|u{MA? zsyCdOCAP`cz=q?P-3QKpI@aR4?kchW*#P2KkM> z&j;Zq$m(PG;?WXiM{s8g+)daFJP*-nO$;<$RV~6bwKaku3Pk`<0D73KxJ$v6@Nf{| zz}suxKw&}@a(Vs=#K#ZY^kB|fd2i-X`H@4OA9hBC!#+Fni6S)ISNW&Y>W(d*PIkq5 z;7+V)KN-Yx0%;h2IF_01ZYsEtWO(H2Dv=&pFWg~T<&n9{ zf#}z4r>4lI{6T|BFTCd9()dAV?CA;$bHeR$QVqTl>?Y53kI;!-(%r@)>44n&a3galAV zA|;`F!=|V@921u8lPw@Qqu2&t;W3pgZgoJ~Le>eR7vne*wB}AZT}5u+`|2d*w7)F- z;Nn8RV19>o5BN*&d;)=!E=i96n&=kY<(y7ptJ6Qb8E&(Ub+QDQtKS7LU*^%es^I&y zve2b=(^5YNjVe^zi^or_c?myRc`NE@c&vjAG zjXH`E--n`QnYRtuJ&Rlc=vBi32EXoPbye_0NGb&i-XF37R940?5C2-I*f2ZyKQUqY zm9ArqWVOepK-!}u{o7|BdRn{lw^WVtj4Ai`jGNySLaC%ctThuCF%v*Mf@)Z2lm<|7 z9I=aPQDc#7UyZd+>}0+Le~h9P)No)DXNdlN!#|vWJP~INL&=+0kq~y10_m{S6R0vE z${FdnuQs@Ni&&e){xAYLMe*f}Qo&nw28&j`x-Mb@n3^>jHSTEk2t6tpkvDlF-A@G1 zu-8sOcwU@S>r$~XjKYw@<}`1B4RQuHg(}Gnp{ijiTPerYq;8dJdDpy#tEg-sDOt{D zn+EB_4)cts#$Rue?ABOfs@texlp#Y6VT=J7&q}W5+rF>m-!~(z@>ASef3H(+sk3;{ z>eDd+9)#L>{!l1P!)eOQyOgc6QpSh$v{+hyv?6J1k&35tcMt@>aD#Lg_s=LZ%=l|6 z3MIL#w1u&83|d96Vb>>Zqf*O7xyfltZ04q*nyjv^JoCRj;14D=xxDHZl*}~_BKmHU zqM{6E{np)zD)Yq)(--Wwu}E+)Xq&5Ip%B#C9MdYDP31q&AIK#ZUg({Dndlp4C9WKl1_Ui z|7M$lc>yQYO28fIByG!LJ&@SFBpxiZ{mG-uTBc~lQaH0Vv~2_iR9OF=L2K< zhfd6y01N`nmBwA~P8LD2*snFB40|#-RO>t*(Kd|q7ZiDSyMwBiqtZi7XVhnUAj@{$Xel!rE8C|ilyVWU@9wdS~E;O73LNx2P|Ro zuoS2OUE?$_lX_EWH-Al$jl-vaAS@=u_V!(*v$uwqUuiGG&F!i$5%qCpG*_}Ta+h2{ zWv3g+E!YC4R_|tANr~HFS%}vTN1BYN<+zd0f~dgaN)&GUQucHv#YDDuz{gg63VR+Z zM_OSORh&!?c)`=z8s|AvT+HN7M$r({Jp_b4q3fXCZ-ucMf$JLAcx!5%P)pCk*kMd@{QCv#)GsG z_%WrDg$U3g4jiaVXV}k?xlj_g^g)NEN~F$3Rcb>7L0I+#C?qA}+}jk9ksL~A z>m&J1f6CV<6E6?U@nEECbDih-mv+{M74m=f<7%4t#VjwIwWfYE^ zA{ja)D>U8t6}c;bL!H)ZN?5zI^aU^ozZdUiO~mk^9Hj5VNU3)ITmS@{(zP66`3U5u zZCO`@q1X2+I`G3o)|lLzv$6n((@K#!FB1yT7qxTQlxUeYBDhVUB`dnGnD6jrS&=r@ON<2w0Q!}r*W+U{%@Rob$;#$XCwWZyzy$hD9V-+`(JfSopU z@Pnm15HDkXy)7PJnTi&Rk_R@L3su)C8?c;?+$x&3k}1wT|RP&OWYiOZ0B^OoY|e$k`TL7vobwz zHb#Xjg9_xuyNJD4@w-$|;)pMMul;}!)qAB|QLNN#ODC()7mZ7)&0ue3lzZ(xTHx4D ztRFh{1WVT4j8FKRn99v=J(=u(7{k1VM$3BWrSJL$k1RbUoey<3SBr+w9NC}lgaIj^ zLVs>^d_6kDGZq<3$o4jONcbyw^t^lxT}&jZjH<2QxdcNospGLah0Y@7U1!ngCq5<< zUL|Q%CTcd8EaF9MGzFiEr(K$`a4a>GY$xNl%lYS!+VX@^pM+AuE!X+;T5@KUzGl-7 zrO0Pkapg!2V2q<2RFq120H~~#vPc{PoiBAB)q#l1u`M(mf=4*T#LQiC=Zyl(LDBdv zuH71{Z^dJ3I{DIZnq^fdDS?^=PHd-zH<;b)Pj?6Q4QhlP@Ho3U_MDoQm5P6P98NfI zZdi=IxK8bR;MMDU+iDes21*M;*t^MhOb>9LPRxlX>OxLW7jeDJJ(AWA?W&}z=(@FK z@a*Sb&v2yfuFsbVI$w8z%I~8b;O3r^pC1;P=L>|ii<3QbX0XOiz|iz_D!&Wb(K{w0 z9wR`uzn^CklMx_iI2mXcuUa94hpuUXJG-v(ttn#p_NfuoEW-0wp<73G?(JW_ACd8m z7IeY3PYitjL0Xrqe}6}-*ueVvOGfXrdS0;0-`7jZshEU9U6I*N2ejkX>O}k#sUY^_ zIi1*x{%bh89AqwW`}P!C0zKlz);WgK6MezHrwb#wCpECj4&74Vp0BAgSPNg~mnUmM zX2IP*xN|45{5<3d@5TcfYhND|hBd~1OhS?`su9-DL^zQhlB~e!`#3vP%dR0D#;C*= z8mS`Q-Duj<^L3F*jwDz_4j}RPCJ8aS|6(ouaTsW)wc3DrL{a|{c*2FxHPAf8WbFDk zPA9?g?DDpk=#y!+<$ACnPf7!et0?>>Y$R9NqjrEGOm_ftciYc6N+e+|Yz+ojU_wM) zdLtm4&a{EQNz1#Q!`Rds7;eGsey0e%W>0(V`$y~I@RFu>GH=#j`;IrUuLFW2?WcjR zr;m}%vPhy%1kzlv&45(%-=&!J!TzguL*w)6T}bDb4>GNJD1_7lHLYvl`qp}Zsu*BQ$m<0lIWg>sL zCwWd~ijp&0pYoy3p(m>HL!fzv3&^Yw8aSD@_sTlf#{-yJE=7Z|neoa0!L@%k%le<2 z-Dr(l^jFDysoG8Zz23{*?ob^=oHS8CHn{q$-BFsl81cJGcqKvx(X{pNr@O^pZVALc zvy>?2%DSE#lu1%I1{Ihl{vQbGc7r-aYq~C$`Dyg}YhGVhMY$>Q@9!6@aI{%twx-K8 z{l3eBfQ^!^`j$2455_CBjwX^so68hX^17?y;;rWD4QCs)Mg9dE=??#_x@*%8i78mN z3L(BTXB!CK^tR2iJMR|Feg)`F;4*U|u#>i@D@9GN`Yr}(#n-_>+$N%DrAM@w)n@ZS zrTL#kwR#FL-F?P(pUUu0x(A`1r%j66@&$opwF7nu0pk4?<-yS|_i&MZDN z$-?lggj1&cku5ayi-utjRo{0`(fRPkDQh3AS^1}nsvytVi#K@+amreXC%n%-M|}Bu z`cE3G*5E+ah(Lf1_kpRNvP8G9O_vX&lXO4pTrg!^FmbBidbbk=HQ$Arq<(iMOHA;e zbBO$(A$&q>8V5Bouy>Bv4dkpqKnkBb_-OK)m^kJfAEdeUs=Q2SMkR~+FZPqoB1YL_ zZ9kVypWK4+8l}?NKRO|7eJaJ9I$v88zGe+U!*}sygAU7DKk-f{Ro~8UeEx%&D*pov z`48pzFBrna$nbw)2m`}^qah6ck?Q=v6go{h5_ZHL@&9nh?3y3*7!5iW_HRa74F#(-_=~# zrCiVV+r}T?hsT~rnxC7MpEtnIw@u{RLi1bd!P$o|-`5Bc5692r7hO%H=f`)8UCvI= zS9h8HYr9|}jIRgWjortEqp#<-?pC%h=gae@j+c(&cZYS&M6_P@qc`*fD4wI-w~NMJ zPRE1#*YopbOu^z{?3zsM-}cI#AR$zO7Cm6i25e57iVGt?OH=U)f)xeG(ht{te1JO+ zlE+zCWtD%1pMew|OIJsN70ScO#Hq3VWJT2hX7LzFrA3)^)zw`D>y{6kHpBsCMLN;a zC^XjzFWy~qjU1-M5jgvj7u2gHl(7Blv<>?;m3=T;?E{~@u#7t`P&^AtSWMK~*u5u+ zw938s!lTogS?T4FaGKeUH&f29$!GdU)S7339Fek6T+z~kjZRQtx)MCw+VG?uHFCtSz3PxF$4*s|7K{nmqJsymP~VI4zqr!u(qZfwE8nxU%xYq}pD6PV z+si9@>=FFxy1ksqbU}z=V7fy$f=DF)vo9}>-t_q-U<70uS!CC<#Ne6?&iHskA4Xb}u=w7WLB2Nk{VBWp8?3+hta#ndD^o zG@J+@TnPD94@Vt){R$|T0^|N3g>F-iP9+A~ zECQ+bLCb=1I0JX9qHMykrKuRVvNEZ2JhqAhBabOX)yH-v0&w+rU}h%jm!d=#S|H}T zsYt;tqC~=bAgo=P>jzc>9bNS=vbh78%H9XkfF3@Ydh{!_8$dhPXKN(?}6bUP*$ zm2iDb)IrhvLsCz|;i?dnO$b>j0AbSO_J(W$^%SzQAgGgwaiD#m6QqJs{Tb0gO1tXv zUh)Y18cn^$uNcZy9n5$4*dweC1;Cv9&l4J{S`&*xvBj8>_!1qGm}IJeu3C ze;R29Qq<>!%;y6B=rD#1zUIp`TV>-zm^yuL?_&J7N_JbgkHlv~xx!&B?nV{ED?rs7 zW{z+2#H#fRs0(xR`m_*Yu8S_EqU^&|wPfW!oh9gPmkX-cXsiEvKtA1w4dk;6-&Cv8MT;GvwJ1bRo1m?~5|43aw_VRp-cfz3 z&Lpujz&gND*(Jkd79@=v9B>nok2?r@o%PwV1Rm48Hm*@aYuVyy)LG+NV1 zA7P5@99YgRlxpem*zM{UPDN4orN5L!GN(K4jpfR(u@gV#FZ4&InEYaLUX75CA za-*ztOzWcoNye{s4UsqSU-V%eV&t?pKBP}g#S?!$IB@>{%=+ndJ|NsoYO$P1?AHyQ zMR-~Xft`W zEOwOJ?yi8{(8*Iz(xKj+@-8ac0nlPG)P0aaE4fFO|MFKI@!d=vACG*VVBK2Lx3^98 zs_WP($iq%)BJiI{tU&iT*^BWQj$+GD=yE~pkdpu#l2D0tTPMB(I0Q84H6s=p75TqCG?u-0F|L`*pFbBm;O+fwhT513tnNl^HJYwC7 z()>-S$6z1Gj57{%Qfd6nP6BeFxO2`_#s^{KZ${sjcbvk=>49)BiJQZntb4tv3s>>U(>a7 z?3SyX(v~3v{w7&&&u&Obg++S;W`wH&6Z0&12!Z{;oX3#qUt0WQrv6TKQ2{}L%Mcu~ z>%o8NY+O%cZH4-Uez^p#D+HZpE^=ZTu?x-!KHn9QA-~#iL#1mWo=h*ni0=tg)#9eD z$3Tj;OoaQ1Hk|9SwCk1dkv(*R6jgcqg)?EpR}j^{@^NT(Eu1fq#m&rWK~e^$PM7IqvX7Xn?C{dKJ-k%{)HxDw zKRMCqGat>^C;9_>_@s5WUypp{B;>~sQ!YzU4GOdQ&_?}MxN0lVX2rIKydy`TF|J(cnd|03Q;Mq zPHuzO{Z4+kf6}Pp(^VdY0(eTkli8>r`7K2ld!1k?hNKHSAb1HV<;+1CXosk~h&770ECU5`&oz^?s6Q?lW``&pd8@Z^&X?5a+;b*6@c}%7? z9rlSb_oEulHaS`~vB+WC_HtxM@mN@5igRdOQZ)dEARU=*g?TBDxrl;qROMG7j$6ER zpH?2a7fFef#_foUG|Y@!d(=9`N~zx=vnf^A zno@)oFP`CtijK9GoC%>V%I6W7rQ@CpO1{KCpAVc1MV<0u^`=}|c{rTUU8=5V(K}tyQ$dVwRw8NOt%|(UFnrV1F(uMOMeu8p62Ukx`V; zq=TkdsJ&ge^rzopo>kjVrD@~O)8LUW8C0pA7mUtGm2W}*n<2VhcBwYej5F{dnq6cw zk$C9kEj>Fr?mXilcivZBWq^c3e`q|o*TG(EtFJqZOSx(ED=c{K7t7!IxV%il3*CC+ z%!%gYAC|Vefvk=S0aQnk`-+VzLzH0v zpy+_nx3=4|acpbZ$RzTzsJ+cV(TjN@Mx9jVFgaA$K-o}!vPY9Twk|n*MNOQ`;JG& zpm7d-k~H)_ln?!x`dbOCGwM*L$($R9w$Ycc01gUxuG zKrAX3Z}nb^${%cxYNpwWxf%FT?ZhVpurjD!b{M5O4m_$k!~mfM$HTBNsz(?1BxAhc zjVl`F^Z`*jHkh0c2ej=x9w!2ha%@|1ldi#fG1G`6e1MAQpK*gLLkKNCndCvkr`31$ zwNy6|l9hHMGgg-3x1@<*fB0W?(RY0Tc~NtsK$Q{SBsw)>))9Bda)g{%*z_CK9Yw{p zd_vMTgcJ!6V`CNDR=L(|OH0zV73?+FG%R@N5g|vyRVrLc)Xl((O4bX!9Ql;hYOXFd}%vBAR!KtsX3N5>16hmYf>jhNxX|~*W zj0RsP9^UQC{JYxKa#8(TQa^ieN*4^HzLpEG#UAlRY%2YfE%YDKUYA0$jw#6^=`Wi1 z3cmvYaBpX!8YwiM$wMO5BTqI6!GG>Gcxoisj|dmDFMu^OU$Z3R5ufM%MG_DeKxbK+#G*P}Z*1~u?nZ6?*>@w$7T zdsbhI8kzRLxQ$~fHEVV9j5~{IhRLL&nwXxr_SXs>)!$ixc&Jh65V~utuOTS zz9YOFhm5&=Eu~apZ$1k=EmJ$wa~xF<(e=AL^Uj{LP;Hy$oiN;wx`Uw zJYV`Xee5XXc7Xc@-1X*jF#5t-Nf(UdW`r<~USZ$^tLvNB6pwb((ok6#LS_{hUg)1` zQ@fO0&OAlXnB{5XF$8hHKQ4ueP8h{M0cmIJnKXFkUQ^aoa>YkLq^P5pBc54`$`zul z6{F|*E@<->08!`M&=?4csM7p@BW+qKlCVl#hCWs>#oftn&zWC~E)}>Xa_d#Att?u% z0d}as^;|O3A1$3u%%J;HGCAY^obdkmxi@Wz>j#erL2`NhQ65crtL*2jw1<%uKI1zj z$X3N@{3($KaO4kIVd&6P^pEdv$BTX?6fo!8}>&_Vdh(@do8(D{8uK+l~@E#`#hl@E3?#J z?w2w&CLRy^SqA(^SJw5jvs5w<7>e12<90Gyji&pF1-%}VUpXNUIXy%1UqnAYu5AN7 zUwlt5p#s{Vj%K+yo)?VnjuAlOxCHfKVv9ou?E#sbMEQt!cxY;Dp@3aM88a5eaGfty>6YktC<@s{)Ukd`zpRrLxKIH>n%!c4U6T}bv9P!*|JhN)UbtWXRzbj zX=fCK*P6M}@de>0i3{z90N zfCG4g&0`B7UT^u=SP-`Fl3(LW6f5%_)KRzMpIud>lkYo=stwORTj9z8ahhALeHeR8 zr+*{jss+&@LH;42LH8M8?cdP`wJ-Bg!mQ@l3*syK>dPS8&EPGH(cz&^)!cDD@UK7s z913RM9VGd_0Xd4d!tzk$I7AvOzZ;*+?7m9yM7d!G8Fj97c4heX9I#}$KM%&MBeWe` z(n^+I?VMN!!``MU#i)M4kN9K$ZP7)_#{*Svzo%eojVdv7S~s$k$clZ|QFa4B7Zd zS1~X;EDF{HHC5`-cc?i8{R16&0kHfZ(8YftiT`33j7)6*mt8RbcXsg~C)odsU1aG< zL{hgk{nP))TM4BI2}#Qn8>^~dby|G{pbvAk{7HMkYi0ZXShg3%6(OHs)@dOW%wvCd zb~Y_5W^;XeiS^Yfo=Ecf`|>uW*L=F&JYm=K{W@_&`=ggr^yBl9^Cvck@0f%~b0rPy z=kw#}Wya0n`~KmNuea-kjb~2EhKrBuH$Z1+3*c1I&g8vWJ#o)F_4LQ};bwt|C+tY| z`y~6_o|eZa=XrChpc5b`Q&Ta5p4ZpaSWIfw-lni4b>88{d_LoSl*(3O_x?$gC zwxexVB!ShN1d-_LqGWNF;`$}PcEo$GM&*5d@Z6H)`+4{DFhU0xq!ec3C>k)zOlKC-a@`ZYmwX)=F)uFm)?SzzJymwtNSrXXCrYc(cM zYpVJ%WIJV)8(m}th@MY{H+o8@qEqbDugx|{?X7ku^?BYGjNC*=%wkW{c@Y7PWP)tF zC5~#Q7?1+#5{&qd@lFsha61T(@piOT;0xNna(vj!GD3hnTKv?q9yQsm3q)Y>jHqNk z$KShR>Et;49ZR8=5jq~xtckD{-W0#>6~TDtB||3WM!kFZG-5b z2O0F54XVdGCC+3VU{ICO&HsM{H|y@6V(_wLH{| zl-6>*zz0ua*1%eGiD?IR2x`}hmQ6C}DUvj-roxBl$Ht(w${M#j(u-SZ3M`xk_$afl z52Q_GO`5H12B_>0Oje0^0FNG-_LHklTtH#}NkAe9!Ll#qJzRsZTF0H(UUlwOpL#p+ zDb1fTn{wvPU_r0^mY#S&j_VUBu8*D&d_U(W?!YznVlZxoY1jY*#St^@xf0oh5urG-rk2L|S5U>EdU~bBNFSn1w zL-dA+J-%mx5}nES$0JvPAkFwqeFrZ+gCN1-wzL@@+u*o1I8v@?=JXJm1N^K)msnp? z5{*3C$lU*ea{;9BHN!d_*S#K^oBBB?B?1eWZ3B%>D$^KmtLB#3}6 zR#Vy*vFJU!`hMIN8jeckq2$OPp}~qOTKju8=T&>@PYZjDpI+Bo{hkD2W?SIdJAUds z;YR$rVeJy8+=beyq5>>Sw7Vk~*S<(H-;Z9+!G_ zZx&kxzGmRRMP4oq_9pzX$_Z-oO&+rv>(Wjp8hOEdyGX5%Kw|FSqvKG)QXQQS(yege z_)d?TXE_6=C<>`r^2J`tfAV6a$7|4aHjR+l3h*n?He8OMvw1|r0Ik8no2b67a%DP* zUz}nQ0i@$4-g1{y2$`n8w!%6(!=Ev>Io}Vl62Am-3u(8 zG9wi+FyG?P(2sE%Sq7!+&R44@FGs5AE$XHdHhIsj*5FSw%5xSbr^_+kQzNNibf*?=nippLE^nPeE0qd;~LXxoeT|do*2ifUJ}n{rmdJc(8dWB^AN5S zTP$(HI%NfvhA9H}vQMc!Boo!>D866Q`tIO2JJO|v$kq^Sq|39)LqG$F(GBA5RJM=P zfy7YzG(_r$$!MF^yYIqe!fJ&#EBaBV9W1oH5)q|{eH@XpazGUdcU?!D1eD96!PY@{ zjeiRDm2V+~OYIIWcxSz*@#3_q|( zet5_0M9y>pI~GSMg6mKqL-KtJ(Oik3)%YHQ5z6ULv-eV!Lr#7|kTpoXe;c6FAb-sp znci~IZ*&iMBXm2r6}a(A_l|TlG`~LMjZaW@S#R0)*lj*pOirRV)HMzu)UQbOTPKk` z;_8cv(^kwG&)W*Q_Rum`EHvPDN{|LQ@6W?5Z`MCyN^M*15&w&)oW|p*70_;?j}*E# zVmJA-jB10~`w7CO-rQnu-u)TE8b8~9LHs@C11HPIA(IM3S=$a$nJd4LjI$P;Hu7f3 zFz`plp?2*>;#eiJXzJmZVj#r7Y;tp`6gj?R>q#czV!lZN2W)BL1Gfoy8)%#flB$Hc z2AKBUzC*gLO2<3oJlTnbEi3YqJ zI%&I+wY<;hnc-zr1zM%@;U2UG*Hpc0aj-+-TY8A(Ks$uc+dAhJo{@;0?5|>~Hbucs zZj%!&kc&I1y8Ff1vUDu_31L1GHFW!3IFk{e%ZbK#a>ot;ZsG_KCQ0y&KMQ-bpN?roljXgo5Hh^7Jo4Rr+(fCV7V?uK z0@ogYkIZMM=rghsvQWiJA8qv$DbD#xW$X@?XZ7D1_K~Sx`hFY0@{4@DhJqtF^*Tec z*{JGF8+Y$p{$RVPJmZGoh+>fQLOSkA0Lwl>m}2|hIe4aKGJz>*AU2FvIb~n(bL!)r zh|YkS3nGI}t@LBi74mm4n{isMuaX+qku_UGYdq$Rdp{7{KJ*I1ee|f_*soNDWkHhG zO~7|wBNDu?0gJ^7P8v(3^Tk4B*J{)4B9{~@b5U&msu@PtX}Vd7G$%OG&R-;7@wa)x zJoK!X4E~_lTkyN#fpy}e^@v~7US{TVIC;qG1iePm7Ww5Q42UgJjlO-Xzeu!N zD!#2?19z&aBs_=I;`Sok+h($R`GDx9tF;QVi%5?L{sZ%%my((<))4PiXHcmDfnvv)~@Hex|3-pC@i9cv4^vg+>ooi zEMTiH^&$Ph$6n=Sq$B;Eo+@+6I=`#x2hmtq?C>Mhw^oMJ^WI_GGoC_6{rs~*M}oQ! zPdZX$bzFxxOOJN@v7GOWOZXw&u6kKO+}3Qy5f@6#c{ql8qWx);5=UbqWau+r4pIMw z%BAYEBRWT$4N3mKK9h#;$1$<{tYPS#J?WCH8gQmLwO?46mpQfuJFx?6kwur{4ikfm z*v1~&AknJB<7U;Ll1N3kQJ~on!4i_pLDVU)6zr1Q-uJa1HUlYfhZ5Yu>}B@X8Ni-m zRfe2aXWpbdnR3Y+=OTNzmX3H%iz~akeK8~SQJWxg#DCN z*3QEAWpW`wjIuS1>Yu%w3|n?z}l8DgYFB=Q9GxquE{~l5A|W5*aUaJY?8y*vSC|n`5psz&;Fd3+ zdj4ALSW=Qxv&+`oR{<_WmD9l=(~2s@u!GrVDQY~Z|J3>gi+04_P-M&`wI|vaJ!ZPo z(@$(P765_`cvw!#@FW^~G(zy^-%d;|RUAA@al*-Q`Fb${eBlGg&|5xzVwQm*Ht9)G zP?-Z3yO_XOFH5#>;6!&Z(xh;F2nKHR&i3^~M9}3L?~$(a-X^HitvO%qP|&-Cb>C$q z$Z8!oN`+w}5cVfwdtF;n)Qo<%eU9}8#kF<}Wr^hGeV3HF6w^WGQ6%vMYQ&H5P5h)v zBNBeZ4mogDfw{Fq4x{)|W;qqB6vXHmcHFfY!$CMve4Iu?AJ(;P>K|&G=9*ImaPN7m z0p^r8?o6rlW2Z_BFskVz(CNCX=kn;;A{Jp6o3_qRIVB>I-%7ar_GpSTP;H1Mm&5l| zjEitiFY+mF7l#p5sZ;H?;Gn6*5>mXxSB-T991)RCVX#QB08lGCvOp*pTgs8HiX?qJ zd1u65ae2KD9^Oly$i!jK3S&IusoBOp8-^OFjTSA31%oVk= zP%{yHo7s?lSZ*PHvRk+nkZ9B}eA?TMd_?HP^7VM=Enbr4Y&ziGR3Irgty4}1Bb9N3 zQj;}+jbtb;srl^wnMwdkA^k3u*;5rh9;_^p@c>@cdA*2-V<=4O=xD%Fm6{aRVI5E+ zIw|&qCI_lpt)Sd|c}@C(D~$I2M>CFs%2{=Rik&~EU|ag`s)EXbyF_($oT%?* zwyfsE%#q5YU>*KCimRIXDKnh4ZP6drY-o#^iVIN-C7~d ztDx}c1dgLQ2}4#^;W?{Hy9)E2jj||2p$X533A&nMf1Ht%0 zp8%HQvI}Y;43*SN3rFa+uhGWaj9}Cf!8>x@BO2z?jRS%q zU`nn!*&-;BgEwmFodf3{&x?agiMs*YKwCdN>ZZ}ZPxR<5J)sCU?1{}6NonAuM*z|U0MD1UAjU}wur*AiL7&WSY-Y1XbwiXvm($IG$gZT4CyPl05gm;!A+@6}Im$~qUs$yoMLWb8 zzv?~`y==R4-2+m3U)qX`@caYwx0&T&VqTV;wT1hd+K!>e59FgfhI0h+A;B46o2k>_Nvp+zCh942N* z{ln3kIz?L)T|{=oO4DtwTJdUj_u7M9YygFBHcs@auT>!xST80RTVPDV)@fCOM{RV< zVon{FTzXuX*i#B58mH^)WvUDBA&_|BtLS`(&2;uqO^C)Gq<&1baLs=&eYz}3%Ay;q zUR3)?S$Nr{=ImtfuzDSKJY8pXYZ3)#0zBZ>u5st^@M5`~E}X44So#lUSr@TiD~Q{z zU6ovq*H(@XZ=cy7DeiYW_6IEKBq&nI9ca>04#dfuZQhPJF+M*-LOc+NSmGi0A?8j@ zbTAA3xENSO3dIXF_is|xm4sdol;>4ADB5lAMVhU_8(Tw+{0M%rE2st@?%YdO6m-Al z5mout_CYasLlwzV;%YR>oL+>ZHOd%Yu?g0jdV7!+g9!tIN62y0ws*L+8)LFw(#Lx! z6ffGUNEv>-;~qQooQO{FMi`t{$C=jl?|hzuHv#WM0CwTI z%#w#BX$`z9yISl`3&?UcGn|1Mxd`fnvm$kc1h9PH~qBcov+dL!E|$Mo$q?cThupnWmgCP7IHX=8_Dc2LI(AlnXe9P9P=DHYQ-2WFQq{#+Rt!-I^CBJ7 zxUO=p5f0G>1`>r@aH-j2WpPH7mhyfI&Lh)6vJjjQ;c^64(te<*08;9*Ea|y&pYV(* zb7A@NQQ<3|B_p@MO zV`crnMQ*nLUgZ9d!RY^|$W6oL_@5rPRzk^2iZSvy>?I+=u5joc4rf^%aJm?q*gxJ< z455Y6S(bR6<>QM@Sot*BIN5%F@acVjdHO)#9lh{VZP<7tyxhMuZVLKvrZ(#>z*J3aSlsy%Un1Mqy#0JM1`*BJ&(QVLNZ%}+T(P8Z zq@In++`RoDV0d=~V|pC@p%;v3>G}GO)zj(xOnIYeyK#@*aWCrm=~y`m z+oJV-^&B6R;v5(2rgefanTj27@Z8!t2w_h$@jcQzn<&1Yy7k?d>PyuV@pj4i-Mt5_ zmVpit$-dla5$e~G@RJIZ_XZp5mUMFtln~k;V3fU`;u}xM%ZPIUV-=i}1A>5`8tP7y zlbS>Ow>cp1RLdVqvITW7Fhqcr$X4PHnH+(8p{IMp#i+PeEU@4dWGrID#fB;}EUi9- zY3~j&lQvOhARgVD=N;L#ixgKdC(0cN>#iN7C;1c#UI(GyYT$VW&@xezAo4P3 z!?wcxUV8~b0y2~0wHH+1fh}k-T7q6aGe0vFO(1qh)E`M!egOz5KnvnC<-n z=1vNx>Q)k{MUXvZhlm!IfzNWON&FvXnpnfbV%*I;TXjfEVn8j&jy0H*E*7vwvqgKT z-PW6@fzBb^u3a-&5{BPXiw4Q?u(gnuHqa~ce%>c>l_aIUNmHf_f(`O}{gpJW)yaJM z1n)w_=K?-+0CTV?YX=Dq6sn;nvS=SWq2GvtG3P?ls(B4T?sgDBj~oW(T3GJ3^%K{k zqW`=FMdI1Om>WS|6R)ig^qD4s8FaLsAyq7DJ?4r^m*x|Nb%Lh`x(V1K$a2-!`l~wu zC@oydz-72O>lInm_NCB9eiDO?`=3Nwps}(OzU*>`jR6H-s$7*KTO|(DMNB~@iJw18 z?Kp4jk(u!k#bwa`JZ;7*UWB!@86Gl53!ig43`+dmUrE`b=F=VRd`KOEi0V#e}=%@A&e#9O~yB1AC#@pWiBKP9B`nHgiQ?#RxV~ZldaQ7%jM8Jzb@FnVYfCFPmCLk*Qgm4)}OrQm-w_;m?dTZd?mK&?UmO+=B47VnKmnsyvvT&oNzaI%i?)jq%KJgAQ z$9Hsot~1X3FCTA%@^lg8M8(x>bk|k(7k8E?NeuZ;RFkY|{`G=2x`1dj1!$}-$Gaku z8W0wV(vE`lB@uQ{f8+?OXHIxy9h#TG;*^5uiK?u!@UW6c>Hm_dfIrXTHJ6GMKle-< zCLc82)J_UW^E{#mF4mSRc3VLVVe-Y!^2RUNWPt(V3>YM9gxHuJg%VC7iUQ={is;(1 ze6Xy-0f=f#W>0g~NeAAHpc%VLV%=%@PLlX2j`bvPI53;8?-n3DdVkP4yry#0UalMrP#3AmCmqsbmeEgH($QOvaq?q0cF2bEoo6xYOLBBmn-%n ze5)1{M-&yUyN!vlKv20WOvm*10vpuW{*4AT9QJD>!S<*ib4Vba6-{O{E1$d`CTXQn zC9)R-{;Dn*)!p*Az6@(6Sc$^LM-S!a9LdthL}}!y9Kf0h{$|@$a5+$GNf8{t`TD1n z;3jC|HNh&d!4Z=Kn4Hd z@B`W$W!Wd+5X|@_*kR zh?Lz=;(lUkE4}QsLthwi@z$Ct?vcf`A^S>UJkkCfMM`Ju5;Mj##C~Twgcy|AQ$z|I zyeqrqv+6Z8n^F7wyE#D8jI(5TT18dd3^b`Jsq5kHGvG1RKqLjzWXUCp?dI03=;R5z zOSynw?s0jF$1Ap8U?Tywcj#W|H;r4@&=g`IjqJ!A<&_Esu+mW;JY%47iE6KOMYJwu`ygKOsIJSU&P3frgtWFbDjj=n@Iut%>@hSGjBzKA( z3CsGhav>QJ1|0UFtep}t%|Ijc8f6xOy%!x;QkL3CXnG-s%Y2ttJ))!uiaM{1&Q{9PC93+Yg$BM5X}}Wlv5yW%%=PD42DW zK`sQ3hOv4C~ZSrB&M}m7DRFAT(r-3nRjbiXULZH4seWA7L$Fa@7b<-P~RPFQX|f3 zfsKKow`(y0x^|IprV#U}K<;A{fD{2z7%YukXiv_Fax^KCu5j2VvF8<0RX~) zPyr>*y`T9LO+xjsKTL zHx=Ms2X)7hpGd3ju7Qf};A9G&1-5|-=bJVfu1T_dSbci3$d*8Dj&0j@qjPL`6`Wh= z*IWRLU`yh|IELzZQE!W4F|bd}xV~crepNJYGr;N|oT__*R54Z^V*Olwv1nnE2P3ylE;P~FLI$G1f0tEAem-UIkCKmP+H(H`{(V9$eE_O zs5-z=P5o>v1v7(28xn>Ryv)d<5~^trPV%->57Xb&T1CRG+^l*sx^@9QC>1CeGnfsg z+H*3zU$^4Uu$jp^@uDxSN#*Ry93)~=FB~JetLghE$952123r)tj8cmnnuC*2u zpvBb?wCGPaq!=rz%;T$BZ<|y#J4X0)D86?011;CSHyKV^+&?kG24uwCJhdI%AB~+> zrSUX~3#CyzEj&hbt-aqahoQ83f^`_qkT_fku70LZLNp9!KwA1KidnAqGSul+GErKF zZs^Z=;;(8e`Yr26P(fQnjmX$l^4agHA`zq7)=RRwZ|t#WW>lk#WzuCBhtU=2iy?5l zLUZgC1PmmhPsz+^<--=w`HHPyi>syAIAm zX7g;3@nO_H^dv*c8qv5jd|SGm)I%vy#*alw5sJQO%zFoX+f7=`aXWaYt6%c$i^b+=TxY0%Zz(iDl0 z_;~vlq4qLsj$m8ei43wXtvcRV7rSWe$VFJHnLJ^xF1C|!jXH`VG6kjCZmXPT`T%%3 z>(6r8PjRT6hOA0Qf|UEcZst>cMFr3#ffiD;uJ&M2)fJB=nEBuE!9w%gD_GgNMqSHTRf$pct``j5`q*m#V^(j2 z)Y0QF%gr2|@YPWEm;2tL4ejO=5B}0hS%8~+ z?NO%1@7oINN7s(j8WGi2na2?e7hm174EQ`g$ zk+{~N<*5Wi_7GQ^nVBL{1htc3o3GsxWE)@{!RLu^)_M^>2d7 z^_44yopMy;XJl-*&^g-`gS#Cgzs5su0`~Yj?gkVfL7pKptU;QSCUe!AeJRB}6AvV> zW7KEzhC9;nF31#qH<_#tgUd$BX4p7Rhed3p;?SaTDy=}m!(+3RkjPE<>Kd&PFb>r}x*M&C-)E3HaPlFl4_ybcNQgNS$I+;dY zkMd)BbZK944-&oo_i1*d+zsiO{Sq-6_GhdInOId8zA!I?GCkru(staglmOBn_74`dd zX`L~OMDv)04SnIjM9pmlJtk=!Ow_&{liqFd330R>z#PYu#T_*3rG3+z`pFHoCrk4? zCv%_57iCX}`w?*av2zG}qzh#B#%GYMgX{sv-Ng(ciSBv7`xA*4J`EzvsKvF~=JEt% z!Q$)fM_R#1nIq&bw2JxrIxALWcmkj zmY>h%WgVrCm`Q!6$)wpcRmRgTTPC-lI9NCFilAGj+L@CToeDs#;R$z5Sj|{k5O+RTs(DfYKy7R8p@5X*t-Ain=xGX zK9t_=Rzm4Wt&X$S=%lV_uS2DD>bZuKF8Y!COP21f(lg@dY2_uwz(7KyhybB1k(U>70ph zPNVf=vO2M)6A_EIp$BCEKBa9T>x*F(e^sca#^aoZ2$Mt7$xi&xLFhDP4LO?Hv5ZVB zC?uU$%Kw&WsL-egjqYzvZ_g;QjaMPPCi?hArve=S!>dN8)-vXMb(<)-f7JSIoe0FO zW)gf0Jr{6VgM>}QMPKvALjBd8l3T5f9oD@tz2zFw+#@kApBSr&P9EA5>=Xx zs3QV_jfE^vAP89x;@e?OZj!+3GmU#A=eTR^zOL0JuOgb&V;(FvT|bgY@ieHc zE&2>8bVNuH|=a`P7uG=8>Z>&@T(C_ z)Z>C|-8sbZW$)$SoT&1@|_5wUCIgf_PA zFS7@(FSYMN>xYq6-=OdNe9r)-^Q?Gv#%x(&Ahl`vY`mYWaDaZsErA@c(AuXh9xmPc zC9n(JO^LeMJV5M9|D{awz%}T?XoFY3p?$d#QZ!Jp&ooU5s!>TE87!|H>;4Vq=Dmh4 zO4AQ97&$YI<;i!5ryN_4uh2cR$wB{=U{^6>-`byNW8g#~xD_L~(r;+Ix2@5vQBlsrbf?7Q z`!eL)A|@Fqis+}}x%B4nD0j=ALX2$(tLw_*J_lW4twZDTXIk1H2zT`0nE%w<{%;Hb zD<=c<|L$#PV)(y$+nN5EA?N?s+n%B>QFSDW*xRR9;M6bc_EHK?SpRt~Wcwo&H*~v^ zAN~h(+UM8uY81)OD>(3t%aYF2y57oG&aH#Kw!0uK*l%dgYy-?5-ngN-zFprxCa+{p zuWt`?TaWHANM(wT?XM4K_g6az=%HhKdgSf1!r%ZnyUTIT$36Qh=B-yjnYYn##2DAJj=XZiHFa$mbV~n)2%rXPSdW&aFUvw=lg6gU>ex58 zQ(p*}M7o;t<;~>>nN0eg6ELo2IjyHhK^8(r1ez!Rh`{i<^6;f2cZilo&pH+!E?hES zOpTPu7Fs}Rkm$co6Yr;{lW5PCy>|w(F$pOnX2+=EUu)Vft?@W3f}#Lxs9R`!8b#71 z$X!CGookZb7FrN~R(Gjz;mC%B`4a!Z(Ywj^scn_#{Pa)>OteCppb1;;9bb9XWEkyx z-ix*pwjP5*Cz2l@R`+op^_w=cg6+YMm&`suL4Au48%L+OzBW`cgYWGIQ*f%QGB*{K zn=(s+hh+Ue6YcLPU+547FU_6QaVgrAJE!&jSpKl?$xHAGke1v6|KExZ2_nJzlB{%K zN2qFz$(~oGF0l-Khv<(g`=GCyb@VQQJEuwf~BqwOK`gkYYe{& z3sZUz0%ZqLK-P{h^JgI`ZmX>0G+7L2i-Czd=S8u;e*+L%4HzL?nD32t3VR}#F`R(% zdLkh1p)9VDoRaSEiMeH&+zM>p1}%soDd+srwd0K$CYt#;y!C{24guSwzU4lNGela# zETPek(*bcw)m;73ki?IhbjzWFI$kU^+x%eKAt;&6!1h!2-fPF1oj!d(msb+elR#`M`mYh9B+9@Y9#{@A6_JrxJqnn#xY?zG%S zs+O{G2r=P15qL!_D}WX(?%7swqw0WL^bt zymPw%GUbEP<`p?9!UmYO-Jovj=xev-=e}<#%Tc*`TgXY-ZRhMH^bWSb0}wZNBg8Nx zZ^@zH6!=k=uZ_C=3c@@)XwI?i*Ps)@i?``~ZYlXE@ww;DUh)X2$wP!>?{{y?A#8K0 zvs&?ANBjWIeWaVgu4d{%T5)SGrze^qSGwKJd?{LToB8frYZJB%tyWFSKP0(Wbh3sd z4{KR(wLKVBR5cUb(Kyw`{~x@`znsKB2}+Et|BW=m#QcAI zlYerS{*yPMmo~9Ab2eurVB}=s;NydKa&|N^uz_~ZK2e^s!x6#!V|vq)bZSCwE50#5 z>zWk5Y>~50F$b5-c2+K*1%wP8Giy-QbA$Onj0L3fCk=G=TvlgvX^K8~|34XK|(H zwciIqP6>~fHvug~Zqq$4LB~YiZkb16~c;Qc>vu`kU5R{ z21Mw8BnGsYuB}&M_@WMX_*34?fG}@z`-+Aa2vPIFtaK3vM)XY+!vBL-`;X-U%c-A7 z@+Al&@wgm);r;2aD&6C#@hx=bsEW&QaChI#7O+3;|a{PM+-G-TmJ5V zY}1(Nj(6)7obkQ^Uhnw1nrd);4~%~9(X}Tq<~?L@XBZD`4@^JM>l-5xejc_zd2u;! zI}o*v?yB8T_yb+`Frf!duZWKIuu$zlJM##H=K*W^c)7s>;lLqNh#^^p`2GQt`INJ= zGM3iPI%fIVHU_!*uA*%%GC8|O#gyB~hQcL%4V(JmP?kIeJ=N!q;T_8*=#zy>nbU=y z;TxsoD@ub z%sNf$rjqkqDnz)tW__++%K7p^PdY;ezQM#z}p}Ch4hsTpsd?Mmh~PDP>GK z_u@wGyyGNB!x%}w)~N~=3-%{)Ggis46hfm6OK0C&& z0S#2z20ps_@(HN-<*kg_WG!QjLKB!c;2cqV_NCcf=T6tPN(uwHv%n+~` zid4ZGBq_#rp^=b2!RfI-p`zPuc^LfyM@C@kpCy=67znC^I$6pzgPvjdTj$+|9#}cc z;Kg#o7 zt}MRH@`c-X&(gkb8ub9E=dw6&#%2oH-yc%~ztQjO{F1lP=sItOcyY(BqQx3Q5DegU zrgcnNwO&zuNFWmcj*l0yX76&pU-%ZhS@vJzpE8E2^GukHj*J`}e{Z|fc^MhqcQaE9;)S=Wf_;9PwbtX)e{{D)4*qE%|B5Dx zq_$a@1B0E7`S*W#X2Dt~7{PJkr<{%oy&qU=G)M%xiLxNcj+-`~YBC zPgv@(R@4>u5S|RtpA-Tn-EC=rZn=*0Zy_a!sC7A8?)9K|RkOyZU!tBw&V~9UOQ*uS z)3P?kr;ZoF8Jbc!AwCB9GwC5ICEBUKLO+IlKA4;jd9Mc*JqZJ6r~}PGp!M_rl7t-q zXEf3S;Kus!SvuHRIZ%}9LdN+g2IyfH%;l*1U@&tB&~q50LC`{b1*_^cysk>>cZ5gq z8$MYHzX@k6$y$gA*Io`0@3^MJ;8^4N?M3{cyN0SuoA?p;^*)u{hX0eb`*-3cBO@!z zf3=3;eTC%h{XQDtXxaP2z#3 z7qvDpb0T2*yYaVOpNq@i#wbAeR^vHXA@x)BRgZ0|8AXi z;sxY}2@pf>J|j7A0c_3LS!AX&WW49tRX_a)n-kXqq%R8o>{6=A@bEvn-vzOsTm4>F z7=I5bx?&u{9A~C;FbQMWM-^WI_qKnNH*sr{k%ZZ@lN#+PVq}Y1tUIL5VCUd{<=EG_ z;PyJ^N6Fr;>pAz8xj->1hE1{5y62eWm1E=G!g48~%faA2z9Aad+qaQ|EIohkii`rJ z8RIpUK&dFH(6Y2?RmFe)ydwx*@KH}Srj<~_QfeH#ki9YbY6fm&l1gIos;K`7fb{Dl zA8A%H@d5UYvhny&!vD9yNdH@6dL%YDq0;YfE_CnJu+o{-E{G$WGzZCmF zHT-u}_un-9f6?9k%j0seaQq)GcgM>{A7`ZHJbOb&!b)RRvhkC2N>k%I8Fi|`dQ7j8 zErNC`eb~{Eyfw&(`+GL*W?eE6MG`hRo&y*{9FCBGk*zqmj)IV+Aog2`}eI^-|l3W*XeWyv)ODmr<18E9`5FRNZ|s7Vf=DK ztJPcMN0n}(xAgm5bssv_T3*a!c#j)%8uL?;+;MQ_ECaj zG7(3F7uOcIT4j7kB4y8T0%$KN!=>dL$;5B};w8^DFgw|$Z(jsK6Ost{TD)^}y+~{R zMFK*~-*~ttx40d5KZJ z{Jb2@+L_P2wr$44MUT%|F|+~=7s!H3f0k5G&sCiKu`7`e(IcOaI;Y@rmsy2(ruN`- zfx9JrmOGcg+KjvXhHsrKA}5;HZARF6LAX#Y;)culTZoG&C=Se8 z?j%e6;Pd?2(ipfqaB;@>%$DB`lTA4DHT2NBH#_I+27Nj5GqDwFE6OR3fyhUQL3w9$ zhE&Z@0mbQ6Eu>?y$6I1-hTsilOH$zQa%hwJL%!(!WkmEQJ7@;-Qt+ad;*Q%}Og}1U z><7+Eeq@_9q5Ph)@=7wAc1IrJrGU_}b-+t>wlnRmJaDlODX3_oWF+*pNa_->ITB@C z-BGZqcT!cY-mc$!O#3@IB^4E8hMFcl>uNINXnJy5T&}4^lNPFhO8}=xLF|F5B+v*8 zc3i{asU@Nyx)m|Y;OXORzPTocV6#rJIYc<{QAY{|dCQpTK~pk9-c?VGGK8{3LlSYA zNttboQgw59J`0WQfrVv_75ViRS}O^&lVB*yIDIA_1v{}`Qv#}ish~{|Rv=3q>(S;K zugbcL;4`+O2z?XXRbjVZV2cKbDqcQ6%eiI<_U_0K z7Sv!!62-jGRzP80ssY*C^SMjoR zMy@YeZlbXfqCSO8SmG>Q4I8Q@NmC+`MN zYwo!k^QfQ%MjEU2m!MJPSl!UXZEV5pl9;ec%a&P9SP|iDpH$OO6@iVtApgrlF-N7A zF&JZ&eU+wVWto*7T?7=$Oy1Sn^Tfs4I;NBX$Iy{brBl*+n5;Ac5gL^69a3LQUNo1` zq%(3GM4>&pq_i_%+8Pukvw+MnxN&5%HWCs~MsFy&=(KtM=a7s*Qt+H$-^8XdoSsv5 zx^YxOqy!Y2N~IrJlmvOM|2~5OaZx!PO8HDwOe>vMQjHp{M1bffTO~ka3dv-gEACn4 zi?yOES!6l5n#7AtAO-7@Q(BFhU757ZEb05IvC6D*gfXSY^a9hwHi|)5`0*04kc}ccEiaaPi+T4RD4ljv>E7j|w9{KfN1UGY85s}dj}E2l;&(?$ zgzB~Ph*N{8d2z7)BKkl@HEdYhe z#!n_)h+Ha~YP=}PiU6q|`2_dJF%@^Mu?ogxEM+^wWGr*K|2d}60-7Jois<1agmYGO%~?03RN+LxBMyqCDEC z^)Y-x?G!(v=!Xv;+yjHaFhjwpvwI**06=}n?!fIq?vegJ?_Td=(lb{d&zmykh#G)@ zSdd&tNFt4m(DnKid<_?P*81pg^`;6qd(1y6IHUc>jT1sGU3bXs86E=urjUEMCP1j+ z5$91q&r3suy@wDV7151wPj^cQQ>X~p8_GM$00w~Yw7U~vnR#$b4gmn*uQfdA?g?qH zKy$KB_PJqc6hmx^=LB<}9*8E4$*^Oi{LBqVCe!(k`Ao&e!YAd()N*oilAEu_eTpQb zQR}L)z`T#1^v3~JC(%VFJwFnWgGJJQPFv}0Aos2=S&8?miYi_W*-Ke*(3WWuFE=GK zlb)Rh&XzWztH-=v&y^-_++2(4Wvs^md^xO@rpkVrpsbHjV3icHD#|w5w7{{KRm%>* zNnNT4%cGX`-wvEZp_IOk8zWKZ*CO&Bk(Vk%nurbG$_JUh$64nt`ZkimP< zyVIXX>0mgI(AihJwP=q^A@iAQ~5+Hl_4-+G&?Nj+Xte$TxZ2W-iL<4G#QRqJN{-Jfr2#y{_GZAs$%{^RL1xSkZ3Znxek*?NOloWL#qK~t_hi5%S_YR#fqMB6LT`Hd`3 z#}!9MC>w&@1nclUY~GW|v z1ZOA_+vPXV=shR{Y(2P`L_Po zxPI!VLwNQO<#7*zsN~(hPxib|1H2CZxbxVDL+Ex7frON3En5$g0qpbMPs#Dhhon`&tW52Jpf5ny$%P(dFnpuC-$v4$+T|% zK#);lTV%<80y1z)Zrr#V(5hgt;%Q&~Q34Q8l8~@z68@m9b3nL#=8u8sPNp^IMMr=~ zv3a6XSFT!B1gKe4O;jyeDZ-?{?-T=aWB|Z=9g?ap{LG464 zZ^u*Sm-J{$$k5S1iz7NU!sB5_$M`}0;sX)@vGpn}@>4Qn0t$tZV%6oS`!MTtD;F*f ze_EfcpuALPlPPzaBpHmWgfz=mnww83B(Fk;caNp)z{@4=&NQ`jm1bJSarRI#QAc05 zC6q6ReP_#K1Fh6)d9U;Nac8*ihUCZ0P)@?xw1LDB#YDLw7+ac&GL4I#t5IN{iHi4n zM&1$Ou-xM-5JJ!=MI3G8pFR?^`(*93%-gz@GX&Ww>;=U`;qbaAkcVwGmMUURhiYpF zz45FpD++rYtQ@Vjr{M^c?AK)_zn^od-Y-;{4>Hqwu1W9UXfMD$Q}}hp(UDb<0XQE{Bq1Bq{V7H&(qpd{14SgxkPsrzr-DtvoFFU;L9e zgtkB8gzut9Iil8Hi`q%xxsly-@}$_vj-H=U^OA=UP;F?j=?x>%V@Fl7ESQaACVYz) zv`h^ACv-SE5b?DkVNQM}NsMWxp{Z$)JxScIs@spmjvcP6+Ym^MJ3t`j1dMYK#(MGI zz%t@WvIpS=0_VuH#pp%$HA}cJiI>sk*3qo&D7J&EqJw1{yUl3H}fAu`jyh*V!CO2 zwKEb1Xr~hy9P24ttNK4rfig2-H4Sny)f*igU`-`3YN_eX88ACs7BhxpFk_{TATBh_ z<%$`nD`=-{X_0OoYG5##>hmY8YV55FSy;qzjvKiU#onQ^MrLNFjZ)z%1y9?g@Oek$ zAYO?>5FD7tqMX$yG`PLPzynUoK4g|7xYA)w7ExMNOFIm)cVBsXq zP5W(iZAQ0_1^|6r04J&3#@hmJ-zhPS)-#APS_;-r>DInDmyQOp~S9L+9tki#@Bu+oV@HzU(b!mB7gWV zfMaEDRL%MB#5ZsK@9WhXyPxWJ>fNT_hnsjgBUj&0g$O+03iQq)xpY%n2Tdrv8m06G z+-D@{9U_{!Xg_=yl{KVO!QWYkGjp&_F<`32Lx(kgq5m@YMVU%kVg84IdzAwhqb9-# z6u%Dp49Ee5+!1+VIB@~GGFD*#X?FVgh)AlXQg9nSTKrFi9FWM`u z)iR|I+W8IW?Wb+gwgq%XE9*I1iublV8|wBqj6Qp>q|;0l%Nf0gy1UowCT==*nqQTu z20vdYF0QN_`NV~}N#vBg^>}Oz-!Zp?KNxCJ^O>NdXA+AM&Jiv%w7&pfq`b_vKl6W9 znmv~)On@d@EIO|`?+$00&y|jxe^#Dq)~4X>r#svNh3!GJ%N=5pn#nU{stSKBUzP4g zZ33`58=tTXcVQ-cCKDFa`C*wBlbA4r9zF(|;g`)Ru-^LF@9gVyw_|ss&cBH!_j#AMfm}R#C;8oj*=p3T^%4#pSHAB$;sf2S z0PI#Ih6oT~ptn>)m5mLYp}|zwN8UGG2SxyCNbgEUpuh^Df=;HHdIG`HODk54J#z=I z2(fl)z;3!!R}6YLdcm+fMPrpNN2rpk%&@VPFK1rnb7}MJ$~@F1MeUZ9QGV7A9H|?H zJt;oL+Rrcq7h}Y3bW9CA`##<)50HhUS>g`?hh~j04d8&Q$Ib`F|84;vueR6zvBl!W z8pbmHlgRf52F|-Yz>W1j5?t1D9}y16C(|VJCKGDLV5Tb5O#)~_4iU)8yspRIa2lS2nITvM9Ey?A;28*v;<5geq@Yyr9)Ic5fVievcd5dlw)Qpa#WwlMm}@A&DwGCn z%}FG)o~F_#^Eb<@*4AUKHuV#<34h6h#k$8%Bab62&6M7`#ye1Q;?atXo-SR z3=WyLbz1m()j!yEA~|a#TQ$Q%aUD5Wqcq-P`l$jPo4vK3S*1=0h78KbWrU*a^Hq;m zF)+HcMMR2EdD?hb@?S0!RRPvK?I%A+xn8;|e(Qy9mEQWm84|p|RCpP1e>zu!a63#d zL%Ux;o~8NW<#wgX_)Th)I7NeXRIFKw-$;(kcvL;vGb;a}i+&U_$dxe`ThF1Xk{_jH zr)s0t2$E|oa`+PAUB!rwR6z%$0EhX7sxJjBjnQvB@CK4?zD(RgosTTCApERmcOAKUODq z5H^VZ4_ilfO{1lbi|8nvl{d^L$rN+X3?d9vd%cHB@{q~dftVf^)0YW69~EyK0Teg{ z!ms1gXWU+o*M2H7+23)d&V!i}JSahm3i4EVNN$lF>D{zgcF|jaFtKOu7k8p}dlu6o zqoRgP_6PNu436nT_2`sx$;~1#n@w|D?Ba<4xCp)35&^>$5wOa2{^WRMha(JsiokV* z6|j|IG$|n9CB$NXb7C-RGm!RhWc8=LKaJn7sc-iSHDzq5J?R=vt%Z*I4y{fKdLAeH zhTO6l@Aj8-XkB}Nv5wxIzFnWI0+oynys*~o_w~x^X_c^wIC?T4-^3b$r}-#FAqF!o zcmRVfYb;I%t3$jlq$>*MlR$3c3S}v3wyMmj@y`e$Cph=GgM30`eb|wr%S#GM#u0q9 zljpTetr>NeppF{%M$9;Lp$48Rv(2wvL&V5|{o}?TM3OCpzUlg`Pgxl z0yU@MZ0E&v+Tq>dB|$ub;+AZYSv&FsPTb=t-Z1eN{O~KMCH>JJ#>h?+^u;hpOnqC5 zXv$U(l(a#5ZY!TLNcpg9rmjI{JUN|t-k=A~>TdLy-+gPjjci8QY~omEk7Sq%xZnjYdmA{u`zbo8BR=JCfTK%Qszpc@Ll zozTF&M6ey>!7`*5_lnrGS;#$_@Oo{v0r1DAvi8=Ql^YLp0TNH%)ADO99d_Xi=-kO^hK`Ay8rAvMU+`l`f>BNqk0=E}rt83iGpr;8*Os z(94;nNG&!)4t@!rNwQvzvG+mBHff`;5iNEd_>x$aspgO+tATx0DD|qiRNC8C_!?h% z0U(cC3`6FC1xJ@xB0;({@-PK6+)mAcQ+U;@jwJP0;ZN}|loNRc_%JC0FGA}q7zE=1B*t11JQvVkZvdtPTkw`tdn* zE{2PC>jbwcQ!!Ix{mEF|+KicFV5-uDhY4-0JZ%Bv9}GN#L$NWi3cu`9yms@_-yJX8 zH0H*VowwI^?B(1V+|CzT7iF$Cy)|6Cbg?>*$cv&5{jinlt)~`M+*UWbt?r>8K~em(+1dU97xwj@UPv(xO4DZ}|-xYpQ2YYN(}` ztRzgo;X1Gx^(vy@Jqx7KiZNgu9ldQtDG-efl&? z-Waekx-m*e=P$fzRBk(h03CW%qk)Fii}Y6`;`k~=vitv%aIf>%4!xvp?TmsVsTAi~NZN;4crced+SrE(wM7>c)xi z*1xOs`rdq^fkftKB+E1Yn3oQTFdh6zM*XEETOars!0;Vlw~Xz}7<~KwKI^BHn_;<; zTLqS{TwdAf|3CDmn# z3q2wm8Z|{XW!)Fl{3gp3<|{aA#D$$eMUE-nxLHH0O2#-!-m!dS`I(44x&pDhWGR}% z3Ek^tfw9COiL&S@;XY8gD0qPKH_Ns}nkNS;D8YvXZbwWBl*R$vh-GosI+K&=tL=Mm zx{(=KTd!$5dbprjE>|o| zB56J-+S`R9%^)9)bsK*Ym^Zq3v;QCnRg^9{$|b6*ygGArh-zNTR_8+Jj_11P;(O(b z(G|w(`Pw;(Rz+{=-bd0OH0pWj?wd`bNI!L5gBW5BzAL6g5#~Yjmmhgr2Y;n*{oag} zQ$NT@vu)co8xf#x)-VWgi9kEk5@-f#y{vf^x;j&(Ndh$H)*EItE@3wtW-!cT+KueV zv_(IHW-!D)1_Lslb*O;Gn^Ba3UR@8dG%pwm$O=QvpMGmPWVy^oQdhsI(r7x>q_>P} zK6qS_Qu~g)$D;YM@9-?XLShMr69|GJiSCV$jZ0#IR$KRTS?(C+G&j;w6IAJsNG>jk zOD|Xy{m7Qsalt<^PLuE8QGikF>dkJlT-)j4U>g6PRjI2rtu$+s94uwaNPlA(ox%X?pT61)gN%z4#b*vpAEV z$up>sDImK9+mzmV`p`3c%ycAISX{b?v{7aj*^+uXrty0*8$lvjTY)TJ)1CZZsYX> zDei8|KkKmw8wP{$#I6+0AJ;psWDD?lTL&aFo2GkKV>`cMT(xO1B2tLiQ*`DhfkEqR z78k`sVitQZKiA(vk~oipp~#~-3bvqLWGS5AvpE)^^0*I-$h!}NlVsP=f^TFYCrL(R z!(B=_8wR2JJ`>S-FN&krd&L!YGtP&LkDp}KPp@5V-I$7lgU~?~kA=v)xrbiDWY?*g z0gi&?C86GCAt+_D^S2*^Q1sk>H^b!F_Ulkv+4TvM>lec?6qLi9%-QKKgZSBboDbZx z?kiBmw7(~kcV=T`d6qI+Bw=W5lAg1~t74~ym3yJcl@6-i4hJ(lKYH_d4stR^83oSR zGH-}y3>GOQvj0X8S@#VncP;=&!^Kd(4q!Bvw)qINERS^ie(n=8L7WuA808UEJjdQc z@3FUzB)pUr&7YF0upJCK-`_5Ai=2UgMJ$_SLt4~2jUS{GMBCH3$uI%*Y{%lL)iV%rc z+?*z_@xP#w>b*vBo>uc8FgS^~(t}wcjKFRsLygYkt!x?})zW!zi8k@(!EmMAGU^6_ z(oTKFiA>kN5@1JEOxj{EU!nS=t+3YPi18F&$r2NZi3ehp(O%E?Nxn7T=)5Q;a*IYi zrW^uLSOSO7Js6Qa80K}CQ3EID4qaN>2mK=n!g2&dA4JR_#J7Qtx0^z>sEzB93KTV? zfZty~c^EXaR2MWA*FA%zJTmnVKNm84g|4Qy;exX4G}>r@^R_wt_)yr_L}g zIO&J`MaMnM!TV*Wh|*wK1dXZ?pWaFjcPH&d566r~(pw)vGFB@3l{f-PcLYqlYXoXE zYo1Bkvqu$q`Q4EzRyI>{ee)4= zk^MM)`Q~Jwr4L8wz`Q!)mhSN4dI#)L4B%7=Y5BZSvwcKsu$HiieVuN;U0LP`I(pK4 z8}2QCK7&{cArODmk$`uPujY4@BlnQMvCrXUMO^@>Z>hrmPHD`a?JqJvi-39b${zs2 zgqlxag=m*st?xmPaV(cilt-o0gI~|r*x;2y?O)}3!K0tGla;szW&4+8pmm?Ohb{i_6aP@dB)+%u}G>vWrX zu?kuUuhe5{s+n>Pl~iSgR85D+2J;3hY3pPI`(5YPbx+j$Cd$;A31fFrHP_RX-}jN& zbk&vLd`{NZQ_uh$gzAP97e3r?8UOW`m}r+DlfprY`IJ5Mcws_XrWqAm!6!$Ijs833 zTeU;a{s>h?0yTk1C$glwbP0FnTv9891iML&2UOyG1tI;PCwLl zxd$EvSmsfrHb4db#MJ0-6>`ft|HkbrDmC4TSkW14-EpUiQa_Fv*sQ+ZEV!&J(1iGY z!%@oP@fd=W^M9DOBlx}_3<)dye$E=3+u^J`pLP2AebBe3qUpKTm&EwV@L;9M6k0Sd zDS>+N8<%L=en?KIP1% z3y;pty^Lb_#cQYV!xIY~FZASNz40gC!-5*rhjC9EQqNVy z?vgyxd&aUG35suV=$+V4^q9Q)0X{rXn=NY&3R{%lk<+GA=_iCRO&ctxp5w#T5}PT_ zKO-z=HFMDT8c~r8S-u6)ii>>H2CVv9LepR|vCef{jUll(N(zK|5z$UAF;4956qzEh ze47aU+F1<GXuY(X5-~xv~#ob-CmZ+UQ-UR7#|tEOl^7E{8dN$vg_@*zewIiCuq6c$SdxT2e}Sl3o+ z>6pm5)Iw>!u~C}RVD5r6$wzKhLw=7GjkM468IEl$lPNx6!M4LpR`|A*N&-`(&PsOY zW*(S#ntV!3+gC_&j<2qo5t=uMC#Qa-Y(#x0b_dO&)N@l1|N`ADWou zuS6_c$y5#jrt`)mq3<1n7v43Q$-)AS9aki}PG)AZp`32>?tGjPN~lit2^uhS7)OU0+OF;bv{iglebXJ2(;To0Uc^>E<2{y?y`LownoL1*N|r0vojhKFoFrek8U73` z3yFYp*XB3&WA9NTh1MIn7E~^>uD7ZC_pHe2nR7=BpNHNds0J*s(^j{L$KTn8yRI(A z6jORqTC?A?tmyV;b22(tI`b)%uv)=9zTxwLcgY`ooWxEKk#N+_&B)_tcXOE%0;^A|Admt zD*+8i-uSmJtIm#pFhZ9IUX;oz%y|zXmWH-|j?0{ag{lKF2B9nysK0gJk8BJH7=k?8 zBbMoYn<$AXD%es-*-Hw)ScvxDc`MB>B&Ws)O4CD9Oi z%uhxC8JIWfjsd>0vR(FBfDDncmAqc2U3#s`r16~m!%hKl4y#IB30E;&zPspEzJR2W z*^(jztzS4GnR5;yk!}^?(L!uhCg)p%HCuj2fc08ss2n@c(p*WX2b+ZTxQco?=hDVf zl?=Tq?Nx+zxDvQx0B4r7*i^$MQ<@Bg_loZK)Uc;i>7VXQ8j9sYu6%%UlO$FRlHnIL zS-+(KR=ya)fgC*E<3uZQ_r*nfBdmQp?z0iQ+v`%&>PtcpBC-X@*Cl54XvgbE+MQV> zUEL`CwK0?H&F6btmvN6xj^%J1GES5bOz~eJ^gbN`+o%93h%O-52iFG@T0lOUs$TF5 z>uX$s#L9bM_wrzB5+qP{R9ox2T+qP{x>G-C5X3jbD&H3)! z`$uI}?NwFTc~WaXdp~Qx^}~CKfwyfoRtvq|$5OOIGr|u_iD;>KJlwdLDqmkYwqd-~ zc$Rp*H2N92!Srwh^YY^LG%{bQDW3&@?9Z*eNi_y9X<$2C`EXO@z^lQ=_&tP!1uemDfqXYDIRWdmJ4K2f!|~%mRk4f`@F^6gW8?aX6S1y>Tj>LDB~r zT%wOx$p`Q$cX(Y94g!I$k#$he*nfe}I{Xau+A?9P^qJZ@@A0o4-(TF^ca?vTDQi5h z8ixPy9@Q@AwDmgl{`mT|(x9PBkXra@5n}wI3WsO-Jg--8^Y{t6)B4FV-kX&R@fbxK zj3qR7gJ2Tuw|g4N?VVyMaAhnSfbr|v>@#@_oG_+8*KvvWsh36}TqAq zwDHL~^q~&Kyshn(z16@#cuA%@0~Q9@JtV%2<;Me*^P}VRYLLBApf)QlBP(nS>2gDj zxW+v84<{&?`zKI;eHjkSe7TC zs=JaMo>(2b!`+}Y9eH`?v?7drBcE_b=~wVh7Z!M`a=YW8)41ro{KYU2*;@410`%1a z@xgW@*4Vy@+0?uHg8RZ3giE5?X7f?(GSI$VCHfZI6v>V=a(W z7Lplk0M343$6$#;%;YV9H-8xPYXM55h=6=UsAW=+o{Mx1ZsIIK-rr#42)|46mL_;w zW2}Xsmqj>e5pML;tOXYh&c7DBZ6Es*uYPB9GRC!%KxqMY?YG3IjAp>=&Dv|Tx|jR} z%ovfMK&(?smC=iL^05ulj6qU4_oyvyZ_;de#J#Y0{-P5;0+r;eWuY{SM!}wBEeF;< zS#0BvXb!bP{zE!a^Ts>!%g-YEqqiicwnufd)?9h|zQDaI2}a+}1NK32ZXlv}Jxwoc z0HsziVw%30F&}Dvg$rd|FA#4i168g&L)DMADXW%!dlfYrtBzj{DCRVcT9{of_xMF0 z$9;%nIG@{fCZ7PaI3Bi%TUk%4o9y0~Zv%JX4x5_CKd;2A-zM!VceXes`q*^PxpWGt zR<~5XWP0%Z7;xNGMbgf|2>sX;5L#l^2jv+3I|r~7OeQwx#4y-&(Ss}37}-_Hsyt9p zVz0hvG-_Sv&sXP%F{b>k)`(%*ym;&STn1|8?!5pg`& z?-?^aUe4#1AL5_LJhnxXJ$BC#njeRM!=1C!+w2z*%n2_CKNYD0zUt}#YNh&sXRX}6 zcibdaxDP!^T^ZV4C0q{VJ@sz?K4#ryfR5WU9?)uUTw zm|U-u-8DXYMNv)2%g3MlGl%!Y2puth!jiBmVV&jqP)&*Pm1<(&a(Ir#Mu5Zudycl- zC-H{3VZ&&fTlpYq>?Rv8IBKXf&xA2+xnct(S~y#$YH9?=Io$MT1yG>yeL2jU>-2+ z_|ULjB^fK``gS%-z&At()}UZAV8(;*MQpK z5`eL>3y*biiH14Pzt@pqe&0(7_~r`SR~{S;{!UBAoz4Ta`w8CBTfB2_R_@{6Y{tw|IVX9|nr|V#9Z6&X3W&Z^l{-<)Gr)Bt3E?+BpBG3{x{_k zJ7MKd`@`poLojk9$Bz(12^_CMh7kI7W*OVZ?<87?#~0h>b%1#-AB#sDe(gDSd1|zs z@qM>zwWdS|PSaWq^3J6R>KDbj+ny-VYNu)=+-0XjnEcalwpt|9W1nK z4!T!iV}nN+Q?I+I=oM{{;WX}vI`44k*mr%7B!f@br-XByq2cqY8!5t9^HIakUwX^h zb6Wq|F0WeV9z<<;-dkC4`c%=j{2HNIU`|L_q7l2*#@PsayBUZ|JJy!C;Zv&@9x4K z=cx6oo)@Tq;vy1~LSuTI#sTmW`Rd1!lARIjGVV_Jvy=dvEBqhVQJQXF`l8 znb)&>hW41ytCuskZQHc2XU^9P&DCXkPZbe8213>k6q#e_i_=Q!PG%go)PQC%$F4?J($w5g0*j0mM7f-XfwQ*krZ{ zi^aCP$ANzR&K7cU9vbUq$JBCxv;7{e5S@iTiM8Jkc#dR;wjuz%9hb)%MosD~bc?fZ zTbuco-X2f@32$QGh36d0QsUc>;FwQ{M_^|gz#+V)hI*ysmP}bB0czcsSp1`m!0{Tl z1>tg#M5C*E_mpPh3ZfRJOMQfNMa+pLOI{eugJL0;qUJ~ln36anKeL=5F5Oq3{sZ>h z=iMbL<~1(QA-4DTD63f!d2dCLv>dxT0w0WS7QX>NbniVtQ#{`6bUL{513p@Jqay|`Vfqb0LXW8g8u=HL7u};-hc*W| zXIX2tNA~6x)!^jl0pxxyzItW8&cIP0ww}Lzy;eAH*D|d6I(utXP8asj&&oOKI~t5d zP{?813g|F)$^pCgxFI}e@L`bDwd&ujM5l<4%cw~Uw6us}m8WLKPrp4jdi)NPd$eg- zVhnGi=?-d>#jxct_2o%k2lhawF4`{hL&`5;j)UL$fl!tX048LslhS!$SWo zG}-a%(+TQ?8KN*4tlwvY*A{-%)H-Jr!n^PUFu3nNPwI<@#m=j=m;sfN-cMymqEmd; zGuL#H93JQJ$GXI$L-lj4a>FB!F}^1+ZaaHH^`*{a{4&?!Eg!~BZrHUJiyQ{k7fl-;JaEjgX)ZoE>)T{`D9<3v19#LJ*$CUlhovM&ERTwVZH48Y3ch(;m zMLk>7S#MkHr~Tjhb$BV^MXEbHD2NyIdC^5~i+5aPzq|jqCkPunHGCZ4`xr@{&~ECM zX08aTX{2ruuT>v!q!fAqi|f$K337WhsL|E-_SA#0p!_VYlqckHK6EKLcEy(v6Lsd~ zM^4PqFl0GDLq`vBedXjYHYLzY8bXO{jp^C=sNAt4HWA5bXZX#U_12H7xwzSoJJF@5 zD7}=sF8~i^!78ZKhZ1z->b#=VuLm+ z&`_y%4hWMnG-%p?+aI*^LWo`FZ6%W#GFLnNW+J9EV4En&(P^c<3xOy}WgSgQPE4A65|&BV1?~i%LHc_jNB+}>i!EK({144;j@u#)@oO-B@V`V7H0JJc$$7dNOk@2c}zprNnMvM zKmsb0+TxMt5K+jA!{Nx z@X^5G02c@EZv`$HeaZiFqAQRsC87n<%vW0SMBsw-`T6)qb8 zHw@i14e#miBuuc<`Jwucoz~<@?V;_4A6jWX6lfZOvEKlJA*nZv&91%Ae~d~YbkOAR zdw>azlFPYZiWI-tChRsDi$_vCYb$V1%2LMeVFKO#cxx6*`A~h>>)zI_n@T@mcs(sa zoeEIj%kvo8!mdCYhSId?IVNVc7lCdQ>X;7i!G)s`I zP*P3sxMBvhG>D!qpj>QlE)||vs5oIlchNgPQ|^mK&oz^ghb*GImsG(s3zReONYFA2 zL{mCzk}DIlCTQ5Pv9`WCoJgBW92RU(;vw~D%wNKU)`gsOYa*-ud97y)r_+F9NA4lC z{npK<(5Y6fhlI#7SbrVL{5Z+8{`#KG+$rLI{s7ic4GB&Du}@85?`TGw(Hj+Oh z+#Jsw%r}$E7>hh9Wj0mI{T;-OWzVB8r>-L{EUeZ>%|cEo&G&@?!c_GH9-UgWDB3lN z-RrcmY#vrcPihJ&Al;#kjZ+xbZf+u?5|?Ecg73EnEe%=TbMQ)Iy2LB6Y&C(Vhs5KiYF&yWZ}UR0M9vU~6Yz-;SV~G=@}(y!q@)#zBn6D*A;6ezBNLAc^S!XQP@X2V>kV zh}NxWz-Txf9%oNNR9BfYR&6Ac8Y7!rwF4+=V|N)IUn&{`F#BDk9smO3yfRUtjks(< zhffMFMV{z)pg2^X28hg{m`#sohwoxts|TdMez-2@FalsrcT4ncx!B>2C}Ihz2oPh19Kv(it{eZX8?CJ*C&& zSs(J{TGl>Q^or>tq`*K{R{wfgQu6UdFciZjjbw&4jDs4`=QqIH6_GcCJcVWopizcx zH5}5p0LIoW@C2#lGk}N%!vb}v=oG-U1la8zHHgh$so=$sqS;EuSd)P8Db-ii@@>?o z%(k*|Jn(j*7K4IdleGhx&Z?Gt%?99-l{0Rc|*e-zzZHT4zLs=vF6$$bKSC3G-%l*fB7*UTDpxPxGCg~UW zB=ruJyXdT%$Ud^)D?UEHc1LjC<#bQy;<@2zolebXd!{J7V-P#wd;W^ z#486V2ClOSs_{{>IK`X?p3`Tj7tBu`*RHByk%~43FA3U1`b3 zqL;&aIyNX;Y3yWZZumY@sQtX+tbfnd>k(orRVc1slcRL=x~@EWHE*NXh)jovncZZ1 z;_?JDBo~kpYhVa$gE*0}QI3uT<~e$72C}-O*X`m_hL9ohnY^q4JJ+a#Dz(T4z%T$5 zP91lZoWvLpo|qs1PGI^2uE@YyXk4mJo=gncSpYFCOVMvOeaKLF&TGeC4(GRrDY0if zP=acMs$+uKjy#s!hCjBzKn`JU#EJ!b8Y5@os2CxB9$q3}4iA8C;nWz(a{Nd5d3tCy z0T#TTTCvD*(Y6fHk2v^|l5CPp_nf1b-1h914P~rwC1VB0inG$vfED6SB%O(y}XkuA8Ui zfzlZg_7PjtUR_XG?ffDoPSX48V4gPHVUY-$7*W)KX{ub5p)MI#ixP)Gc*fNW18Dg4!a9=$x48YgXUFPo{EF^$qg*lVr_ak2 zd#&41@0<*qJm#Q-6^7NgDbmW9`+L7KU@Pd2BefPS2?I|ne)kLE6!>9!vWUHOd#UPV z#}{J%czDSivLNI*x&bTMMtFsrqf7zGT~|H)2BI7QsVXHUeOJlwi{m*23kg(ZX8HQg zdr9?}O5AA8>4jFg_3Lg+6Wfc;^~=nKb#acTC9D*tneFq6aotYUjXKtdi4w2_kBVLL zT_TM97rdcx{Um?!nCU1~Pc58{o!4$*MOhZqjcuo*R*^Va+Et51e1g2t)nfr_HTgX1 zB6%CM1Tm`w<{jLaQ#^z`b~%KwLs})VeqoL2_Hr+JGyv5|8 zY-8vR7WTDEZ_A!ph%315YCeS2U1dHA1a04zduIV4k)d7mM1S^r?;*{yr5I8e7DNdq z2dd8Ixjnbo)!JSEAsF^tQU23aNCdYH{sD+%s2W~CYG_ha(^4xXyL)wKv1@H!;^vdi zTisrnbeF25%>WP&_L=K*#6a*O;)A1Rv{sj`;AH8Vw`X5#6^rPZ^1kNp2JO+8mGHOm zeF6b-u>uDwv@YO^x|m@7VP;X|U)N$0Vg<4{2C2j@2+*qJ2y_-Sce<(^>{XN$+A&Cl zfT;C#cvP&N7~FY*Lt=TgSSFaHuSBzP0|lNYV6`({R!O%;VonLuDZ82j^>V-&V|x(I z%~I@qra*yQcfjnh3?b*;%i$q*%_hY^^q!FQx%4mb8W6qEs08ro-?U9aRei@A%dnd)b={gXQ* z16az9M@K?IjT-VaP%K6hn++$Ok_Sv-WkPGnbJB^E_lsssE0?=r1iq>19#gwAZnjek zWvy47v>WL6O69+@6p}QjQM3?MYaWPII}^v>Wqb6~_lSu0g-YnrtQ`)+_ho*tG#dFv z1Y)pbIhbaP0-;;xYb+G!k{hDX7Yi^(ry@2(T5{(}Q|lGx$1f&gQ+PJsJCxva)8n1P z7H>xt<}8>nL9Qkuw$%zs#tPUSupW5(-veZhNt@#`wDr@Yz@c z7pM(@SSj)FyP#;w_i>iOAfupj+kt*M ziA735(6rVT+-+hsYAOU6b!#rye$18;Atx{{!Ld67;e~Q%d!UB|Y7ii$Kncao#OAV` zP((vdhX!IN%ofU#J(D8*{Tj{B4O7ULTd@e|kSb%)Q^-esp)z7%B6AItt&+Pp zCA&)zbq>KN_&ILlLw0%?Q1#==X|#m!KDBcBeK(BwExVhc=(}#&IVKxU(Knpph8v8A zxclQe?>0V^sadReBW{m`#zC9_meFKTkK2+|Q$P)bEX|+W_pcP z1+B*B?1n{!&DvoNqo*|}Dx;saO}B*soD~%bE7T;T$6^Ptp)HCUPF3mC@w8w_0EwgD zwI>Y_$5*^6W3StPjYm9FTU-pAb)W;5%=-B(di zMALIT%nDT43cSE_)daeS$uUW=?yIKjDi-)9k3!7;sF!%Gz^<2Ofrr-w56(v zsLx8lzJfWfazJNZr3A#! z$CJC6)L2w}@|?>mgW|V_twYSy6`^Odkgx|)(f|&gm0iPpEnfuq3*CF&%bXi^>D7|- zDO(5ELb97Pd4_mk=X}UKR>@WVWWlW*c{5giNRg@xC{*`1M`73C8O#MlG(!IQ>(rfa zUlv;1+5A_0P@qFKYz-Y?vRcg2BW)CvfM*Ras>}H9l*pk(p0uEJQykZ2 z1gCi~>&BPhi*4@%i}euRwuvM}7mj^rHwo>XtVNd@OOB@~MdzCZ{1MKLd#yr;UQ?VS zoPO8!2axkOQ$`ok5MWV$n#ilHQ>Y?!`3B z@t$)GaBTe;reZ$ThqSToUn8^FO@e_iNshSU8=L3w$-yifHO{j8zEjkLT3&&CQiy0n zf2c=40^`xBpjklMo#KWLso1#NSVu!1a_jpCTiB#nc11)k>yUaAdn8L(7| z1-2@PNK&oJ0(&5c(&WxStX!aR7 zrO=(}sayFPpk;u}PFm^P$$Y1OuVt0!C`SsJh7UVAHjC&ECwdA=KALAOKb04&kfode zy(7Sw&%$igAYV{DM}M%5@Y<5jOp~38v2MSjx-UAFMCST6Yj%B*^txBQg%rNk*-$uB zuxPHJA!Ep*q&{SM5G=kkApT;?-Wp?DW zstXFH<4zC`m=>d^D^e&Qu2GemGL-y;LS)zeEgWJ+6rWUa`$)w|FzR{kHskclWL%GR zbK`B?NTH42A>Yo@Ny(SIc(OVSLusP&klGQ%m?6b8E%oPElx};5Fthk25LfdLdm|VF zNCLnOv#v#{rPZ8yXs<(N2wCN~loSXJH!-p9mei(4Q~>mu~xp*lc*3rQ`m@kSdGc_J`5z<*g#ASXhSmf$Br; z&>=nrcc5Qr-w6&#CzvqhGnM2D=%X`J-SD3_G~6grE{8#4%|{DbkVAyX8fGNa#9A^C zjYy}{(>=Dxhhpm?=N9wd=`W;BJ=9VOn3@D3Q7z{aLv3xPGGk3@s?FEEGT+F=QY zHJV;HwD;Y6+pDR-@pzf2ix6EugtJ)iz|aP6Qc7!TPC%U_rR$AABr8X|zaX^owR+q- zL0yW?*sd?g3aWV;7B)MFs-!E?XP=j_sJM4^!kI1J23NRuF6~4G9wuEnfLJF~07#EX zbqjsgMn3FbR(LqAD|x1SmNnmtTec{>K_99Zcsj99WHfoUVudWr4Ftv%MJ*lTjIY#Y z@igAf1ACfQ)!5LtdmPhkGN^GACsUKIzl_(H66!m;jL9}Xg!Qa5nb55bT%g6ONJ*Su zXs_41yxVGJ*329`r_N#i!k&GJmNXOQReRrkbx5j&sVT{klUQT!KuiwfuR>lUHB&WD zbYn4p#zRO4J*|9hC=*82DZ6~3%-tQU4^(OJLkesIOpuNsGo=2(rs4<)=wIEeBXVN( zR>g7QfxZ)-X+ZKWAn@T1xFr0?d!L(ujZt*V0-bGXFv-zlcVbqc$P5gMJMZb`Yq}Hx zUBez$${PnaM;JKRvfdt0@fFSY1rE}FC#r$Wt-6n8wV6xPmn-f&6bof2%N(oJL(GaJ z9aa>sN@|K$L2y5;Bc{zH?Dn8H`q)>8oBMIT@?+JC=lQ7ZUN`v1b9L_c@A#g|b{ZZKxMA8Rnt+$VsH(hj^&_i*2 zg%^wQ4Y<{To!$L-h2Na(aDMt-Pk#I+2x(pkY5c&d4^AK? zwoC!{R{)N~GlBF)^(*0vhQK3eW)>lV%;xl|gbk|QVFo*Q?sfuGP*+wv;boXl&KUv* zi>T%W;zTFyRLOF$(cCPbTYGpEvsP0>QaC)_DDAX+421>_KTz_{O3UYWYgOlkH^RH2 zvKAf1u((a!PhugQ+G2zGIoq;dm~ZDLfBQBEU!chaGCHZ+f+&$NMg!q^e*6PbFn9?@ zI4sf1+hK28^I<^zi~YFXEz{RD8yZ;T7r2}(&|J3OCa@=aEuvkk-cc{BXk#agQkbnV zGi$e7JLYU)dSB@}8$H%$&c-4!8HVLALP`2-<5WQ$>032`?TsGMIX%V=B*&Q#`N5L< zi~-_iJ9#2xgfPhU-S~aJLjS@$w^lAnd(#IjjjMb)9u-8el+_BA5sUjPoPmnDZB8q-4y8{oa!j_y zr27i(I}n?zEidbZtVIe@80GAO#g2QA84HvT^@c$M0qqx=uL-5TRd~#k1fEnN9b5Q< z0=$|K-KO=Y_pR7O*7D@fa6lRcK0-jK*+?0(Q8PLW=up6O@@}GXa10MRu}^;BBz6uI zUjZ@W6Ky8$c8QKE(hS zFYA=6(bPl!5LFBvz{8tSCyP2eR>SInOR7FhLlIJ}X(T~e5>O(+>7pZnb&Zfb7WCbX zN2TVnmCHU?y#N%pIgAhWBNPbq+5yEQ<{&8!_fyU$M>0_mi;E}G|2GGhWv1Zl7gq&=vVmnklLjm%V(-L2jPF-^(S0wDJ7m774cGD?6n8KW}`(^OJ?$xhDdHkN~NMG2isq{O;RdT^wsp zd(ORuVa4_7jyHY6Ni%(6SOhsBh%Um;%XobZS}{If7Ed$ICFid1F)@omJM77CKjmms ze{F7a^^U$=B>Q$^?Y&d^aW2Lcn9=OLII!`!LhFn;MydRg3_BoPY;R>3yx%O*p)J{U z63{WZmJY+lR@9d>ZzeCF@}t~=DsuU|O2u@Z9J;0!iermNmt-OnFYPK{qndh_Wv|n; zQSLtk1r2!um(^?|0#)Tu@-6T!DtVM=5BIuG-;9;>%JFE{%jc2gz~z z>VmgiE3Q?DHXosUlUI8lNV@e$*BN13vy)s^oVPFjYJn=MMfZ>8ruO}x^xvlx76i%;?A3?rZ-iED*^$R7&su#o@63MiD58_wntLFsHpH>i= zTZX=42Xa8ir2uI>&cTIMH9Ojp>jfuTFAKOB?GR%R)zvJ4e-jG_Ngzf>qB)}pzO|F@ zSMMlYR{n;BG=QzixMo07VTET}bbzE!44VkEl1xb4uk+Dvr={dpw}1Y;zO$dC^^Fzf zjyQ6Md|O0F-ZR2}JtZUID&!Kay!uFdyMf688gU_l(cq9;+xZ|TF?=!;ddq+DaO9`( zs4%rzwYpzQ%3Q_R64lsjc&nXwxag zi=1;u+6BBPX#&PL9Q^snNU}7hT?6xsoIwtsv_p+YdFpDZHF^$N)8wQu5Adg%NdFAsLRg{n-q6<;&L-NtAOApn;*P zmlYcqG-Z2-KBOxydO0?`>)hLhjEKoq>*~21&WuUSDBS@^8trF@_%1f;!oevH84cz) zy|;{_K^)X7TKDOUAI)Q98{x=U_FcZjxBB5^nz6p?dh>1#cvMA9n>{$hA(lJ+&(lX* zLZ-E$yh&|5^=bovO%$YRG)_pr%ayc!!xXD!y_Q54*Yt~<%wmIX0y@?>4-3V3VUL{# zv_Kpy4_T{{PAt(*FP!eQ#_J`C^VISbOX@j=B)u!p%@(Lmu}=NX0@Z3;@y!}$=8I07 zw(no$Vj)sxCdtxG`N-&?hg9SjCDOmLXO@T#Q2oGsr1n+W=?*WD^^sjgas!Z8M8oZyV|QSmNqVy3(u;FuRcO6 z-D@3Jl&PmuceTwKKqduJt%!$l9Px=qNIC$gPck)iFw(oGaJCU;!^XDjAd^5Z`J$RAt#K}saX?k;YHSpmu{VN| zhsqwXtflQ^NOL%9@X_n!Z8tflTFWovei5HaJES0Lm9<7YiS{bQr<6qlzl$ZtWsBC8aypE-(^{*QR?g5! zi)*cc_zW_*1*!cZ8B}WqlUqm4aeD#i$}?X9^DQWd9j^NhEvDpnKL%Vh%LVSH&Y9Zl zes$3}(?)rvnFH-Qk>tEwOFc`G)4@@`msm55`N>nG%b!vFTV66C85y%X%z}xgTIs?` zv)T)E!Mun=TyoBdg+^Cq-5ghJec}0gKy~|66fcy!_P4MBL0!hwJB~zsjPKPtB!%xQ zC)cw~4}wajEBJytxo#DoFH{3X+Do|=Z|Fq|WLm+czs0M{9I~vgt63w<1Jq5pq2MC- z#U}B|XGA3;a;IV9e8{p;NTcO#;OoJM=EfpTD-5|_#mxNGt>V0q`(gl2N8LRr+~W0S zc^<{d_{EJ9&((TE@Yzopjo3ZT)8|iYXAg{MYx(EgR;7YTgQZ+gnzON=UQ+-Mj(@=Q z(EOG(@Bj<*O7+_#7Tx=m>|%cDAS&wiP%oUol@ZHYlhlxBx3kW5(+DRIO-UprGP6Ho zJYtb1Jh_5ahmf{?yo49CKll9mYY%kxCoL~|Yty6E)3MB!)4(E_ElS6DX)X@?x11Ga z_a+us%z_F<6wTW8BXaYftMqDTjfX6VP32`yYIaFvr+GhL%p*=-g^96O*I zTP+T%^YZUR=DiQj zLXZV@G)mX>K0;HNQ+(=@CFa+Q0Q^!a{NNkJ3TC=le_C*f=>80ZLTn{PHH8#IkhPG# zGh#@N7keBOs_^hYm+E|p9lqfwIvgGC&xv76&qfCwCzV1vCg>%X^6G-}xaTI1R2cV~ z6XIs|_zl+UeG8hP#^BD~4qb4A!+S!sUXdlKDS73z6sEdTXQjG%qu>6Urs~bhGOOlA ze^>HN=(j6L{b z6X=Ry-M3{+*PVQ8WH9SYCb|WtMkBnM+`?JUGwPOfCBl>2NMG-Bg4GMGtS>I0M+twD zvNuafBF_9oR*B@>plmzDu;)GkYT%P8N=FJpGf4{xQmkIqF86PZ52(c&H*R*O@*_q1oTuvIxL*KTrEZ=JMyPZMpuE0p=( zVu>eMQvpv27e|CeHXFOV%~=HY_QNcs0fyonc4}l5h--WH)ZDr%~r=l#6tUc`RnVu z%&e3^V8KO3ps=obMu^5;USspi_tVR`?yC+vy}5~!5|UDc#>@=ujwYw+C4QIh%#{DVa{aDB`Tzz}XjyfCj*Vn|2vsAa?O%ah$k)dlO zjlY2W{rln=MBsuxqJ*g5jN>+%r$sZ#RE3C#30I^Fhf!69g=FyrZ(+T}XccJikyOlx z`b18=lHbq0i zu@mpXY#W2tfWbN^TlBsnuJ}}g)89&Gb-}+@C1mxf4Z?b3s;?Ogqdr;+9Ro~S|ENjY zjRUPQ)bJWa+yyyMeZPb2pNhEI>bs2JnS*p0VBN!)`*0R!V-dRk+z$D4RuI27&fpjg z=WqhFm%wfVqFXg6FJ6z6wGi@JZ4O`faL!!zlg5lW`ssmvn9aegF0Qu^P8mgP+LdV$ zv-C3}xqRzwweV&{`w%g1hAe(Igl0N?!m8|T9Ma%-IHhyBr9!1-?7(p@pF!H2@-Nmb z99LR4s~nBhfaK3PFNR{?IC`vL!^kTBb>Jt+13DHw?w|wmQ`>-k(fTo;2UyP6d|T4+GX-gP(dgoj0M~y+;B&a-Lo`*KRgz zo)Cp7G2N{%IUNqH`5lihR-2sL-_O?|luTS-QIVy)IG-9ve>1-Uy1%P4F1*UWy#aG` zM^3tGyS5Cz+KD$j1txN&hf>oGP2b79zV#fPIA5jQafQjVfs;kCs9qO-BJ*|D5l@-- zONh;O%SR=;b-f>_x6dGRC8Vsmkf)whyd!gjWMp!F8`Sq4B97efl%RWFo*c4r2}i)8 zaFcL5Zy1{7c-+w+6^1)9rHwQY-nsW*|^{h!<+X zAhNZ1aiIDP;v==N8zYONJAq_a`yjEkeDR6H+)qgyMzSj_zEc-=If6`RJd3MtgT_8Y zW+$E4^>4bfaP*UCB{R#$>$>~l`H9mRfY-^KTQ=<-RInrkv*9zN*J$+ zN)P?_9imTfwOKFAymxWVcD!x*->ojqyq2XI@ket5L5mL zPc|=H8lLlc-n27^H%!&^!8!T_d|V+;`gh#pAK5$f3@ohwq)Go@!*>M!2Td}xGc}_6 zk7%9$B1+7x|B=4(Ur9X7|7Da|nQ_@z{|}?|#h3n(#ly<><&yrH#q%e8=Ud<|=9y&A&OCOHYaT7kLLspLvrv)RlnIthz9H;N;Y z)OXtqW|zLWU&m%kC{L5b(So@C;_VA+>hRIjaIDhdIDV^=28Ec;^&4o#JSl?-^Nxys9VR7G8rw{kp=2s>QiC-uLRE z66rUu{`YnT>TZgNq|~V1mj3bt*&(CR=CM+5MZY}mQs)!FHm3Q+J zgs-aNrzPF?M(wjQz4`x$!q#{!VbyZC7;gMnKxef z2e7x?r`%~Vw`ll_axzAVH}zAgcL=uVyZCtMd=@Q^+$Uy-e7ZMY7$OTOMPSCKs^VNm z2aGv6PS80Cv%SsR>xIN#0OhkMP`lOQ$-i6G|38C;{YusNr^)>&WHa*=xpU;1RsK3zZtKXmB|8Jnv|42q+_;daI_bcj; z1pGhU-=9wZ=A!yXG7>XA9sR$iT^*=+TVO1Ces=zpQ=nAOM^udwUwcZfLo|;S5wyZu zi>p8ol#?@5=O#7Ezv$=jjL2upSRRR1ZiY7@rch*nyh6O zT+LmU%NK-06!aFrs24T zpoPIF4MAgC;`zS)fDTY1oz8e17gxpUb5HNHoi#2walZag_X$va0bsLSRCq)j$fCss zATt-qH4|S*oVBU25c)P0vz5WubC58_9_@|xj8%fR7Xx70h!^D|!rka5>vHnv+*Gtz#?y6ht zs|eN`gqQ)tiV)^C#@9O$R4qURt*#iF9FrxfOCqd%4ZTD++J8_(gs5rO!GXJbt z&}3hh9pP(D7q}1P4{R^bunn}|0t0M#$bJAP~r0K@!?dmZe7}^O-zTxiEwR*}^jKb6i3PXNC zP457?p;)xv5!&os%Bj4krtBU21%9mP^ShMu5=K@n_+|oP2NhQOyEaO|S(ebDJZ&I9 z5%&OAJMLp8Ona=RsO_%(HTj#C3s5FX?{)DtdoIYi<31ZJOg6Co%&?1#g@eKG>l}Z6 z9Bj;E7!V-O53Pcu?vKl^^(N;jpf+v?@1m)-na;b;#6zsx4(yVWoXLao4TZX$N;Bbv z4Ppr3Rew8Dsfom9Q}Y-wPFYVg04TRLI zP#ahmjHL2+VBb=xV+itO^HBip!c&#mJb%qKB2QZI4es&6pj(K66pQXMngmG|LiD%* z+=Mb=yzFyX@(ce&WSPfR^^b3npF|p&1PVato!sus1z1Q^QIWTRE)}&mU_}4X%$cbo z{F<#w0oDxh!kVwTV9d@+C|NhZLrK-xCFf!`0)lYU7$M45f*qEc8`=p%r$Xw{TW-#v ze&Dm>_EJHd-eS}^=9|4TC=)!WwD)7n{^%I2`M0;MedFf`TFnTw@m_>m(%5r%?nOXN=A`g2n6Zp_(U<`=Kl8V>ozZ;d;M{!0idV+@j02BO zQA8?UA8ZL^eR}5MeE5pMpFtvfK+0IpzoWqvFvg*44w%jKxnmz`T%=Sq`}bhf(ek40 z>oTdDd~WIsU>>kYe}A(QaRZhbg}Fije`*0>HCQOMhA2E_I#GxGh>=E0UYh6&jVL+y zI?*H$?Clr`VesrD$SLTa9Hh?*1X`cw(J3&cjI#WI-8id z=&7U@T}m5y`!JHBZod;YSQ+#rJz@^!P?5^~yz_2H4sgHe$|fQ{Nd*oZNN6sM#p6ZKkyB@T;~${2h^m^5Gs{rWrD*G!>5?-%eCd+J zuJ?WkBBm}YJd0@4jEYXuoJIQ%M4g+f&94DywI|w&jZdG+;x?{wChJo-B;rQKeHafj z@bgo2APV#%Y#k>o9IVNb#_CB^Hvu67D)|10thA|8r^8=?O0(^yx2K~Ab}HnoeC);3 zd?h-@u1v9XVQXBOmvhB(n)7s$S>?>p>DTKbMq~SRc?E@qbjshYMv{^vj?hyhqkC8@ zlCSkqVi#*;2S^z!tDjG65sB;kc)}@GOp#7=4W<;*N6c2BQjaMnpXABXg{Pa(C=?LE zDUGxRlAhgg-7e$K?n)3Rkv2h9-qLA7U4(a!xqpI4JHrS8>~{{o=MDVsb^7W5U0P-} zV(ypzukV_9t~%)4_Th~atk9Y0TvE1LhAwdJZ*cr|ny5mqs9ulKce)Q`FhJ!Bb@Nvi z*?8~paRxd!5!bLepTLp53?_AxSxjN-AU5wqQg)L(b0xxs)1?4L@VntFWtTa}tFsF~ z{!O6McNfkf&O3$IR~w@As@Dc%c?ToSr zkjR8>#ofkWmN0l?myQ+mQQ$#CJ5qe3Xh{_;R`1T5TMl1>$I>y0!J)>7$nq_YitV$O zk?J$ao20;|wXM>aW7)LPZmu`3+iud2>zwiNA#ljW(PFeZ_s*|0;!4cQ+<{jv|Au|W zt^FkWrAe?&+W3zu)woyZS8S;p;?lMWPN*^d{q~YOtwD52Z9+y(?tr9v>*Vpe`N%M2 zh5oc8=2Nx&!df#^_mIQbF82OreVIXb1Wf!v@>F%-5AN~h*A8-ak5pl1Q?eg%8{IZI zZ~E__bW8V?sJJj3z)w%_c-5pDdx49?jTMNC4l(ceG9SVRmGDiXahow(odWq@9%h|) z)T62ST9lWG8*87TL)4F<(OMMIZYZsUT2|1_pgii%g)B&zy2r@2k$4u%=r7I6F&&?# zA}8+g?zNqot|ly`8o`Nq`J>@6X2DI!%-Ng2Dgei!BcZx)wih>V2Gh;X?lL4H>oW)@ z(xTM%A_x94d#!VJCQmU>cZ50WrY6uL01tp0RYs?=ohHFZ|H{DBGhk?O>g*X3Cx$Al zPFRc0Df6!1?#nh|-PQcWs`IgSIRSncC>nFCa;(z*oOhRZb!?=X*ZC|TdUq#BIYVs2 z0?7Nh{lx3IUF|>PBcamtnh|wNdc*IPX#b(Ncby!=5NXF6yj9Brg~X}PA9bZ3cBuw-ig(opymDp^oeAn@s$>X?NK@m(#S60tKG?uh^W zK>D+%Bo^uk( zcKSIkVcWZA+xj{ZI>tI_IE+=iptU)gB^HWq@3D?fFoR}KouL+9>Na?d$})8rCXp7G zKDO(Alygxh4_lCTfVF#*R*a~xK3pN~ABwx!fr{~HwwMEww;@a*R*9i|1>q${i{P9|iT1JjYxwfdr zr4RP&334G&UkwT0iaZXm96J$Id&Uz+wfGa4eQJSREonbx)SPj8*HH6Aq6x~sWxulV zyQvW+CFqu`?{t~~4gYL&&z5m5axN1}p?h}phM(I-=XZoE54L~zzWeup8G3q_|I?t% z@ITF$;rka6=PO@^lGn;y|G(04=xA8}e;tRGj`=HahK}{0!)E?b$oVhhX8t<$KT&f2 zC2xk|U-M@E*R9G^8UJss$_k86TQ}Jw7lx^EC=HtlVRk}#=IHra{c3bp)~;7JU9~e# z4@2HWEbUru)9)!W#1q13VgGrnvcvSe6Ec94YM4!546$S&2_Jq3 zCMJ)zo^*NFzVM;ibLEd(a=u7%yZjOktY7D5tnNy2O~*G66$^)dvltlumkBQagS-E4 z^^bopS^i7v_*W_6Z%go>GE@F`1OG`%_|i50Zz+M{zt2ozqGM$GM=fF9%}iTvF!?Jp zWg^<1U^K@3bZKB9hN9Cz?IzlSrXiZ9!Cbe?N*5!{+}wA(H^JijcU~ZH@NPfe4Ut&_ z^VF}8=sZLONkq)Js|2g-e!m+`YL@H#EsSTG@^#!|xqc*EG7BL@GMsO;K7O>sOtNl{ zx!;a6Z@O0|{9vogwuWZQ;mvY1WOh-)m@OQqhK5PM(+j)0k5#KKR|uRs1y`@kK;M6?W3^>j}wwY zp{sV!;s)L2!fH$AhF~np%U6Exb{DLg4fgq)YgA@ol_fpvw?rL`r6C;}vT{OUcI+qW zYz64rJxp6C99E$VUsYSlq%GO%?ad>zz0);I*N`@E4(dYCD$9g6p$%ZbTr{|Cn7bM- zjrT5AQv#mQ5c3e8tFJF?E8c~-;Rum zs!#0X^t63tr%<>RjK{?2)1mL&vy~UglmcgbMW>veb!eKA6f*Z6E@7aDC@*gWB6}># z-vyK@@3&$T>z^x3l`ZPI!rPq#Xxib$FOkJ*R7-^wWU}igETPj&>eULfTk{dOf$ATq z%#M71P|Im^36UOEYuD<)nrVzkmooylFSJBijZW!nSW(WT&X@pJ0kEc_5W{hbfmtxw z(ixmIKV2Ysz8h<<`;dwF z=Q$)*OFpO-LP$w&Fj^3pxnkJ>J$QF0q~lh=zDn9U#KyYu5eboOYGRCRdUE|V;zA{> zTU_wR3^Ua`jkiIsgaOCJT{Ui$^c1vadw}e0?I@sD%^hL#6huAZyOxxEIaA!!ztrEn zJF8%HES=_JMFudV74d<`9!Q=sa=!Y$o8WG0XyEEy!chcCE-bE~ikhn|qvb4fol4^@ zY-J8wre+$_JM>$pd@9oO@MN(_7cgO@K7E)_tN8j)RU}LZFN-eZVG(K;=92Tfsf-RQ zhc^4xpM}(`A8(iq&YPQ`ra@gVi-DXuknFyDWNVm7XD-y{S7Gp7dx_UB6C;W*Ko}+Z zm$%4EJJ8pPiRdl#? zN4;egQ^?@RB0O>u8>MBCj9fL|jLKS+;c1Nkmw-P~YWcKxbzlH^J?=uEK`wC@TZn!f z8PO^v5fN$E(YB?gBzchcr8ifN18-b0>>+3DO zKL#=;jCGlGY<6yLzg++eHZF4Unx@4Y6zg)!>FV`a$T?efu<)Av8TxJZ4YI97w%F76 z^ABJKu29xw+1mo=Hqkjc@PhdW^iFRCD>o}Q+@8?G@8_Rf-&QJUuun=rGA)DiIQ{+Ei@_YuDX31vPc$H!aE6)stJ=s}Gmko`N~lfBop+BZ z2#!w};TUvh5Dz+-SRjS4ArwgX9$co0poetxw+ZJj z1CObjzeHlo!Dqt%z%A$3RY|*=gL$DS5?Q9~qYNuKGCk?k+%c@bv`y&F*)I&2puv`X5&V^4erp+&9lA zAFm9rJ6Z0}qvzWYO)UO)^se+j{&(rWdd^s_A_6F8UnYV90%R*N%P_0Pzg_&y0Tw;P zOiB3969jy8CtbBFF3@Mk;fsBY1cg!h)8@2Hc7i+2TMtc~TlOrjR6|4I*N2<{QT4v> zklmC<()2fSs^aI#*AT|^g#B`i95UDKq&qA7HXF$lhvpc3l|G zMRo=86%SC%C`k1ZbK`HY+v8Utr#T_I9(@-nMqYz}>roMqVM2+?!k2@w;RiG{yXNKy z5WNoiPSVXx?vzjGjwpi?fOxrzN=&|jS)6JREwZd!Fh564?VxkF9d34y%lEXQrt8B9 zNK0Z{Dt(mMni>B%T5f>32pU-YB;pP$Okq|7GElqDEu@>f1!%$Ig)5aZmmQzzbI1vX zBC&T;mGLgOI1BorfvhtxeT=-*7BMJ}%VEP$jlju^`8mrAlIuAF0PMPQx5W58)RSc^ z^di2)!{eQ$4%doX>!QRhKcxyf;84mZkHDfWQqIn2s_(g3CStdZ)gL;Lb?c5dR)FXGAqgn4KNFm^utDec_lswcVs$>bhYosfaE;?H~|`%`Mg| z_8C+SV^%5Kb~6NQ+l{w=?6+${BO$m-(XLH9KN^xJeJ;-{F4CKx`=4NwzjhAkw7tpHmsX_5NiQ2Kq>5KJy7Hq&21 zqlpoI;>hjxh-AOVMsTza_AUWwfejdA;N zt536vm{Chf!Su(gv@3!?hZ}YLc%F~O;iweR87)uAc(2d-j`Y1Um51L%+77*3G9T!! zF%6{dNNf(w+EjD&`w>gHFn{`W6pX#Ohl8qzuVkST_>cVpBWl+}CYwmRxYnXTJ6Gcw z?ux>!1zV0vc&N0R8*C?m68dH6rajV$jAzS6@XjI-c#`a%xHF~;PL~)Bc{|zOZ{s5N zelTxK_S%Tu2?G~tueL02h)v{efHa(U2+=DSxC^LF{&abOC)C2&o6A9Y(8Mgw$(wr3 z3otFek-1@7VWcKnl^b^0S^QTMHz;d>~{8T?07bj#j`tq5&wpl z(*(~EMUG-RiP!z`3~)Y|;X(3h%>+#nDgDrIz8!0Bcx`l-6$v4Z6H59buRve>7}Tsc zX8-9R{=7OcUY4TfDnZjs)?-}sylRL+t4!2ORyVDIsx(4VyZY!Z5`tRT)Ec^B9!(*K zaXpY9#8y+MzVJ*Zb&xq+`23vQk?1kC2UM&H9{GF_7qGWi;@Vxx%mj9;TEAi+DPSww zC=5!d86!@4JfJQWdnT_72LU)dKBE=rfeZZ|0m-R1z#M->kh_GUTbu)x3Y^XgEg6)oU0`~s=axGMIHhG?c8|Au+ub%@X*vOf&2J1@nz_k?j96G zDMCCyUI?3OYs(3{_f?&L!~e7`D#~Xo?Wk$1WuqUSZt!rqw4hw>nk+Ss8NYrQ$$5Gi zCr`8e#PxC(lT_$XxrY%hbuJ`1{9kE7v?sLsNZ*5ZcNA zc>#QY-Et!$vy8MxT53N2&ZK|i(XPv~kn!BeS@5}e2!Tpz-W>zN5)lv^@o%dcnX|_E zTy0RRopZ^*9mi0t-*hs44K_N*0%b1ep0ac*dn-W@dVt51-`)5vl;Eiw=6|Bw}g-MTrN?ksrW>R)*w2{W< zRkj>D!CkCl;Y@BJA6!J9S;@DQO5U5Y&Qr;*q~C(7@vZxlZ#eyB=E889?} z38pIuIj|W7$N52Daz2R{6?K)B6Yro8C{G{Tqf(<%lc3lcOdl9nhY->z$&B^awCFp~ z4q{SPsM>-Is-djAo9nJOIyIzzA}Li>m5-5DS9G1sGh=1h>? zB11v#F;|pYwz-wTlq77r=hEj|<`K3Udj=>bD#mC?>xwX;LZAz>Cs>@4)XYaxWX7IO zOf{F*Kpv(*3kvNfkT;*UAL$`X>lwPl#+-;aS~>noohV);Vnz!~AmR}@RaOi!4MGeV zCmQI;u`eNKbFy0BQ+s`Q8|B*EVc$$?J1k!<>HYkOPqp08dvr{sJ}Vv0Lu2lA@KQVV zD0H%9zyIy_`d--3c_V(b-_hEF6>-tJ5I{3Q?mP&TXbADoDMv8*cSFrIFmaJ>-Iwe#=91Vy6VDq`tF1jSR>zum#r8T zmUDT%*eAY(PqTJ;s8h{zV=V`m2Or99&Bl+=sB?BRpj5#5EE6>iY}!5X?qr{w5=Wn! zFpr#QPIaCL9AQ6oR`L!`L5rR^Q%G^n&dx~AVXGql@Wo7;EFnNNCI%OUK1^J5O_nSR zD-LtHhimMh=sZi(3C$CUV{4p4pAq=y)&rDh^A%#h}__ zqgX$*rA38$2E;41N~)v2>}vp!MT5^p>q~E7jp7>Ca_ZKbAFMy9bVjrODR>P1Ypzkf zz6uP{_+*ZO^0Hhh$LDw}9Qlk~6+yMy%t+#}2|?85$m7A|0qBj>KF)-a$}RQ5Y*;*T z{nm^RkJ+@WMagE@>N~Hlyu=iQ@>q}yMWF6C`TkY4_C@fm!q)2jgH$ur@3{{ z!`aDz%cTZJOrM2xy>kE21qFX%okt&U!k^ZY0fDa}e(MnvtF2Zq3u}4!SBPGAPpQlS z{m){du{{`w>EmOXWHufOO&#<4v)~kOD{6?>UivCLP91CG?&$)lOA;I{mJR9>s-2GV z~w*+ZHBmf_4V$c+axHdN_K*OtK{P8f1)YIBYE>e64Wr<4ct6)g4U2XiC%ToO$= z*FVIt@k>+FaKXqn+dJLQDx7d0QgtlVZAKfAVL2`|xR;e3W((#Wn$I6Q>pgDjR}%^S zyl~IoM{XSE?e;Fx!x+Z=!<;6 zMuTHuAl#kfNhrb|2U5Xcg)VVIR0N}mTqr}hb3@#ppam|+2`?qAI}*Myfx=ue4?{x= z!P(&XGP6!!$@c^2*%f3UT<=Rsnq(|~^9>=5kUz} z=VrZSkV=bb=;Kf!)fqru-Xxmly6q#k)#~#Ar$p&O@?rDsoJwPZ)<-0zkPl>2W@URV zV{O*$#Jo_%$_kOpBxBNIaVW<{0*!8>Mpu|W#Y5MGr7xlSJ2A*I?R2w0bW53l z`>%0rZAEAA0N)38RJ_J-V3wJaIlSA*E-4aJh56ZGd)ybpk#ZjfW_dT%H*#Ze~u`p7ty80+W<< z&Do0j3S`GBVswl3U9vCJo7Rj?JhX5!2a{cmn1yZF zeJEU0znGX^a%#Nh2CuAEoE#R0ylYthJZL!1O{R!!E9JB!i{ZP0%}x*L+a2F#;mLi% zhv1zfzV#%A*$<~S`347;lAD#Lo3q=`N*d8L_>@rGQ;f-+5}!5=h5PalM*qfTwS@ys z&B<*ivgE<#c+iAqP=c>@ZEsH#}kK z>~XrQv|R^1$CXU=ZM6qWC(r}XPcvOpAO*AUbEe@uzufIFmAA{Z1A!wlmQZ5On-5#p zpBNOhx1=W&EV8~HxBKQ9Dm``*f8a!7Y zV^S_z9d0*pHrwqzH#73DE#A44Af#YmjxZjf-J+P;+18At0W(A4#6c)d7IrGm&Ms7E zDWMe4(AqS4K9MEXn-@LnIXU11eIO}I2!X0|ab&2!Vn9YHXw$=-Yd@^(3jtR;6RtUPB82!Im*flGj(zi*F(j$1_g5KU*ct>w@|E6UFX+VX6GbFPkc%MKv5`@wmm;3Y0Yk|; z2S|Q-3cZkWzHJj`RQrqp@ER*>@$?CEVSchp=@vqf!VxkIX2L{ycF>Lb@j zZDoOXImNZ@AwdtlQ#Xs5qB1VPt*E-{YD2{c|F_XDpu$Tw?2kKPp~1)B9`-kPzVFA| zqt}ovmZ~k=X77@RUAdPmi4O^PlPV(l1 z(a81piI4v1+e<^|B*z(`q;>R+T)($(UoNfdv}nX?!xHa6?sp_LK=yb%nVEuw>QEMQ zzqTfesHo7m3iQuhlum;=-f@&#fzmXX727oEymFnmeSi7W{urjwDrY-^e!9a0))RwN z|GA_cb?xL|u8_NG5kWgudr|wo1tF)6{X(Z8yx)A_U;wVPY)WUDo9foxtE!&G+upJG}RWW<0q(# zoF>yxE2xJoi-ty2r)cQUjVmT5G?!%M7mkPP+**at%z=1|U@_4i3hp4f?v}`b{WM07 zqf(CPkmLCFVrBvcfByQB6ld!wYYVg}5d^=Ett2)iVjGZ#|0eKbOmy<=k&?ticD&4D z7#A@=5?XgXv^>K#gzQ+m3%%D(_sV{OzlWRL)MghZ*gcgVbs`F#C#%q?eV59To=NBt zJe^0~Afe=wZ7tzCq1i)f3TXUTT}_SorIxy1F?_C$_7UWYVwoyRcIM$6n_JVL-e%zE z`82(M0=>D}AUrIze&lpvc*@Tmb9R@6Ec_`d8Zn*TBmJLZTjPxw$Qio#peC-8%$n|;P5 zxPz$s7GaItLoUaS05y(DtWLwHA3A0>4BRR~yBt`!`GU+q(4aD;C(b0ptHeqdc#rW@`{WZlU`{EvC^bLDitVw#T7sAAnp zy~MV7ji9?Gcu=ZbG|t#f^Ts4dM5Xz3Dx*m`H-#Va?%%s5EhcPvmOB+lC=hU`wG* z^w{NA5`)HwYeXaM5G?+zSLAI`guzBXDZkF}S=Q#mdd|=^i``Hiqo<2@_Fkv1a>ftS zuxk2Fct<5m*SyN(hI)sAWVpwN7H{jyd|lQTyfD_2cNSv6`vbUcuSA`Q%Xvwgy&c)3 z-x5135WAZ~O89g#`Q_mfZL9)mtb3}01w&q5(JgE2tbu$p0bssa-PR}AA${GDO(zF7 zb%S~Ood`}sOq^k~jwb-3S*!pCP1fKk9IA4(iXS`Saad}^Js&D3je2%A2a5S3Q70A3 zOod*X55~-Qk$U)Q^2SR4#RsYka=rVACZw0{v*Qhr+=1KMdjp=V`CAM`jQ*jx&@PRjlj; z)0$8t_!P(Zok?2>GcvJuPu1o3!6JU*<2f#)K3 z>4PxQdlfWZIu0tn-ylGuJ=At7Z%7UyD<+IK14aI=DpXqmW)>6N4}<|8?a`V>IZEV8 zgJkcE$(DPF7K$tBP?)Kc8|@W><5I!{lnLxN02hgVuIDafK1<08(efp_B?NjJNrJ>l zwV`pbz8?V6Zz%E!SIn9y<2jOZ$H3*1NSZzA6YpDKXa`3-bpg%_ZJN{^K+Kp5{r&ym z&c_bN*HYfTc7~M>vxS@GL1t2-}%XB?jyQxXKwf&lU66<(1KAZ&R(T?>fjvcsvm7Xd&pa=In3*{7N6wQq z8Duk`Eh01IRx)-kx?>GGf6dulMhj~y+k~N@R}Hm>TCnV9W6sUx`c2|SmywOAnWu=p zMPTXoN_n|!weLl_M%YHuR-~G>Qjw9Xnfs9)ssmg7TJ?VLJ`Jv!Gow3r$h3e*Jpp$L0X6+D2nL8 z3l2;mDdWR_Q~GGlM|I@a;cWhy5!Q3v$8(E1*cOxzFi}GSiRnhmp|MnN&LJ6#_E%Ry zIuOBThWGy&)5D42uRsv!#spD1T)6fsqs({nBqyUxaQtQ@qbzg)SwMm*qzEpSQj&D5>2ap;`af(*WbfH2>OTUA9=w0C^YY%}`qdUS73!<(sX4)Kp4 z4R{M6zWd?>`R*>@odQ(L%brJ16rm!dszPFPm7eDv7u4_ksXl+QvjjTyB6fG%lwo35 zJ0dkAtEImB_~P@JZ4UjZe-|Ars5c9I1&ts1=)Ts*!HWb5j5@wg9SkG}33y#LM(kHQ zsByOEpg}hvXK6B+YAK^e#Y{uRLW2MSf=rtzS8GIh6o`I>Z*20ikG;LD|JnEx1Qdig zU@?4aEi};QFUB9L<{3E9I%09m`J9A~o3@3isou&xYTQfrHs#*;oyW`?$p*9>!v-6F zsorY+X={s!X?7f9P%SXvHLz?|kXh~ZUluIZ&D$jR5G${Fld)YS0t{9q)?>EpO0F zaos&UtdZ>PZg)Mz4y~VQp-?LuUYda28v6cIE+h1FHD>*Z5MLwSgkkbmZSx-I4Pj=Y z2z?Q)F?;+g{8szDwIPUR#`j&QFwGyAgiH6!*^@9{1Mi3ZbfLF9yMW?PL~GBTPyM^t zL2lO9;H%PG#Zl@>B{feQs>K_e!xBoPQm=*UThoeNquD5%x{)3AJYT2>Ahzkea z@eDsGD^1gu%fhuXfA?>nUlQoX^P>G|<@Ls{_wElj zOg7Jhr~cg@2Ick_kUqDHM^@LTX8K9e!_Vu<;~kHH+>c!QkkG3%Q4VLu?fz(AXV5*4 z`P9VN%UVnu>}yPyH!v%f9~=1BF_G@vSMM5|hZ7#3JCDb+Qt>JorW4g^e;AT{XN4RO zIA8;ayZ<8jVsNA7;USaS-BL>_A~|EsT#za&ck55#ore;QT;96zVP`P={i+PZ zyl?bwq^z7ev=|-gQ_~HptS6kDnTfMB86dAG?F@Eggl3*L5xzQjt)UciFHQi-9G8uZ z{iucY{}y9@LF1j>OM<~h5(V?CHo=4jO9Js)=T32Vc4%WCV|J<9<5zeiDhEjCHVTjx z4GXkHfJ`q>uS#zj4_5JeUsohT!uUnZ?>{Ci4D%ZY)(n!RMrs&*CkOGkP4sc=0~Z?! z7f~k@aK}fQ%d9`I<||fVF@G`IVer|i85KEMD(M-2`%Vqyh^w8(P8gk15xm83ealbN z!uVB%tc#3@n5sfq8gj15Q=E|m2I5OFW6(p(gNYciWtb28OPDC9ofwrZ7v&wuaqz}a zNE){vj@SokusiEWo4;6JKMpp6CfcXrqw^c}c>L+VvjG1G)lW-9^ACpb|0&ha`=1)Z z|Bm!C{1eg7!ti&ZpNaO%2~JP{&rb0F1N8qlK5%;afA0hTn??AaO~e0&=%=OoH}LU4 zdW1D%C9HgY!v$Qvg>bI;dTXE*1>?%h_3?4buHyWmQH^PdLWcc#y%-Z)%>x~-ZM}qo zO{J}PNNqnIc$$A4_ZDvYS5k8y{SSe~S8!#c9JdlNz!8C<^JQYrCXTKjc zKi4}YkJI7z5p^nm`sq9iaU{t18+6r;OH%B<2d;z=k zqkLQhe~>eLI%GF|0=!XiKG)U;bjCxyQMQ^q8O?xxDBfI6LViuL(!W72XTIgbzLo25 zWmYC{XI6Ixcuic=T}`rDZCO3TK>uxGnClICv)S8gu>QCBy$; zuGxQUU}yMRZT|@Hf5Gy91L(h+LjTrC`UUO($Npac|Chu4@B4rK{`>wHRR6a&(ZA3A zwPpUA{|mPN3)uhL_G|2~#?imV|C;}gW54FV#v%T?-rtY?>-fI``d`QYb&bEB_civ{ zIP2Fx;jit#aoYb2w9oJ_&_2VzLi=AW!T$}kPfx?j{7+8%xi&X<`T1s=wv76RjAYJ5 z&PLAUWN|~HTH?fHKk#IA@MLpdDhwaG-(69tblSh+qWw~H>iPHavZ(9uY)uI7t;-Z# zp@hp98VU}x3O~x?Gjhs>)J$O8+fnkdPga%2uFC6;dIUH-FEcMQt-xv*Ju2_Z4qX=P z)M|Cs2lj`;1mSo_D9sXJ{Hfmw{or2Ec(l?c!^dBZ#PEv#Xt>a1LMKUJtI-(%JY6k8Zw{4x2y}?-i$C}T%3?U zJ<^ObPtg``c045HTfMoy+kurY|2`F3q-ZxgLS8?`+-=Q~Y+eQ$rH1sdn(KO7wR_7dqa#!#&1BF>^&$P6YJyR2Iu>=-z^ zpTO}`GqaOM8VSlj6yLTkuda*(rLrh3rYPl0=_)QSwK7gyU}$HZ&lxjLUtZXlE}lqP zXFLqRnEw3adg~num+wGZo{D<#SP#VH(|l-qTRiQowWIQS`P`>~YW|8jCtwT47QD>* za2Eg-%~deQr)++>MZAMl9pN&W3(XL0{Rq^F%mx^HRA_*l^MHN^I-1;}y28A_I#`M1 zG9F>GC0=GbX_L8z=ZvV{(V|Xy93iuv4Az|mP-8%-9WO#qHt(d&I8E!cVT=zP8Xx>B z?FOgFkvqbu@{4HmuDIPMC(ros=W)t%JAV98hhULoa^B1A^^hst^tNfQ@R+lF`d(Ji=*BKMgIpc}-cxI|4(3!6c`+x@Ks~hD6Leq08WW zHbPIos%kMJ+W2**yZWvzHBPZ$G-B`lfXfc`BY7r!=UpKC*wgNaMD$ zt?=}OTqi6tBzmf%6Tc;G=($r(iyVXodh+wXoyw>a(GEpc&fLB+4 z(WcgAQJUSKM)Yw?lUn_w+wxO+kS}zwZ%i~PLCbvbt%CNjts{dL^Bk;PT~zEyON?M` z3w9MC)gbaIH6QfD$H%va0n-q2^WDcMVR~$7s4YXC%P-+s>K7rr7DoVmnW1~lKzr1D znD~PFaYpde`kCrftyx&;1bSGV9O|Ve8&gf#&a8zwIo?jvJ)##&&8QA6BN2+31)sfQ zEr^o;z!L%l%F5{q43eC3qDD?awP%c5+T$!BZV4-U01PW2d=}eea_^I=oFWUzkBod^ z&e`&`)omb-DMdQr;IZi4S;6N)IVD@Iu0&$BqncmecnsO3y(ZIK^}H12&RFASeL$Ul zQ2HRjy|S9$4a3c@$U~`slKCf-qQ2&2D2dDS*>y_PXEQ+wPw1+$o(WNZY0QHLr!V<{ z58CIi{5s@pY%gbV6>(-~n>?du-2h`G-hHFv)1DQK!K<;0jEbMp6)x?Z(9gkpWJ}iS zb?$EsDK(AdMUK}Tao;kW@kqQ2{uGGC5U;Xzr^zPKDl z*9`}C8C1$jef?H)Ix}k}eV$4Ca>o5%k1v;om$q3{M<@zb4My7E&OTK-9ha->>W#UVP}1RebGLNwonjn5pPsUXEslb@;| z34m`V1_8r#zZ={B*ct5TqZJsaSBOuKECpW3c8fyjjdZT^`I>um)Hv@~jOhmK3g^IO zd(nVIBl$}bIAB&ya2SSBSYgo%sH(U?l3NYOpDH(J=z<7KtJpkJRde1v`iPz%30mBl zMQI5WYICO8VoK}2fO8tLXnCj)b~3*McQXV>AS#a4?jX9RUoGDuc8$b7RB@s=1g=_> zkoS6@iF$4qPS)92{UQo(q54EtvVD{R|6(-YnNiQk3Vn^i39U~X z$0c!Gc^}4rcAmM+`f{#-A-Mn2h`Ab_NkvFd4*u{T#4yEt!L;p-?JM<)GVw1xvmLOHn_@M-0Px>_2jPxc1Az2`qe}zc8l<4y+SB3j? zRCa0{UAP!w#rKuvXQ8Z-?4j%)KSV@97F9h-y$?(scmF79lx$c2mM+AGoIZjV!B8)aXr9Ns=EaVs^C|;&jQj=aSGA|V?L!< zrVl--GviNE(dv-HWyF)Wxf&!(J-IB2dNkegi<@byQmH)2ysP^E4?sY_ze6s{kC&U_ z0XDx+QsiigO@QoA!B*B}lQv#SsxF0b%8|l!A%D!R7eCLM>`P&`-c2bo`NB`wtj?wI zs~Rx&Yv5wjr^8gSH@Q-BimbelZ?%EG4lAsj&4tk6-=#uQYCMGpQ(IFb4&{c%4ce%g)MyIRJyO_K(j_W=`{<(1r7otp5w>|7w0G8| zz^{!-_jx=8SWKl;L#dI}ScAxf3U8R-Ew^tBgnE|_l+^XKL^fv^KGe0)*WTXZwm+P0-Mw&O zVRKWhBe8gdo2-qtm--&^!SUePAlHb3 zpJu}#E(@>YI!$Xhu_EG~2uwXW*IE4-B&e|1nj z?N4;we)zFcyveoSg?axrKfcX(x9^aT@IKJkpvL(&I>GV6<+IPxq@6w$3r!d;yDV5R zgF(pG7+6ge`VPAeH(ph=EPw#RfiA4>S)^9?u2blaK3Q)&&oq3~@*WZXV{-E}d51Wwr9|**@_3Ty$Iu9hm2k3t=M)1eB$NDi?4C zupFofv;_tOTLVV}rvhgJ=K=yNmuVp@I!O!91uh1RjjXgLupzKdE7jM~uQRlQ3($>v zE)Z7PQ>n3e)O3JSb3h4T=Q1l;t$Jh@%^@=}n4NAPv8Htvzl$?^L>uDu#GEEZx}h53 zCLNwe`pln>!_|~1<8f_g*Tu!3E$OeeI~p6=e_EN`A;x#aPwgns9uOa36f`XE7$495 zH;fzd-&3m~PCpDO;_R=#AN5ByvN3`75P%|uTkp?3fNZ`ayXf9`viD{GWewD3FRX>9 z@BQBC`(Xa<*?+XUD=R&4L-sg*if{nFMv20o{TM|0bZ4JtG6+3`v4fw3oRm7gp)5eN zO~%8lxQet`A%~(WNvttF9mybOmwa z#UShr0#0l6F&^ah(gY4Na938T+Oxo~Jr&U+s{8^Q=Sv4{E^peyXoPY?$_wez`-cY@ zD-z#}Gf+;Pp*-e1g^J6HHJYFtnsFL(m6hCMFDjFy(jvRPs8o{5itNU3Wxb=@AkMvi zZ7FSaY2FpKK=ss69+tkMJ*~2%xY8--D))I>_GLQdZ=g1swxVrbL$7{pRFgA9fc-Sp|g^YrNcEP zZ6&y*BrJ+)zQ)vMI%*;z6PQdqm9eN;Mdhj%O}e6}0*PcoJWsi|_}lZzM0{!$1qWhW zd2;!swMkDAP+n16Gewt!h2(KZN~b`iSm$;X>F_U0ySFV#^xZhkQaLrathIl6tnH@0 zo7!Si7OZb?yMKBj=Ic$)TNrEaxw)r328NbZUF9a5$p7hq!m@b_t7lXMib|&U&Qu25 zB2M!gYdi(%wkb{JMWJ%#W`-{+<)y>qCn$#M&|^w?bqV-AaSygiDG575CYu^?R64NP z0Tw#|2ZXVVfM8dFfT7S>*Qsk5X@G$SP#Sg8Yq%j+wJciPFk|=I z4>T=mDBRO_OEvjP!L(btCboGim)Oc81+lJMr>FW-rIG=-M`G>Og+BMjH>~b#5r>^q z(y6gv0fkvOJBN%>JPV^Lv{zZaDzQ0%bphCF-)F}FS~Cz163yFI4ln1ese6afvJJL}4);3ZD*hOu$GN`KP3vQmk|yCx|FbY?Fr$ zg`38qD2%A^&bZS$idqa@c~QVgDQNQW)^Mz(w1@<9tOjx*)7v$SJFprI*xlGVyQJr# zWyzYA-&~a(PSSmaqgmxnJXkzqNmJFDlGuvm*1HHjg6bUsw>fps_cpD6@qrGL$s7z9 z3fY20qJZ4EVsBr}E>74D!n=$P%PC?VpztX`tDobHppjWZu&K@I z+%(9&c_)IXq+;? zulWzS*k0Dw)KLbZ*Y?p$H@JxMixeg&-v0fWU;p~OxBv5`uX%9R>`a3sHDqSb8f^B# zyB}Rf*}r}G+5h?Pr3Y8l*RMLbblIWRjg6}hF`bQ|o@@tshx4Nv)QJ|u!-{8fJ1m;I zVk({*qH9y9_Do$l^}(s!R5oLQmf!*^5>>MuwNklDE~7=ItjNL`!yII9jlSp3WV$iZ7tn)^@3J1TP`bVrP^ZpY$`I?@Ll`XJK>C;LwIVXfGIL$^BG2V5VtaD7|*j`oqGwC+sbxjv57 zKQ}{FwP$T}?io*LPsK?&`;`~qP+wJkyeT%H+L)RZs49R0U8Fj}#YkEWG*CVmR1+#0 zHGyhUO*7N!m^d@-EYvoQviSt#`fpT==k1>uE$NLAEydKX<5dBO{9F8-YT>R}4 zb^uZwKHsnPj8ncxB@gjDI~=qRqK5<%(ZQu-h=LsrS$} zgv#V$#szaY0osvr1x?6`e|^4uL17C~>p(j2NbQ~D53Sz6B<8J5$&N&<@bSlMmj18a z!fDA8;Wv@p#o_Xf&g?#S#B235E}Px^z@qZ(b8GtCi8*!kH%yhJsyX<9C!Q2^57>*g zZkTcJ(xymiepR@szQ(8X$Lfnu%zkI?=6U5hy+GE+_m-^x><5jCBT-Z9m78J(k(Ncc z@!l;h{nLwzruVnB473Cp&%l@e%Kw46SDx@MTt2OAq-`JOzscj<_`7-BL#5IZ7kF_wo`n}O)m*_F&%_7VP1YI`KWusCz%(vRCodmlG!4{a$5w6Q@ahWYxLxf#>^Q_5n?tfU!q zX30$DZ(_3%Gtr4ilBG7OTOy(aoCGJOixM7{piSB!ZI$*(T$Kc}1l`gg%^a0DBtc`h zbdI(niUfE>g3}U645!O0RQ6O7{-FA@1Vhq@G%Aq+2|^N>kWSQLorPGDNu&@+I}A3! zoY}@RVa1cwB;Q0bNs+c%J8mbXwf;#}Qv7)}Me626BB7yiKNDr+{q|%s_m`7^&+I6; zMjXyRsEn6mT?73&0-*BmumBMgL?%VvWKQbk7iC|$@y~yloqcI7y!?&#|Ev4=Zykaa zfBJbm0OQ2<*?@ zX%j&bW1}%ZF)=nCI~(H~M`IUaIHsv~@hUYD12G2a7!1WmVtZqRRXi6gR<#Ckr=+4_ z=ZFkQ7UhsUE}xdq$~uD_O_!mdTa?qzVs{bGd*>UOJVSxO4(Mvl0I-Bv0OC1TepNMO zyqpp_6aDmGs$O5KP6H5Jn7-*IxvqCcbX|7!z4I0pwxsGDTe5?9J_Oa|6Kh$#%))f& zYqaMjd2QLray%}`>5c46yOEx0rD%;(YS2HZ#|F!T7Hkjzyp#?CPG@O3DuiosAyT5x!&CI+0dD*-JsrqrHt>S zCy5Ecv_7;N-GdGzdbstWm(c5oW4UMOnP)mhrFFK|EgdR*DuoSA>RuCIN+YJUY1A}k z8aL_o(!>Q5G3E6gt)ZFfIaG#4h)zjCqqaJci?)`jcW=C z{J&9cTj34e-PclVYI-fD*xQDO!JIRAU1#bVxJs;(I&5!t4qi!d*@N1Zf>JkByINiN zE*sosf*QV!$E7^gbulT2^1v(Ghqtyn;lu84A9?hF;dpoFx8H@rAN>gY|M_HR_ly^R z^9h|(6d~>>d^7UE-;~R|#chdNh|%CPV8P~vtj#;uJK){o-Q#`P`@Z+G*Ki?cMtjps zhP+_&25CKclU9A?C1YMV;)M|}1icB`i6Afhe69Cc+VdkXmu9UK-WD&p?1j@_c-af1 zUTC4+w=nR)7CPw5blA&YKI4VCUZ`Rn;F}+5trOn0wB55_PGmjbqyt{|a(lg_UcAK% z8P>AJi_fz0CMU~>wEnB=9lT-V`{&-HR+HbS7=W)c3op5@ z#gc0b$5*Y%T5d_d#wi$P=d^5lm@#k?2a4~|^j7kL6H)(6?i+1>N9U5Y)26;v>*pRb z8-4*z*;jtS>3DK!wZAq;_a4eGAEEf;LI;#pM0AP~kr-A{>7UA3mR^J2WiaTis8_Ey z*pN$fgyZJzOLF4yFIz&Z)Jv1(En){(9%j z+f|dpYtvvdo5JKz6ed0@L3~B2T~W5F3?DFnVAyHE-{9b34$K@F94ON1p*k6K(*H_) zBt9M|p?F41aB)q1h)+@d>RcXpPuk~prX^IGHi{91Li04eKFI)(Q}6pV{hk>^^F}W1 z6SYDlWY3F9Q%E!9-3Fi?(RKB9%^HY*S@FQ)?3UzhPpnO@uf-VPacJ9+{YN$!ZOb%G zzbzWOBe`u$N2DHpxACP1+RY{tJ+*8X3o1W-(OU^`tlGP{)Fa|Q8HBefuC`DBJk39W z!tktO5p-T%xsDhpR!l_LikAH2lDazrHIcT+V1#UqoQa%^T#Rr-5pdC>Zd%B(uMLrt zT7}LO@kj8_E=1sz)|6;{Sv@&A**DjoC3)=?#73>M7Bn-Rxowv z!|F2;VBNMxh(7{c1TIG4r3kQbw4!(f)56uP!M+I5dhK1lQtg^-`%L7e2;LWgc%+ZD zbVV>Lc|Af{nk{2Pgr9aP0w?Jl@n{6(2wU(5Z8)76q0||HP^2o7j*LY1M#ds%BNrkD zF%qKL@d#I7u@rWaoYiiGS?vnL>7X0=(!^_Xq=f#nh8kLA10e#7MsFb}DpkB_ zKQaO`ipC2@l7ASCw!^#=<>jPxOwRqB5g#@?LzP}%Mabz4RrtJ>A?MBBim=leuJE!9 ztz_@z$`f^?>vnmyo6kts0j$@kr6q8-0DvF&*k|$79l+)M) zMXo@gNP_u=fx=FY{X$@rOE%}(vbf(-vx20HRPGQ2)H z{-%82xZEUn$;2ze+jMMgQurQuw~Pf=dw_O)SN=f8ugP$db=ogKDU(?;)XCtIWf|-8 z{pg(bE8Aq)Aa9lNB6)?3u?!!}a7I2Sbhx@-lgyOlssd28R+E%PjIB z-L9&y%5YRZDdWi@Sxc<7D`Ard`tF8L*bG5?`Br(Kd{iduWVrI~YPumPASRU9G;6MtH2r4Who^;4-6X%(A zh0JlNSy1erWdATT+EQB05l3}(qJJ_3AU+%)_)?#Je9~qAd7u6DV%ZmExvS+%u5X>Q zG3FirKaXpoPIDs8Jet=VXLe|wNfr;Fc_szODS^x#B{8kA1+H~VlKt7Qg<9vWZ1!?4`txwzU(KDz!GxXXMOV?y0r=OI{x*Hr=)#Jqe>M3X1Q65AJMEf znk^#x3T#2Yq%uS- z@jUb=`cK6t@|;Isn_^xdF0Z-5mhh|%3;GtiiXug>w%Wp)(t@c?YnRL^xFOJ8@9;Vu z-e_T6qr1GB&7+j+G%Tjw3pXj^1Nukwc&=^0hUcOIgdHzj{_}G}-gADCCM;!EyV_*# zGWVHDlc~$pXCi(92K5{C`}9Q7r}dcA*XZ#e1}ei^trnZkrpdOQdD&L47vWjm%epsp zM5nW?H2@R(stm-&L|4D!&-n4EACNyp(`o;>|EyovEc(a%SoBxXf{g#P|AJqKXmZHE zmzIyy0==JUy^H%*?Rmq(+_Sb(dn(TORMm#W&Wsz7TXcuq#NgIM6^C1OOKC4MSVZDZ z+mKby5tGxcgPf1)io~o*Y??M)9oKxgnjc(zgB?Z;x08-Wdom|MU-5HKVs$*uT%5wB zES+;bU8^Sdz|P=$maAd~7DXqJQ}Ik4o2Ht~5Aky{VXADj5| zv17R4Seoe;+~YZof~}Xz?Hs#ZVFkZZ)N8!WxXVa-(QODXG%PpZg=9IwM8`=S7W6OV zPEuX|2`^m!>2rcvWzimrOcTc3=_wdpf?&ij-yj(B_HIlyODNJBIxKco5Qx!4m613i z#Yhw>n7*%OR7#(nEUnnfI@GfwoTNK{MkJ$@Y*}e-pjZ{fkXR)WPK2Yh5|4<0Gvbhl znUZECByHwxiU7Q@T)=;)+ALt^FsGbzSYx7)sGj7P!;G26^QzjH@~_fao4m>q?V z>5e%LZl?`m9JpLqSnDe;_Q^;Z0DWVL|_S78GE2Xr3XuzMLJ7s-RQ_V)`A$j&jgcJk6fORP9O4htr42p3h!i z{+%7mXNEUzFKVhwI3lfc9$EC>Z{q0<`;HId6OSx;^uA3a`}*&HP_Q|TM*%y$-~aaf zhwi&~`(u5R{!D&8vY;?p`n{lNvM~m6L1{MI)J5(UZfqB6>P|CkHo{#xxRZc&EsCIs z62VF&6B(sKFITWuG3+&r8i>KU-lPw#=e=lBo9Q3cyp%aleLN0KIgJ$4K^@6z2l;}V zvi9~Thnb`D%zH0*n{Jskd!Wwkt{a#=>y{=jKKfMlv*Ud*2;Fc4EY1E?_SwC^KRj>V z;otAw`-dY77asXTPDZ7B_6ntc1DdP&QAn_Au7r!Y_<@MiHsIwZA=-bQS!#0%@-@ES8~`UBdU|HdqemX( ztO4}k1`N^IievZRf9%#+>{j+%5&OkWPhNcd;D0~e+xzr?A3XT+)4h1~q2G_*bkpeX zAKHEPo88^tJiGe@;HT2*r?S}-C$g8GUa;V4;1NMhmp|n`rZ6_60BS<#72hEYyN!p9 z_>c&9`*-;d`N`e3UA99uQp#9{YA~r>9UPhh{1h-u(WAa{g`St?deXJs^-1Tc!++DHjg!*ZswcWo~fBZHPQT0GqyEDEou(YR+;AU zW`3I3oNmU5w!Y9z6q+8Q-A*^3ZPrb3WJdM$;Ei}Q4UX`DE)yZ4fg*EUIyO<#TW zp`qU{A^&LVX$oZd($2w&QynX2h9`c8=^n;zxFh@TxdR|Rf8(r{m2+z?Ld}A89XEaL z)Dsn&xiq6;3~j| zawixaU@}?3%-o>ZRunA4Vo{N~cWN_ghK92gt`xkdn{idMLLa28*G-Y$UFBBXX*XwZ zuPB4=GI*c@HdR1(#b5=FR)ACiJ4Ki+-YsIYSW(9L^aFJObagfr zvT5p)8I@BS7H#Rjam(T=pFR7RqoG)+zAHXsQFBG2sqcYI|NhmDW|z;B^WQXG{*2#9 zX+%UN=q{yuqZc+h!DRJYS6fLxSwnCmnME*_6A2dl^n9g%2&||NN#K#nP#Lr+We_TZ zp|a7kvt>lzo2Hx=M`Vh>B_uLn&iEMyGZfCT*>!oYz`%4BLOBf00SKzOBA~zkjzJ@y zLs#zT{DZ&VcCx5rc9*>6dmHK}{_$nMMcr-Bz~a(uy} zK6Qi#jCg1q=P&SB;fMGU%}GE0T$7?|jZBeJbqh~<@r&?7&6bdlvK-Lt9pcZYRypYr zJ_oyC|G|SAtXnD0AEP*5h8oaeWlennJ+XF{!l57(mCBi#u8si ztc28r*BYmZ)6&y$dfL#mv1u2kaTRNk%e5A5VIf>KHB&bTR5L#)6Im`VTyGCb38_UQ zl4SRmuh;S0`TaaG@Osojfj$||$9RVLnii}k=8isQq}33WdB&YND+uGtR*pq()J`IA zy~d<5Xd!)?NssPa>-3?~!F5lrsV(hTy5WKLo;__8ffLDEZ^!1|dcO70;_io**TaeX zHiujKnp(G}3a2k4M{hl{x<38wZ1(tluOC{|7PQ;{yjQTA_@=GDJP@r)F5eG7cTnw*{-4oj#BXSI$iM<=c&%|DeU5b%?F)+p8=keI;7>+3?zNf~NZ$GUL zvQmF69wU!`KwIHcu`@C3k9DyD`dHJNSX&IU0q|CBfHhjPu2`S;lEX1Rrt}S{HLUKQ z7<<*bu@7RprqLMS5Od;(VpXxR*m&%8OqY&jVnZ~;<$U>-RjMrp1{Q)EO_$li>44X- zDd+4^%^A`L*5$bW5=~dm>B#Y%%`KN> z=zV3%W}b;-I=f^qv3JN6(AjT<}<4Qae zABvxi^WMHHgJQr2=3F$C%LBHOV9AjZQc_an8Ze2Xi7Of)PAw`iF-aPlji1Fmzg3V&%(Ku#s%xecH0J#zVE2A0CY`#-pCroa6st2ciCt21xh z_UzKBOBc2}kHJR|bd=QG^vq}f3>oMT6=u&qIkk4i*Uvxm-9PP`W^omm;9g%{U0#oB zLWn;=`Lz>`Dv1Jpj~>_SsctY|Z_xKT9WJLs@4Q)yl`-qw$W6Jk8$xavb)R)JbHGoP zMuVt248qN6D_D7h+kmN>M-})12euQzI`Ebg9&l1^$2sK0^PDi#2~lUQ6Fc>0ho<7> zqF|VM%`}~Xe$6Xi9lyfYC#rX7f#ggQ$!Rk72wbBva}S(^*N!dy?3I(*`eVl^FFwJ2 z$dtrO4(|C&hbEQA&w{yoSQMAOLDw2Y2bK6rfLftlz_o^U1Df* zjKKs3lYt6-CW;bqCSK(=I*JZVp^IM?YV&xIUnHZckX&oe=kj7Mw}Y*A%7)htW&fW2 zKTIz!UAQB&YX8#8+^Ro&|K{&*pZ~YtkQy!b?))8@nwlls=TXMKh1`spe!%YFy`?<( zr0~3e7YnNd+$8`fz}vzfg-+}2t?FI+8`ViP73FQivph(Ko)9*Hi4WGKx>IJ!jOPlgaHB5 z)^JdxQ5M05_=AWOxe!MToL;0NA64#AOZ6N5xuD+n;^+G(?+snC@8#|WG2TT#6g`q8 z!v16QN@{ktm45Nf*&zHbtHseB$bN`3{KH5OcPT+Vhh4Z0cVo_sO#)1u6JT;dqg4Oa zPUDX*s-nQwOtie?&mX8`!nlA3g{{Iqx<*c5Mpyf9&Z-(j&d>$p@qFj<|9D%?2+%D6 z0~uxwWP!YXQaD2aL+C5m5O^rx`tk4oL_I7#BfKP#tpYR&T>^edcvtv9Ap2+w<(s4Q z4RH7#VK*y!NqAkjBoMkSaiK}T&(MZ=D{FS!8rE#pMFF~mJ~ozLU@iKD)wJVb+RI=Q z;IaVti14iNzVMO2(e0r{jkJu;TMf0M>} z9#gBLJY^11J6{`3;`QK|NPsi@H`q~=vmkXWt^IAzUPkf3q2a?(#l{K9i)Nw4h%+O6 zK?@@(Guu_Ypc5oY3LOG>=@S9YF*3YFY|%#czWSPaRDk^qWndj8 z8AV778G#DVj48eJV^teH`u!iMY+ETg0^3@QD6x%FY~>D*vwdYYW37iW_|P8pGTZGT zT28e6W>`(;3V8zVH)5r6EyLfyc=jn@IW2ui1$8#RPd=GX?=5dE^X|y(MpH1=RR}_Cf zOqBB_mV;C|QFU0AG!J=I65 zv8&ouZ7o{6-LRjY<(*<@x)Y~6hdRePFLZKcYj;`?S}{A)>?E>&#*E7P^}N^Zje56v zANBHHedT(=cA5^Fc!SBC6NdY>xH5LPr9Wo?V%G~<2=s9O@Odg1(+ty5z@^ia zWL++7y?zQ@k)%Fv7y5jD=jy(?o|fXbfB*K|BWrG2Gj-X%!OYihsowqh;P!HNb$2Q? zFX411=A}~I)o$|19S6%?%1vu${&@UHKiT$ddD(%rbMNb$TGe~sL}b?Rjb&vw4$qoB zJg2N|&M6tcJZp&!8#uJzS3mzdq(QG_Ve7~l=t-QOOXwi_CnH?%B z7t8VVGv&Zao@Y^mxujA=CD60waI*YV`I&N3RZevv+B8&7M$5;_vDU<45mgu3Uo>PK zG43^zF)DO3+zywhM$KX``PC!L;|d0&_i#kayIOP3Ru-4LPKzoZ*22m)XK}p$iw;=( z=N+<-I71az9kRLSb@|UbXz?$v)@l`3fIAH50G9-M!F_ ziQD4=6B7gqHxzCy+*e34h0s>mU5L*W!s$Zjro|@nuMp`pZLj< zW8}luP^fQ7x945h9c*q6;@*i*CPlF9%kOYH0u#R&%?>`nVob7g$h+hn%6DVv0j1Yu z+ig2+Bg@M-mSZ1b`j$xt2H8vbb6DSruSVUZD>BdtDRD z-E7K6SA4BM&O&hTc@}M=*-I$?gf*LD7#&saJ!(H`$9xdB`5yIQp7I-PFk&CZRwGmH zQEy?y7D+_bMz%!uMEFFcB{G+0jznILydTlqA_Fw@CQV+B=o(lNW{tPdTFw>;(#Bh8 z(`O?(gWlSkc7oH8u~_VU#x)=jt8>7fM-J^yw1yB%CcRtClpW9Cq`Jlscnv;G-l`(K zk+~yFmaSZ}<%Ur9#Znw&J$*&F zHYK7{9dJP%2b{&4aoFfI>|heRL~IasJZIK(wgDqB9Y%|gO(vK0YxKmFJ2U^3?tBw+$13~yvR55Q|bhIgNycYm z_aEVxwG3$gxMa{`?HS*jQ84K{EDV? z#UupD>RLE-408vH8)n^jV|d1@Xls$PBlb{UyZP6?U4t{rMA3S2x5xM6oV~G(3IJ#M zHYzt-(S3?&Zjb_Mt*%|SP)8Q&K*wA<4yTXeMOwsZaY!5!FNmClU7VRp4;BljxV$Q7 zG#cOzs}5lihp>XvcnFW;F$z3v;H?`Gp$ti{V_vJ4B;94U<+;nyG?7TelW`_@(l12m zB4Q8MQeKkOJSHS|Y~pFWXZ`cp$N6js{s5)fcc63|Iq=!KJ*0Y~pU%IB$}a!L&p{=q z4jogv?w7&kl3m@dLDvQsUsE_p4+m0P`)DoZh!*=TN4G{fL&aM3J|$p<9-U6-cDnry zs6lDKN<*e$wBc+67pz`uWJ|B08(qa5K*k`Q>W1ybP+VN3!t71x+0nzMsvbo(5%xX>|!OmX!UQkD-RCd z@!dOH7VLh0S=XUdYs8UACOw_Q^Anu82V)}gjXX9wF>!syB;hNdlg z23>yodC)zbcG>>4(JO<#42+A$@DK3+#-C!HfD33T;$eD*Y7|(Z^ad|n9{WD?i&4$fnTr=su&@=4 z@eRj4RJ9x&gkLRl@x7DhzV3G#y?sXsK>=kwsK8`bJqQ3}T8a1!2wkz>au&YtA%yY%K26ADK@tMy%4rsb!iN!uLv2a3CKv{G>| zF9dfW8dx46?t*B+@&Xd|u(%{GV1xyZD5W$ZiY1jwCHVTPs5XarW<%rM14hKytC1@y z2@WveQ~4y%Ks3Wy?)HrK69N5vvCQOUN1d!)@N<6NO4b5NVkJa(G<*5@&Hcv#KiPiw z+QEdbwW#C83^S9pe&xA!so|wJ_@Xm(vD@$7?z;J3E?`mMO$LsmVa15{8;x?{fa@{sCozf*cGZjI(=s;5 zBb9(Ep=G3UZzUeB9IHHCNoY}}b}`fCQGMU^KD9QmC9o$z0-4gG(veb9${)6|OBeZY zo}2-Ug+#EZ9(ywJB|i+{{E|qYFG#gQc-F=ReT!U0a*?aOrm(iOfT*97V?R6n<-tIJ zmQmg`M)!9*8hdVgJ=8F#=6bqi@etk0ha6yJPo99GXQm|;Ieb2i204NAf^|N6qhY7xc7IF84o>#OzRdY)+m z*hX~$BV$bYUk_k{t6fvF*eRRSusl@5(rW1`+n>80&#sB)jnd>AHyKVqZG8$RoyA%_ z0m(;FvwNar%I4$4xBQ^X)LQA7HnX#+|IT(_#SOPM?b$PYyMJ2$%%bLoDo3O>(lMud z>M!3dR@csX{ur$3+jC1zffIZ`G`eiUlm+YCZoIW6Kn(K*-N^kjmMWoq^noCn^iMq1 zvZlMyMPX5&1Wa8VHqgdNrlhnU9~4{!wKrF{0#f-0^P^(jr|R8uC_x4;)A z`mjBmr>1bzv1W-ckTVt#9(l4$T@Gmj}f8sZUX8w_VT;mgWYIxL) zD$r(S>NX5EhBrC`@Wf4`gcwRn%6w}#@!Kg!n1ZG#Q?N26Gi7wj*(qGvP{hipCO|t_ zi-YSCa+hwBb?)`XA=`)z+iYO78Lz<)h8xV>{&rsfcCcU)%?JV4-21-{DcJGEA{@dL z8Q>2?$2K%IZhCI>lJCr6Xpo$)XlUS9?_Ji^8_3|NCQdji>)VM~x#Vlh2M*ma-7V#} zKC$SoSvPLDu~anRg9o$SoI%gCwJ#z!gP+-5;prC<&#t#$x~!hz&+!*|@~@hEa6}P4 z{zP5Gub|7Px##}+k>(G4?oS`&{DQ{c|3Mw(VNtH}_uqS8rK0b%jyOm-p;!u7kuwU^%M_J^3U^l9}lZ|sN*|$%;Lr{ z9si#iX*=cc<0`K_dO-an{|S#*@tb)p@Lv8!{xzN(;*~A;t8}!T{6YRZJlT{V*SKss z8~6F#+n-<5tFFk;aNfe)M66wNRhb9OJLt)qn`2XpR`q<{H1%|T9CK9-O`qj2@C0k! zgL1CB*Bel6{ImSaJTbt*D#U)EU5_NAbs0?7fXcKQnKvf;sZU()%)Ld`A)65As z*>1JrI$MVgv#ZmkHe#H-6pf_UBzQ@J??`Z1f}IkSNQc7eJ^YGY1az!H2c!8r-umf)zwh9aA# zof4)4lrx(`34P6W>3NA4BzXES(*KpPr2O+!>g&?GEOqFcDt+ZD_P#|DE|KbJOQK!A z-up=I@^y5_Zq+4$PJ&M)I3ocY?Nx~^mY|E3O0)}Er}bF9Hh*1v#VYkh3GSBwh9nqd zz0OH^w{%!~Mj}JGo8MTGz$LL&Xm3+0W_GB%C15w9Fh@&q<3$Fq*ICXb?URU0+Mq4& zjKpglYUwe|oa0>*(Gew5rY1-WnM;VDfQ3qfin%~7r%#K;Y86dLH{dW3o7t?Ht7zkV z;IzvOnEh#3Z7oo1%xz|D<|sNinQ)d&_Qy4=)Z|6VYRZI@T5xlm3D%Q0%i@!P%L5D< zuiQ0@YYlQHt?TcqT{W5J%f(l(UPWix)#pN*KOZ1?#pTH21Fs1YOy)yVUD?^S*`wLR zwb{0f7`*~5u&NSnhN@q{54jIN{crM(&jxvmrjgAhPkgq79H;3?Ti8l1_T#K#haqS&^k5F zXjfS^ZgX@yaJ2&<|N5#Eq^I5t_U>TrvHDFA$Kx2|II<6j<)!JpGtM~n{+|t+JA7^nG6n>zm2V`A6 zWfXaF8sGUQ818+%C^f@z=?}+_;px1=cWI?n2cGPfe6Yx*y@y{wVdSM6-#;n08J)YF z_$YUh!`d&WY`5>XPea zJNUTBFk})zQ2!TgJbGrO5Uw=?J@xv;TSt!Jnas{pIcKmAR(~z~a&`w8OXh9qedhSh zU%jyy&&hI=szi1F)&)0gzPVYP__Mzj&w+c>x3?Bf{9Cy54vIsS6h2Q-`1s*#O6zJr z%=N=U7tD4++5$Ben5BDBhnM>y=9}i5?ISOF;iUqY?FOd{V$Nw!Y!dwwQ$Jx0u~Jy^pu@E(`mCH=5R4 zVLq+M@KzVkTlI7s%&HMC6^;rMH*(5X%6vxq1oyr`T@VrW#Nb@5ajj|exQIjuK$HLJ5c^Yw=@v_oxwFQJ97{mk98=G@QRO(~73X9IxK8c^dS9kg@j|zLWXmS-kqh z&SR6Y>Jxv%HZ2@M)0=Va#Oqh?{$Bna&tzgND9V$aCKxnsFm5%H4i4_-c5#$(&LVg- z*-7wn0yCL?V!qjAz8MpWwZvo;<{Nu?j^ZT88ENGP6Z>r~vcd$MiT&_ew}~^Ec=Hhq zoAFMJGnmRK4Hz3)yqy>n2SjWWTg18I7V%l}BT@Ihcv+-El%ko(xTewnr?h7QZsN$Y zyJz&V^|i;AZHzr*YzJ(CB&=<~e1##l{K3+K?F18S3t6@WwxpG0ICksSy1ImH!Z~(j zcgZFpA^W*4mO$YIAC(+z@Me?yg(O@RyOk>9h1=Yx?h*=Lj!Wf(<$K*4JHT>D-JYbL ze$(&MuV1%%x_f%wz;VhdDAts9+$tdJCF?b|XvTK4pIPqo^4OZkV~@pP_GU+6%%;hQ z;j4u^g{WA@okE1`!;vY#P?_a#XWoVDN;I#kKiAImFc3-|4Y4M zRSH-5zP(=3Dm)MN&%wr5Q4Xl6=*?ce2M*tm@O=`_m9Qw`n-c!5^sf?`B`k__FlR5;-KDmHtK&;u6MwsYfE;khV(1Dk%~HL2D+ZH>8`A zFfQSw^sq!Yg(xu^VGTU7l4faTuLrNQA9dmlp7bPc@uYw4JZewB>A_yHYV38~kbd>G z(LI6D^XYTg`|GhFYuj@(=}$%LTo;VU&65P7zs89DvtTABKpj~{CH-k%ANvoiX75)J zPND*I*&~VhdHEIjf+36TTM*0|Gd8nTqeUhwTfWOBg#fT|w8`9nNgjrQE31&8)#kO? zh^c5S<3qs1d?{QiB3Cn~{-RjXc#&p3$HH&Ch&dh*S#kWV4}s=p|qZUBfqY)$mP@Ic{}(9B-@46;O@S{^-K<7i>1s#rh1at}{Tf z@%(&xH4uU>gyw(KNlXK!Jtcth<q@ZE z72YSx=r28cB*lGsJk;C&zcy+}Y15T7b?ef??3-j8%MC@AZniYc#MnkNW67E>St{Bn zk)jao*Ht8x%2GnNg;KP+RHUM0|DN-n8OBWY`Q!I^{I18HJM(_M&v~8adA^o&-sf@e z>%INb#+paR>K)frtlDs#x8%x}4LAjE^laD7>}hUNwoaB`Jf~5n87~3uxY-Z}RmhW3NZ&J;POU+wwo1z9)4eBtn!=!hto6mOh14w?7kblLLqkLTgP*g&b^Ij zoaeiSR`8YQ?fr{K#ow$Gt?uq#FYoAkJ>V$0xm=w+k`}Go@uAW(>*lJSoR*3Y79;k% z^^|r;ZM02L3!1ALxy$+#CHP!)@WI0zY(n+x&rzXk)Ay5}DY~k9SWnMRTbxo$&o_$)F zXZWPCDB$LmKNk?Z;&_Xu795>-+K$qf^vv zV7m=rO4Z)_J5*L=x>yZ%&2h;%60BR?`rNdPKEoy>nQU!s(Rql8>v2CMbECy3G-3Zx zQ-sVf&1*ktKeebv#qVBD-Y~d0b_wSJ+sxx#$;#%%{W%K#l`TON0^_o^O5YDH3rjtq zygdA4buk+3$@_EVFNC-kskOB`_MqmbpUlyq{l4fVQ~9}RH91-Nyz3!#^=l_8 z^Lvbg6Hq7TyjF^*OLODQO){wmFcy(e*F_|#S#COpfZ0qsb}2b&PD5Kgk5n6PSIW->2&g1 zkCWP$sPblw+W}V-K4k8UzH{jGZS8wCS5Dr1f3i`ZQK(IEIWQyl8}>FXzR3f#Oe=n+ zhRH*-YLh!0^!X<*N{l`&m}6;WG2bvlZN-w`48`|J}+ zt)AGB*h6VMx>axFPxtm|?`~Ea+f!?MX^%sk9HI-i49@;FGrB6OJx+G^oSHjQialdEDspObz0$Z`6uXALpO8HcgM zTC2RbYox2_KJc+0Y5BeOTFl!nS@jY2u(JV=ll|U5ksZFX(fnxa(W#+V|LPfPkBsh} z&fvFIn(7)luGPHr;PH~;SSwtVQ-vZsAY*dDw3AixY}T8}c{>Z|mj8LQ%y{xEo$Cb? zo#);Prv^@3m-B5x-sI2;*Zud&wwkV!;Y>_PoakhP$0r*)9E|U>ZPi7E`W#jKd%eYp zN}0`aM9qDhO+DM1-%x-5bBprjOr`5XQ~eI+sb*JN?%*uEn|NFQuv~vWUeir`lG(^a7c#xg%+1 z6i=Gj{rODc1Jd-3b0 zh!S?M)HQX(-#%xz9zTyg zAJn{|Gw4~tow+XyI_byi%?M{EZhXVEbdHxj?Gx#y{5m0;e7~Sx7i$%ei;bM8wt+FB zLhj~@RTJ+OqP+Ggm7FMdU>c~~ZHg(~Tbp}Pt(B2iZjW8Wwq;Ce|9HK;sachpQT`io zXT;g^txR>)gH1I%^Se*kN8_v~A4AP%&P&d_xMSlq+i2qxPjbptY&BFjGBj7r4bLmr z=G%NrLcPx@8B9XCXWV}EH)=t>$|u5Ha%K#1rCnpxKSwvrC7)F6e5rZA@kUT#1Uh%n zFn3$g#+6OzQPl0<6gzen{my4`&yIzR<)NcQ%N+oOFED-w3y|HPElBvxj&MLzx?>Ej^B$N zzn+*S7x`Fek0PxMby#P<{Fhy`UJg$^)P&oos^V=KwQ2i-Nmdn2X3vzTw9dX?`X*9e z=hmI$^PBT2b1YU=Yt-*LWT0U>dDEn!PgWkWN&F*TpW?XK<5Cx2K%X)S(%N>y>Bg6S z&tF_(x?WhhPRO6t!_BbkeKVk)JEG^s>}ambT3Xbhz&di*S1x{G+MPOD%EALWSElB8 z7MmvMR$ZF$E#*<^DFyD6wbZ)b6DI1MaNuJ$t*#}tw}y7J%=K#{JR%-{eO2F|8E7BJ zzH+>+Xm5`C_7K;?3E}hTUN7;ljpV*HOdtlDDduq(s;W%9qnC`CHnL~_6}Q-iZF==v z-!j& z`2V@I)Ae~Q^b57)hyM!Re6xno@mu({9maL*Lw)9zt=^qvuk(4z*N8})MTrp>!@O)W z-8DSMu5Z5l7HSyX@A>uQ1)CDjJ6;&L(Cd`w9P#ohx#_bKIdjIgTDgYDRewBx&{@VS zYe~rd6gHVBi+}l30#wQT7qU}>+4%1-gWuRL5<6*Nr~Ets5+g_eaC$@l8$TtWm_7+{C8?# zlx5-(?z`fMSkrXXkOQ|AvlRC(nq{51N6&D5SJ}nmO9$<^MAyT=PJhJRUwqNIZQj#@ za#fcZjaPHH_F+2%Fi%Mepb|}VzqIy;cj&I>YW=H7KP~co~EI!&y>YZ z-F4sMZmSQZ=?>S%=<|M=>KRYUIvQ2czv4;LMU%_Ks_C`60vDVoKl7*$puBMVct+1T zB4hK%hkwxCulbV2`m(#^b|*P7y!eq1TYY|k8^trw;orveUA}i?qiM$sYElYh%XU_} zcs4PvP|7k>o0@epdb^HZZ%IA<>_$R5Ct$rt871=R>cBPMW>r{rgfjVa9wiYiA9e)! zW#%y5yNxh1YO<$fE+$`N(*0M@j$7KBlh7F;6Z0~*OQnIjW3Te*MZYe5wwb?3|EkzX%%N=t6XY%)I#=>f*99K^i9!DnPVFP%9 zB4LGM9UYAAc0U%zgyYLMV)5L#Y(GASi(IZ}_^>d>Qgx46T=v%Q*F2_>MKzDHBs7hR z`%%ebDh-Fiz`y^HI_HHSLaV?ki}a^9a{UF^}L}CgfN+j>t)#!WA_<1m!^gII_B;+ zlH>VAkY6xE8W;Bat`E1M^AvcYANm7OS6=Fp7VAcvH<+f}@@c3E$Qrp`JUqmx^lx~R_laKx0ZMBJ^+hF_N7tKZeMiNeSmFpquAcDVh1s5IWN zWjOGH)1|v_pM^erV0~0Gi|x?wS)EzbxOnyDwyN~)(P>`EbBeYM$#bh!Vy?M{>>4Z& zIM;%e|Gd)cYn9`_v$j{N+~asRZ11g^uVA7Gir`o|r zclYp`)|$jG+945tU+BBE**)ARr^U;q+s!#4Jm+=eu$yIC-y{99%5-Y~!Wtmz>Bq72-P+3(>bTm=u+A~8jZDIVI?3RJ2O$e(1W1?YLFzAq`{M24Z>UqGTTCCw4gxv+hI(NgW>L*$kq!hO zxCigU4-rch=|^feBYGiz66g!{fp6Fj;uqZ2)ERB;}pSm+LD5*!2Iu;OyuY*>6pjFpiI z#+DVtcNXs^meUY#vSPTiFjfpM%$0yX2*#SlhMXWrnAJ%% z5}HcHJ7Y|_9DhGBxD9YkhA+<#>TuhRq1%Am8^CsBS(_SS)}r~qZFO}qbbt^H!9ycM zh)qLm7XJwzq=>f|9>_Rc!bA(#|8IVfRY(jEC|&;F0>VL$u@*-p=F9(y$#95QiX#$x zHNk&_2#Hg1M51lcf8|Da=r-b3BZ<9=(T*d5tqhk35(iSY9%I26{Vkp_kh{W& z5O#!nAf%%pJs}+_$bcXVviaUD9TQ^{6D*cQ#A3-*@Bs-GkHums@Y)D`gKG+qkILXR zxPbS_H1IJ7AMhRxGF{L{)CXQ8IzSr=&Agig+GFEpQ1umZ${)eAguee8n}9~aAY@t#oj`Ky zM3!1&#-}luj>zDH_$J^6`Xv%YLN3JtL2gOlKJ<4Y;2aMC6R4w{Ax0kKjDjbP%eF$8 zNP-YrS-=^RIK-U;3Vy~J^iLS*01mQh0XPUW=oWxKWDS7Rf~Fo@ea7GkQ!Pjb;x-+J z51~;=DH4$4_@Llt_+Sh{J3@d|^p|Sn2$U8-pV-G4J&H^&`p+T6jjsNmrgg|YNw z`5L(K*&JWNE)R{Q0bhS_Z#42REKt6Y%?f030qa{?@S&8&1KV&THqXzSvE7K{hHO!h z2UEDu#aR0Dz1hC-X%~q^6xay+x@)j}HBAkdi*JM}Vr$%gzRRU^SqyNHD%xm^!d|$H zj;14U7Qpm(V~N^HZ4zN1!WB(yRfB!;B5#8c0Cd9$Aiw|xasu=`WO!f#BL<(PVWf@6 z;&50z4v)hTz=qR7eYu!fgMbxK;GBnm0!oB`XflaJAfdOS5g85yKO`6ap?w81_!~M4 z{2P;za5!KiaT(}Kg1#j#1JQ~jNXl?HDqcbc`htl|^abtU>C8BNpesQF3+PM50(X~e zC#4Th1gT?uJJ6Q~4~@q41?^~(8~`#2ASlG!;lNTvlb8nu%V?%&I2sSfPKlnGy*uCm(WKez>}=;W6(%a_9cMBc}ZRfU|dOj zBs_3N34K5&#Q_OVf^S?Mu;IQz8bK~$)@i;Ej7tgM05S@Eo)pId;sZ~Run~|Ea1#81SRhcP#-I?1 zQe#la6e)dhXD&G|Fu7D;Do$D-l^|(f9F|JLO7TJ^!{@p2a{~v`QdnqMX?-+2R>DRg z1__dO12U4N?*bV}Fp}fasFHCGayIx!q8-TN;&IOBGT7cME<6skVF$B78pl|3ID9mW zNqE@2)^{rhoVyDzjL?o~9TJ6ZK*50%ARN}f5bRe-L?bdCtSST}a410~80(_`cN4fn z04p`!gXQMM^Y=kxs1!r2F^x($HZ~yAiN=N?q0q=i1gZfQMRZtaImp7 zF>L=*c-V3ehKqSGe3d;y!>4!9PAxj9bOdS`e!Hp zjt}>5T61u+H-FLGZ{7bp&cAEX#ope=!N?9^^Iuy0yZ*fj`IoZ)uC>48dTs5$a}oSa z?<{SN%mK~}!j>+!Mh?#ZWqe^@m`&{MT>c!xV?7=BW%fkhHG_bC8>eJGpR~o6+r&*u z#|;*@qnIPvYfDJEf>xviFi4=81Ku%#^e~_K=f+Ocau~u)A0-3Iz67%STS;O^8dU## z#Sr211&%q#-l|}BWwHsJcDKeZQREUu^3C0tI0F58dF-{e+eULM*2Dl#She^*;+jf9 zGIU=S%ItKOwEMP%{ViEXk}<7r6Qgk(hhpV~t)lVa=7|!zoBnyNPtK0Ry7abv^(*y^ zJ9?BOVXcj_86!m^V;@@4``bQ$bvjR2tUs%d&?W)bjZ?APMn;=UkfqP%AfGKZ&mMQ- zwpBn_l8)nPpPrt6Q-lr%Db~R{jkFCD6dMl4ZpPj2R-LpP6SCbp73MiYN~Xn&`qq5b zVMIfmZKhigeUDy#bcLEaO)K3%V>8=)Zsrz(9TKI=J;yxR)3FRUv{fOc=c6wBj+;}@ zZH~*TF!7_ugBgFdOPjS$x_f|HK?TXQ~?)lk3t{ z^*!ZlLz8QN385}Wv}zOVtwUEr2^rn>WCnC3=I&+1yfw!mF*B4^&8uqrHC-?iqzg~8 zW}M88Jt-d zZ|<2Pg_e5G*1YMd*9yq$c@xfpNM1=pRcyeDK52Q;%d7V;o%Em)Enl{;Ac=Tb!y{p? zPEhxQu*?x(oC9*BF(~L86rCdTqN%9)yv@UvvYw%e?k`R;GO#?1JCjwLOJTV@>9fKX zVC$zk)V9!u$>ICv>KDC+?fR4tErzCYnc{6B@0E+H&_APSa+BBM#+(ot>4p$>6re+;;{|Xkn+98VvB5qL5Ipg z!h$Bm2v(A5t7}`coEF+>15=6zq?`qFF|)RG-JbZPnaQ={JJt=B&$a+0SPoO71E*2k z^TFk0B=SZJEv#KSkV9@VblVM$=abtM6&57k<*7QaFLyQi8MG#2(-Clyz7M-jX?h@S z9~r++;D|qUNF?oBC2jL>TT;z6Wfq5}(%fZZp3?@pFHB0~M97hDSOn~aE8a`M%-{5d zKe6e;(0@Q5ou`UU##ON4ha+)CA9?Sw;5Eh1&kwiZ^R|&W^ZG~+M3*f$&-(HDTT`dO z+#CCa`pUuv(M=<-NuOGR2#H2gB>?&-kS|L?!>dLQrVcFx89@T}iOX;C+YIzNb)5_H z^PrHNBLE~JKEO)An~Ms^C&xaTmI&5sHKm0l7Ss$mop!*)7hA=m%k<(Kixj8wJHy>; zL8DLP%ySgzrq?$l&H$uS(VedfY59@e;M%&v@&|H5;5Erc?#3Fa7y7;AO+-=b@m|iM zfrhyN_e71a)Il3UuAzVim!L9aZpBM zqKq$C1i}hOSg|Co=t4y~Ou`?`tb$cQ*GuM`ip$-`@+QFPijd32B@CC_GaeUv87u7m zxDawEpTfOAzXx(Cce8bK0yJg%wO9yA{XyY0UIal!{-(vfzo2WEtVCw0)#k&4-w(I9 zkND1oE&}c5vX3Zk>+RRygq$oM>3`%ts{kPDmE5wuncv{8&FS5W^mSgfo>4pRtwP=K zn-cQ7>)MXJl?BXV zyuB;F$>+JjCyJvLx1+!4h*piy=#KeJR&2>?9nwAMIMuBZ!rQ#b!#bt-NKCfvJE_DE zcz;ey(LSN#FMp};u+?p-A2abIc@JT+T_utC)@ge%dsTmv|AEpkFhXd=As~-%1L9td z6P&iV`_q@oZXStX-~QVtqy-VzV-48@zn)(CAE*qj`y$on-Te#oxm@W~xnlcvX#Vs! zNpR#alxFtLv&vdt<%{<#`u0BH*+oyTJDYbK3m{>8l%6ZN#~jkrEdUBFUfHH-^}5)r z>#h^L28Gd0D^T^sivJ$3cXMeEw<5?LvesZ)A zIr%Zk`vC_EFDn%)?Bw3~Fx=(CjSLMhh!Kp6$qD->RtX-Npm{C4K3+&>qaeuxKkS6B z1K@R=g1G9*=*?k+Z;gPtch=A4g5r4!D=@(PG95-pf$(T=?cH?MTV1b8$_z(WWM^-# zN_-Z1T1u-|%eUQX&x0m`4tb54WFaDUdxRU8j?wb3fPIBTtl+W?RV*n+=Q8<^x*x<$@x?F^+T^TJf=Te76%qVK{(&h zQ%bw1`#3V&N@xEZ)9NkZy9(n|oA1p+T)g`4VoKGkS<>hG`-a8Cwp zi=Fs_azTJ+P)h4s4(6e%%p(w1WIDN|cJD{)q{rKitMj=>YyJt=mU0jQXJjL6Hl)%} z!CSZ-h*8QERl`FKUbUUl9-T$8;F5{L^UorwlW%I_+UETG-hBOpX+Bx=rmPVe)g(tV zw2`GgSXnv~5BQ=>WYq}XQ;q1fB(x2WFiz?)^`lWj8|u-^RfYrTr~GBp zn!xt5);?gIsYx$ywF-=qkxH$;4$jNj63MdzFp)kX(tU_`lOL;#xdS$#Hbh{M6oq2e zq3zaDG#fpnaF!^K$7>+(g4a4vG>lSW#;)GiPr=4+*C&oFJx*Cg%$73~t_l8SH2`SUqr!XMV|~l# z&Jb&iGk~!K9urL$CMvZeHfH*DRbwT+Bx0t;7p=r(1&eF*Np_fV=wuAbvjKTQKj?BF7pQqx4}5jTgXV_8 zCiKAcfpd5G_(&*}xy6j7uX+2n;@q20V?V~=5p7QzVu7~M=44D9CT(l=ld`)zt4mLp zsPi3|V5?9vVQ|^!>k+L5+hMD)Q zn!=ii-#}jI{L$#NY+5L(YjQ;W`I3{I0$C|vg~543W~YH5FBa69qi3@)Fl-YWzpT4Q z3LZgc-AG8D57VNlv=LH3DeIlZHJeJrjUS7%oJ=_ssWs|Y_V7E}^g+rjuf&8r71>H$ zoc3Fx(zEL(p$=Pp6dLd4)Z|tl(#K$mkBnI%wSxJU5@_-s4|8`S??ZWoLu0l$r&)n| z(}0Ws>G_sU8r1=~inkE6>w|-&BVxqhscsn9*BeKNO8LoBydu4hsSuO+i(H?Bgn0Lt zEY7@Do1B$5<<*wCDjm&RTYHM(&v)VZbH6IyO=WyS9BYere_F=ehvP#`B?>m}Yz#`c zl2HW>h-%}v$9WH0SUQ#FvJz9JmcmO%%aMjQX-$I395SFe1X!=G+KD}x0lv6eKF#i` z7@gF_OnV$F*La*l4icOY&51dW%{MlPIi413{7S_vQ=5=3To;l3^~4=NPTrcgEKvosm7rp#Z@a!LF%i<#4sfYPsy3;J|O4?7ckUS@EC(u^a-+VL5NNXLI z;wu(>?&ryDO(K~uW2oO;#i;Cx{EQ=bix&KG>U`MC^!?bwUX1n8=V8g>CT>^kuq8T@r}MO8lH?O;!dUy1)X?hM7t#=*bZ11(nGN7tYXNN zN9g<*xqBD4#Vj>l*|}=eu9vS zkzrzwg3nNSwa~|5j3`!!=Y&oaM40$g;a{0B-GCEZ2dZyigdmD?-khY|*_=4bm-S0= z2#m4MoHXR@77a{6l);jp-O#tQ6> z2rqzP43rQt{p`Fp*BvK+;+|FaMaE{*BfwSWGUmsF{cx@R&8dP``a_ro)H%!Gz?{S~ z%o&I>(xO%C+G5`NsdQl~55VA&MCH1a31#vw`q6zqCwJ#VNCK2iuOv+y+EBi9?>!7Y zKl$uBNtuZ;o`5lftSRJ3@jEZ@7{3Cil)62F49T;>9YW?b1+AyTNXHS4j6mPiMHcci z+9C&pR)A=}`{LZ;o2J_Q0IGN`$AQUUZUSeF-FGJ%s{yI@t6jj&rQ0>=J^>7!E}eR@ z@ZTJEJK%%wxUZGSVN-b`SB#b`l72W=w9sqT3l5O^YQdvjE&%~{m55C600rF4vqt1@ z65=CQn)6}KQ?4}TXSKd5G!DE_V1Y5asGCo!R$|^hy+iBXC9bVZ6G^sOtLID z1{U-LVJU2rR8Z3B7cwp^K*If4v;NAh-mc*kCmV<=HJ=*1jTWAtp5ARj?(g}cKwOec zTVPz#BB=2F;Bg1>-jDUAR3Si7%!bqTAT}^ups?p5=fs1?rl^_F4sGwXm#j+#m>)lx zsJS0FV9tcxQ^bUw50Qwh@pz}N4LI;Ddx0Y6CaJ8plD=coBt^Hj&rk)!Z0Q28iGLzb zg+26*f@Rt3A0GtOVcZ5S$V(ITt#_lp)=Q2GrVT->+Vk!;L^m^=JW}iJJ^ZF-?*0Co z$AiXw5{RT2i=_iskAQ5RXQXMX2a?z~&03>WHi51fuu5b`dUrvLBcMG@V_gL8do)%J zILaUm*Qj_Z5;U>TT;KayiUa+&Mg~ikHAl3-{A27QRL#33otV>&N?cJkMBHtPiNNpQ zU>WLJxb;hVm~k zESXP3BNK>GWPm>na(w?RdnO+cgrPzX~UvF&S)*tH}D&N6D4nOUOJ5|7-`AO+aMIY8t-=M#*4w^qd^saYfeDt&8k_IYFr|#%pcy?-bKd>vgwYJqV2Da z1gb~P#Jj>H_?}kqVS=pYA_oiziiGd1-+-LJ0CRCTm|or~zOne`MN50=$@a5mheN>Dy>M zV`v?x`kT*_U3up%dI>4tx86LVg|`gu1*xBOH-#H2*(z5MV7!|NU@9xr!oxD*dG`oa z8{Zu&jlqxyQEO!tgQQimV*tr3d0dg0Pa#KQ&qm!r!pXrcTr?^QWd&qT(D8hCeBl}G zgE;%ZvD?7~qG-L0g~|SHHUyS7Hx##zoJ(cn2DP4&C50IKo?e5ut^g_GySQqVmC;%_ z$lMJKW9uzd-`eeICkZI@OkcNatJ!*R(pr)Zipya_kRl#@Av4s~p#^4ZiRx_)Co4=l zd1hRNK&3K#D7~N%bT3Vj5wU!Q+ypls>bWh86@}VH(G!$65I_VrIe{1&JY!i=vS4b5 zmv1nKZFH|(HSiFsS`KGQ#~Ao=RN#JHz*|`;x%$p*^C_imw~H*BD?gJ&(WVU|d=Xwq z^hB`EoL1-?TV^=rV-G0I24}l%svSVTOfI2!Fm0WS?;G@+3ReLI+C< zm9%*!d-^Ge@5tzR?J03vh8e)8UArye#R(k9uEko;uZ9-Y$9#4EkVcus+rtX0_#-d< zUB0a05s^T+2EwpF&QsZV%BjHPHi(B@H&D?eo>Ylj3O zZ0!T4C}ZnHyI1n(CQKIw<+bTjP1wiUjvlHSPVO)U&$X`L24-~H`w7{IzAwHo4L*=4 z+R>$)-Vyh2e20WxBUBN@pa+Ba==Hk^pT6f+T<|u}T^+lX+uFhx(X6=WX?$oW>(Q4= zvzdxlzRP(dB1t~tqQY_&$R5bGH$sq)rWMKY1*Et=ADF9zZ-z7ki@a+Cx&7!yY$H9= zm#_M*_1r}nxS2it)aqODi1ISyhex%(>%JPK!8y7K_bP!6`TNcR1Gl#+;y$~?pU_I$ zSS8;^4VCnrL**x`G5cfj0OpwCB+PTc4{Ry!*x{YhVYc)snjwX&MRCRC@{G7u22iQJ z$*B#7@xYDjOPOq=68$;%;Q8_^ORc0o+ow2`eP9%_)CZ|OI(0dxRo`cx^+auGg`3ie zuToyKC+$0$=XN8Q_n6rQvg=f$E*EMfZt{v0)H~D>p;WSr)iT?a^X7Z3O5#sxdV4f_ zYeiDwfC*0h)h8#AlLX>(Hrz9Pv~IDIs9HDpqw+P~ynr_MbzgJrkD`=&8F)WMezYhO z9TWi{(kgr$+k5b0lzR?OMucGm%gBadFt2p4z`3}+Z6PM|{$6JChOWh$sBqck;&<^|>HW zF18jdo{i^>$oM^>?5yCr=@9w#cX)8|m5mcfGT4IP&lC6OWgs(RFu?krwY>0cZF@xI zQ5f88!u(J7jA|ERU+P6UouG^fkRM5vz#h@Np<>um2}cd7Kz$cA)ssyzf!p;e60-)? zlH8Jn&=^|z7t36n5JI4;WjtHD5^0e(O9an(;`5GrRQxL~zSAtr`$`~%Ynh0Fh*wt` z!qt33ke1$~MwN>9ng<93u^Mvg$fkh%87tCK3|ZW)DH-W!3NJ)7 zFCgY1OSvIj;Q^3sF0GBsd*P;0DTPWBcy;Uiu=N%Qes~d(H3~CI*0T9Jb4Cq=GGTQd z0~{XMgdK=G1OeTaE2yA~gRUD@5mH#kU}R=v*BWS%bOlWc%K_MoZvI;O6wxe%;peA^ zQ{}i|hK16^)g6YL>~-@Bpj?AOaF`ULj$|%Q^Lx7bIC>+9a4S4gY8;U?E?3}lZiJzr z77~QWYf+(!P{V+MNoBt-YvA3s?}LwvdP;j+rlJgvWMpi?m?2b+kwp`syXT%tVF1F@%N zhn`VyKq_rW@j_gcy}+^_?9U86r?V{@1ZUmslGI^34#;Fgv~^-OYVJX9p(MHE7R#K` z-X5S~io{987x*UU#A)}oC+p%8!kWfRfrMEpK%(Z*SQom&o>7rX29E8#eR~gmZGa7b z$D_J*qtaYfMT%H`?wz0!V??&D zA!yp{knDoC4;`4az+Bsf-#}57BN-u?-vpBkhNN6eqb)c>P%$G7wFS#oQXoNGOoPsW zm0Qb|xX#W6KdJYsFLcE9U_WtIO!LMTpY_gX_o?P|IqiE^dFf5$#U<2fUAxD=Tc~xX z<-yK5{djD0mA@2(p2P;m5B-+mRBP2gr*L1>) z)harxO~0}^yu8l4yS-Lr1U9)jWATz4$%@|jVb1RjmC^z2oAg}x{Du0|<`t^|Dd&tO zB5oJ985EMkNe_UXJBM~Zn|}V%k$6;cKaGpCJ#y~(@UrZtINnvbt6i}tUDbU%MvGN7*qxZC5$eSAJH0ss{xA&BE!v`rhF6dU4cooCZ9k+KE`J4l3f<3CgtS~s_%RS_^*9XHn;-32)yI z>&z!2whpnu)J!Q*;~il~LIU7FQp_Wsu^0o+0iE_mH6ufk(DF*yYIA)KhA}kwt z$~kKqi#nFKgYBhO-m>Ek{G^*Wn4 zTFTs4+DVF;$$8ROPt5te1Le4sQ%UvNlyZ3yMG94BYt8k; z6k~3uk^LC@L_LQe_SVo)+Yn8$yy}T$VaKTqU@yCE8P`*52PjYbAW%6Fzm81I;}$6= zIrgndorAfv9P+@$bxmXADNIk%mLn7cj)xI7E4x0XPG^B=9V)-GTCr47x19&0qhaE% zY^miMhdbDxsmIaGXS8;y1oXJ0c;C~H3Vamk)Y&G0u>lVh$^WpHP<@`(O9%x?WOWWB z1^k6)EIYzK($FfF55UyrNx=?3ma&A9;aMeTdtt+pnQrlxsrSZQfaR!uAN=U580tf~ zmq)xrlXZ(h0ZkcGW+8SR_Kd1`m}HCllDMB>MX6Z}o4q*o5Y4KrGZ-(Nt=+_;F4k^d zB~W(QikG~+{mqtrWSdvrdW~{s1G|0N;uqa%%cPcyJc0J*iae*}GoWgzR6P+P=PXYe znBww`uju_z?V$rug?;*vfi?)ho>gP!^#<2RK}S~83wrkHVFO0jAsZh-f$>q}uBMDf zCZmC&Iv@Nih*5o1Tz6AjJoWJWn!D&{+cW3z(e>BN3wHZro3Kwa)Xk4{At3nX;GPan zfW0SJt<|6FlS0O3k?UnyK2(*Mx-nHmIFI*frA9{`Q`!^WI1*MdLo&m3H;jhpg6U?U za5+KG9$Gsz`=Po;w)O7?_ z7FvS=0lj{H`dtBBj9!n3yaF#5LN|bu^NZ&dA2Aa>BQYVs&cxo-($1WZSWQ)&j+0n` zABNZ7%*@gR!2Qb^i|)m1%ZTuW)y|pwk19T5S0_8}pKe>+c1E@UXKoi0?%(~lxPPbS z{@KtUcRXw??X3BTEnHk2xEUDS-QDTkS?KMZ%o&)txVRX8))N2Ol&Q(T=p0;~Y<^Oi znlQXLX}x-gIWsWPGco+4eU>OY5Tvzywd*kl=~gP+{x7RMUekL zC@(UzFqku#8o3zJxmg0-Nq$NGM?e01Z@K=}+drtef17yB#DqT^<#us0vU4`Gcltx9 zf64GN^Q{LG9@OmvKFbWDt@%#7TOFaIpO41YuS2QR?X(&go$pfml`uaI9+m61)7 zl|zn+L*-Al|3~ugoF<+oFNPU@CPv2pp#QUi*VKgj-vwh;Wn$%KX6I(+;AQw#`3I&s z!0r#nM1F-AWhnf7I)AE!g`SyI*3!<^gYe%g3|{;ItpFx23@prywoF{4!mgG!rgU6v zjBI}j^B3YDE&S_IfAyZUbat^cA^fQud*fG_mwD%E=kl^(FtQRe@bmsTuH1j<`d8^+ z5I=AKv+w1rJD{qCtF5t}k)@3@gUhchy@Q?k@yr9yG%OIrS+%sP9T{1g+ZzDnOaZBF zVU#X@#vAToUHSbjQydda+XX6QG9G#|tQ-fZW;=Q|5mKj^45KQhC?@#Uk2W7JD6$MD zoO5vGeNkYds?`zXC%Ej$j~oX4t#rY-z%oHQW#Q$hgG#H`+FC;E#&i{?kL(? z;J>hyRZg1J=ZM^zq|J~5^$Lq&;1Ax zV=SHW6!fMfQV*AVGHG*_jh$R$0=4`XW7$UvJ%VJ-s%;U6VBx|g7?*vct_jrK$WD}; z%__C*3M%ePUm14=1^U~Z{UwT-XIWTy5!QDIo$+{K`bz z{zXqqyZC&MtG#!&;jYQ5ZVH{YWdQu>$>w2E4V1?Dubo6MR%p7Q)j^J) zUgRJ(xqx|aH2y-ytb`IW(nI9&gv1ovS7iMj-ilQG zQ%cZ*@Q)nA%{LoJKDnkW9|GmnLe|x*nYvKs(sBdW4G1{CCP>vyhOBmdmN)aQ02clx zr#q|;r;#i-@eY=Lnb_&LjOO#&?o~Z}cJAnxBcxu&b|P}*Db;4DNa+!3t26>h#`9Fh zRYPg?&l=$z3+@mxG7BQ2blGCc$r@)5sc!BZqhH4E)wNjyn8bBGWtxzE*Sg^M752%9>lvZZgsDodVi)4(lcX>=WXppJoCu?iP;C zbUvsLP6&(+=PB24Zx}<0SzHFYS~noO-e9b;H9J>MUpMpz zQjU}qaOD#*0H@{}JYdCX;4Dl@2))ID zjUmm*z9$}~toIcd-;MJf83a9DJ4&k{D)hN4uR%!h)R%C+(F)C{u`aQ#S9P*v9hxib zhzQ~LZ$Jsz;&I@E{K5c*1=d~M@qM#W3@r-AU8EP%l_YP)ykceAkT%;9+fICsf)cY1 zJvWJ^99aZTyr-6v?LKpvLaz;^m%7h-;RN;V7Tr#ke70*aM`7znOk&Ilxw*2cHOZwk zq_=wA(+gQ8WOqX=luSPF`#q~MBaUjt&{9OgJv9S%qo5wXUv#cMf1SUV%7O=W7GMwe2$=GWZULf1?zEW2MoU9 z$Jh(T_Npu%f>r^&Z{G5H7G8Vezz%8&dCB_33YTv5ONVoK6v%BcqRI<9q(BSmb~qy6 zGHs7nW{%Ts9=9@-p5`6Vr9+ObT$^=s=-09f&dRUU%hh7m>{3HP-iH!XqjM6hpXBIi zW87ukdn(PAyWfy(s9V$-`Fzwo6R(`DsCw?yq5~cN?vzoFh7qJV!fPV17wLe#6~bhV zAm5&P09eq`SwJy4Zj$WTxBFqV39%9-%5`XfRz}Fk@07^P&g_OOx8PgwartdrQp4#0 zpFt@r%pNqM3S9P>Og+q%%y7yyUwN*cDuU4}7++wsbWas)=2iE%P89{ZsY#sOO83g{ zGY-K=v9imUd#y)a246BlqYx*AoUWzaKtN0B2H)aRFLhk+N8Gnw#?oY;2srgN(NZIy zNJ}`9BlxesnTNwcxwklXlcIKYq7UAF^5vC@t=u2FnNo6i++psO1|Soo2e%L}I@5Cx;bCV^Mx z+2J=T+aMtwJgFGwAhKbsXK)_e4n6nM{o`lg?M~eqx%W~fwBS$3B)0v)g(sV+IWs8| zEYsczs8}d)=)n&TafjmXgEwU{uLNU-mN%JKhUG?}s%MO8d6gDXMUNiLaA)Wq%|U&l z4nFq|d}bRDQnK>VZTs{r&_F6D6S8Y3MXQ6aLW~d(1)VW9XWbCeC-7~d7DJ?Sw#c#g z+bsAO2riui;mKnB*pR19b{Ij#kOI+InQxdF?)$Xh*u8WEjehI6Yr|Xviv8gA`OO=} z_@g$MQKpD7`ob$u6vhq%Dv@!xLu}jYC(A^KuFS)d282S}1X`5@qQv8i_8EFt-SL(9 zq~S6JgXiUu{A%}kDw{9}H-*td!xNpN+WrP8fn~DDx23%qc;)oQU|)*fV;UOnl$lX; z&Lm6FX>4w++?!V6?UNJ;GXLi^RI({ibt)mb(|@ z!iH0Hi4sxezqH8D`~#~b_daz78Yvk!`V9Fi?wA#U_wbm<2=;oU7sCky_OAYfQg0#l z=whD_&lm-GQ=5=bCy7Qdo&f49;`ZX&N{FrS6I|fU`|EPYUOlvwnOS%MFX#aYKQpB{ zOuNh^#fb?P-x>Wt)Wu~tDfe0`OSEPoY%MFkTI%JTYP%{GntlHuaKal-Rws0aY@Y5l_9pdr;<~NJEfAib+iYZb+k!W`4ADfma6$< z+2&?ecHx5Q`#};HD|BLa`&)`Hz@agcD4~Xjg=_l!qM!<`4EV%$T$pIX^XW}SZ!lZ} zn2F1K_CD2c_W;D)@dk;VM}?EA8X5f9(hOw<#U20;Is%Xx@f5c$PjCD12pE$A%caK_ zoP4(w$BU3WXJM1mSQ@*RhA={d`hy+z))$-X?QhTYlB{MM6I%R0U)NXD5OiMhm-Tgm z^uMjIXfNxloS3Q*os_&dgYC=d&6F%4v5lP#rOje6?nUi-NuekHqvbs4DW*VX!?41pS$tnb-wA|w0_r&q8` z6&N%$*(boI&2VLHL>H(nKr;4xl7 zzd4)yR4)FFEY=UrjDlv2AZr>(!Sk}AVSh0^gxXGb8dq1;z3BB_$ zC*5_qPjfWByTsXW-;Ok4NzD(fmwak@DC&iT(uff3(sB6hjRGhQ2Ii)!dgGQn9-&_c zB=PJ6uRJgi(BRJ(9xwZLr-@@qr8R_4Z9{o2+zlYWRGtR&3r}uAg{tC1EUs3M=2Ia4?yMIP`4ZQm+ zF}}=;|J3sT3&Z;Z?AL7i-Ff9T5bv*K`vPY2S6JS!8S{(rcZ24uZ_8gv1><*5mtTDU zWbXWn&x7nG==8-R^lJS4#r3PT-}j&Yu77Cif3{HkC(7@u?CW0juf)Ol?>pE3vzu0yuIbKWu2f!J2BLDyZ literal 0 HcmV?d00001 From 9d80ad16f921c9a78255841394e88fa97fb1291c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:25:39 +0100 Subject: [PATCH 615/949] Remove old tools. --- tools/check-compat.sh | 5 ----- tools/fuzz.c | 31 ------------------------------- tools/putty-utf8.sh | 1 - 3 files changed, 37 deletions(-) delete mode 100644 tools/check-compat.sh delete mode 100644 tools/fuzz.c delete mode 100644 tools/putty-utf8.sh diff --git a/tools/check-compat.sh b/tools/check-compat.sh deleted file mode 100644 index b7603a80..00000000 --- a/tools/check-compat.sh +++ /dev/null @@ -1,5 +0,0 @@ -# $Id$ - -grep "#include" compat.h|while read line; do - grep "$line" *.[ch] compat/*.[ch] -done diff --git a/tools/fuzz.c b/tools/fuzz.c deleted file mode 100644 index 39a2a4db..00000000 --- a/tools/fuzz.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -#include -#include -#include -#include - -int -main(void) -{ - time_t t; - int i; - - setvbuf(stdout, NULL, _IONBF, 0); - - t = time(NULL); - srandom((u_int) t); - - for (;;) { - putchar('\033'); - - for (i = 0; i < random() % 25; i++) { - if (i > 22) - putchar(';'); - else - putchar(random() % 256); - } - - /* usleep(100); */ - } -} diff --git a/tools/putty-utf8.sh b/tools/putty-utf8.sh deleted file mode 100644 index 73cb5fb1..00000000 --- a/tools/putty-utf8.sh +++ /dev/null @@ -1 +0,0 @@ -echo -ne \\033%G\\033[?47h\\033%G\\033[?47l From 1df39aa9620b9bb3fc36a52cf9a2765095be561e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:26:50 +0100 Subject: [PATCH 616/949] I don't think we should carry around scripts. I'm not too sure about examples/ at all, nobody is maintaining it... --- examples/tmux_backup.sh | 81 ----------------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 examples/tmux_backup.sh diff --git a/examples/tmux_backup.sh b/examples/tmux_backup.sh deleted file mode 100644 index bc0bf370..00000000 --- a/examples/tmux_backup.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -# -# By Victor Orlikowski. Public domain. -# -# This script maintains snapshots of each pane's -# history buffer, for each tmux session you are running. -# -# It is intended to be run by cron, on whatever interval works -# for you. - -# Maximum number of snapshots to keep. -max_backups=12 -# Names of sessions you may wish to exclude from snapshotting, -# space separated. -ignore_sessions="" -# The directory into which you want your snapshots placed. -# The default is probably "good enough." -backup_dir=~/.tmux_backup/snapshot - -######################################################################## - -# Rotate previous backups. -i=${max_backups} -while [[ ${i} != 0 ]] ; do -if [ -d ${backup_dir}.${i} ] ; then - if [[ ${i} = ${max_backups} ]] ; then - rm -r ${backup_dir}.${i} - else - mv ${backup_dir}.${i} ${backup_dir}.$((${i}+1)) - fi -fi -i=$((${i}-1)) -done - -if [ -d ${backup_dir} ] ; then - mv ${backup_dir} ${backup_dir}.1 -fi - -## Dump hardcopy from all windows in all available tmux sessions. -unset TMUX -for session in $(tmux list-sessions | cut -d' ' -f1 | sed -e 's/:$//') ; do - for ignore_session in ${ignore_sessions} ; do - if [ ${session} = ${ignore_session} ] ; then - continue 2 - fi - done - - # Session name can contain the colon character (":"). - # This can screw up addressing of windows within tmux, since - # target windows are specified as target-session:target-window. - # - # We use uuidgen to create a "safe" temporary session name, - # which we then use to create a "detached" session that "links" - # to the "real" session that we want to back up. - tmpsession=$(uuidgen) - tmux new-session -d -s "$tmpsession" -t "$session" - HISTSIZE=$(tmux show-options -g -t "$tmpsession" | grep "history-limit" | awk '{print $2}') - for win in $(tmux list-windows -t "$tmpsession" | grep -v "^\s" | cut -d' ' -f1 | sed -e 's/:$//'); do - session_dir=$(echo "$session" | sed -e 's/ /_/g' | sed -e 's%/%|%g') - win_spec="$tmpsession":"$win" - - if [ ! -d ${backup_dir}/${session_dir}/${win} ] ; then - mkdir -p ${backup_dir}/${session_dir}/${win} - fi - - for pane in $(tmux list-panes -t "$win_spec" | cut -d' ' -f1 | sed -e 's/:$//'); do - pane_path=${backup_dir}/${session_dir}/${win}/${pane} - pane_spec="$win_spec"."$pane" - - tmux capture-pane -t "$pane_spec" -S -${HISTSIZE} - tmux save-buffer ${pane_path} - - if [ ! -s ${pane_path} ] ; then - sleep 1 - rm ${pane_path} - fi - done - done - tmux kill-session -t "$tmpsession" - -done From 0b22d574e0adc8f27b31fea9e278731ded06ac70 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:46:49 +0100 Subject: [PATCH 617/949] Update FAQ for new behaviour. --- FAQ | 51 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/FAQ b/FAQ index c7d3d15a..2227730f 100644 --- a/FAQ +++ b/FAQ @@ -352,42 +352,33 @@ lock(1) or vlock(1)) by using the following: bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tput civis && read -s -n1' -* vim displays reverse video instead of italics, while less displays italics - (or just regular text) instead of reverse. What's wrong? +* I don't see italics! Or less and vim show italics and reverse the wrong way round! -Screen's terminfo description lacks italics mode and has standout mode in its -place, but using the same escape sequence that urxvt uses for italics. This -means applications (like vim) looking for italics will not find it and might -turn to reverse in its place, while applications (like less) asking for -standout will end up with italics instead of reverse. To make applications -aware that tmux supports italics and to use a proper escape sequence for -standout, you'll need to create a new terminfo file with modified sgr, smso, -rmso, sitm and ritm entries: +GNU screen does not support italics and the "screen" terminfo description uses +the italics escape sequence incorrectly. - $ mkdir $HOME/.terminfo/ - $ screen_terminfo="screen" - $ infocmp "$screen_terminfo" | sed \ - -e 's/^screen[^|]*|[^,]*,/screen-it|screen with italics support,/' \ - -e 's/%?%p1%t;3%/%?%p1%t;7%/' \ - -e 's/smso=[^,]*,/smso=\\E[7m,/' \ - -e 's/rmso=[^,]*,/rmso=\\E[27m,/' \ - -e '$s/$/ sitm=\\E[3m, ritm=\\E[23m,/' > /tmp/screen.terminfo - $ tic /tmp/screen.terminfo +If default-terminal is set to "screen" or matches "screen-*", tmux will behave +like screen and italics will be disabled. + +To enable italics, create a new terminfo entry called "tmux" (some platforms +may already have this, you can check with "infocmp tmux"): + + $ cat < Date: Thu, 4 Jun 2015 08:47:23 +0100 Subject: [PATCH 618/949] Note version this happened. --- FAQ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FAQ b/FAQ index 2227730f..21e6167d 100644 --- a/FAQ +++ b/FAQ @@ -357,8 +357,8 @@ bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tpu GNU screen does not support italics and the "screen" terminfo description uses the italics escape sequence incorrectly. -If default-terminal is set to "screen" or matches "screen-*", tmux will behave -like screen and italics will be disabled. +As of tmux 2.1, if default-terminal is set to "screen" or matches "screen-*", +tmux will behave like screen and italics will be disabled. To enable italics, create a new terminfo entry called "tmux" (some platforms may already have this, you can check with "infocmp tmux"): From 75061cb45d68ad3a7a75839a2f658813e697c55e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:50:20 +0100 Subject: [PATCH 619/949] I no longer need to care about GCC 3. --- Makefile.am | 12 ++---------- configure.ac | 13 ------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/Makefile.am b/Makefile.am index 29294d87..0aec7ae4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,8 +22,7 @@ if IS_GLIBC CFLAGS += -D_GNU_SOURCE endif -# Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly -# different flags. +# Set flags for gcc. if IS_GCC CFLAGS += -std=gnu99 -O2 if IS_DEBUG @@ -32,17 +31,10 @@ CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align -CFLAGS += -Wdeclaration-after-statement +CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign CPPFLAGS += -DDEBUG endif -if IS_GCC4 CPPFLAGS += -iquote. -if IS_DEBUG -CFLAGS += -Wno-pointer-sign -endif -else -CPPFLAGS += -I. -I- -endif endif # Set flags for Solaris. diff --git a/configure.ac b/configure.ac index 0dd1151c..cd3ab5a3 100644 --- a/configure.ac +++ b/configure.ac @@ -44,19 +44,6 @@ fi # Is this gcc? AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) -AC_MSG_CHECKING(for gcc that whines about -I) -AC_EGREP_CPP( - yes, - [ - #if __GNUC__ > 3 - yes - #endif - ], - found_gcc4=yes, - found_gcc4=no -) -AM_CONDITIONAL(IS_GCC4, test "x$found_gcc4" = xyes) -AC_MSG_RESULT($found_gcc4) # Is this Sun CC? AC_EGREP_CPP( From 8fcac1b79449b09466320fb823775e4a24ba19dc Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 4 Jun 2015 09:26:35 +0100 Subject: [PATCH 620/949] SYNCING: Update for GH Explain the release process now that we're using GH. --- SYNCING | 66 +++++++++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/SYNCING b/SYNCING index 218685c3..9ef3ef06 100644 --- a/SYNCING +++ b/SYNCING @@ -1,10 +1,10 @@ Preamble ======== -Tmux on SourceForge has two git repositories [1] "tmux-code" and "tmux-openbsd". +Tmux portable relies on repositories "tmux" and "tmux-openbsd". Here's a description of them: -* "tmux-code" is the portable version, the one which contains code for other +* "tmux" is the portable version, the one which contains code for other operating systems, and autotools, etc., which isn't found or needed in the OpenBSD base system. @@ -18,7 +18,7 @@ repository will take at least that long to appear in this git repository. OpenBSD code). It is assumed that the person doing the sync has read/write access to the -tmux-code repository on SourceForge already. +tmux repository on SourceForge already. If you've never used git before, git tracks meta-data about the committer and the author, as part of a commit, hence: @@ -37,11 +37,11 @@ this information has ever been set before. Cloning repositories ==================== -This involves having both tmux-code and tmux-openbsd cloned, as in: +This involves having both tmux and tmux-openbsd cloned, as in: % cd /some/where/useful -% git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux -% git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux-openbsd +% git clone https://github.com/tmux/tmux.git +% git clone https://github.com/ThomasAdam/tmux-openbsd.git Note that you do not need additional checkouts to manage the sync -- an existing clone of either repositories will suffice. So if you already have @@ -50,56 +50,56 @@ these checkouts existing, skip that. Adding in git-remotes ===================== -Because the portable "tmux-code" git repository and the "tmux-openbsd" +Because the portable "tmux" git repository and the "tmux-openbsd" repository do not inherently share any history between each other, the history has been faked between them. This "faking of history" is something which has to be told to git for the purposes of comparing the "tmux" and "tmux-openbsd" repositories for syncing. To do this, we must reference the -clone of the "tmux-openbsd" repository from the "tmux-code" repository, as +clone of the "tmux-openbsd" repository from the "tmux" repository, as shown by the following command: -% cd /path/to/tmux-code +% cd /path/to/tmux % git remote add obsd-tmux file:///path/to/tmux-openbsd So that now, the remote "obsd-tmux" can be used to reference branches and commits from the "tmux-openbsd" repository, but from the context of the -portable "tmux-code" repository, which makes sense because it's the "tmux" +portable "tmux" repository, which makes sense because it's the "tmux" repository which will have the updates applied to them. Fetching updates ================ To ensure the latest commits from "tmux-openbsd" can be found from within -"tmux-code", we have to ensure the "master" branch from "tmux-openbsd" is -up-to-date first, and then reference that update in "tmux-code", as in: +"tmux", we have to ensure the "master" branch from "tmux-openbsd" is +up-to-date first, and then reference that update in "tmux", as in: % cd /path/to/tmux-openbsd % git checkout master % git pull -Then back in "tmux-code": +Then back in "tmux": -% cd /path/to/tmux-code -% git fetch obsd-tmux-code +% cd /path/to/tmux +% git fetch obsd-tmux Creating the necessary branches =============================== -Now that "tmux-code" can see commits and branches from "tmux-openbsd" by way +Now that "tmux" can see commits and branches from "tmux-openbsd" by way of the remote name "obsd-tmux", we can now create the master branch from -"tmux-openbsd" in the "tmux-code" repository: +"tmux-openbsd" in the "tmux" repository: % git checkout -b obsd-master obsd-tmux/master Adding in the fake history points ================================= -To tie both the "master" branch from "tmux-code" and the "obsd-master" +To tie both the "master" branch from "tmux" and the "obsd-master" branch from "tmux-openbsd" together, the fake history points added to the -"tmux-code" repository need to be added. To do this, we must add an +"tmux" repository need to be added. To do this, we must add an additional refspec line, as in: -% cd /path/to/tmux-code +% cd /path/to/tmux % git config --add remote.origin.fetch '+refs/replace/*:refs/replace/*' % git fetch origin @@ -110,7 +110,7 @@ Make sure the "master" branch is checked out: % git checkout master -The following will show commits on OpenBSD not yet synched with "tmux-code": +The following will show commits on OpenBSD not yet synched with "tmux": % git log master..obsd-master @@ -162,22 +162,18 @@ Release tmux for next version % git push 1.X -4. Build the tarball with make dist. Now that it's using autoconf there - shouldn't be any weird files (such as the original and rejection files - from patch(1)) but it doesn't hurt taking a quick look at it. +4. Build the tarball with 'make dist'. -5. Split the release changes into a new file. This should be named - tmux-$VERSION-readme to make sourceforge show it automagically in specific - parts of the project page. +5. Check the tarball. If it's good, go here to select the tag just pushed: -6. Upload the tarball and the above file. Make the tarball the default - download by selecting all operating systems under the file details. + https://github.com/tmux/tmux/tags -7. Run make update-index.html upload-index.html to replace %%VERSION%%. + Click the "Add release notes", upload the tarball and add a link in the + description field to the CHANGES file. -8. Bump version in configure.ac and uncomment "found_debug=yes" to create - a debug build by default. +7. Clone the tmux.github.io repository, and change the RELEASE version in + the Makefile. Commit it, and run 'make' to replace %%VERSION%%. Push + the result out. -9. Update freshmeat. - -[1] https://sourceforge.net/p/tmux/_list/git +8. Bump version in tmu/tmux.git configure.ac and uncomment "found_debug=yes" to + create a debug build by default. From 2c29b3e82cd2e6dcedaa14fc7620b389be2c56a9 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 4 Jun 2015 10:35:40 +0100 Subject: [PATCH 621/949] SYNCING: Few tweaks --- SYNCING | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/SYNCING b/SYNCING index 9ef3ef06..52591af7 100644 --- a/SYNCING +++ b/SYNCING @@ -17,9 +17,6 @@ repository will take at least that long to appear in this git repository. (It might take longer, depending on the CVS mirror used to import the OpenBSD code). -It is assumed that the person doing the sync has read/write access to the -tmux repository on SourceForge already. - If you've never used git before, git tracks meta-data about the committer and the author, as part of a commit, hence: @@ -154,13 +151,13 @@ Release tmux for next version 3. Tag with: - % git tag -a 1.X + % git tag -a 2.X - Where "1.X" is the next version. + Where "2.X" is the next version. Push the tag out with: - % git push 1.X + % git push 2.X 4. Build the tarball with 'make dist'. From b67db455a87618ef438609f2caef667573e8744b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 10:37:39 +0100 Subject: [PATCH 622/949] Update TODO with some items from old SF tickets. --- TODO | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO b/TODO index 93d11f4f..4a3ec3cd 100644 --- a/TODO +++ b/TODO @@ -32,6 +32,9 @@ * flag to choose-* for sort order * choose mode would be better per client than per window? * two choices (first one then second, for swap-pane and join-pane) + * choose modes should ditch the key bindings and just have fixed keys, and + be more customized to their purpose (d to delete a buffer for choose-buffer, + a preview of buffer contents, etc) - improve monitor-*: * straighten out rules for multiple clients @@ -55,6 +58,7 @@ * message display * prompt input * multibyte key input + * searching in copy mode - copy/paste improvements: * incremental searching @@ -119,3 +123,4 @@ environments * multiline status line? * customizable command aliases + * automatic pane logging From a3edfd9e84f4f8e9b74425cdc4b00fe9cb3d0ba6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 09:42:29 +0000 Subject: [PATCH 623/949] teminal -> terminal, from Corey Farwell. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 6be5d906..41c0bc85 100644 --- a/input.c +++ b/input.c @@ -43,7 +43,7 @@ * pretty stupid but not supporting it is more trouble than it is worth. * * - Special handling for ESC inside a DCS to allow arbitrary byte sequences to - * be passed to the underlying teminal(s). + * be passed to the underlying terminals. */ /* Input parser cell. */ From d058e963fda8a4847cd572e5fb62cd851fdec69a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 11:36:44 +0100 Subject: [PATCH 624/949] Update mailmap. --- .mailmap | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index a7a401ff..3b43fa18 100644 --- a/.mailmap +++ b/.mailmap @@ -1,24 +1,34 @@ Bob Beck beck Igor Sobrado sobrado +Ingo Schwarze schwarze Jacek Masiulaniec jacekm +Jason McIntyre jmc Jason McIntyre jcm Joel Sing jsing +Jonathan Gray jsg +Kenneth R Westerback krw Marc Espie espie Matthew Dempsky matthew Matthias Kilian kili Matthieu Herrb matthieu Miod Vallat miod -Nicholas Marriott nicm -Nicholas Marriott no_author - +Nicholas Marriott Nicholas Marriott +Nicholas Marriott nicm +Nicholas Marriott no_author Okan Demirmen okan Philip Guenther guenther Pierre-Yves Ritschard pyr Ray Lai ray Ryan McBride mcbride +Sebastian Benoit benno Stefan Sperling stsp Stuart Henderson sthen Ted Unangst tedu -Theo Deraadt deraadt +Theo de Raadt deraadt +Theo de Raadt Theo Deraadt Thomas Adam Thomas +Thomas Adam n6tadam +Thomas Adam Thomas Adam +Tobias Stoeckmann tobias +Todd C Miller millert William Yodlowsky william From 1de74e27e535b3e294c265974973a7743c8374a0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 11:40:27 +0100 Subject: [PATCH 625/949] Spaces -> tabs. --- .mailmap | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.mailmap b/.mailmap index 3b43fa18..a9b07251 100644 --- a/.mailmap +++ b/.mailmap @@ -1,34 +1,34 @@ -Bob Beck beck -Igor Sobrado sobrado +Bob Beck beck +Igor Sobrado sobrado Ingo Schwarze schwarze -Jacek Masiulaniec jacekm +Jacek Masiulaniec jacekm Jason McIntyre jmc -Jason McIntyre jcm +Jason McIntyre jcm Joel Sing jsing Jonathan Gray jsg Kenneth R Westerback krw -Marc Espie espie -Matthew Dempsky matthew -Matthias Kilian kili -Matthieu Herrb matthieu -Miod Vallat miod -Nicholas Marriott Nicholas Marriott -Nicholas Marriott nicm -Nicholas Marriott no_author -Okan Demirmen okan +Marc Espie espie +Matthew Dempsky matthew +Matthias Kilian kili +Matthieu Herrb matthieu +Miod Vallat miod +Nicholas Marriott Nicholas Marriott +Nicholas Marriott nicm +Nicholas Marriott no_author +Okan Demirmen okan Philip Guenther guenther -Pierre-Yves Ritschard pyr -Ray Lai ray +Pierre-Yves Ritschard pyr +Ray Lai ray Ryan McBride mcbride Sebastian Benoit benno -Stefan Sperling stsp -Stuart Henderson sthen -Ted Unangst tedu -Theo de Raadt deraadt -Theo de Raadt Theo Deraadt +Stefan Sperling stsp +Stuart Henderson sthen +Ted Unangst tedu +Theo de Raadt deraadt +Theo de Raadt Theo Deraadt Thomas Adam Thomas Thomas Adam n6tadam Thomas Adam Thomas Adam Tobias Stoeckmann tobias Todd C Miller millert -William Yodlowsky william +William Yodlowsky william From a863834574ec02b87ff0e7245ef31f0d4543ab34 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 11:43:51 +0000 Subject: [PATCH 626/949] Add support for a single "marked pane". There is one marked pane in the server at a time; it may be toggled or cleared with select-pane -m and -M (the border is highlighted). A new target '~' or '{marked}' specifies the marked pane to commands and it is the default target for the swap-pane and join-pane -s flag (this makes them much simpler to use - mark the source pane and then change to the target pane to run swapp or joinp). --- cmd-find.c | 65 +++++++++++++++++++++++++++++++-- cmd-join-pane.c | 2 +- cmd-select-pane.c | 31 +++++++++++++--- cmd-swap-pane.c | 8 +++-- cmd-swap-window.c | 2 +- key-bindings.c | 3 ++ screen-redraw.c | 44 +++++++++++++++-------- server.c | 92 ++++++++++++++++++++++++++++++++++++++++------- tmux.1 | 71 ++++++++++++++++++++++++++++++++++-- tmux.h | 13 +++++++ window.c | 5 +++ 11 files changed, 296 insertions(+), 40 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 98f1e187..2b862f95 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -29,6 +29,7 @@ #define CMD_FIND_PREFER_UNATTACHED 0x1 #define CMD_FIND_QUIET 0x2 #define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 enum cmd_find_type { CMD_FIND_PANE, @@ -759,7 +760,14 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* Find current state. */ cmd_find_clear_state(¤t, cmdq, flags); - if (cmd_find_current_session(¤t) != 0) { + if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { + current.s = marked_session; + current.wl = marked_winlink; + current.idx = current.wl->idx; + current.w = current.wl->window; + current.wp = marked_window_pane; + } + if (current.s == NULL && cmd_find_current_session(¤t) != 0) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no current session"); goto error; @@ -798,9 +806,24 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, } return (&fs); } - copy = xstrdup(target); + + /* Marked target is a plain ~ or {marked}. */ + if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) { + if (!server_check_marked()) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no marked target"); + goto error; + } + fs.s = marked_session; + fs.wl = marked_winlink; + fs.idx = fs.wl->idx; + fs.w = fs.wl->window; + fs.wp = marked_window_pane; + return (&fs); + } /* Find separators if they exist. */ + copy = xstrdup(target); colon = strchr(copy, ':'); if (colon != NULL) *colon++ = '\0'; @@ -1053,6 +1076,24 @@ cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) return (fs->wl); } +/* Find the target window, defaulting to marked rather than current. */ +struct winlink * +cmd_find_window_marked(struct cmd_q *cmdq, const char *target, + struct session **sp) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_DEFAULT_MARKED; + + fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + return (fs->wl); +} + /* Find the target pane and report an error and return NULL. */ struct winlink * cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, @@ -1072,6 +1113,26 @@ cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, return (fs->wl); } +/* Find the target pane, defaulting to marked rather than current. */ +struct winlink * +cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, + struct session **sp, struct window_pane **wpp) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_DEFAULT_MARKED; + + fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + if (wpp != NULL) + *wpp = fs->wp; + return (fs->wl); +} + /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 943cdce4..b995b674 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -75,7 +75,7 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index f237e8fd..5ea4bdc3 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", - "DdegLlP:Rt:U", 0, 0, - "[-DdegLlRU] [-P style] " CMD_TARGET_PANE_USAGE, + "DdegLlMmP:Rt:U", 0, 0, + "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_exec }; @@ -47,7 +47,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; - struct window_pane *wp; + struct session *s; + struct window_pane *wp, *lastwp, *markedwp; const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { @@ -74,9 +75,31 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + if (args_has(args, 'm') || args_has(args, 'M')) { + if (args_has(args, 'm') && !window_pane_visible(wp)) + return (CMD_RETURN_NORMAL); + lastwp = marked_window_pane; + + if (args_has(args, 'M') || server_is_marked(s, wl, wp)) + server_clear_marked(); + else + server_set_marked(s, wl, wp); + markedwp = marked_window_pane; + + if (lastwp != NULL) { + server_redraw_window_borders(lastwp->window); + server_status_window(lastwp->window); + } + if (markedwp != NULL) { + server_redraw_window_borders(markedwp->window); + server_status_window(markedwp->window); + } + return (CMD_RETURN_NORMAL); + } + if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if (args_has(args, 'P')) { style = args_get(args, 'P'); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 918a2e4f..c4751c36 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -58,18 +58,22 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); + src_wl = dst_wl; } else if (args_has(self->args, 'U')) { src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); + src_wl = dst_wl; } else { - src_wl = cmd_find_pane(cmdq, NULL, NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, + &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; } } else { - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, + &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 655b910c..51682e37 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -47,7 +47,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; target_src = args_get(args, 's'); - if ((wl_src = cmd_find_window(cmdq, target_src, &src)) == NULL) + if ((wl_src = cmd_find_window_marked(cmdq, target_src, &src)) == NULL) return (CMD_RETURN_ERROR); target_dst = args_get(args, 't'); if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) diff --git a/key-bindings.c b/key-bindings.c index a3cb0bea..ab4751f9 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -177,6 +177,7 @@ key_bindings_init(void) "bind ? list-keys", "bind D choose-client", "bind L switch-client -l", + "bind M select-pane -M", "bind [ copy-mode", "bind ] paste-buffer", "bind c new-window", @@ -184,6 +185,7 @@ key_bindings_init(void) "bind f command-prompt \"find-window '%%'\"", "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", @@ -222,6 +224,7 @@ key_bindings_init(void) "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", + "bind -n MouseDown3Pane select-pane -mt=", }; u_int i; struct cmd_list *cmdlist; diff --git a/screen-redraw.c b/screen-redraw.c index babc74a9..799f5c55 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -26,8 +26,8 @@ int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); int screen_redraw_cell_border(struct client *, u_int, u_int); int screen_redraw_check_cell(struct client *, u_int, u_int, struct window_pane **); -int screen_redraw_check_active(u_int, u_int, int, struct window *, - struct window_pane *); +int screen_redraw_check_is(u_int, u_int, int, struct window *, + struct window_pane *, struct window_pane *); void screen_redraw_draw_borders(struct client *, int, u_int); void screen_redraw_draw_panes(struct client *, u_int); @@ -175,13 +175,13 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, return (CELL_OUTSIDE); } -/* Check active pane indicator. */ +/* Check if the border of a particular pane. */ int -screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, - struct window_pane *wp) +screen_redraw_check_is(u_int px, u_int py, int type, struct window *w, + struct window_pane *wantwp, struct window_pane *wp) { /* Is this off the active pane border? */ - if (screen_redraw_cell_border1(w->active, px, py) != 1) + if (screen_redraw_cell_border1(wantwp, px, py) != 1) return (0); /* If there are more than two panes, that's enough. */ @@ -196,7 +196,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->xoff == 0 && wp->sx == w->sx) { /* This can either be the top pane or the bottom pane. */ if (wp->yoff == 0) { /* top pane */ - if (wp == w->active) + if (wp == wantwp) return (px <= wp->sx / 2); return (px > wp->sx / 2); } @@ -207,7 +207,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->yoff == 0 && wp->sy == w->sy) { /* This can either be the left pane or the right pane. */ if (wp->xoff == 0) { /* left pane */ - if (wp == w->active) + if (wp == wantwp) return (py <= wp->sy / 2); return (py > wp->sy / 2); } @@ -274,13 +274,15 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) void screen_redraw_draw_borders(struct client *c, int status, u_int top) { - struct window *w = c->session->curw->window; + struct session *s = c->session; + struct window *w = s->curw->window; struct options *oo = &w->options; struct tty *tty = &c->tty; struct window_pane *wp; - struct grid_cell active_gc, other_gc, msg_gc; + struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; + struct grid_cell msg_gc; u_int i, j, type, msgx = 0, msgy = 0; - int small, flags; + int active, small, flags; char msg[256]; const char *tmp; size_t msglen = 0; @@ -314,15 +316,29 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) style_apply(&active_gc, oo, "pane-active-border-style"); active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); + m_other_gc.attr ^= GRID_ATTR_REVERSE; + memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); + m_active_gc.attr ^= GRID_ATTR_REVERSE; + for (j = 0; j < tty->sy - status; j++) { for (i = 0; i < tty->sx; i++) { type = screen_redraw_check_cell(c, i, j, &wp); if (type == CELL_INSIDE) continue; - if (type == CELL_OUTSIDE && - small && i > msgx && j == msgy) + if (type == CELL_OUTSIDE && small && + i > msgx && j == msgy) continue; - if (screen_redraw_check_active(i, j, type, w, wp)) + active = screen_redraw_check_is(i, j, type, w, + w->active, wp); + if (server_is_marked(s, s->curw, marked_window_pane) && + screen_redraw_check_is(i, j, type, w, + marked_window_pane, wp)) { + if (active) + tty_attributes(tty, &m_active_gc, NULL); + else + tty_attributes(tty, &m_other_gc, NULL); + } else if (active) tty_attributes(tty, &active_gc, NULL); else tty_attributes(tty, &other_gc, NULL); diff --git a/server.c b/server.c index b53648f0..52fdb0c3 100644 --- a/server.c +++ b/server.c @@ -50,19 +50,85 @@ int server_shutdown; struct event server_ev_accept; struct event server_ev_second; -int server_create_socket(void); -void server_loop(void); -int server_should_shutdown(void); -void server_send_shutdown(void); -void server_clean_dead(void); -void server_accept_callback(int, short, void *); -void server_signal_callback(int, short, void *); -void server_child_signal(void); -void server_child_exited(pid_t, int); -void server_child_stopped(pid_t, int); -void server_second_callback(int, short, void *); -void server_lock_server(void); -void server_lock_sessions(void); +struct session *marked_session; +struct winlink *marked_winlink; +struct window *marked_window; +struct window_pane *marked_window_pane; +struct layout_cell *marked_layout_cell; + +int server_create_socket(void); +void server_loop(void); +int server_should_shutdown(void); +void server_send_shutdown(void); +void server_clean_dead(void); +void server_accept_callback(int, short, void *); +void server_signal_callback(int, short, void *); +void server_child_signal(void); +void server_child_exited(pid_t, int); +void server_child_stopped(pid_t, int); +void server_second_callback(int, short, void *); +void server_lock_server(void); +void server_lock_sessions(void); + +/* Set marked pane. */ +void +server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + marked_session = s; + marked_winlink = wl; + marked_window = wl->window; + marked_window_pane = wp; + marked_layout_cell = wp->layout_cell; +} + +/* Clear marked pane. */ +void +server_clear_marked(void) +{ + marked_session = NULL; + marked_winlink = NULL; + marked_window = NULL; + marked_window_pane = NULL; + marked_layout_cell = NULL; +} + +/* Is this the marked pane? */ +int +server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + if (s == NULL || wl == NULL || wp == NULL) + return (0); + if (marked_session != s || marked_winlink != wl) + return (0); + if (marked_window_pane != wp) + return (0); + return (server_check_marked()); +} + +/* Check if the marked pane is still valid. */ +int +server_check_marked(void) +{ + struct winlink *wl; + + if (marked_window_pane == NULL) + return (0); + if (marked_layout_cell != marked_window_pane->layout_cell) + return (0); + + if (!session_alive(marked_session)) + return (0); + RB_FOREACH(wl, winlinks, &marked_session->windows) { + if (wl->window == marked_window && wl == marked_winlink) + break; + } + if (wl == NULL) + return (0); + + if (!window_has_pane(marked_window, marked_window_pane)) + return (0); + return (window_pane_visible(marked_window_pane)); +} /* Create server socket. */ int diff --git a/tmux.1 b/tmux.1 index 1233d1be..015e5ee7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -298,6 +298,12 @@ 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 @@ -351,6 +357,8 @@ This section contains a list of the commands supported by .Nm . Most commands accept the optional .Fl t +(and sometimes +.Fl s ) argument with one of .Ar target-client , .Ar target-session @@ -451,7 +459,6 @@ Each has a single-character alternative form. .It Li "{last}" Ta "!" Ta "The last (previously current) window" .It Li "{next}" Ta "+" Ta "The next window by number" .It Li "{previous}" Ta "-" Ta "The previous window by number" -.It Li "{mouse}" Ta "=" Ta "The window where the mouse event happened" .El .Pp .Ar target-pane @@ -481,7 +488,6 @@ The following special tokens are available for the pane index: .It Li "{down}" Ta "" Ta "The pane below the active pane" .It Li "{left}" Ta "" Ta "The pane to the left of the active pane" .It Li "{right}" Ta "" Ta "The pane to the right of the active pane" -.It Li "{mouse}" Ta "=" Ta "The pane where the mouse event happened" .El .Pp The tokens @@ -493,6 +499,27 @@ may be followed by an offset, for example: select-window -t:+2 .Ed .Pp +In addition, +.Em target-session , +.Em target-window +or +.Em target-pane +may consist entirely of the token +.Ql {mouse} +(alternative form +.Ql = ) +to specify the most recent mouse event +(see the +.Sx MOUSE SUPPORT +section) +or +.Ql {marked} +(alternative form +.Ql ~ ) +to specify the marked pane (see +.Ic select-pane +.Fl m ) . +.Pp Sessions, window and panes are each numbered with a unique ID; session IDs are prefixed with a .Ql $ , @@ -1440,6 +1467,13 @@ option causes .Ar src-pane to be joined to left of or above .Ar dst-pane . +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic kill-pane .Op Fl a .Op Fl t Ar target-pane @@ -1795,7 +1829,7 @@ commands. .Fl o applies the last set layout if possible (undoes the most recent layout change). .It Xo Ic select-pane -.Op Fl DdegLlRU +.Op Fl DdegLlMmRU .Op Fl P Ar style .Op Fl t Ar target-pane .Xc @@ -1823,6 +1857,20 @@ enables or .Fl d disables input to the pane. .Pp +.Fl m +and +.Fl M +are used to set and clear the +.Em marked pane . +There is one marked pane at a time, setting a new marked pane clears the last. +The marked pane is the default target for +.Fl s +to +.Ic join-pane , +.Ic swap-pane +and +.Ic swap-window . +.Pp Each pane has a style: by default the .Ic window-style and @@ -1911,6 +1959,13 @@ swaps with the next pane (after it numerically). instructs .Nm not to change the active pane. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic swap-window .Op Fl d .Op Fl s Ar src-window @@ -1922,6 +1977,15 @@ This is similar to except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . +.Pp +Like +.Ic swap-pane , +if +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the window containing the marked pane is used rather than the current window. .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window @@ -3488,6 +3552,7 @@ The flag is one of the following symbols appended to the window name: .It Li "#" Ta "Window is monitored and activity has been detected." .It Li "!" Ta "A bell has occurred in the window." .It Li "~" Ta "The window has been silent for the monitor-silence interval." +.It Li "M" Ta "The window contains the marked pane." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp diff --git a/tmux.h b/tmux.h index 78e64170..ae1682af 100644 --- a/tmux.h +++ b/tmux.h @@ -1688,8 +1688,12 @@ struct session *cmd_find_current(struct cmd_q *); struct session *cmd_find_session(struct cmd_q *, const char *, int); struct winlink *cmd_find_window(struct cmd_q *, const char *, struct session **); +struct winlink *cmd_find_window_marked(struct cmd_q *, const char *, + struct session **); struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); +struct winlink *cmd_find_pane_marked(struct cmd_q *, const char *, + struct session **, struct window_pane **); struct client *cmd_find_client(struct cmd_q *, const char *, int); int cmd_find_index(struct cmd_q *, const char *, struct session **); @@ -1850,6 +1854,15 @@ const char *key_string_lookup_key(int); /* server.c */ extern struct clients clients; extern struct clients dead_clients; +extern struct session *marked_session; +extern struct winlink *marked_winlink; +extern struct window_pane *marked_window_pane; +void server_set_marked(struct session *, struct winlink *, + struct window_pane *); +void server_clear_marked(void); +int server_is_marked(struct session *, struct winlink *, + struct window_pane *); +int server_check_marked(void); int server_start(int, char *); void server_update_socket(void); void server_add_accept(int); diff --git a/window.c b/window.c index bc17656d..a1d6792b 100644 --- a/window.c +++ b/window.c @@ -543,6 +543,9 @@ window_add_pane(struct window *w, u_int hlimit) void window_lost_pane(struct window *w, struct window_pane *wp) { + if (wp == marked_window_pane) + server_clear_marked(); + if (wp == w->active) { w->active = w->last; w->last = NULL; @@ -661,6 +664,8 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; + if (server_check_marked() && wl == marked_winlink) + flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; flags[pos] = '\0'; From 4a6c06d6a981d05c5ab3dbc7a365ff740fbb7feb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 14:29:33 +0000 Subject: [PATCH 627/949] Make unsetting a global option restore it to the default. Diff lying around for a while, I have forgotten who suggested it :-/. --- cmd-set-option.c | 19 ++++++++++++++----- tmux.1 | 12 +++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 83d31b93..77c7d7c2 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -266,16 +266,25 @@ cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, { struct args *args = self->args; - if (args_has(args, 'g')) { - cmdq_error(cmdq, "can't unset global option: %s", oe->name); - return (-1); - } if (value != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", oe->name); return (-1); } - options_remove(oo, oe->name); + if (args_has(args, 'g') || oo == &global_options) { + switch (oe->type) { + case OPTIONS_TABLE_STRING: + options_set_string(oo, oe->name, "%s", oe->default_str); + break; + case OPTIONS_TABLE_STYLE: + options_set_style(oo, oe->name, oe->default_str, 0); + break; + default: + options_set_number(oo, oe->name, oe->default_num); + break; + } + } else + options_remove(oo, oe->name); return (0); } diff --git a/tmux.1 b/tmux.1 index 015e5ee7..e479afbe 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2273,21 +2273,19 @@ command), a server option with .Fl s , otherwise a session option. -.Pp If .Fl g -is specified, the global session or window option is set. +is given, the global session or window option is set. The .Fl u flag unsets an option, so a session inherits the option from the global -options. -It is not possible to unset a global option. +options (or with +.Fl g , +restores a global option to the default). .Pp The .Fl o -flag prevents setting an option that is already set. -.Pp -The +flag prevents setting an option that is already set and .Fl q flag suppresses errors about unknown options. .Pp From dc0d34e13792ff14b5c02fac01af013dfe111c83 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 4 Jun 2015 20:34:22 +0000 Subject: [PATCH 628/949] tweak SYNOPSIS and usage(); --- tmux.1 | 2 +- tmux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index e479afbe..fbf0e180 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2lCuv +.Op Fl 2Cluv .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name diff --git a/tmux.c b/tmux.c index 77a39248..878180a7 100644 --- a/tmux.c +++ b/tmux.c @@ -58,7 +58,7 @@ __dead void usage(void) { fprintf(stderr, - "usage: %s [-2lquv] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", __progname); exit(1); From 6b2129696fbefe44d0adce1f2a11b4ae477cd07e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 23:27:51 +0000 Subject: [PATCH 629/949] Move the nested check from client to server and compare the client tty name to all the pane pty names instead of comparing socket paths. This means that "new -d" will work without unsetting $TMUX. --- client.c | 16 +--------------- cmd-attach-session.c | 7 ++++++- cmd-new-session.c | 19 ++++++++++++------- server-client.c | 21 +++++++++++++++++++++ tmux.h | 4 ++-- 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/client.c b/client.c index e2ffa546..48109613 100644 --- a/client.c +++ b/client.c @@ -222,7 +222,7 @@ client_main(int argc, char **argv, int flags) cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; - cmdflags = CMD_STARTSERVER|CMD_CANTNEST; + cmdflags = CMD_STARTSERVER; } else { msg = MSG_COMMAND; @@ -240,24 +240,10 @@ client_main(int argc, char **argv, int flags) TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; - if (cmd->entry->flags & CMD_CANTNEST) - cmdflags |= CMD_CANTNEST; } cmd_list_free(cmdlist); } - /* - * Check if this could be a nested session, if the command can't nest: - * if the socket path matches $TMUX, this is probably the same server. - */ - if (shell_cmd == NULL && environ_path != NULL && - (cmdflags & CMD_CANTNEST) && - strcmp(socket_path, environ_path) == 0) { - fprintf(stderr, "sessions should be nested with care, " - "unset $TMUX to force\n"); - return (1); - } - /* Set process title, log and signals now this is the client. */ setproctitle("client (%s)", socket_path); logfile("client"); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 46d568b4..f889019c 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", "c:drt:", 0, 0, "[-dr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - CMD_CANTNEST|CMD_STARTSERVER, + CMD_STARTSERVER, cmd_attach_session_exec }; @@ -81,6 +81,11 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); + if (server_client_check_nested(cmdq->client)) { + cmdq_error(cmdq, "sessions should be nested with care, " + "unset $TMUX to force"); + return (CMD_RETURN_ERROR); + } if (wl != NULL) { if (wp != NULL) diff --git a/cmd-new-session.c b/cmd-new-session.c index 9c767489..0fb849e2 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_new_session_entry = { "[-AdDP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", - CMD_STARTSERVER|CMD_CANTNEST, + CMD_STARTSERVER, cmd_new_session_exec }; @@ -145,15 +145,20 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } /* - * Save the termios settings, part of which is used for new windows in - * this session. + * If this is a new client, check for nesting and save the termios + * settings (part of which is used for new windows in this session). * - * This is read again with tcgetattr() rather than using tty.tio as if - * detached, tty_open won't be called. Because of this, it must be done - * before opening the terminal as that calls tcsetattr() to prepare for - * tmux taking over. + * tcgetattr() is used rather than using tty.tio since if the client is + * detached, tty_open won't be called. It must be done before opening + * the terminal as that calls tcsetattr() to prepare for tmux taking + * over. */ if (!detached && !already_attached && c->tty.fd != -1) { + if (server_client_check_nested(cmdq->client)) { + cmdq_error(cmdq, "sessions should be nested with care, " + "unset $TMUX to force"); + return (CMD_RETURN_ERROR); + } if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); tiop = &tio; diff --git a/server-client.c b/server-client.c index 36408e91..01c8b313 100644 --- a/server-client.c +++ b/server-client.c @@ -46,6 +46,27 @@ void server_client_msg_command(struct client *, struct imsg *); void server_client_msg_identify(struct client *, struct imsg *); void server_client_msg_shell(struct client *); +/* Check if this client is inside this server. */ +int +server_client_check_nested(struct client *c) +{ + struct environ_entry *envent; + struct window_pane *wp; + + if (c->tty.path == NULL) + return (0); + + envent = environ_find(&c->environ, "TMUX"); + if (envent == NULL || *envent->value == '\0') + return (0); + + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + if (strcmp(wp->tty, c->tty.path) == 0) + return (1); + } + return (0); +} + /* Set client key table. */ void server_client_key_table(struct client *c, const char *name) diff --git a/tmux.h b/tmux.h index ae1682af..8406955b 100644 --- a/tmux.h +++ b/tmux.h @@ -1374,8 +1374,7 @@ struct cmd_entry { const char *usage; #define CMD_STARTSERVER 0x1 -#define CMD_CANTNEST 0x2 -#define CMD_READONLY 0x4 +#define CMD_READONLY 0x2 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); @@ -1868,6 +1867,7 @@ void server_update_socket(void); void server_add_accept(int); /* server-client.c */ +int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, int); void server_client_create(int); int server_client_open(struct client *, char **); From 4219939c10980051a6d272853c96b6e1931639b1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 08:14:16 +0000 Subject: [PATCH 630/949] Make it so that if a window or session target is prefixed with an =, only an exact name or index match is accepted, no special character, prefix match, or fnmatch. --- cmd-find.c | 92 ++++++++++++++++++++++++++++++++------------------ key-bindings.c | 20 +++++------ tmux.1 | 11 ++++++ 3 files changed, 80 insertions(+), 43 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 2b862f95..7b4d4c3e 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -30,6 +30,8 @@ #define CMD_FIND_QUIET 0x2 #define CMD_FIND_WINDOW_INDEX 0x4 #define CMD_FIND_DEFAULT_MARKED 0x8 +#define CMD_FIND_EXACT_SESSION 0x10 +#define CMD_FIND_EXACT_WINDOW 0x20 enum cmd_find_type { CMD_FIND_PANE, @@ -381,6 +383,10 @@ cmd_find_get_session(struct cmd_find_state *fs, const char *session) if (fs->s != NULL) return (0); + /* Stop now if exact only. */ + if (fs->flags & CMD_FIND_EXACT_SESSION) + return (-1); + /* Otherwise look for prefix. */ s = NULL; RB_FOREACH(s_loop, sessions, &sessions) { @@ -455,10 +461,11 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) { struct winlink *wl; const char *errstr; - int idx, n; + int idx, n, exact; struct session *s; log_debug("%s: %s", __func__, window); + exact = (fs->flags & CMD_FIND_EXACT_WINDOW); /* Check for window ids starting with @. */ if (*window == '@') { @@ -469,7 +476,7 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) } /* Try as an offset. */ - if (window[0] == '+' || window[0] == '-') { + if (!exact && window[0] == '+' || window[0] == '-') { if (window[1] != '\0') n = strtonum(window + 1, 1, INT_MAX, NULL); else @@ -499,40 +506,44 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) } /* Try special characters. */ - if (strcmp(window, "!") == 0) { - fs->wl = TAILQ_FIRST(&fs->s->lastw); - if (fs->wl == NULL) - return (-1); - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - return (0); - } else if (strcmp(window, "^") == 0) { - fs->wl = RB_MIN(winlinks, &fs->s->windows); - if (fs->wl == NULL) - return (-1); - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - return (0); - } else if (strcmp(window, "$") == 0) { - fs->wl = RB_MAX(winlinks, &fs->s->windows); - if (fs->wl == NULL) - return (-1); - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - return (0); + if (!exact) { + if (strcmp(window, "!") == 0) { + fs->wl = TAILQ_FIRST(&fs->s->lastw); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "^") == 0) { + fs->wl = RB_MIN(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "$") == 0) { + fs->wl = RB_MAX(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } } /* First see if this is a valid window index in this session. */ - idx = strtonum(window, 0, INT_MAX, &errstr); - if (errstr == NULL) { - if (fs->flags & CMD_FIND_WINDOW_INDEX) { - fs->idx = idx; - return (0); - } - fs->wl = winlink_find_by_index(&fs->s->windows, idx); - if (fs->wl != NULL) { - fs->w = fs->wl->window; - return (0); + if (window[0] != '+' && window[0] != '-') { + idx = strtonum(window, 0, INT_MAX, &errstr); + if (errstr == NULL) { + if (fs->flags & CMD_FIND_WINDOW_INDEX) { + fs->idx = idx; + return (0); + } + fs->wl = winlink_find_by_index(&fs->s->windows, idx); + if (fs->wl != NULL) { + fs->w = fs->wl->window; + return (0); + } } } @@ -551,6 +562,11 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) return (0); } + + /* Stop now if exact only. */ + if (exact) + return (-1); + /* Try as the start of a window name, error if multiple. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { @@ -868,6 +884,16 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, } } + /* Set exact match flags. */ + if (session != NULL && *session == '=') { + session++; + fs.flags |= CMD_FIND_EXACT_SESSION; + } + if (window != NULL && *window == '=') { + window++; + fs.flags |= CMD_FIND_EXACT_WINDOW; + } + /* Empty is the same as NULL. */ if (session != NULL && *session == '\0') session = NULL; diff --git a/key-bindings.c b/key-bindings.c index ab4751f9..3dc3a054 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -161,16 +161,16 @@ key_bindings_init(void) "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 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", diff --git a/tmux.1 b/tmux.1 index fbf0e180..7ad33530 100644 --- a/tmux.1 +++ b/tmux.1 @@ -404,6 +404,14 @@ An pattern which is matched against the session name. .El .Pp +If the session name is prefixed with a +.Ql = : , +only an exact match is accepted (so +.Ql =mysess +will only match exactly +.Ql mysess , +not +.Ql mysession ) . If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no @@ -440,6 +448,9 @@ As an pattern matched against the window name. .El .Pp +Like sessions, a +.Ql = +prefix will do an exact match only. An empty window name specifies the next unused index if appropriate (for example the .Ic new-window From 2f586905fc7dc4a3f65a1bca1ad94e28ffc3508d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 09:09:08 +0000 Subject: [PATCH 631/949] Fix a warning. --- cmd-find.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index 7b4d4c3e..5e0b1dbb 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -476,7 +476,7 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) } /* Try as an offset. */ - if (!exact && window[0] == '+' || window[0] == '-') { + if (!exact && (window[0] == '+' || window[0] == '-')) { if (window[1] != '\0') n = strtonum(window + 1, 1, INT_MAX, NULL); else From f7598b8a267eeab926ed0a934f36595077aab605 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jun 2015 12:44:15 +0100 Subject: [PATCH 632/949] Only need *.ch in compat. --- Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 0aec7ae4..99163c2c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,12 +6,11 @@ CLEANFILES = tmux.1.mdoc tmux.1.man # Distribution tarball options. EXTRA_DIST = \ - CHANGES FAQ README TODO COPYING examples compat \ + CHANGES FAQ README TODO COPYING examples compat/*.[ch] \ array.h compat.h tmux.h osdep-*.c mdoc2man.awk tmux.1 dist-hook: make clean grep "^#found_debug=" configure - find $(distdir) -name .svn -type d|xargs rm -Rf # Preprocessor flags. CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" From b0782df8a64f744b7c067e6f918ce5217ea09e57 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 15:10:13 +0000 Subject: [PATCH 633/949] Do not use the key variable uninitialized (in a debug log statement), reported by jungleboogie0 at gmail dot com. --- tty-keys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tty-keys.c b/tty-keys.c index 1df22d4c..75e06526 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -501,6 +501,7 @@ tty_keys_next(struct tty *tty) case -1: /* no, or not valid */ break; case -2: /* yes, but we don't care. */ + key = KEYC_MOUSE; goto discard_key; case 1: /* partial */ goto partial_key; From 8c93b768e4864be330c3d6a7962892135224f0f4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 18:01:12 +0000 Subject: [PATCH 634/949] Instead of putting dead clients on a list and checking it every loop, use event_once to queue a callback to deal with them. Also dead clients with references would never actually be freed because the wrap-up functions (the callback for stdin, or status_prompt_clear) would never be called. So call them in server_client_lost. --- cmd-confirm-before.c | 2 +- cmd-load-buffer.c | 2 +- server-client.c | 37 ++++++++++++++++++++++++++++++++++--- server.c | 11 ----------- tmux.h | 1 + 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 5e4816ed..e6104574 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -117,7 +117,7 @@ cmd_confirm_before_free(void *data) struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; - c->references--; + server_client_deref(c); free(cdata->cmd); free(cdata); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 3a26db39..8f653929 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -132,7 +132,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) return; c->stdin_callback = NULL; - c->references--; + server_client_deref(c); if (c->flags & CLIENT_DEAD) return; diff --git a/server-client.c b/server-client.c index 01c8b313..b10c1e13 100644 --- a/server-client.c +++ b/server-client.c @@ -31,6 +31,7 @@ #include "tmux.h" void server_client_key_table(struct client *, const char *); +void server_client_free(int, short, void *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); int server_client_check_mouse(struct client *); @@ -85,7 +86,7 @@ server_client_create(int fd) setblocking(fd, 0); c = xcalloc(1, sizeof *c); - c->references = 0; + c->references = 1; imsg_init(&c->ibuf, fd); server_update_event(c); @@ -161,6 +162,14 @@ server_client_lost(struct client *c) { struct message_entry *msg, *msg1; + c->flags |= CLIENT_DEAD; + + status_prompt_clear(c); + status_message_clear(c); + + if (c->stdin_callback != NULL) + c->stdin_callback(c, 1, c->stdin_callback_data); + TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %d", c->ibuf.fd); @@ -213,8 +222,7 @@ server_client_lost(struct client *c) if (event_initialized(&c->event)) event_del(&c->event); - TAILQ_INSERT_TAIL(&dead_clients, c, entry); - c->flags |= CLIENT_DEAD; + server_client_deref(c); server_add_accept(0); /* may be more file descriptors now */ @@ -223,6 +231,29 @@ server_client_lost(struct client *c) server_update_socket(); } +/* Remove reference from a client. */ +void +server_client_deref(struct client *c) +{ + log_debug("deref client %d (%d references)", c->ibuf.fd, c->references); + + c->references--; + if (c->references == 0) + event_once(-1, EV_TIMEOUT, server_client_free, c, NULL); +} + +/* Free dead client. */ +void +server_client_free(unused int fd, unused short events, void *arg) +{ + struct client *c = arg; + + log_debug("free client %d (%d references)", c->ibuf.fd, c->references); + + if (c->references == 0) + free(c); +} + /* Process a single client event. */ void server_client_callback(int fd, short events, void *data) diff --git a/server.c b/server.c index 52fdb0c3..cc5a63f6 100644 --- a/server.c +++ b/server.c @@ -41,9 +41,7 @@ * Main server functions. */ -/* Client list. */ struct clients clients; -struct clients dead_clients; int server_fd; int server_shutdown; @@ -205,7 +203,6 @@ server_start(int lockfd, char *lockfile) RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); - TAILQ_INIT(&dead_clients); RB_INIT(&sessions); RB_INIT(&dead_sessions); TAILQ_INIT(&session_groups); @@ -325,7 +322,6 @@ void server_clean_dead(void) { struct session *s, *s1; - struct client *c, *c1; RB_FOREACH_SAFE(s, sessions, &dead_sessions, s1) { if (s->references != 0) @@ -334,13 +330,6 @@ server_clean_dead(void) free(s->name); free(s); } - - TAILQ_FOREACH_SAFE(c, &dead_clients, entry, c1) { - if (c->references != 0) - continue; - TAILQ_REMOVE(&dead_clients, c, entry); - free(c); - } } /* Update socket execute permissions based on whether sessions are attached. */ diff --git a/tmux.h b/tmux.h index 8406955b..fa36fe6f 100644 --- a/tmux.h +++ b/tmux.h @@ -1871,6 +1871,7 @@ int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, int); void server_client_create(int); int server_client_open(struct client *, char **); +void server_client_deref(struct client *); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); From 10e90ae01f53a67a1b7c3a2c498cefb73c6a23b4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 18:06:30 +0000 Subject: [PATCH 635/949] Change deref to the more sensible unref, and add a couple I missed before. --- cfg.c | 2 +- cmd-confirm-before.c | 2 +- cmd-load-buffer.c | 2 +- notify.c | 2 +- server-client.c | 6 +++--- tmux.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cfg.c b/cfg.c index ff43976e..37474094 100644 --- a/cfg.c +++ b/cfg.c @@ -108,7 +108,7 @@ cfg_default_done(unused struct cmd_q *cmdq) */ if (!TAILQ_EMPTY(&cfg_client->cmdq->queue)) cmdq_continue(cfg_client->cmdq); - cfg_client->references--; + server_client_unref(cfg_client); cfg_client = NULL; } } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index e6104574..248515cd 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -117,7 +117,7 @@ cmd_confirm_before_free(void *data) struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; - server_client_deref(c); + server_client_unref(c); free(cdata->cmd); free(cdata); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 8f653929..897807d0 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -132,7 +132,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) return; c->stdin_callback = NULL; - server_client_deref(c); + server_client_unref(c); if (c->flags & CLIENT_DEAD) return; diff --git a/notify.c b/notify.c index 19bf17e8..8dfbd6ba 100644 --- a/notify.c +++ b/notify.c @@ -121,7 +121,7 @@ notify_drain(void) } if (ne->client != NULL) - ne->client->references--; + server_client_unref(ne->client); if (ne->session != NULL) ne->session->references--; if (ne->window != NULL) diff --git a/server-client.c b/server-client.c index b10c1e13..8739b5ab 100644 --- a/server-client.c +++ b/server-client.c @@ -222,7 +222,7 @@ server_client_lost(struct client *c) if (event_initialized(&c->event)) event_del(&c->event); - server_client_deref(c); + server_client_unref(c); server_add_accept(0); /* may be more file descriptors now */ @@ -233,9 +233,9 @@ server_client_lost(struct client *c) /* Remove reference from a client. */ void -server_client_deref(struct client *c) +server_client_unref(struct client *c) { - log_debug("deref client %d (%d references)", c->ibuf.fd, c->references); + log_debug("unref client %d (%d references)", c->ibuf.fd, c->references); c->references--; if (c->references == 0) diff --git a/tmux.h b/tmux.h index fa36fe6f..7068eb44 100644 --- a/tmux.h +++ b/tmux.h @@ -1871,7 +1871,7 @@ int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, int); void server_client_create(int); int server_client_open(struct client *, char **); -void server_client_deref(struct client *); +void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); From 641a9cd3f591b0ace3ae9947ebe6ab889b641eed Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 18:18:32 +0000 Subject: [PATCH 636/949] Similarly, for sessions use a callback to free rather than checking every loop. --- notify.c | 2 +- server.c | 19 ------------------- session.c | 31 +++++++++++++++++++++++++++---- tmux.h | 2 +- window-choose.c | 6 +++--- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/notify.c b/notify.c index 8dfbd6ba..b51a5e59 100644 --- a/notify.c +++ b/notify.c @@ -123,7 +123,7 @@ notify_drain(void) if (ne->client != NULL) server_client_unref(ne->client); if (ne->session != NULL) - ne->session->references--; + session_unref(ne->session); if (ne->window != NULL) window_remove_ref(ne->window); diff --git a/server.c b/server.c index cc5a63f6..001a404c 100644 --- a/server.c +++ b/server.c @@ -58,7 +58,6 @@ int server_create_socket(void); void server_loop(void); int server_should_shutdown(void); void server_send_shutdown(void); -void server_clean_dead(void); void server_accept_callback(int, short, void *); void server_signal_callback(int, short, void *); void server_child_signal(void); @@ -204,7 +203,6 @@ server_start(int lockfd, char *lockfile) RB_INIT(&all_window_panes); TAILQ_INIT(&clients); RB_INIT(&sessions); - RB_INIT(&dead_sessions); TAILQ_INIT(&session_groups); mode_key_init_trees(); key_bindings_init(); @@ -264,8 +262,6 @@ server_loop(void) server_window_loop(); server_client_loop(); - - server_clean_dead(); } } @@ -317,21 +313,6 @@ server_send_shutdown(void) session_destroy(s); } -/* Free dead, unreferenced clients and sessions. */ -void -server_clean_dead(void) -{ - struct session *s, *s1; - - RB_FOREACH_SAFE(s, sessions, &dead_sessions, s1) { - if (s->references != 0) - continue; - RB_REMOVE(sessions, &dead_sessions, s); - free(s->name); - free(s); - } -} - /* Update socket execute permissions based on whether sessions are attached. */ void server_update_socket(void) diff --git a/session.c b/session.c index 907bdee9..5061083a 100644 --- a/session.c +++ b/session.c @@ -27,12 +27,12 @@ #include "tmux.h" -/* Global session list. */ struct sessions sessions; -struct sessions dead_sessions; u_int next_session_id; struct session_groups session_groups; +void session_free(int, short, void *); + struct winlink *session_next_alert(struct winlink *); struct winlink *session_previous_alert(struct winlink *); @@ -109,7 +109,7 @@ session_create(const char *name, int argc, char **argv, const char *path, struct winlink *wl; s = xmalloc(sizeof *s); - s->references = 0; + s->references = 1; s->flags = 0; if (gettimeofday(&s->creation_time, NULL) != 0) @@ -164,6 +164,29 @@ session_create(const char *name, int argc, char **argv, const char *path, return (s); } +/* Remove a reference from a session. */ +void +session_unref(struct session *s) +{ + log_debug("session %s has %d references", s->name, s->references); + + s->references--; + if (s->references == 0) + event_once(-1, EV_TIMEOUT, session_free, s, NULL); +} + +/* Free session. */ +void +session_free(unused int fd, unused short events, void *arg) +{ + struct session *s = arg; + + log_debug("sesson %s freed (%d references)", s->name, s->references); + + if (s->references == 0) + free(s); +} + /* Destroy a session. */ void session_destroy(struct session *s) @@ -191,7 +214,7 @@ session_destroy(struct session *s) close(s->cwd); - RB_INSERT(sessions, &dead_sessions, s); + session_unref(s); } /* Check a session name is valid: not empty and no colons or periods. */ diff --git a/tmux.h b/tmux.h index 7068eb44..699ee81e 100644 --- a/tmux.h +++ b/tmux.h @@ -2259,7 +2259,6 @@ void control_notify_session_close(struct session *); /* session.c */ extern struct sessions sessions; -extern struct sessions dead_sessions; extern struct session_groups session_groups; int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); @@ -2271,6 +2270,7 @@ struct session *session_create(const char *, int, char **, const char *, int, struct environ *, struct termios *, int, u_int, u_int, char **); void session_destroy(struct session *); +void session_unref(struct session *); int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); diff --git a/window-choose.c b/window-choose.c index 2af56e23..c71fea3d 100644 --- a/window-choose.c +++ b/window-choose.c @@ -209,11 +209,11 @@ window_choose_data_create(int type, struct client *c, struct session *s) void window_choose_data_free(struct window_choose_data *wcd) { - wcd->start_client->references--; - wcd->start_session->references--; + server_client_unref(wcd->start_client); + session_unref(wcd->start_session); if (wcd->tree_session != NULL) - wcd->tree_session->references--; + session_unref(wcd->tree_session); free(wcd->ft_template); format_free(wcd->ft); From 1cb073d48efa74b2e593a2beb8f56fbda6f9076a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 22:01:17 +0000 Subject: [PATCH 637/949] Use fixed colour tables rather than generated and do a quick search for exact match before doing the distance comparison. --- colour.c | 377 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 292 insertions(+), 85 deletions(-) diff --git a/colour.c b/colour.c index 82f8533a..5929e982 100644 --- a/colour.c +++ b/colour.c @@ -29,91 +29,306 @@ * of the 256 colour palette. */ -/* An RGB colour. */ struct colour_rgb { + u_char i; u_char r; u_char g; u_char b; }; -/* 256 colour RGB table, generated on first use. */ -struct colour_rgb *colour_rgb_256; +const struct colour_rgb colour_from_256[] = { + { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, + { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, + { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, + { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, + { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, + { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, + { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, + { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, + { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, + { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, + { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, + { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, + { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, + { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, + { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, + { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, + { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, + { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, + { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f }, + { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf }, + { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff }, + { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f }, + { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf }, + { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff }, + { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f }, + { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf }, + { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff }, + { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f }, + { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf }, + { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff }, + { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f }, + { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf }, + { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff }, + { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f }, + { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf }, + { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff }, + { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f }, + { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf }, + { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff }, + { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f }, + { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf }, + { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff }, + { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f }, + { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf }, + { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff }, + { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f }, + { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf }, + { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff }, + { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f }, + { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf }, + { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff }, + { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f }, + { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf }, + { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff }, + { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f }, + { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf }, + { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff }, + { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f }, + { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf }, + { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff }, + { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f }, + { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf }, + { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff }, + { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f }, + { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf }, + { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff }, + { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f }, + { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf }, + { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff }, + { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f }, + { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf }, + { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff }, + { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f }, + { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf }, + { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff }, + { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f }, + { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf }, + { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff }, + { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f }, + { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf }, + { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff }, + { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f }, + { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf }, + { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff }, + { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f }, + { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf }, + { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff }, + { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f }, + { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf }, + { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff }, + { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, + { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, + { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, + { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, + { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, + { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, + { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, + { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, + { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, + { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, + { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, + { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, + { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, + { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, + { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, + { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, + { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, + { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, + { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, + { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, + { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, + { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, + { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 }, + { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, + { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a }, + { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, + { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 }, + { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, + { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda }, + { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, +}; +const struct colour_rgb colour_to_256[] = { + { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, + { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, + { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, + { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, + { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, + { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, + { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, + { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, + { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, + { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, + { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, + { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, + { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, + { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, + { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, + { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, + { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, + { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, + { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, + { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, + { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, + { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, + { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 }, + { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 }, + { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 }, + { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 }, + { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 }, + { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 }, + { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 }, + { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 }, + { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 }, + { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 }, + { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 }, + { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 }, + { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 }, + { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 }, + { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 }, + { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 }, + { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 }, + { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 }, + { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 }, + { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, + { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 }, + { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 }, + { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 }, + { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 }, + { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 }, + { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 }, + { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 }, + { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 }, + { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 }, + { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 }, + { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 }, + { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 }, + { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 }, + { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 }, + { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 }, + { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 }, + { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 }, + { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 }, + { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a }, + { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, + { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 }, + { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 }, + { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 }, + { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 }, + { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 }, + { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 }, + { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 }, + { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 }, + { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 }, + { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 }, + { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 }, + { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 }, + { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 }, + { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 }, + { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 }, + { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 }, + { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 }, + { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 }, + { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 }, + { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, + { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 }, + { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 }, + { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 }, + { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 }, + { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 }, + { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 }, + { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 }, + { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 }, + { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 }, + { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 }, + { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 }, + { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 }, + { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 }, + { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 }, + { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 }, + { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 }, + { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 }, + { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 }, + { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda }, + { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, + { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, + { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, + { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, + { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, + { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, + { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, + { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, + { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, + { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, + { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, + { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, + { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, + { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, + { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, + { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, + { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, + { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, + { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, +}; -void colour_rgb_generate256(void); -u_int colour_rgb_distance(struct colour_rgb *, struct colour_rgb *); +int colour_rgb_cmp(const void *, const void *); int colour_rgb_find(struct colour_rgb *); -/* Generate 256 colour RGB table. */ -void -colour_rgb_generate256(void) +/* Compare function for bsearch(). */ +int +colour_rgb_cmp(const void *lhs0, const void *rhs0) { - struct colour_rgb *rgb; - u_int i, r, g, b; + const struct colour_rgb *lhs = lhs0, *rhs = rhs0; - /* - * Allocate the table. The first 16 colours are often changed by users - * and terminals so don't include them. - */ - colour_rgb_256 = xcalloc(240, sizeof *colour_rgb_256); + if (lhs->r < rhs->r) + return (-1); + if (lhs->r > rhs->r) + return (1); - /* Add the colours first. */ - r = g = b = 0; - for (i = 240; i > 24; i--) { - rgb = &colour_rgb_256[240 - i]; + if (lhs->g < rhs->g) + return (-1); + if (lhs->g > rhs->g) + return (1); - if (r != 0) - rgb->r = (r * 40) + 55; - if (g != 0) - rgb->g = (g * 40) + 55; - if (b != 0) - rgb->b = (b * 40) + 55; + if (lhs->b < rhs->b) + return (-1); + if (lhs->b > rhs->b) + return (1); - b++; - if (b > 5) { - b = 0; - g++; - } - if (g > 5) { - g = 0; - r++; - } - } - - /* Then add the greys. */ - for (i = 24; i > 0; i--) { - rgb = &colour_rgb_256[240 - i]; - - rgb->r = 8 + (24 - i) * 10; - rgb->g = 8 + (24 - i) * 10; - rgb->b = 8 + (24 - i) * 10; - } -} - -/* Get colour RGB distance. */ -u_int -colour_rgb_distance(struct colour_rgb *rgb1, struct colour_rgb *rgb2) -{ - int r, g, b; - - r = rgb1->r - rgb2->r; - g = rgb1->g - rgb2->g; - b = rgb1->b - rgb2->b; - return (r * r + g * g + b * b); + return (0); } /* Work out the nearest colour from the 256 colour set. */ int colour_rgb_find(struct colour_rgb *rgb) { - u_int distance, lowest, colour, i; + struct colour_rgb *found; + u_int distance, lowest, colour, i; + int r, g, b; - if (colour_rgb_256 == NULL) - colour_rgb_generate256(); + found = bsearch(rgb, colour_to_256, nitems(colour_to_256), + sizeof colour_to_256[0], colour_rgb_cmp); + if (found != NULL) + return (16 + found->i); colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { - distance = colour_rgb_distance(&colour_rgb_256[i], rgb); + r = colour_from_256[i].r - rgb->r; + g = colour_from_256[i].g - rgb->g; + b = colour_from_256[i].b - rgb->b; + + distance = r * r + g * g + b * b; if (distance < lowest) { lowest = distance; colour = 16 + i; @@ -217,47 +432,39 @@ colour_fromstring(const char *s) return (n | 0x100); } - if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0')) + if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0) return (0); - if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0')) + if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) return (1); - if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0')) + if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) return (2); - if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0')) + if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) return (3); - if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0')) + if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) return (4); - if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0')) + if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) return (5); - if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0')) + if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) return (6); - if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0')) + if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) return (7); - if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0')) + if (strcasecmp(s, "default") == 0 || strcmp(s, "8") == 0) return (8); - if (strcasecmp(s, "brightblack") == 0 || - (s[0] == '9' && s[1] == '0' && s[2] == '\0')) + if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) return (90); - if (strcasecmp(s, "brightred") == 0 || - (s[0] == '9' && s[1] == '1' && s[2] == '\0')) + if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) return (91); - if (strcasecmp(s, "brightgreen") == 0 || - (s[0] == '9' && s[1] == '2' && s[2] == '\0')) + if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) return (92); - if (strcasecmp(s, "brightyellow") == 0 || - (s[0] == '9' && s[1] == '3' && s[2] == '\0')) + if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) return (93); - if (strcasecmp(s, "brightblue") == 0 || - (s[0] == '9' && s[1] == '4' && s[2] == '\0')) + if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) return (94); - if (strcasecmp(s, "brightmagenta") == 0 || - (s[0] == '9' && s[1] == '5' && s[2] == '\0')) + if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) return (95); - if (strcasecmp(s, "brightcyan") == 0 || - (s[0] == '9' && s[1] == '6' && s[2] == '\0')) + if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) return (96); - if (strcasecmp(s, "brightwhite") == 0 || - (s[0] == '9' && s[1] == '7' && s[2] == '\0')) + if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) return (97); return (-1); } From 55b96a5bd5786f162b258c58627deb8e829cabd7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 22:33:39 +0000 Subject: [PATCH 638/949] Handle the RGB colour escape sequence (\033[38;2;;;m and 48;2) like xterm(1) does, by mapping to the nearest in the 256 colour palette. --- colour.c | 34 ++++++++++----------- input.c | 91 ++++++++++++++++++++++++++++++++++++++++++-------------- tmux.h | 1 + 3 files changed, 85 insertions(+), 41 deletions(-) diff --git a/colour.c b/colour.c index 5929e982..ac4b72fa 100644 --- a/colour.c +++ b/colour.c @@ -281,12 +281,11 @@ const struct colour_rgb colour_to_256[] = { { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, }; -int colour_rgb_cmp(const void *, const void *); -int colour_rgb_find(struct colour_rgb *); +int colour_cmp_rgb(const void *, const void *); /* Compare function for bsearch(). */ int -colour_rgb_cmp(const void *lhs0, const void *rhs0) +colour_cmp_rgb(const void *lhs0, const void *rhs0) { const struct colour_rgb *lhs = lhs0, *rhs = rhs0; @@ -310,23 +309,22 @@ colour_rgb_cmp(const void *lhs0, const void *rhs0) /* Work out the nearest colour from the 256 colour set. */ int -colour_rgb_find(struct colour_rgb *rgb) +colour_find_rgb(u_char r, u_char g, u_char b) { - struct colour_rgb *found; - u_int distance, lowest, colour, i; - int r, g, b; + struct colour_rgb rgb = { .r = r, .g = g, .b = b }, *found; + u_int distance, lowest, colour, i; - found = bsearch(rgb, colour_to_256, nitems(colour_to_256), - sizeof colour_to_256[0], colour_rgb_cmp); + found = bsearch(&rgb, colour_to_256, nitems(colour_to_256), + sizeof colour_to_256[0], colour_cmp_rgb); if (found != NULL) return (16 + found->i); colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { - r = colour_from_256[i].r - rgb->r; - g = colour_from_256[i].g - rgb->g; - b = colour_from_256[i].b - rgb->b; + r = colour_from_256[i].r - rgb.r; + g = colour_from_256[i].g - rgb.g; + b = colour_from_256[i].b - rgb.b; distance = r * r + g * g + b * b; if (distance < lowest) { @@ -409,20 +407,20 @@ colour_tostring(int c) int colour_fromstring(const char *s) { - const char *errstr; - const char *cp; - struct colour_rgb rgb; - int n; + const char *errstr; + const char *cp; + int n; + u_char r, g, b; if (*s == '#' && strlen(s) == 7) { for (cp = s + 1; isxdigit((u_char) *cp); cp++) ; if (*cp != '\0') return (-1); - n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &rgb.r, &rgb.g, &rgb.b); + n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b); if (n != 3) return (-1); - return (colour_rgb_find(&rgb) | 0x100); + return (colour_find_rgb(r, g, b) | 0x100); } if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { diff --git a/input.c b/input.c index 41c0bc85..7a371c62 100644 --- a/input.c +++ b/input.c @@ -126,6 +126,8 @@ void input_csi_dispatch_rm_private(struct input_ctx *); void input_csi_dispatch_sm(struct input_ctx *); void input_csi_dispatch_sm_private(struct input_ctx *); void input_csi_dispatch_winops(struct input_ctx *); +void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *); +void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); void input_csi_dispatch_sgr(struct input_ctx *); int input_dcs_dispatch(struct input_ctx *); int input_utf8_open(struct input_ctx *); @@ -1609,13 +1611,71 @@ input_csi_dispatch_winops(struct input_ctx *ictx) } } +/* Handle CSI SGR for 256 colours. */ +void +input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) +{ + struct grid_cell *gc = &ictx->cell.cell; + int c; + + (*i)++; + c = input_get(ictx, *i, 0, -1); + if (c == -1) { + if (fgbg == 38) { + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = 8; + } else if (fgbg == 48) { + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = 8; + } + } else { + if (fgbg == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = c; + } else if (fgbg == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = c; + } + } +} + +/* Handle CSI SGR for RGB colours. */ +void +input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) +{ + struct grid_cell *gc = &ictx->cell.cell; + int c, r, g, b; + + (*i)++; + r = input_get(ictx, *i, 0, -1); + if (r == -1 || r > 255) + return; + (*i)++; + g = input_get(ictx, *i, 0, -1); + if (g == -1 || g > 255) + return; + (*i)++; + b = input_get(ictx, *i, 0, -1); + if (b == -1 || b > 255) + return; + + c = colour_find_rgb(r, g, b); + if (fgbg == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = c; + } else if (fgbg == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = c; + } +} + /* Handle CSI SGR. */ void input_csi_dispatch_sgr(struct input_ctx *ictx) { struct grid_cell *gc = &ictx->cell.cell; u_int i; - int n, m; + int n; if (ictx->param_list_len == 0) { memcpy(gc, &grid_default_cell, sizeof *gc); @@ -1627,28 +1687,13 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) if (n == 38 || n == 48) { i++; - if (input_get(ictx, i, 0, -1) != 5) - continue; - - i++; - m = input_get(ictx, i, 0, -1); - if (m == -1) { - if (n == 38) { - gc->flags &= ~GRID_FLAG_FG256; - gc->fg = 8; - } else if (n == 48) { - gc->flags &= ~GRID_FLAG_BG256; - gc->bg = 8; - } - - } else { - if (n == 38) { - gc->flags |= GRID_FLAG_FG256; - gc->fg = m; - } else if (n == 48) { - gc->flags |= GRID_FLAG_BG256; - gc->bg = m; - } + switch (input_get(ictx, i, 0, -1)) { + case 2: + input_csi_dispatch_sgr_rgb(ictx, n, &i); + break; + case 5: + input_csi_dispatch_sgr_256(ictx, n, &i); + break; } continue; } diff --git a/tmux.h b/tmux.h index 699ee81e..4ddf5c4a 100644 --- a/tmux.h +++ b/tmux.h @@ -1953,6 +1953,7 @@ char *xterm_keys_lookup(int); int xterm_keys_find(const char *, size_t, size_t *, int *); /* colour.c */ +int colour_find_rgb(u_char, u_char, u_char); void colour_set_fg(struct grid_cell *, int); void colour_set_bg(struct grid_cell *, int); const char *colour_tostring(int); From ed6c036ee3192029e9d0a60e8f9bb2a4ccfb99bf Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 22:50:27 +0000 Subject: [PATCH 639/949] Use ints for the calculations rather than u_char, they could end up signed. --- colour.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/colour.c b/colour.c index ac4b72fa..a56ddce9 100644 --- a/colour.c +++ b/colour.c @@ -313,6 +313,7 @@ colour_find_rgb(u_char r, u_char g, u_char b) { struct colour_rgb rgb = { .r = r, .g = g, .b = b }, *found; u_int distance, lowest, colour, i; + int dr, dg, db; found = bsearch(&rgb, colour_to_256, nitems(colour_to_256), sizeof colour_to_256[0], colour_cmp_rgb); @@ -322,11 +323,11 @@ colour_find_rgb(u_char r, u_char g, u_char b) colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { - r = colour_from_256[i].r - rgb.r; - g = colour_from_256[i].g - rgb.g; - b = colour_from_256[i].b - rgb.b; + dr = (int)colour_from_256[i].r - r; + dg = (int)colour_from_256[i].g - g; + db = (int)colour_from_256[i].b - b; - distance = r * r + g * g + b * b; + distance = dr * dr + dg * dg + db * db; if (distance < lowest) { lowest = distance; colour = 16 + i; From a5c55e439383202547e442f6afc0c8c664687728 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 7 Jun 2015 08:36:03 +0100 Subject: [PATCH 640/949] Update TODO. --- TODO | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO b/TODO index 4a3ec3cd..ce7b1f15 100644 --- a/TODO +++ b/TODO @@ -6,8 +6,6 @@ * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions - * exact match operator for targets (or break the substring match - and require eg x* instead of just x) - make command sequences more usable * don't require space after ; From c4e811e51936edab66803a7b9e099ac135e6e19b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 7 Jun 2015 21:39:39 +0000 Subject: [PATCH 641/949] Add -E flag when attaching or switching client to bypass update-environment, from Steven Lu. --- cmd-attach-session.c | 50 ++++++++++++++++++++++++-------------------- cmd-new-session.c | 13 +++++++----- cmd-switch-client.c | 6 +++--- tmux.1 | 25 +++++++++++++++++++--- tmux.h | 2 +- 5 files changed, 61 insertions(+), 35 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index f889019c..b2a2f8c5 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -34,18 +34,18 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", - "c:drt:", 0, 0, - "[-dr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + "c:dErt:", 0, 0, + "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, CMD_STARTSERVER, cmd_attach_session_exec }; enum cmd_retval cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, - const char *cflag) + const char *cflag, int Eflag) { struct session *s; - struct client *c; + struct client *c = cmdq->client, *c_loop; struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; @@ -79,9 +79,9 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, wl = winlink_find_by_window(&s->windows, w); } - if (cmdq->client == NULL) + if (c == NULL) return (CMD_RETURN_NORMAL); - if (server_client_check_nested(cmdq->client)) { + if (server_client_check_nested(c)) { cmdq_error(cmdq, "sessions should be nested with care, " "unset $TMUX to force"); return (CMD_RETURN_ERROR); @@ -93,18 +93,18 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, session_set_current(s, wl); } - if (cmdq->client->session != NULL) { + if (c->session != NULL) { if (dflag) { /* * Can't use server_write_session in case attaching to * the same session as currently attached to. */ - TAILQ_FOREACH(c, &clients, entry) { - if (c->session != s || c == cmdq->client) + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c) continue; server_write_client(c, MSG_DETACH, - c->session->name, - strlen(c->session->name) + 1); + c_loop->session->name, + strlen(c_loop->session->name) + 1); } } @@ -126,13 +126,13 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, s->cwd = fd; } - cmdq->client->session = s; - notify_attached_session_changed(cmdq->client); + c->session = s; + notify_attached_session_changed(c); session_update_activity(s); - server_redraw_client(cmdq->client); + server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { - if (server_client_open(cmdq->client, &cause) != 0) { + if (server_client_open(c, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); @@ -157,23 +157,26 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (rflag) - cmdq->client->flags |= CLIENT_READONLY; + c->flags |= CLIENT_READONLY; if (dflag) { server_write_session(s, MSG_DETACH, s->name, strlen(s->name) + 1); } - update = options_get_string(&s->options, "update-environment"); - environ_update(update, &cmdq->client->environ, &s->environ); + if (!Eflag) { + update = options_get_string(&s->options, + "update-environment"); + environ_update(update, &c->environ, &s->environ); + } - cmdq->client->session = s; - notify_attached_session_changed(cmdq->client); + c->session = s; + notify_attached_session_changed(c); session_update_activity(s); - server_redraw_client(cmdq->client); + server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; - server_write_ready(cmdq->client); + server_write_ready(c); cmdq->client_exit = 0; } recalculate_sizes(); @@ -188,5 +191,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; return (cmd_attach_session(cmdq, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'))); + args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), + args_has(args, 'E'))); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 0fb849e2..1533e5f0 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -37,8 +37,8 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", - "Ac:dDF:n:Ps:t:x:y:", 0, -1, - "[-AdDP] [-c start-directory] [-F format] [-n window-name] " + "Ac:dDEF:n:Ps:t:x:y:", 0, -1, + "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", CMD_STARTSERVER, @@ -91,7 +91,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (session_find(newname) != NULL) { if (args_has(args, 'A')) { return (cmd_attach_session(cmdq, newname, - args_has(args, 'D'), 0, NULL)); + args_has(args, 'D'), 0, NULL, + args_has(args, 'E'))); } cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); @@ -230,9 +231,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Construct the environment. */ environ_init(&env); - update = options_get_string(&global_s_options, "update-environment"); - if (c != NULL) + if (c != NULL && !args_has(args, 'E')) { + update = options_get_string(&global_s_options, + "update-environment"); environ_update(update, &c->environ, &env); + } /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 369fc917..23751d73 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", - "lc:npt:rT:", 0, 0, - "[-lnpr] [-c target-client] [-t target-session] [-T key-table]", + "lc:Enpt:rT:", 0, 0, + "[-Elnpr] [-c target-client] [-t target-session] [-T key-table]", CMD_READONLY, cmd_switch_client_exec }; @@ -119,7 +119,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } } - if (c != NULL && s != c->session) { + if (c != NULL && s != c->session && !args_has(args, 'E')) { update = options_get_string(&s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } diff --git a/tmux.1 b/tmux.1 index 7ad33530..16e979ad 100644 --- a/tmux.1 +++ b/tmux.1 @@ -670,7 +670,7 @@ section. The following commands are available to manage clients and sessions: .Bl -tag -width Ds .It Xo Ic attach-session -.Op Fl dr +.Op Fl dEr .Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc @@ -709,6 +709,12 @@ session. .Fl c will set the session working directory (used for new windows) to .Ar working-directory . +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. .It Xo Ic detach-client .Op Fl P .Op Fl a @@ -783,7 +789,7 @@ command. Lock all clients attached to .Ar target-session . .It Xo Ic new-session -.Op Fl AdDP +.Op Fl AdDEP .Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name @@ -858,6 +864,13 @@ By default, it uses the format .Ql #{session_name}: but a different format may be specified with .Fl F . +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. +.Ic update-environment . .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client @@ -912,7 +925,7 @@ Suspend a client by sending .Dv SIGTSTP (tty stop). .It Xo Ic switch-client -.Op Fl lnpr +.Op Fl Elnpr .Op Fl c Ar target-client .Op Fl t Ar target-session .Op Fl T Ar key-table @@ -934,6 +947,12 @@ toggles whether a client is read-only (see the .Ic attach-session command). .Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. +.Pp .Fl T sets the client's key table; the next key from the client will be interpreted from .Ar key-table . diff --git a/tmux.h b/tmux.h index 4ddf5c4a..265fdc40 100644 --- a/tmux.h +++ b/tmux.h @@ -1801,7 +1801,7 @@ extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, - const char *); + const char *, int); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); From 02a848d77c7f41acecccd58d7819a562eef7c704 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 8 Jun 2015 09:46:14 +0100 Subject: [PATCH 642/949] It isn't supposed to... --- TODO | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO b/TODO index ce7b1f15..6f8d732c 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ - command bits and pieces: - * why doesn't command-prompt work if made read-only? * allow multiple targets: fnmatch for -t/-c, for example detach all clients with -t* * add -c for new-session like new-window From a412dd616f07167610e3290e1cfc0e40eb9ec664 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jun 2015 07:07:06 +0000 Subject: [PATCH 643/949] Fix loop comparison broken in last commit, from Thomas Adam. --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index b2a2f8c5..cb58d4e2 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -100,7 +100,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, * the same session as currently attached to. */ TAILQ_FOREACH(c_loop, &clients, entry) { - if (c_loop->session != s || c == c) + if (c_loop->session != s || c == c_loop) continue; server_write_client(c, MSG_DETACH, c_loop->session->name, From bbc0898060a02515461cbd90d7af35bf91d9cb3d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Jun 2015 12:56:04 +0000 Subject: [PATCH 644/949] wp->tty is a char [] not a char * so it can't be NULL. From Thomas Adam. --- format.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/format.c b/format.c index 72d6bfc9..eaa979da 100644 --- a/format.c +++ b/format.c @@ -872,8 +872,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_synchronized", "%d", !!options_get_number(&wp->window->options, "synchronize-panes")); - if (wp->tty != NULL) - format_add(ft, "pane_tty", "%s", wp->tty); + format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); if ((cmd = cmd_stringify_argv(wp->argc, wp->argv)) != NULL) { format_add(ft, "pane_start_command", "%s", cmd); From dca084e703ce54c500e4af7ddc1e0c2b65d4d403 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Jun 2015 15:39:23 +0100 Subject: [PATCH 645/949] Don't leak dotfd if fchdir fails. From ettl dot martin78 at gmail dot com. --- compat/openat.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compat/openat.c b/compat/openat.c index 5cd9e551..6b04eedc 100644 --- a/compat/openat.c +++ b/compat/openat.c @@ -40,8 +40,12 @@ openat(int fd, const char *path, int flags, ...) dotfd = open(".", O_RDONLY); if (dotfd == -1) return (-1); - if (fchdir(fd) != 0) + if (fchdir(fd) != 0) { + saved_errno = errno; + close(dotfd); + errno = saved_errno; return (-1); + } } retval = open(path, flags, mode); From 29c29e771767b037f2929b889bb0de2b0b6ee138 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 14 Jun 2015 10:07:44 +0000 Subject: [PATCH 646/949] Add a format for client PID (client_pid) and server PID (pid). Diff for client_pid from Thomas Adam. --- client.c | 8 ++++++-- format.c | 2 ++ server-client.c | 6 ++++++ tmux.1 | 2 ++ tmux.h | 2 ++ 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 48109613..4b7bbb1f 100644 --- a/client.c +++ b/client.c @@ -348,9 +348,10 @@ client_main(int argc, char **argv, int flags) void client_send_identify(int flags) { - const char *s; + const char *s; char **ss; - int fd; + int fd; + pid_t pid; client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); @@ -370,6 +371,9 @@ client_send_identify(int flags) fatal("dup failed"); client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + pid = getpid(); + client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); + for (ss = environ; *ss != NULL; ss++) client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); diff --git a/format.c b/format.c index eaa979da..49cf7506 100644 --- a/format.c +++ b/format.c @@ -271,6 +271,7 @@ format_create_status(int status) *ptr = '\0'; format_add(ft, "host_short", "%s", host); } + format_add(ft, "pid", "%ld", (long) getpid()); return (ft); } @@ -703,6 +704,7 @@ format_defaults_client(struct format_tree *ft, struct client *c) if (ft->s == NULL) ft->s = c->session; + format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); if (c->tty.path != NULL) diff --git a/server-client.c b/server-client.c index 8739b5ab..9beef4bc 100644 --- a/server-client.c +++ b/server-client.c @@ -1044,6 +1044,7 @@ server_client_msg_dispatch(struct client *c) case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_CLIENTPID: case MSG_IDENTIFY_DONE: server_client_msg_identify(c, &imsg); break; @@ -1218,6 +1219,11 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (strchr(data, '=') != NULL) environ_put(&c->environ, data); break; + case MSG_IDENTIFY_CLIENTPID: + if (datalen != sizeof c->pid) + fatalx("bad MSG_IDENTIFY_CLIENTPID size"); + memcpy(&c->pid, data, sizeof c->pid); + break; default: break; } diff --git a/tmux.1 b/tmux.1 index 16e979ad..70b43a7a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3353,6 +3353,7 @@ The following variables are available, where appropriate: .It Li "client_created_string" Ta "" Ta "String time client created" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" +.It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" @@ -3396,6 +3397,7 @@ The following variables are available, where appropriate: .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" +.It Li "pid" Ta "" Ta "Server PID" .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 "session_alerts" Ta "" Ta "List of window indexes with alerts" diff --git a/tmux.h b/tmux.h index 265fdc40..dfd6bf49 100644 --- a/tmux.h +++ b/tmux.h @@ -425,6 +425,7 @@ enum msgtype { MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, + MSG_IDENTIFY_CLIENTPID, MSG_COMMAND = 200, MSG_DETACH, @@ -1206,6 +1207,7 @@ RB_HEAD(status_out_tree, status_out); struct client { struct imsgbuf ibuf; + pid_t pid; int fd; struct event event; int retval; From d96ab3401960ab4a7c9434dfda1ebdc5204873e0 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Jun 2015 10:58:01 +0000 Subject: [PATCH 647/949] Add window_activity format, from Thomas Adam based on a diff originally from propos6 at gmail dot com. --- format.c | 5 +++++ input.c | 4 ++++ tmux.1 | 2 ++ tmux.h | 1 + window.c | 3 +++ 5 files changed, 15 insertions(+) diff --git a/format.c b/format.c index 49cf7506..67bce925 100644 --- a/format.c +++ b/format.c @@ -749,6 +749,7 @@ void format_defaults_window(struct format_tree *ft, struct window *w) { char *layout; + time_t t; ft->w = w; @@ -757,6 +758,10 @@ format_defaults_window(struct format_tree *ft, struct window *w) else layout = layout_dump(w->layout_root); + t = w->activity_time.tv_sec; + format_add(ft, "window_activity", "%lld", (long long) t); + format_add(ft, "window_activity_string", "%s", format_time_string(t)); + format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); diff --git a/input.c b/input.c index 7a371c62..d1ff17fe 100644 --- a/input.c +++ b/input.c @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" @@ -849,6 +850,9 @@ input_parse(struct window_pane *wp) wp->window->flags |= WINDOW_ACTIVITY; wp->window->flags &= ~WINDOW_SILENCE; + if (gettimeofday(&wp->window->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + /* * Open the screen. Use NULL wp if there is a mode set as don't want to * update the tty. diff --git a/tmux.1 b/tmux.1 index 70b43a7a..400378fc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3414,6 +3414,8 @@ The following variables are available, where appropriate: .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "window_activity" Ta "" Ta "Integer time of window last activity" +.It Li "window_activity_string" Ta "" Ta "String time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" diff --git a/tmux.h b/tmux.h index dfd6bf49..da91d4db 100644 --- a/tmux.h +++ b/tmux.h @@ -892,6 +892,7 @@ struct window { char *name; struct event name_timer; struct timeval silence_timer; + struct timeval activity_time; struct window_pane *active; struct window_pane *last; diff --git a/window.c b/window.c index a1d6792b..4639944e 100644 --- a/window.c +++ b/window.c @@ -295,6 +295,9 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; + if (gettimeofday(&w->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + options_init(&w->options, &global_w_options); if (options_get_number(&w->options, "automatic-rename")) queue_window_name(w); From 021cdbe1c0111951a1f63b09b41c9e3db3793db5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 16:44:49 +0000 Subject: [PATCH 648/949] Use an explicit job state instead of avoid closing our side of the socketpair and setting it to -1 to mark when the other side is closed. This avoids closing it while the libevent bufferevent still has it (it could try to add it to the polled set which some mechanisms don't like). Fixes part a problem reported by Bruno Sutic. --- job.c | 13 ++++++++----- tmux.h | 6 ++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/job.c b/job.c index 5675eec3..0b348eda 100644 --- a/job.c +++ b/job.c @@ -100,6 +100,8 @@ job_run(const char *cmd, struct session *s, int cwd, close(out[1]); job = xmalloc(sizeof *job); + job->state = JOB_RUNNING; + job->cmd = xstrdup(cmd); job->pid = pid; job->status = 0; @@ -167,14 +169,13 @@ job_callback(unused struct bufferevent *bufev, unused short events, void *data) log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); - if (job->pid == -1) { + if (job->state == JOB_DEAD) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else { bufferevent_disable(job->event, EV_READ); - close(job->fd); - job->fd = -1; + job->state = JOB_CLOSED; } } @@ -186,10 +187,12 @@ job_died(struct job *job, int status) job->status = status; - if (job->fd == -1) { + if (job->state == JOB_CLOSED) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); - } else + } else { job->pid = -1; + job->state = JOB_DEAD; + } } diff --git a/tmux.h b/tmux.h index da91d4db..ff0b4c84 100644 --- a/tmux.h +++ b/tmux.h @@ -714,6 +714,12 @@ struct options { /* Scheduled job. */ struct job { + enum { + JOB_RUNNING, + JOB_DEAD, + JOB_CLOSED + } state; + char *cmd; pid_t pid; int status; From 0ff335961eec019d776f19bd8c26cce7cde0effa Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 16:50:28 +0000 Subject: [PATCH 649/949] Move the shuffle code from new-window -a into a function and add a -a flag for move-window too. From Thomas Adam. --- cmd-move-window.c | 13 +++++++++++-- cmd-new-window.c | 18 ++---------------- tmux.1 | 8 ++++++-- tmux.h | 1 + window.c | 25 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/cmd-move-window.c b/cmd-move-window.c index e765b625..b15df4f6 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -30,7 +30,7 @@ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { "move-window", "movew", - "dkrs:t:", 0, 0, + "adkrs:t:", 0, 0, "[-dkr] " CMD_SRCDST_WINDOW_USAGE, 0, cmd_move_window_exec @@ -38,7 +38,7 @@ const struct cmd_entry cmd_move_window_entry = { const struct cmd_entry cmd_link_window_entry = { "link-window", "linkw", - "dks:t:", 0, 0, + "adks:t:", 0, 0, "[-dk] " CMD_SRCDST_WINDOW_USAGE, 0, cmd_move_window_exec @@ -72,6 +72,15 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); sflag = args_has(self->args, 's'); + + if (args_has(self->args, 'a')) { + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) + return (CMD_RETURN_ERROR); + if ((idx = winlink_shuffle_up(s, s->curw)) == -1) + return (CMD_RETURN_ERROR); + } + if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't link window: %s", cause); diff --git a/cmd-new-window.c b/cmd-new-window.c index c05a0ce8..9cead449 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -51,7 +51,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; const char *cmd, *path, *template; char **argv, *cause, *cp; - int argc, idx, last, detached, cwd, fd = -1; + int argc, idx, detached, cwd, fd = -1; struct format_tree *ft; struct environ_entry *envent; @@ -59,24 +59,10 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); - idx = wl->idx + 1; - - /* Find the next free index. */ - for (last = idx; last < INT_MAX; last++) { - if (winlink_find_by_index(&s->windows, last) == NULL) - break; - } - if (last == INT_MAX) { + if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } - - /* Move everything from last - 1 to idx up a bit. */ - for (; last > idx; last--) { - wl = winlink_find_by_index(&s->windows, last - 1); - server_link_window(s, wl, s, last, 0, 0, NULL); - server_unlink_window(s, wl); - } } else { idx = cmd_find_index(cmdq, args_get(args, 't'), &s); if (idx == -2) diff --git a/tmux.1 b/tmux.1 index 400378fc..fb014898 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1544,7 +1544,7 @@ If no .Ar target-session is specified, select the last window of the current session. .It Xo Ic link-window -.Op Fl dk +.Op Fl adk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -1558,6 +1558,10 @@ If is specified and no such window exists, the .Ar src-window is linked there. +With +.Fl a , +the window is moved to the next index up (following windows +are moved if necessary). If .Fl k is given and @@ -1623,7 +1627,7 @@ and .Ar dst-pane may belong to the same window. .It Xo Ic move-window -.Op Fl rdk +.Op Fl ardk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc diff --git a/tmux.h b/tmux.h index ff0b4c84..663832e5 100644 --- a/tmux.h +++ b/tmux.h @@ -2166,6 +2166,7 @@ struct window_pane *window_pane_find_right(struct window_pane *); void window_set_name(struct window *, const char *); void window_remove_ref(struct window *); void winlink_clear_flags(struct winlink *); +int winlink_shuffle_up(struct session *, struct winlink *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); diff --git a/window.c b/window.c index 4639944e..82c10606 100644 --- a/window.c +++ b/window.c @@ -1378,3 +1378,28 @@ winlink_clear_flags(struct winlink *wl) } } } + +int +winlink_shuffle_up(struct session *s, struct winlink *wl) +{ + int idx, last; + + idx = wl->idx + 1; + + /* Find the next free index. */ + for (last = idx; last < INT_MAX; last++) { + if (winlink_find_by_index(&s->windows, last) == NULL) + break; + } + if (last == INT_MAX) + return (-1); + + /* Move everything from last - 1 to idx up a bit. */ + for (; last > idx; last--) { + wl = winlink_find_by_index(&s->windows, last - 1); + server_link_window(s, wl, s, last, 0, 0, NULL); + server_unlink_window(s, wl); + } + + return (idx); +} From 84f0622c852761a2d6688e944bc01a03af78c52a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 17:02:15 +0000 Subject: [PATCH 650/949] Break cmdq_continue inner loop into a helper function. --- cmd-queue.c | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 61b14147..af987f59 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,6 +25,8 @@ #include "tmux.h" +enum cmd_retval cmdq_continue_one(struct cmd_q *); + /* Create new command queue. */ struct cmd_q * cmdq_new(struct client *c) @@ -160,14 +162,39 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) item->mouse.valid = 0; } +/* Process one command. */ +enum cmd_retval +cmdq_continue_one(struct cmd_q *cmdq) +{ + struct cmd *cmd = cmdq->cmd; + enum cmd_retval retval; + char tmp[1024]; + int flags = !!(cmd->flags & CMD_CONTROL); + + cmd_print(cmd, tmp, sizeof tmp); + log_debug("cmdq %p: %s", cmdq, tmp); + + cmdq->time = time(NULL); + cmdq->number++; + + cmdq_guard(cmdq, "begin", flags); + + retval = cmd->entry->exec(cmd, cmdq); + + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error", flags); + else + cmdq_guard(cmdq, "end", flags); + return (retval); +} + /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { struct cmd_q_item *next; enum cmd_retval retval; - int empty, flags; - char s[1024]; + int empty; cmdq->references++; notify_disable(); @@ -184,23 +211,7 @@ cmdq_continue(struct cmd_q *cmdq) do { while (cmdq->cmd != NULL) { - cmd_print(cmdq->cmd, s, sizeof s); - log_debug("cmdq %p: %s (client %d)", cmdq, s, - cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); - - cmdq->time = time(NULL); - cmdq->number++; - - flags = !!(cmdq->cmd->flags & CMD_CONTROL); - cmdq_guard(cmdq, "begin", flags); - - retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); - - if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error", flags); - else - cmdq_guard(cmdq, "end", flags); - + retval = cmdq_continue_one(cmdq); if (retval == CMD_RETURN_ERROR) break; if (retval == CMD_RETURN_WAIT) @@ -209,7 +220,6 @@ cmdq_continue(struct cmd_q *cmdq) cmdq_flush(cmdq); goto empty; } - cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); } next = TAILQ_NEXT(cmdq->item, qentry); From 85120b37eaa33f572f8f4c12a050c431a6efba3e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 19:56:08 +0000 Subject: [PATCH 651/949] Change break-pane to take target and source panes (-t and -s) in line with other commands, from Thomas Adam. --- cmd-break-pane.c | 19 +++++++++++++------ tmux.1 | 8 +++++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index a1da0a3a..c7af7865 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -32,8 +32,8 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { "break-pane", "breakp", - "dPF:t:", 0, 0, - "[-dP] [-F format] " CMD_TARGET_PANE_USAGE, + "dPF:s:t:", 0, 0, + "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, 0, cmd_break_pane_exec }; @@ -48,13 +48,19 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; char *name; char *cause; - int base_idx; + int idx; struct format_tree *ft; const char *template; char *cp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 's'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) + return (CMD_RETURN_ERROR); + if (idx != -1 && winlink_find_by_index(&s->windows, idx) != NULL) { + cmdq_error(cmdq, "index %d already in use", idx); + return (CMD_RETURN_ERROR); + } if (window_count_panes(wl->window) == 1) { cmdq_error(cmdq, "can't break with only one pane"); @@ -76,8 +82,9 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) free(name); layout_init(w, wp); - base_idx = options_get_number(&s->options, "base-index"); - wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */ + if (idx == -1) + idx = -1 - options_get_number(&s->options, "base-index"); + wl = session_attach(s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(s, wl->idx); diff --git a/tmux.1 b/tmux.1 index fb014898..033330b7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1233,12 +1233,14 @@ Commands related to windows and panes are as follows: .It Xo Ic break-pane .Op Fl dP .Op Fl F Ar format -.Op Fl t Ar target-pane +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane .Xc .D1 (alias: Ic breakp ) Break -.Ar target-pane -off from its containing window to make it the only pane in a new window. +.Ar src-pane +off from its containing window to make it the only pane in +.Ar dst-window . If .Fl d is given, the new window does not become the current window. From 164ba041c9301d8c804a9f76da8ac62a8de286de Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2015 23:53:56 +0000 Subject: [PATCH 652/949] Remove a stray : and tweak paragraph. --- tmux.1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 033330b7..f0dd569a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -404,14 +404,15 @@ An pattern which is matched against the session name. .El .Pp -If the session name is prefixed with a -.Ql = : , +If the session name is prefixed with an +.Ql = , only an exact match is accepted (so .Ql =mysess will only match exactly .Ql mysess , not .Ql mysession ) . +.Pp If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no From b43b13faf9ba620c3150bfe459fe6b38e58f6079 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2015 23:55:24 +0000 Subject: [PATCH 653/949] Use xsnprintf. --- format.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 67bce925..8a52b0e1 100644 --- a/format.c +++ b/format.c @@ -338,7 +338,7 @@ format_find(struct format_tree *ft, const char *key) case OPTIONS_STRING: return (o->str); case OPTIONS_NUMBER: - snprintf(s, sizeof s, "%lld", o->num); + xsnprintf(s, sizeof s, "%lld", o->num); return (s); case OPTIONS_STYLE: return (style_tostring(&o->style)); @@ -679,7 +679,7 @@ format_defaults_session(struct format_tree *ft, struct session *s) RB_FOREACH (wl, winlinks, &s->windows) { if ((wl->flags & WINLINK_ALERTFLAGS) == 0) continue; - snprintf(tmp, sizeof tmp, "%u", wl->idx); + xsnprintf(tmp, sizeof tmp, "%u", wl->idx); if (*alerts != '\0') strlcat(alerts, ",", sizeof alerts); From f557c7d8ca48c79136e4997b2cba0a08ee039257 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2015 23:56:01 +0000 Subject: [PATCH 654/949] Use the SRCDST define for usage. --- cmd-join-pane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index b995b674..cc6432a4 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -36,7 +36,7 @@ enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { "join-pane", "joinp", "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", + "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, 0, cmd_join_pane_exec }; @@ -44,7 +44,7 @@ const struct cmd_entry cmd_join_pane_entry = { const struct cmd_entry cmd_move_pane_entry = { "move-pane", "movep", "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", + "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, 0, cmd_join_pane_exec }; From 8abcea18a24dea24d6049fefa31c877133489092 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 20 Jun 2015 08:43:55 +0100 Subject: [PATCH 655/949] Remove monitor-content options which have been removed, from Guy Hughes. --- examples/tmux.vim | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 4d64514a..a6bf74e2 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -222,7 +222,6 @@ syn keyword tmuxOptsSet \ update-environment \ visual-activity \ visual-bell - \ visual-content \ visual-silence \ word-separators @@ -243,7 +242,6 @@ syn keyword tmuxOptsSetw \ mode-mouse \ mode-style \ monitor-activity - \ monitor-content \ monitor-silence \ other-pane-height \ other-pane-width @@ -253,7 +251,6 @@ syn keyword tmuxOptsSetw \ utf8 \ window-status-activity-style \ window-status-bell-style - \ window-status-content-style \ window-status-current-format \ window-status-current-style \ window-status-format From 78723af99f52251aec11d586a507e34b5d0fcaf6 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 28 Jun 2015 12:01:19 +0100 Subject: [PATCH 656/949] README: Clarify SYNCING is under the ISC --- README | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README b/README index 7859ed71..acf15632 100644 --- a/README +++ b/README @@ -52,9 +52,10 @@ welcome. Please send by email to: tmux-users@googlegroups.com -This file and the CHANGES, FAQ and TODO files are licensed under the ISC -license. Files under examples/ remain copyright their authors unless otherwise -stated in the file but permission has been received to distribute them with -tmux. All other files have a license and copyright notice at their start. +This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under +the ISC license. Files under examples/ remain copyright their authors unless +otherwise stated in the file but permission has been received to distribute +them with tmux. All other files have a license and copyright notice at their +start. -- Nicholas Marriott From b298478435c2e32b788566d1fab93dee1b71ff60 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Jul 2015 14:24:57 +0000 Subject: [PATCH 657/949] Update environment with -E when attach-session used on an already attached session or switch-client used on the current session. From Cam Hutchison. --- cmd-attach-session.c | 6 ++++++ cmd-switch-client.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index cb58d4e2..e7cde0f0 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -126,6 +126,12 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, s->cwd = fd; } + if (!Eflag) { + update = options_get_string(&s->options, + "update-environment"); + environ_update(update, &c->environ, &s->environ); + } + c->session = s; notify_attached_session_changed(c); session_update_activity(s); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 23751d73..4bac540d 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -119,7 +119,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } } - if (c != NULL && s != c->session && !args_has(args, 'E')) { + if (c != NULL && !args_has(args, 'E')) { update = options_get_string(&s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } From 235e0bd65a49b469d661a9831067e3f745fbbaad Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 12 Jul 2015 19:46:58 +0100 Subject: [PATCH 658/949] Update imsg*.[ch] from OpenBSD, including bzero->memset. --- compat.h | 5 ----- compat/imsg-buffer.c | 11 ++++++----- compat/imsg.c | 17 ++++++++--------- configure.ac | 1 - 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compat.h b/compat.h index 2c6f1c67..8666a1d8 100644 --- a/compat.h +++ b/compat.h @@ -185,11 +185,6 @@ typedef uint64_t u_int64_t; #define flock(fd, op) (0) #endif -#ifndef HAVE_BZERO -#undef bzero -#define bzero(buf, len) memset(buf, 0, len); -#endif - #ifndef HAVE_CLOSEFROM /* closefrom.c */ void closefrom(int); diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 6756de0a..241c4f4d 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg-buffer.c,v 1.4 2014/06/30 00:25:17 deraadt Exp $ */ +/* $OpenBSD: imsg-buffer.c,v 1.7 2015/07/12 18:40:49 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -74,7 +74,7 @@ ibuf_realloc(struct ibuf *buf, size_t len) /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { - errno = ENOMEM; + errno = ERANGE; return (-1); } @@ -149,7 +149,7 @@ ibuf_write(struct msgbuf *msgbuf) unsigned int i = 0; ssize_t n; - bzero(&iov, sizeof(iov)); + memset(&iov, 0, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; @@ -233,8 +233,9 @@ msgbuf_write(struct msgbuf *msgbuf) char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; - bzero(&iov, sizeof(iov)); - bzero(&msg, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; diff --git a/compat/imsg.c b/compat/imsg.c index 9db26ad6..982ee069 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.c,v 1.6 2014/06/30 00:26:22 deraadt Exp $ */ +/* $OpenBSD: imsg.c,v 1.9 2015/07/12 18:40:49 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -34,12 +34,10 @@ int imsg_get_fd(struct imsgbuf *); int available_fds(unsigned int); -/* TA: 2014-09-08: Note that the original code calls getdtablecount() which is - * OpenBSD specific. Until such time that it's ported elsewhere from - * , I've mimicked what OpenSMTPD are doing, by using available_fds() - * instead. +/* + * The original code calls getdtablecount() which is OpenBSD specific. Use + * available_fds() from OpenSMTPD instead. */ - int available_fds(unsigned int n) { @@ -68,7 +66,7 @@ void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); - bzero(&ibuf->r, sizeof(ibuf->r)); + memset(&ibuf->r, 0, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); @@ -89,7 +87,8 @@ imsg_read(struct imsgbuf *ibuf) int fd; struct imsg_fd *ifd; - bzero(&msg, sizeof(msg)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; @@ -317,7 +316,7 @@ int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) + if (msgbuf_write(&ibuf->w) <= 0) return (-1); return (0); } diff --git a/configure.ac b/configure.ac index cd3ab5a3..24aded53 100644 --- a/configure.ac +++ b/configure.ac @@ -99,7 +99,6 @@ AC_CHECK_HEADERS( # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ - bzero \ dirfd \ flock \ setproctitle \ From 73f9f0334ce639e2f05cb0646af2c81881859a2e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 13 Jul 2015 14:19:50 +0100 Subject: [PATCH 659/949] Check for flock in libbsd for AIX, and remove some getopt.h includes. From J Raynor. --- arguments.c | 1 - configure.ac | 3 +++ tmux.c | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arguments.c b/arguments.c index 05ff97eb..ca6cc760 100644 --- a/arguments.c +++ b/arguments.c @@ -18,7 +18,6 @@ #include -#include #include #include #include diff --git a/configure.ac b/configure.ac index 24aded53..de336fef 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,9 @@ AC_CHECK_HEADERS( ] ) +# Look for library needed for flock. +AC_SEARCH_LIBS(flock, bsd) + # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ diff --git a/tmux.c b/tmux.c index 62f8a808..39f344c0 100644 --- a/tmux.c +++ b/tmux.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include From cc768d77ec89c43e5770588b36d584d5d18104d3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 13:28:50 +0000 Subject: [PATCH 660/949] Revert to marking lines as wrapped on newlines, fixes problems with capturep -J. --- screen-write.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/screen-write.c b/screen-write.c index e38c9f53..37e2b548 100644 --- a/screen-write.c +++ b/screen-write.c @@ -795,6 +795,8 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) gl = &s->grid->linedata[s->grid->hsize + s->cy]; if (wrapped) gl->flags |= GRID_LINE_WRAPPED; + else + gl->flags &= ~GRID_LINE_WRAPPED; if (s->cy == s->rlower) grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); From 6308c48efd7a80dad5701721f76b9aafb9b814f8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 13:36:29 +0000 Subject: [PATCH 661/949] Add a -s flag to show-environment to output Bourne shell commands a la ssh-agent. Mostly from Cam Hutchison with some changes by me. --- cmd-show-environment.c | 68 ++++++++++++++++++++++++++++++++---------- tmux.1 | 5 +++- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 7737752f..a61cf3f4 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -27,16 +27,61 @@ * Show environment. */ -enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); + +char *cmd_show_environment_escape(struct environ_entry *); +void cmd_show_environment_print(struct cmd *, struct cmd_q *, + struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", - "gt:", 0, 1, - "[-g] " CMD_TARGET_SESSION_USAGE " [name]", + "gst:", 0, 1, + "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", 0, cmd_show_environment_exec }; +char * +cmd_show_environment_escape(struct environ_entry *envent) +{ + const char *value = envent->value; + char c, *out, *ret; + + out = ret = xmalloc(strlen(value) * 2 + 1); /* at most twice the size */ + while ((c = *value++) != '\0') { + /* POSIX interprets $ ` " and \ in double quotes. */ + if (c == '$' || c == '`' || c == '"' || c == '\\') + *out++ = '\\'; + *out++ = c; + } + *out = '\0'; + + return ret; +} + +void +cmd_show_environment_print(struct cmd *self, struct cmd_q *cmdq, + struct environ_entry *envent) +{ + char *escaped; + + if (!args_has(self->args, 's')) { + if (envent->value != NULL) + cmdq_print(cmdq, "%s=%s", envent->name, envent->value); + else + cmdq_print(cmdq, "-%s", envent->name); + return; + } + + if (envent->value != NULL) { + escaped = cmd_show_environment_escape(envent); + cmdq_print(cmdq, "%s=\"%s\"; export %s;", envent->name, escaped, + envent->name); + free(escaped); + } else + cmdq_print(cmdq, "unset %s;", envent->name); +} + enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -48,7 +93,8 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) env = &global_environ; else { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) return (CMD_RETURN_ERROR); env = &s->environ; } @@ -59,19 +105,11 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "unknown variable: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - if (envent->value != NULL) - cmdq_print(cmdq, "%s=%s", envent->name, envent->value); - else - cmdq_print(cmdq, "-%s", envent->name); + cmd_show_environment_print(self, cmdq, envent); return (CMD_RETURN_NORMAL); } - RB_FOREACH(envent, environ, env) { - if (envent->value != NULL) - cmdq_print(cmdq, "%s=%s", envent->name, envent->value); - else - cmdq_print(cmdq, "-%s", envent->name); - } - + RB_FOREACH(envent, environ, env) + cmd_show_environment_print(self, cmdq, envent); return (CMD_RETURN_NORMAL); } diff --git a/tmux.1 b/tmux.1 index f0dd569a..db5f314e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3542,7 +3542,7 @@ flag unsets a variable. indicates the variable is to be removed from the environment before starting a new process. .It Xo Ic show-environment -.Op Fl g +.Op Fl gs .Op Fl t Ar target-session .Op Ar variable .Xc @@ -3556,6 +3556,9 @@ If is omitted, all variables are shown. Variables removed from the environment are prefixed with .Ql - . +If +.Fl s +is used, the output is formatted as a set of Bourne shell commands. .El .Sh STATUS LINE .Nm From 81069f66f96dd83025fc6f2990619eb861199e10 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 15:37:26 +0000 Subject: [PATCH 662/949] Add a format to show if client is a control client. From Bruno Sutic. --- format.c | 2 ++ tmux.1 | 1 + 2 files changed, 3 insertions(+) diff --git a/format.c b/format.c index 8a52b0e1..aff34abb 100644 --- a/format.c +++ b/format.c @@ -711,6 +711,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_tty", "%s", c->tty.path); if (c->tty.termname != NULL) format_add(ft, "client_termname", "%s", c->tty.termname); + format_add(ft, "client_control_mode", "%d", + !!(c->flags & CLIENT_CONTROL)); t = c->creation_time.tv_sec; format_add(ft, "client_created", "%lld", (long long) t); diff --git a/tmux.1 b/tmux.1 index db5f314e..b4c608cb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3358,6 +3358,7 @@ The following variables are available, where appropriate: .It Li "client_activity_string" Ta "" Ta "String time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" .It Li "client_created_string" Ta "" Ta "String time client created" +.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_pid" Ta "" Ta "PID of client process" From c7374c31c4ba176e94825e8d734b5abe8a6879b1 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 15:49:31 +0000 Subject: [PATCH 663/949] Initialize cwd fd to -1 so that we don't close fd 0 if the client is destroyed before it is changed. Also allow ttyname() to fail. Fixes problems when running out of file descriptors reported by Bruno Sutic. --- server-client.c | 5 +++-- tmux.h | 2 +- tty.c | 9 +++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index 9beef4bc..4c03de91 100644 --- a/server-client.c +++ b/server-client.c @@ -96,6 +96,8 @@ server_client_create(int fd) environ_init(&c->environ); + c->cwd = -1; + c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; @@ -1253,12 +1255,11 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (c->fd == -1) return; - if (!isatty(c->fd)) { + if (tty_init(&c->tty, c, c->fd, c->term) != 0) { close(c->fd); c->fd = -1; return; } - tty_init(&c->tty, c, c->fd, c->term); if (c->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; if (c->flags & CLIENT_256COLOURS) diff --git a/tmux.h b/tmux.h index 663832e5..e082aeab 100644 --- a/tmux.h +++ b/tmux.h @@ -1600,7 +1600,7 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); -void tty_init(struct tty *, struct client *, int, char *); +int tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); void tty_set_class(struct tty *, u_int); diff --git a/tty.c b/tty.c index 63380c29..ffb36cb1 100644 --- a/tty.c +++ b/tty.c @@ -59,11 +59,14 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) -void +int tty_init(struct tty *tty, struct client *c, int fd, char *term) { char *path; + if (!isatty(fd)) + return (-1); + memset(tty, 0, sizeof *tty); tty->log_fd = -1; @@ -75,13 +78,15 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->client = c; if ((path = ttyname(fd)) == NULL) - fatalx("ttyname failed"); + return (-1); tty->path = xstrdup(path); tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; + + return (0); } int From e45d624df288d914a1628d373ff245b03f7d600b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 15:51:31 +0000 Subject: [PATCH 664/949] Fix line endings. --- server-client.c | 6 +++--- tmux.h | 2 +- tty.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/server-client.c b/server-client.c index 4c03de91..09348b05 100644 --- a/server-client.c +++ b/server-client.c @@ -96,8 +96,8 @@ server_client_create(int fd) environ_init(&c->environ); - c->cwd = -1; - + c->cwd = -1; + c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; @@ -1255,7 +1255,7 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (c->fd == -1) return; - if (tty_init(&c->tty, c, c->fd, c->term) != 0) { + if (tty_init(&c->tty, c, c->fd, c->term) != 0) { close(c->fd); c->fd = -1; return; diff --git a/tmux.h b/tmux.h index e082aeab..3aceff9d 100644 --- a/tmux.h +++ b/tmux.h @@ -1600,7 +1600,7 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); 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 *); +int tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); void tty_set_class(struct tty *, u_int); diff --git a/tty.c b/tty.c index ffb36cb1..374fb8c6 100644 --- a/tty.c +++ b/tty.c @@ -59,14 +59,14 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) -int +int tty_init(struct tty *tty, struct client *c, int fd, char *term) { char *path; - if (!isatty(fd)) - return (-1); - + if (!isatty(fd)) + return (-1); + memset(tty, 0, sizeof *tty); tty->log_fd = -1; @@ -78,15 +78,15 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->client = c; if ((path = ttyname(fd)) == NULL) - return (-1); + return (-1); tty->path = xstrdup(path); tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; - - return (0); + + return (0); } int From 4e637b1b610f93d474a60daa4ab78afef66b29bc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 18:10:26 +0000 Subject: [PATCH 665/949] Ignore environment variables that are too long to send to the server. --- client.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 4b7bbb1f..63432926 100644 --- a/client.c +++ b/client.c @@ -350,6 +350,7 @@ client_send_identify(int flags) { const char *s; char **ss; + size_t sslen; int fd; pid_t pid; @@ -374,8 +375,11 @@ client_send_identify(int flags) pid = getpid(); client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); - for (ss = environ; *ss != NULL; ss++) - client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); + for (ss = environ; *ss != NULL; ss++) { + sslen = strlen(*ss) + 1; + if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) + client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + } client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); From 8dcea2cc14448494d25a6a68618ae7088bef1b95 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 18:45:18 +0000 Subject: [PATCH 666/949] Reset G0/G1 state when resetting everything else with send-keys -R. --- input.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/input.c b/input.c index d1ff17fe..095816c3 100644 --- a/input.c +++ b/input.c @@ -801,10 +801,7 @@ input_reset(struct window_pane *wp) { struct input_ctx *ictx = wp->ictx; - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; + input_reset_cell(ictx); if (wp->mode == NULL) screen_write_start(&ictx->ctx, wp, &wp->base); From bed3069fd746741286e624126774f98ed51fbbdf Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 14 Jul 2015 08:14:35 +0100 Subject: [PATCH 667/949] Add _LINUX_SOURCE_COMPAT on AIX. --- Makefile.am | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile.am b/Makefile.am index 99163c2c..3173ba99 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,6 +50,11 @@ if IS_SUNCC CFLAGS += -erroff=E_EMPTY_DECLARATION endif +# Set _LINUX_SOURCE_COMPAT for AIX for mallocing 0 bytes +if IS_AIX +DEFS += -D_LINUX_SOURCE_COMPAT=1 +endif + # List of sources. dist_tmux_SOURCES = \ arguments.c \ From 8c96e2a6d941bf5438b587c3d4a5ee1c97f9aa14 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Jul 2015 08:46:35 +0100 Subject: [PATCH 668/949] Implement osdep_get_name and osdep_get_cwd for AIX, from J Raynor. --- compat/forkpty-aix.c | 4 +++ osdep-aix.c | 64 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/compat/forkpty-aix.c b/compat/forkpty-aix.c index fd558eb8..6894aa44 100644 --- a/compat/forkpty-aix.c +++ b/compat/forkpty-aix.c @@ -40,6 +40,10 @@ forkpty(int *master, unused char *name, struct termios *tio, struct winsize *ws) if ((path = ttyname(*master)) == NULL) goto out; + + if (name != NULL) + strlcpy(name, path, TTY_NAME_MAX); + if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; diff --git a/osdep-aix.c b/osdep-aix.c index 24051ab8..0a3d12e4 100644 --- a/osdep-aix.c +++ b/osdep-aix.c @@ -16,21 +16,75 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include +#include -#include +#include +#include +#include #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(unused int fd, char *tty) { - return (NULL); + struct psinfo p; + char *path; + ssize_t bytes; + int f, ttyfd, retval; + pid_t pgrp; + + if ((ttyfd = open(tty, O_RDONLY|O_NOCTTY)) == -1) + return (NULL); + + retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); + close(ttyfd); + if (retval == -1) + return (NULL); + + xasprintf(&path, "/proc/%u/psinfo", (u_int) pgrp); + f = open(path, O_RDONLY); + free(path); + if (f < 0) + return (NULL); + + bytes = read(f, &p, sizeof(p)); + close(f); + if (bytes != sizeof(p)) + return (NULL); + + return (xstrdup(p.pr_fname)); } char * -osdep_get_cwd(unused int fd) +osdep_get_cwd(int fd) { + static char target[MAXPATHLEN + 1]; + char *path; + const char *ttypath; + ssize_t n; + pid_t pgrp; + int len, retval, ttyfd; + + if ((ttypath = ptsname(fd)) == NULL) + return (NULL); + if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) + return (NULL); + + retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); + close(ttyfd); + if (retval == -1) + return (NULL); + + xasprintf(&path, "/proc/%u/cwd", (u_int) pgrp); + n = readlink(path, target, MAXPATHLEN); + free(path); + if (n > 0) { + target[n] = '\0'; + if ((len = strlen(target)) > 1 && target[len - 1] == '/') + target[len - 1] = '\0'; + return (target); + } return (NULL); } From 3192178f15c9bcc88021ceb74189173d562e7694 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Jul 2015 06:53:47 +0000 Subject: [PATCH 669/949] Initialize client fd to -1 as well, from Bobby Powers. --- server-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-client.c b/server-client.c index 09348b05..c2c7070f 100644 --- a/server-client.c +++ b/server-client.c @@ -96,6 +96,7 @@ server_client_create(int fd) environ_init(&c->environ); + c->fd = -1; c->cwd = -1; c->cmdq = cmdq_new(c); From bad8d0fd200c7718a8f51f52b9dd2f23c74d996f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Jul 2015 13:09:07 +0000 Subject: [PATCH 670/949] Do not call window_unzoom from window_destroy because it will try to add a notification which will get confused because the reference count is already zero and end up back in window_destroy and a double free. Instead, just destroy the layouts directly. Noticed by Thomas Adam. --- window.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/window.c b/window.c index 82c10606..6351eff5 100644 --- a/window.c +++ b/window.c @@ -341,12 +341,12 @@ window_create(const char *name, int argc, char **argv, const char *path, void window_destroy(struct window *w) { - window_unzoom(w); - RB_REMOVE(windows, &windows, w); if (w->layout_root != NULL) - layout_free(w); + layout_free_cell(w->layout_root); + if (w->saved_layout_root != NULL) + layout_free_cell(w->saved_layout_root); free(w->old_layout); if (event_initialized(&w->name_timer)) From 96dcbe217bda0b065df8079e33d14a7192e14c46 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 19 Jul 2015 08:07:55 +0100 Subject: [PATCH 671/949] Update tmux.vim from Ben Boeckel. --- examples/tmux.vim | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index a6bf74e2..dcb58246 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -191,9 +191,7 @@ syn keyword tmuxOptsSet \ message-command-style \ message-limit \ message-style - \ mouse-resize-pane - \ mouse-select-pane - \ mouse-select-window + \ mouse \ mouse-utf8 \ pane-active-border-style \ pane-border-style @@ -230,8 +228,6 @@ syn keyword tmuxOptsSetw \ allow-rename \ alternate-screen \ automatic-rename - \ c0-change-interval - \ c0-change-trigger \ clock-mode-colour \ clock-mode-style \ force-height @@ -239,7 +235,6 @@ syn keyword tmuxOptsSetw \ main-pane-height \ main-pane-width \ mode-keys - \ mode-mouse \ mode-style \ monitor-activity \ monitor-silence @@ -249,6 +244,7 @@ syn keyword tmuxOptsSetw \ remain-on-exit \ synchronize-panes \ utf8 + \ window-active-style \ window-status-activity-style \ window-status-bell-style \ window-status-current-format @@ -257,6 +253,7 @@ syn keyword tmuxOptsSetw \ window-status-last-style \ window-status-separator \ window-status-style + \ window-style \ wrap-search \ xterm-keys From d4ce210713cc907fc69497c51cf9b64d2f414ca4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Jul 2015 10:34:11 +0000 Subject: [PATCH 672/949] Correct the tsl/fsl sequence to ]0 not ]2 (from Marcel Korpel). While here, Xr xterm and remove some advice about elinks that is better elsewhere. --- tmux.1 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tmux.1 b/tmux.1 index b4c608cb..69bde40a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2709,12 +2709,10 @@ and .Xr terminfo 5 entries if they exist. .Nm -automatically sets these to the \ee]2;...\e007 sequence if -the terminal appears to be an xterm. +automatically sets these to the \ee]0;...\e007 sequence if +the terminal appears to be +.Xr xterm 1 . This option is off by default. -Note that elinks -will only attempt to set the window title if the STY environment -variable is set. .It Ic set-titles-string Ar string String used to set the window title if .Ic set-titles From 92af3766ecc456bd8ade8fe5746d27f4557fdaa8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Jul 2015 15:50:04 +0000 Subject: [PATCH 673/949] Add an option (history-file) for a file to save/restore command prompt history, from Olof-Joachim Frahm. --- options-table.c | 5 +++ server.c | 2 ++ status.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ tmux.1 | 4 +++ tmux.c | 28 ++++++++++----- tmux.h | 3 ++ 6 files changed, 124 insertions(+), 9 deletions(-) diff --git a/options-table.c b/options-table.c index 4ad45d37..7f75d22c 100644 --- a/options-table.c +++ b/options-table.c @@ -83,6 +83,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "history-file", + .type = OPTIONS_TABLE_STRING, + .default_str = NULL + }, + { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, diff --git a/server.c b/server.c index 001a404c..d1c0bb3b 100644 --- a/server.c +++ b/server.c @@ -240,6 +240,7 @@ server_start(int lockfd, char *lockfile) cfg_add_cause("%s: %s", cfg_file, cause); } cmdq_continue(cfg_cmd_q); + status_prompt_load_history(); server_add_accept(0); @@ -250,6 +251,7 @@ server_start(int lockfd, char *lockfile) set_signals(server_signal_callback); server_loop(); + status_prompt_save_history(); exit(0); } diff --git a/status.c b/status.c index 0ffc0427..96ff8994 100644 --- a/status.c +++ b/status.c @@ -47,11 +47,102 @@ const char **status_prompt_complete_list(u_int *, const char *); char *status_prompt_complete_prefix(const char **, u_int); char *status_prompt_complete(struct session *, const char *); +char *status_prompt_find_history_file(void); + /* Status prompt history. */ #define PROMPT_HISTORY 100 char **status_prompt_hlist; u_int status_prompt_hsize; +/* Find the history file to load/save from/to. */ +char * +status_prompt_find_history_file(void) +{ + const char *home, *history_file; + char *path; + + history_file = options_get_string(&global_options, "history-file"); + if (*history_file == '\0') + return (NULL); + if (*history_file == '/') + return (xstrdup(history_file)); + + if (history_file[0] != '~' || history_file[1] != '/') + return (NULL); + if ((home = find_home()) == NULL) + return (NULL); + xasprintf(&path, "%s%s", home, history_file + 1); + return (path); +} + +/* Load status prompt history from file. */ +void +status_prompt_load_history(void) +{ + FILE *f; + char *history_file, *line, *tmp; + size_t length; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("loading history from %s", history_file); + + f = fopen(history_file, "r"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (;;) { + if ((line = fgetln(f, &length)) == NULL) + break; + + if (length > 0) { + if (line[length - 1] == '\n') { + line[length - 1] = '\0'; + status_prompt_add_history(line); + } else { + tmp = xmalloc(length + 1); + memcpy(tmp, line, length); + tmp[length] = '\0'; + status_prompt_add_history(tmp); + free(tmp); + } + } + } + fclose(f); +} + +/* Save status prompt history to file. */ +void +status_prompt_save_history(void) +{ + FILE *f; + u_int i; + char *history_file; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("saving history to %s", history_file); + + f = fopen(history_file, "w"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (i = 0; i < status_prompt_hsize; i++) { + fputs(status_prompt_hlist[i], f); + fputc('\n', f); + } + fclose(f); + +} + /* Status output tree. */ RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); diff --git a/tmux.1 b/tmux.1 index 69bde40a..7320df4f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2396,6 +2396,10 @@ passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. +.It Ic history-file Ar path +If not empty, a file to which +.Nm +will write command prompt history on exit and load it from on start. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. diff --git a/tmux.c b/tmux.c index 878180a7..a3481461 100644 --- a/tmux.c +++ b/tmux.c @@ -198,10 +198,27 @@ shell_exec(const char *shell, const char *shellcmd) fatal("execl failed"); } +const char* +find_home(void) +{ + struct passwd *pw; + const char *home; + + home = getenv("HOME"); + if (home == NULL || *home == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + home = pw->pw_dir; + else + home = NULL; + } + + return home; +} + int main(int argc, char **argv) { - struct passwd *pw; char *s, *path, *label, **var, tmp[PATH_MAX]; char in[256]; const char *home; @@ -320,14 +337,7 @@ main(int argc, char **argv) /* Locate the configuration file. */ if (cfg_file == NULL) { - home = getenv("HOME"); - if (home == NULL || *home == '\0') { - pw = getpwuid(getuid()); - if (pw != NULL) - home = pw->pw_dir; - else - home = NULL; - } + home = find_home(); if (home != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { diff --git a/tmux.h b/tmux.h index 3aceff9d..7edc4636 100644 --- a/tmux.h +++ b/tmux.h @@ -1469,6 +1469,7 @@ int checkshell(const char *); int areshell(const char *); void setblocking(int, int); __dead void shell_exec(const char *, const char *); +const char *find_home(void); /* cfg.c */ extern struct cmd_q *cfg_cmd_q; @@ -1943,6 +1944,8 @@ void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); void status_prompt_key(struct client *, int); void status_prompt_update(struct client *, const char *, const char *); +void status_prompt_load_history(void); +void status_prompt_save_history(void); /* resize.c */ void recalculate_sizes(void); From 669059aa19ec831e9a8afc008c978ea7806692e3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Jul 2015 09:06:15 +0100 Subject: [PATCH 674/949] Fix a warning, from Kosta Zertsekel. --- compat/getopt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compat/getopt.c b/compat/getopt.c index 03ad9e80..9cd3b3d2 100644 --- a/compat/getopt.c +++ b/compat/getopt.c @@ -94,7 +94,7 @@ BSDgetopt(int nargc, char *const *nargv, const char *ostr) } else { /* need an argument */ if (*place) /* no white space */ - BSDoptarg = place; + BSDoptarg = (char *)place; else if (nargc <= ++BSDoptind) { /* no arg */ place = EMSG; if (*ostr == ':') From d33adc4fd0f7657ede3178c9b1f33c2d5df3c524 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jul 2015 08:45:45 +0000 Subject: [PATCH 675/949] Make -q suppress ambiguous option warnings too, from Cam Hutchison. --- cmd-set-option.c | 7 +++++-- tmux.1 | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 77c7d7c2..761cee93 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -110,8 +110,11 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Find the option entry, try each table. */ table = oe = NULL; if (options_table_find(optstr, &table, &oe) != 0) { - cmdq_error(cmdq, "ambiguous option: %s", optstr); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "ambiguous option: %s", optstr); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } if (oe == NULL) { if (!args_has(args, 'q')) { diff --git a/tmux.1 b/tmux.1 index 7320df4f..a4422057 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2322,9 +2322,9 @@ restores a global option to the default). .Pp The .Fl o -flag prevents setting an option that is already set and +flag prevents setting an option that is already set and the .Fl q -flag suppresses errors about unknown options. +flag suppresses errors about unknown or ambiguous options. .Pp With .Fl a , From b254115acd54513cd4b5858e31afc7980e93246c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jul 2015 15:18:10 +0000 Subject: [PATCH 676/949] Tidy up the way terminals are described and move some structs out of tmux.h. --- cmd-show-messages.c | 34 +--- status.c | 1 - tmux.h | 32 +-- tty-term.c | 477 +++++++++++++++++++++++++------------------- 4 files changed, 277 insertions(+), 267 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 2a04bd93..92ac5cc2 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -70,11 +70,8 @@ cmd_show_messages_server(struct cmd_q *cmdq) int cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { - struct tty_term *term; - const struct tty_term_code_entry *ent; - struct tty_code *code; - u_int i, n; - char out[80]; + struct tty_term *term; + u_int i, n; n = 0; LIST_FOREACH(term, &tty_terms, entry) { @@ -85,31 +82,8 @@ cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) cmdq_print(cmdq, "Terminal %u: %s [references=%u, flags=0x%x]:", n, term->name, term->references, term->flags); n++; - for (i = 0; i < NTTYCODE; i++) { - ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; - switch (code->type) { - case TTYCODE_NONE: - cmdq_print(cmdq, "%4u: %s: [missing]", - ent->code, ent->name); - break; - case TTYCODE_STRING: - strnvis(out, code->value.string, sizeof out, - VIS_OCTAL|VIS_TAB|VIS_NL); - cmdq_print(cmdq, "%4u: %s: (string) %s", - ent->code, ent->name, out); - break; - case TTYCODE_NUMBER: - cmdq_print(cmdq, "%4u: %s: (number) %d", - ent->code, ent->name, code->value.number); - break; - case TTYCODE_FLAG: - cmdq_print(cmdq, "%4u: %s: (flag) %s", - ent->code, ent->name, - code->value.flag ? "true" : "false"); - break; - } - } + for (i = 0; i < tty_term_ncodes(); i++) + cmdq_print(cmdq, "%s", tty_term_describe(term, i)); } return (n != 0); } diff --git a/status.c b/status.c index 96ff8994..16f4fa70 100644 --- a/status.c +++ b/status.c @@ -36,7 +36,6 @@ char *status_redraw_get_right(struct client *, time_t, int, char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); -void status_replace1(char **, char **, char *, size_t); void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); diff --git a/tmux.h b/tmux.h index 7edc4636..b3456922 100644 --- a/tmux.h +++ b/tmux.h @@ -387,32 +387,6 @@ enum tty_code_code { TTYC_XENL, /* eat_newline_glitch, xn */ TTYC_XT, /* xterm(1)-compatible title, XT */ }; -#define NTTYCODE (TTYC_XT + 1) - -/* Termcap types. */ -enum tty_code_type { - TTYCODE_NONE = 0, - TTYCODE_STRING, - TTYCODE_NUMBER, - TTYCODE_FLAG, -}; - -/* Termcap code. */ -struct tty_code { - enum tty_code_type type; - union { - char *string; - int number; - int flag; - } value; -}; - -/* Entry in terminal code table. */ -struct tty_term_code_entry { - enum tty_code_code code; - enum tty_code_type type; - const char *name; -}; /* Message codes. */ enum msgtype { @@ -1096,13 +1070,14 @@ struct tty_key { struct tty_key *next; }; +struct tty_code; struct tty_term { char *name; u_int references; char acs[UCHAR_MAX + 1][2]; - struct tty_code codes[NTTYCODE]; + struct tty_code *codes; #define TERM_256COLOURS 0x1 #define TERM_EARLYWRAP 0x2 @@ -1643,7 +1618,7 @@ void tty_bell(struct tty *); /* tty-term.c */ extern struct tty_terms tty_terms; -extern const struct tty_term_code_entry tty_term_codes[NTTYCODE]; +u_int tty_term_ncodes(void); struct tty_term *tty_term_find(char *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); @@ -1657,6 +1632,7 @@ const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const void *, const void *); int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code); +const char *tty_term_describe(struct tty_term *, enum tty_code_code); /* tty-acs.c */ const char *tty_acs_get(struct tty *, u_char); diff --git a/tty-term.c b/tty-term.c index f6f6b444..14339de1 100644 --- a/tty-term.c +++ b/tty-term.c @@ -32,210 +32,237 @@ char *tty_term_strip(const char *); struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); -const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { - { TTYC_ACSC, TTYCODE_STRING, "acsc" }, - { TTYC_AX, TTYCODE_FLAG, "AX" }, - { TTYC_BCE, TTYCODE_FLAG, "bce" }, - { TTYC_BEL, TTYCODE_STRING, "bel" }, - { TTYC_BLINK, TTYCODE_STRING, "blink" }, - { TTYC_BOLD, TTYCODE_STRING, "bold" }, - { TTYC_CIVIS, TTYCODE_STRING, "civis" }, - { TTYC_CLEAR, TTYCODE_STRING, "clear" }, - { TTYC_CNORM, TTYCODE_STRING, "cnorm" }, - { TTYC_COLORS, TTYCODE_NUMBER, "colors" }, - { TTYC_CR, TTYCODE_STRING, "Cr" }, - { TTYC_CS, TTYCODE_STRING, "Cs" }, - { TTYC_CSR, TTYCODE_STRING, "csr" }, - { TTYC_CUB, TTYCODE_STRING, "cub" }, - { TTYC_CUB1, TTYCODE_STRING, "cub1" }, - { TTYC_CUD, TTYCODE_STRING, "cud" }, - { TTYC_CUD1, TTYCODE_STRING, "cud1" }, - { TTYC_CUF, TTYCODE_STRING, "cuf" }, - { TTYC_CUF1, TTYCODE_STRING, "cuf1" }, - { TTYC_CUP, TTYCODE_STRING, "cup" }, - { TTYC_CUU, TTYCODE_STRING, "cuu" }, - { TTYC_CUU1, TTYCODE_STRING, "cuu1" }, - { TTYC_CVVIS, TTYCODE_STRING, "cvvis" }, - { TTYC_DCH, TTYCODE_STRING, "dch" }, - { TTYC_DCH1, TTYCODE_STRING, "dch1" }, - { TTYC_DIM, TTYCODE_STRING, "dim" }, - { TTYC_DL, TTYCODE_STRING, "dl" }, - { TTYC_DL1, TTYCODE_STRING, "dl1" }, - { TTYC_E3, TTYCODE_STRING, "E3" }, - { TTYC_ECH, TTYCODE_STRING, "ech" }, - { TTYC_EL, TTYCODE_STRING, "el" }, - { TTYC_EL1, TTYCODE_STRING, "el1" }, - { TTYC_ENACS, TTYCODE_STRING, "enacs" }, - { TTYC_FSL, TTYCODE_STRING, "fsl" }, - { TTYC_HOME, TTYCODE_STRING, "home" }, - { TTYC_HPA, TTYCODE_STRING, "hpa" }, - { TTYC_ICH, TTYCODE_STRING, "ich" }, - { TTYC_ICH1, TTYCODE_STRING, "ich1" }, - { TTYC_IL, TTYCODE_STRING, "il" }, - { TTYC_IL1, TTYCODE_STRING, "il1" }, - { TTYC_INVIS, TTYCODE_STRING, "invis" }, - { TTYC_IS1, TTYCODE_STRING, "is1" }, - { TTYC_IS2, TTYCODE_STRING, "is2" }, - { TTYC_IS3, TTYCODE_STRING, "is3" }, - { TTYC_KCBT, TTYCODE_STRING, "kcbt" }, - { TTYC_KCUB1, TTYCODE_STRING, "kcub1" }, - { TTYC_KCUD1, TTYCODE_STRING, "kcud1" }, - { TTYC_KCUF1, TTYCODE_STRING, "kcuf1" }, - { TTYC_KCUU1, TTYCODE_STRING, "kcuu1" }, - { TTYC_KDC2, TTYCODE_STRING, "kDC" }, - { TTYC_KDC3, TTYCODE_STRING, "kDC3" }, - { TTYC_KDC4, TTYCODE_STRING, "kDC4" }, - { TTYC_KDC5, TTYCODE_STRING, "kDC5" }, - { TTYC_KDC6, TTYCODE_STRING, "kDC6" }, - { TTYC_KDC7, TTYCODE_STRING, "kDC7" }, - { TTYC_KDCH1, TTYCODE_STRING, "kdch1" }, - { TTYC_KDN2, TTYCODE_STRING, "kDN" }, - { TTYC_KDN3, TTYCODE_STRING, "kDN3" }, - { TTYC_KDN4, TTYCODE_STRING, "kDN4" }, - { TTYC_KDN5, TTYCODE_STRING, "kDN5" }, - { TTYC_KDN6, TTYCODE_STRING, "kDN6" }, - { TTYC_KDN7, TTYCODE_STRING, "kDN7" }, - { TTYC_KEND, TTYCODE_STRING, "kend" }, - { TTYC_KEND2, TTYCODE_STRING, "kEND" }, - { TTYC_KEND3, TTYCODE_STRING, "kEND3" }, - { TTYC_KEND4, TTYCODE_STRING, "kEND4" }, - { TTYC_KEND5, TTYCODE_STRING, "kEND5" }, - { TTYC_KEND6, TTYCODE_STRING, "kEND6" }, - { TTYC_KEND7, TTYCODE_STRING, "kEND7" }, - { TTYC_KF1, TTYCODE_STRING, "kf1" }, - { TTYC_KF10, TTYCODE_STRING, "kf10" }, - { TTYC_KF11, TTYCODE_STRING, "kf11" }, - { TTYC_KF12, TTYCODE_STRING, "kf12" }, - { TTYC_KF13, TTYCODE_STRING, "kf13" }, - { TTYC_KF14, TTYCODE_STRING, "kf14" }, - { TTYC_KF15, TTYCODE_STRING, "kf15" }, - { TTYC_KF16, TTYCODE_STRING, "kf16" }, - { TTYC_KF17, TTYCODE_STRING, "kf17" }, - { TTYC_KF18, TTYCODE_STRING, "kf18" }, - { TTYC_KF19, TTYCODE_STRING, "kf19" }, - { TTYC_KF2, TTYCODE_STRING, "kf2" }, - { TTYC_KF20, TTYCODE_STRING, "kf20" }, - { TTYC_KF21, TTYCODE_STRING, "kf21" }, - { TTYC_KF22, TTYCODE_STRING, "kf22" }, - { TTYC_KF23, TTYCODE_STRING, "kf23" }, - { TTYC_KF24, TTYCODE_STRING, "kf24" }, - { TTYC_KF25, TTYCODE_STRING, "kf25" }, - { TTYC_KF26, TTYCODE_STRING, "kf26" }, - { TTYC_KF27, TTYCODE_STRING, "kf27" }, - { TTYC_KF28, TTYCODE_STRING, "kf28" }, - { TTYC_KF29, TTYCODE_STRING, "kf29" }, - { TTYC_KF3, TTYCODE_STRING, "kf3" }, - { TTYC_KF30, TTYCODE_STRING, "kf30" }, - { TTYC_KF31, TTYCODE_STRING, "kf31" }, - { TTYC_KF32, TTYCODE_STRING, "kf32" }, - { TTYC_KF33, TTYCODE_STRING, "kf33" }, - { TTYC_KF34, TTYCODE_STRING, "kf34" }, - { TTYC_KF35, TTYCODE_STRING, "kf35" }, - { TTYC_KF36, TTYCODE_STRING, "kf36" }, - { TTYC_KF37, TTYCODE_STRING, "kf37" }, - { TTYC_KF38, TTYCODE_STRING, "kf38" }, - { TTYC_KF39, TTYCODE_STRING, "kf39" }, - { TTYC_KF4, TTYCODE_STRING, "kf4" }, - { TTYC_KF40, TTYCODE_STRING, "kf40" }, - { TTYC_KF41, TTYCODE_STRING, "kf41" }, - { TTYC_KF42, TTYCODE_STRING, "kf42" }, - { TTYC_KF43, TTYCODE_STRING, "kf43" }, - { TTYC_KF44, TTYCODE_STRING, "kf44" }, - { TTYC_KF45, TTYCODE_STRING, "kf45" }, - { TTYC_KF46, TTYCODE_STRING, "kf46" }, - { TTYC_KF47, TTYCODE_STRING, "kf47" }, - { TTYC_KF48, TTYCODE_STRING, "kf48" }, - { TTYC_KF49, TTYCODE_STRING, "kf49" }, - { TTYC_KF5, TTYCODE_STRING, "kf5" }, - { TTYC_KF50, TTYCODE_STRING, "kf50" }, - { TTYC_KF51, TTYCODE_STRING, "kf51" }, - { TTYC_KF52, TTYCODE_STRING, "kf52" }, - { TTYC_KF53, TTYCODE_STRING, "kf53" }, - { TTYC_KF54, TTYCODE_STRING, "kf54" }, - { TTYC_KF55, TTYCODE_STRING, "kf55" }, - { TTYC_KF56, TTYCODE_STRING, "kf56" }, - { TTYC_KF57, TTYCODE_STRING, "kf57" }, - { TTYC_KF58, TTYCODE_STRING, "kf58" }, - { TTYC_KF59, TTYCODE_STRING, "kf59" }, - { TTYC_KF6, TTYCODE_STRING, "kf6" }, - { TTYC_KF60, TTYCODE_STRING, "kf60" }, - { TTYC_KF61, TTYCODE_STRING, "kf61" }, - { TTYC_KF62, TTYCODE_STRING, "kf62" }, - { TTYC_KF63, TTYCODE_STRING, "kf63" }, - { TTYC_KF7, TTYCODE_STRING, "kf7" }, - { TTYC_KF8, TTYCODE_STRING, "kf8" }, - { TTYC_KF9, TTYCODE_STRING, "kf9" }, - { TTYC_KHOM2, TTYCODE_STRING, "kHOM" }, - { TTYC_KHOM3, TTYCODE_STRING, "kHOM3" }, - { TTYC_KHOM4, TTYCODE_STRING, "kHOM4" }, - { TTYC_KHOM5, TTYCODE_STRING, "kHOM5" }, - { TTYC_KHOM6, TTYCODE_STRING, "kHOM6" }, - { TTYC_KHOM7, TTYCODE_STRING, "kHOM7" }, - { TTYC_KHOME, TTYCODE_STRING, "khome" }, - { TTYC_KIC2, TTYCODE_STRING, "kIC" }, - { TTYC_KIC3, TTYCODE_STRING, "kIC3" }, - { TTYC_KIC4, TTYCODE_STRING, "kIC4" }, - { TTYC_KIC5, TTYCODE_STRING, "kIC5" }, - { TTYC_KIC6, TTYCODE_STRING, "kIC6" }, - { TTYC_KIC7, TTYCODE_STRING, "kIC7" }, - { TTYC_KICH1, TTYCODE_STRING, "kich1" }, - { TTYC_KLFT2, TTYCODE_STRING, "kLFT" }, - { TTYC_KLFT3, TTYCODE_STRING, "kLFT3" }, - { TTYC_KLFT4, TTYCODE_STRING, "kLFT4" }, - { TTYC_KLFT5, TTYCODE_STRING, "kLFT5" }, - { TTYC_KLFT6, TTYCODE_STRING, "kLFT6" }, - { TTYC_KLFT7, TTYCODE_STRING, "kLFT7" }, - { TTYC_KMOUS, TTYCODE_STRING, "kmous" }, - { TTYC_KNP, TTYCODE_STRING, "knp" }, - { TTYC_KNXT2, TTYCODE_STRING, "kNXT" }, - { TTYC_KNXT3, TTYCODE_STRING, "kNXT3" }, - { TTYC_KNXT4, TTYCODE_STRING, "kNXT4" }, - { TTYC_KNXT5, TTYCODE_STRING, "kNXT5" }, - { TTYC_KNXT6, TTYCODE_STRING, "kNXT6" }, - { TTYC_KNXT7, TTYCODE_STRING, "kNXT7" }, - { TTYC_KPP, TTYCODE_STRING, "kpp" }, - { TTYC_KPRV2, TTYCODE_STRING, "kPRV" }, - { TTYC_KPRV3, TTYCODE_STRING, "kPRV3" }, - { TTYC_KPRV4, TTYCODE_STRING, "kPRV4" }, - { TTYC_KPRV5, TTYCODE_STRING, "kPRV5" }, - { TTYC_KPRV6, TTYCODE_STRING, "kPRV6" }, - { TTYC_KPRV7, TTYCODE_STRING, "kPRV7" }, - { TTYC_KRIT2, TTYCODE_STRING, "kRIT" }, - { TTYC_KRIT3, TTYCODE_STRING, "kRIT3" }, - { TTYC_KRIT4, TTYCODE_STRING, "kRIT4" }, - { TTYC_KRIT5, TTYCODE_STRING, "kRIT5" }, - { TTYC_KRIT6, TTYCODE_STRING, "kRIT6" }, - { TTYC_KRIT7, TTYCODE_STRING, "kRIT7" }, - { TTYC_KUP2, TTYCODE_STRING, "kUP" }, - { TTYC_KUP3, TTYCODE_STRING, "kUP3" }, - { TTYC_KUP4, TTYCODE_STRING, "kUP4" }, - { TTYC_KUP5, TTYCODE_STRING, "kUP5" }, - { TTYC_KUP6, TTYCODE_STRING, "kUP6" }, - { TTYC_KUP7, TTYCODE_STRING, "kUP7" }, - { TTYC_MS, TTYCODE_STRING, "Ms" }, - { TTYC_OP, TTYCODE_STRING, "op" }, - { TTYC_REV, TTYCODE_STRING, "rev" }, - { TTYC_RI, TTYCODE_STRING, "ri" }, - { TTYC_RMACS, TTYCODE_STRING, "rmacs" }, - { TTYC_RMCUP, TTYCODE_STRING, "rmcup" }, - { TTYC_RMKX, TTYCODE_STRING, "rmkx" }, - { TTYC_SE, TTYCODE_STRING, "Se" }, - { TTYC_SETAB, TTYCODE_STRING, "setab" }, - { TTYC_SETAF, TTYCODE_STRING, "setaf" }, - { TTYC_SGR0, TTYCODE_STRING, "sgr0" }, - { TTYC_SITM, TTYCODE_STRING, "sitm" }, - { TTYC_SMACS, TTYCODE_STRING, "smacs" }, - { TTYC_SMCUP, TTYCODE_STRING, "smcup" }, - { TTYC_SMKX, TTYCODE_STRING, "smkx" }, - { TTYC_SMSO, TTYCODE_STRING, "smso" }, - { TTYC_SMUL, TTYCODE_STRING, "smul" }, - { TTYC_SS, TTYCODE_STRING, "Ss" }, - { TTYC_TSL, TTYCODE_STRING, "tsl" }, - { TTYC_VPA, TTYCODE_STRING, "vpa" }, - { TTYC_XENL, TTYCODE_FLAG, "xenl" }, - { TTYC_XT, TTYCODE_FLAG, "XT" }, +enum tty_code_type { + TTYCODE_NONE = 0, + TTYCODE_STRING, + TTYCODE_NUMBER, + TTYCODE_FLAG, }; +struct tty_code { + enum tty_code_type type; + union { + char *string; + int number; + int flag; + } value; +}; + +struct tty_term_code_entry { + enum tty_code_type type; + const char *name; +}; + +const struct tty_term_code_entry tty_term_codes[] = { + [TTYC_ACSC] = { TTYCODE_STRING, "acsc" }, + [TTYC_AX] = { TTYCODE_FLAG, "AX" }, + [TTYC_BCE] = { TTYCODE_FLAG, "bce" }, + [TTYC_BEL] = { TTYCODE_STRING, "bel" }, + [TTYC_BLINK] = { TTYCODE_STRING, "blink" }, + [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, + [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, + [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, + [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, + [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, + [TTYC_CR] = { TTYCODE_STRING, "Cr" }, + [TTYC_CS] = { TTYCODE_STRING, "Cs" }, + [TTYC_CSR] = { TTYCODE_STRING, "csr" }, + [TTYC_CUB] = { TTYCODE_STRING, "cub" }, + [TTYC_CUB1] = { TTYCODE_STRING, "cub1" }, + [TTYC_CUD] = { TTYCODE_STRING, "cud" }, + [TTYC_CUD1] = { TTYCODE_STRING, "cud1" }, + [TTYC_CUF] = { TTYCODE_STRING, "cuf" }, + [TTYC_CUF1] = { TTYCODE_STRING, "cuf1" }, + [TTYC_CUP] = { TTYCODE_STRING, "cup" }, + [TTYC_CUU] = { TTYCODE_STRING, "cuu" }, + [TTYC_CUU1] = { TTYCODE_STRING, "cuu1" }, + [TTYC_CVVIS] = { TTYCODE_STRING, "cvvis" }, + [TTYC_DCH] = { TTYCODE_STRING, "dch" }, + [TTYC_DCH1] = { TTYCODE_STRING, "dch1" }, + [TTYC_DIM] = { TTYCODE_STRING, "dim" }, + [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, + [TTYC_E3] = { TTYCODE_STRING, "E3" }, + [TTYC_ECH] = { TTYCODE_STRING, "ech" }, + [TTYC_EL] = { TTYCODE_STRING, "el" }, + [TTYC_EL1] = { TTYCODE_STRING, "el1" }, + [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, + [TTYC_HOME] = { TTYCODE_STRING, "home" }, + [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, + [TTYC_ICH] = { TTYCODE_STRING, "ich" }, + [TTYC_ICH1] = { TTYCODE_STRING, "ich1" }, + [TTYC_IL] = { TTYCODE_STRING, "il" }, + [TTYC_IL1] = { TTYCODE_STRING, "il1" }, + [TTYC_INVIS] = { TTYCODE_STRING, "invis" }, + [TTYC_IS1] = { TTYCODE_STRING, "is1" }, + [TTYC_IS2] = { TTYCODE_STRING, "is2" }, + [TTYC_IS3] = { TTYCODE_STRING, "is3" }, + [TTYC_KCBT] = { TTYCODE_STRING, "kcbt" }, + [TTYC_KCUB1] = { TTYCODE_STRING, "kcub1" }, + [TTYC_KCUD1] = { TTYCODE_STRING, "kcud1" }, + [TTYC_KCUF1] = { TTYCODE_STRING, "kcuf1" }, + [TTYC_KCUU1] = { TTYCODE_STRING, "kcuu1" }, + [TTYC_KDC2] = { TTYCODE_STRING, "kDC" }, + [TTYC_KDC3] = { TTYCODE_STRING, "kDC3" }, + [TTYC_KDC4] = { TTYCODE_STRING, "kDC4" }, + [TTYC_KDC5] = { TTYCODE_STRING, "kDC5" }, + [TTYC_KDC6] = { TTYCODE_STRING, "kDC6" }, + [TTYC_KDC7] = { TTYCODE_STRING, "kDC7" }, + [TTYC_KDCH1] = { TTYCODE_STRING, "kdch1" }, + [TTYC_KDN2] = { TTYCODE_STRING, "kDN" }, + [TTYC_KDN3] = { TTYCODE_STRING, "kDN3" }, + [TTYC_KDN4] = { TTYCODE_STRING, "kDN4" }, + [TTYC_KDN5] = { TTYCODE_STRING, "kDN5" }, + [TTYC_KDN6] = { TTYCODE_STRING, "kDN6" }, + [TTYC_KDN7] = { TTYCODE_STRING, "kDN7" }, + [TTYC_KEND] = { TTYCODE_STRING, "kend" }, + [TTYC_KEND2] = { TTYCODE_STRING, "kEND" }, + [TTYC_KEND3] = { TTYCODE_STRING, "kEND3" }, + [TTYC_KEND4] = { TTYCODE_STRING, "kEND4" }, + [TTYC_KEND5] = { TTYCODE_STRING, "kEND5" }, + [TTYC_KEND6] = { TTYCODE_STRING, "kEND6" }, + [TTYC_KEND7] = { TTYCODE_STRING, "kEND7" }, + [TTYC_KF1] = { TTYCODE_STRING, "kf1" }, + [TTYC_KF10] = { TTYCODE_STRING, "kf10" }, + [TTYC_KF11] = { TTYCODE_STRING, "kf11" }, + [TTYC_KF12] = { TTYCODE_STRING, "kf12" }, + [TTYC_KF13] = { TTYCODE_STRING, "kf13" }, + [TTYC_KF14] = { TTYCODE_STRING, "kf14" }, + [TTYC_KF15] = { TTYCODE_STRING, "kf15" }, + [TTYC_KF16] = { TTYCODE_STRING, "kf16" }, + [TTYC_KF17] = { TTYCODE_STRING, "kf17" }, + [TTYC_KF18] = { TTYCODE_STRING, "kf18" }, + [TTYC_KF19] = { TTYCODE_STRING, "kf19" }, + [TTYC_KF2] = { TTYCODE_STRING, "kf2" }, + [TTYC_KF20] = { TTYCODE_STRING, "kf20" }, + [TTYC_KF21] = { TTYCODE_STRING, "kf21" }, + [TTYC_KF22] = { TTYCODE_STRING, "kf22" }, + [TTYC_KF23] = { TTYCODE_STRING, "kf23" }, + [TTYC_KF24] = { TTYCODE_STRING, "kf24" }, + [TTYC_KF25] = { TTYCODE_STRING, "kf25" }, + [TTYC_KF26] = { TTYCODE_STRING, "kf26" }, + [TTYC_KF27] = { TTYCODE_STRING, "kf27" }, + [TTYC_KF28] = { TTYCODE_STRING, "kf28" }, + [TTYC_KF29] = { TTYCODE_STRING, "kf29" }, + [TTYC_KF3] = { TTYCODE_STRING, "kf3" }, + [TTYC_KF30] = { TTYCODE_STRING, "kf30" }, + [TTYC_KF31] = { TTYCODE_STRING, "kf31" }, + [TTYC_KF32] = { TTYCODE_STRING, "kf32" }, + [TTYC_KF33] = { TTYCODE_STRING, "kf33" }, + [TTYC_KF34] = { TTYCODE_STRING, "kf34" }, + [TTYC_KF35] = { TTYCODE_STRING, "kf35" }, + [TTYC_KF36] = { TTYCODE_STRING, "kf36" }, + [TTYC_KF37] = { TTYCODE_STRING, "kf37" }, + [TTYC_KF38] = { TTYCODE_STRING, "kf38" }, + [TTYC_KF39] = { TTYCODE_STRING, "kf39" }, + [TTYC_KF4] = { TTYCODE_STRING, "kf4" }, + [TTYC_KF40] = { TTYCODE_STRING, "kf40" }, + [TTYC_KF41] = { TTYCODE_STRING, "kf41" }, + [TTYC_KF42] = { TTYCODE_STRING, "kf42" }, + [TTYC_KF43] = { TTYCODE_STRING, "kf43" }, + [TTYC_KF44] = { TTYCODE_STRING, "kf44" }, + [TTYC_KF45] = { TTYCODE_STRING, "kf45" }, + [TTYC_KF46] = { TTYCODE_STRING, "kf46" }, + [TTYC_KF47] = { TTYCODE_STRING, "kf47" }, + [TTYC_KF48] = { TTYCODE_STRING, "kf48" }, + [TTYC_KF49] = { TTYCODE_STRING, "kf49" }, + [TTYC_KF5] = { TTYCODE_STRING, "kf5" }, + [TTYC_KF50] = { TTYCODE_STRING, "kf50" }, + [TTYC_KF51] = { TTYCODE_STRING, "kf51" }, + [TTYC_KF52] = { TTYCODE_STRING, "kf52" }, + [TTYC_KF53] = { TTYCODE_STRING, "kf53" }, + [TTYC_KF54] = { TTYCODE_STRING, "kf54" }, + [TTYC_KF55] = { TTYCODE_STRING, "kf55" }, + [TTYC_KF56] = { TTYCODE_STRING, "kf56" }, + [TTYC_KF57] = { TTYCODE_STRING, "kf57" }, + [TTYC_KF58] = { TTYCODE_STRING, "kf58" }, + [TTYC_KF59] = { TTYCODE_STRING, "kf59" }, + [TTYC_KF6] = { TTYCODE_STRING, "kf6" }, + [TTYC_KF60] = { TTYCODE_STRING, "kf60" }, + [TTYC_KF61] = { TTYCODE_STRING, "kf61" }, + [TTYC_KF62] = { TTYCODE_STRING, "kf62" }, + [TTYC_KF63] = { TTYCODE_STRING, "kf63" }, + [TTYC_KF7] = { TTYCODE_STRING, "kf7" }, + [TTYC_KF8] = { TTYCODE_STRING, "kf8" }, + [TTYC_KF9] = { TTYCODE_STRING, "kf9" }, + [TTYC_KHOM2] = { TTYCODE_STRING, "kHOM" }, + [TTYC_KHOM3] = { TTYCODE_STRING, "kHOM3" }, + [TTYC_KHOM4] = { TTYCODE_STRING, "kHOM4" }, + [TTYC_KHOM5] = { TTYCODE_STRING, "kHOM5" }, + [TTYC_KHOM6] = { TTYCODE_STRING, "kHOM6" }, + [TTYC_KHOM7] = { TTYCODE_STRING, "kHOM7" }, + [TTYC_KHOME] = { TTYCODE_STRING, "khome" }, + [TTYC_KIC2] = { TTYCODE_STRING, "kIC" }, + [TTYC_KIC3] = { TTYCODE_STRING, "kIC3" }, + [TTYC_KIC4] = { TTYCODE_STRING, "kIC4" }, + [TTYC_KIC5] = { TTYCODE_STRING, "kIC5" }, + [TTYC_KIC6] = { TTYCODE_STRING, "kIC6" }, + [TTYC_KIC7] = { TTYCODE_STRING, "kIC7" }, + [TTYC_KICH1] = { TTYCODE_STRING, "kich1" }, + [TTYC_KLFT2] = { TTYCODE_STRING, "kLFT" }, + [TTYC_KLFT3] = { TTYCODE_STRING, "kLFT3" }, + [TTYC_KLFT4] = { TTYCODE_STRING, "kLFT4" }, + [TTYC_KLFT5] = { TTYCODE_STRING, "kLFT5" }, + [TTYC_KLFT6] = { TTYCODE_STRING, "kLFT6" }, + [TTYC_KLFT7] = { TTYCODE_STRING, "kLFT7" }, + [TTYC_KMOUS] = { TTYCODE_STRING, "kmous" }, + [TTYC_KNP] = { TTYCODE_STRING, "knp" }, + [TTYC_KNXT2] = { TTYCODE_STRING, "kNXT" }, + [TTYC_KNXT3] = { TTYCODE_STRING, "kNXT3" }, + [TTYC_KNXT4] = { TTYCODE_STRING, "kNXT4" }, + [TTYC_KNXT5] = { TTYCODE_STRING, "kNXT5" }, + [TTYC_KNXT6] = { TTYCODE_STRING, "kNXT6" }, + [TTYC_KNXT7] = { TTYCODE_STRING, "kNXT7" }, + [TTYC_KPP] = { TTYCODE_STRING, "kpp" }, + [TTYC_KPRV2] = { TTYCODE_STRING, "kPRV" }, + [TTYC_KPRV3] = { TTYCODE_STRING, "kPRV3" }, + [TTYC_KPRV4] = { TTYCODE_STRING, "kPRV4" }, + [TTYC_KPRV5] = { TTYCODE_STRING, "kPRV5" }, + [TTYC_KPRV6] = { TTYCODE_STRING, "kPRV6" }, + [TTYC_KPRV7] = { TTYCODE_STRING, "kPRV7" }, + [TTYC_KRIT2] = { TTYCODE_STRING, "kRIT" }, + [TTYC_KRIT3] = { TTYCODE_STRING, "kRIT3" }, + [TTYC_KRIT4] = { TTYCODE_STRING, "kRIT4" }, + [TTYC_KRIT5] = { TTYCODE_STRING, "kRIT5" }, + [TTYC_KRIT6] = { TTYCODE_STRING, "kRIT6" }, + [TTYC_KRIT7] = { TTYCODE_STRING, "kRIT7" }, + [TTYC_KUP2] = { TTYCODE_STRING, "kUP" }, + [TTYC_KUP3] = { TTYCODE_STRING, "kUP3" }, + [TTYC_KUP4] = { TTYCODE_STRING, "kUP4" }, + [TTYC_KUP5] = { TTYCODE_STRING, "kUP5" }, + [TTYC_KUP6] = { TTYCODE_STRING, "kUP6" }, + [TTYC_KUP7] = { TTYCODE_STRING, "kUP7" }, + [TTYC_MS] = { TTYCODE_STRING, "Ms" }, + [TTYC_OP] = { TTYCODE_STRING, "op" }, + [TTYC_REV] = { TTYCODE_STRING, "rev" }, + [TTYC_RI] = { TTYCODE_STRING, "ri" }, + [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, + [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, + [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, + [TTYC_SE] = { TTYCODE_STRING, "Se" }, + [TTYC_SETAB] = { TTYCODE_STRING, "setab" }, + [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, + [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, + [TTYC_SITM] = { TTYCODE_STRING, "sitm" }, + [TTYC_SMACS] = { TTYCODE_STRING, "smacs" }, + [TTYC_SMCUP] = { TTYCODE_STRING, "smcup" }, + [TTYC_SMKX] = { TTYCODE_STRING, "smkx" }, + [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, + [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, + [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, + [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, + [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, + [TTYC_XT] = { TTYCODE_FLAG, "XT" }, +}; + +u_int +tty_term_ncodes(void) +{ + return (nitems(tty_term_codes)); +} + char * tty_term_strip(const char *s) { @@ -309,11 +336,11 @@ tty_term_override(struct tty_term *term, const char *overrides) log_debug("%s override: %s %s", term->name, entstr, removeflag ? "@" : val); - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; if (strcmp(entstr, ent->name) != 0) continue; - code = &term->codes[ent->code]; + code = &term->codes[i]; if (removeflag) { code->type = TTYCODE_NONE; @@ -372,7 +399,7 @@ tty_term_find(char *name, int fd, char **cause) term->name = xstrdup(name); term->references = 1; term->flags = 0; - memset(term->codes, 0, sizeof term->codes); + term->codes = xcalloc (tty_term_ncodes(), sizeof *term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); /* Set up curses terminal. */ @@ -397,10 +424,10 @@ tty_term_find(char *name, int fd, char **cause) } /* Fill in codes. */ - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; + code = &term->codes[i]; code->type = TTYCODE_NONE; switch (ent->type) { case TTYCODE_NONE: @@ -507,10 +534,12 @@ tty_term_free(struct tty_term *term) LIST_REMOVE(term, entry); - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { if (term->codes[i].type == TTYCODE_STRING) free(term->codes[i].value.string); } + free(term->codes); + free(term->name); free(term); } @@ -576,3 +605,35 @@ tty_term_flag(struct tty_term *term, enum tty_code_code code) log_fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } + +const char * +tty_term_describe(struct tty_term *term, enum tty_code_code code) +{ + static char s[256]; + char out[128]; + + switch (term->codes[code].type) { + case TTYCODE_NONE: + xsnprintf(s, sizeof s, "%4u: %s: [missing]", + code, tty_term_codes[code].name); + break; + case TTYCODE_STRING: + strnvis(out, term->codes[code].value.string, sizeof out, + VIS_OCTAL|VIS_TAB|VIS_NL); + xsnprintf(s, sizeof s, "%4u: %s: (string) %s", + code, tty_term_codes[code].name, + out); + break; + case TTYCODE_NUMBER: + xsnprintf(s, sizeof s, "%4u: %s: (number) %d", + code, tty_term_codes[code].name, + term->codes[code].value.number); + break; + case TTYCODE_FLAG: + xsnprintf(s, sizeof s, "%4u: %s: (flag) %s", + code, tty_term_codes[code].name, + term->codes[code].value.flag ? "true" : "false"); + break; + } + return (s); +} From 5ec3621101e13e9032d1871f02883757b18d51ac Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Jul 2015 11:56:02 +0000 Subject: [PATCH 677/949] status_out and associated data structures are no longer used. --- server-client.c | 2 -- status.c | 10 ---------- tmux.h | 15 --------------- 3 files changed, 27 deletions(-) diff --git a/server-client.c b/server-client.c index c2c7070f..f33018e4 100644 --- a/server-client.c +++ b/server-client.c @@ -115,8 +115,6 @@ server_client_create(int fd) c->tty.sy = 24; screen_init(&c->status, c->tty.sx, 1, 0); - RB_INIT(&c->status_new); - RB_INIT(&c->status_old); c->message_string = NULL; TAILQ_INIT(&c->message_log); diff --git a/status.c b/status.c index 16f4fa70..d9501f02 100644 --- a/status.c +++ b/status.c @@ -142,16 +142,6 @@ status_prompt_save_history(void) } -/* Status output tree. */ -RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); - -/* Output tree comparison function. */ -int -status_out_cmp(struct status_out *so1, struct status_out *so2) -{ - return (strcmp(so1->cmd, so2->cmd)); -} - /* Get screen line of status line. -1 means off. */ int status_at_line(struct client *c) diff --git a/tmux.h b/tmux.h index b3456922..0f9cb26e 100644 --- a/tmux.h +++ b/tmux.h @@ -1176,15 +1176,6 @@ struct message_entry { TAILQ_ENTRY(message_entry) entry; }; -/* Status output data from a job. */ -struct status_out { - char *cmd; - char *out; - - RB_ENTRY(status_out) entry; -}; -RB_HEAD(status_out_tree, status_out); - /* Client connection. */ struct client { struct imsgbuf ibuf; @@ -1215,8 +1206,6 @@ struct client { struct event repeat_timer; - struct status_out_tree status_old; - struct status_out_tree status_new; struct timeval status_timer; struct screen status; @@ -1904,11 +1893,7 @@ int server_set_stdin_callback(struct client *, void (*)(struct client *, void server_unzoom_window(struct window *); /* status.c */ -int status_out_cmp(struct status_out *, struct status_out *); -RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp); int status_at_line(struct client *); -void status_free_jobs(struct status_out_tree *); -void status_update_jobs(struct client *); struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); void printflike(2, 3) status_message_set(struct client *, const char *, ...); From 736d8350e930c37b12b9e78d92def3b701040667 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 7 Aug 2015 15:06:17 +0100 Subject: [PATCH 678/949] +history-file, from Ben Boeckel. --- examples/tmux.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/tmux.vim b/examples/tmux.vim index dcb58246..0e9476ec 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -184,6 +184,7 @@ syn keyword tmuxOptsSet \ escape-time \ exit-unattached \ focus-events + \ history-file \ history-limit \ lock-after-time \ lock-command From 13b7fd82c12ff2a0812d2b378a49011c12028796 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 12 Aug 2015 08:55:20 +0000 Subject: [PATCH 679/949] Rename left/right/up/down relative to active pane to add -of suffix (left-of/right-of/etc) to remove conflict with left/right meaning leftmost or rightmost pane. From Ben Boeckel. --- cmd-find.c | 8 ++++---- tmux.1 | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 5e0b1dbb..4adfe681 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -102,10 +102,10 @@ const char *cmd_find_pane_table[][2] = { { "{top-right}", "top-right" }, { "{bottom-left}", "bottom-left" }, { "{bottom-right}", "bottom-right" }, - { "{up}", "{up}" }, - { "{down}", "{down}" }, - { "{left}", "{left}" }, - { "{right}", "{right}" }, + { "{up-of}", "{up}" }, + { "{down-of}", "{down}" }, + { "{left-of}", "{left}" }, + { "{right-up}", "{right}" }, { NULL, NULL } }; diff --git a/tmux.1 b/tmux.1 index a4422057..19f2af36 100644 --- a/tmux.1 +++ b/tmux.1 @@ -496,10 +496,10 @@ The following special tokens are available for the pane index: .It Li "{top-right}" Ta "" Ta "The top-right pane" .It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" .It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" -.It Li "{up}" Ta "" Ta "The pane above the active pane" -.It Li "{down}" Ta "" Ta "The pane below the active pane" -.It Li "{left}" Ta "" Ta "The pane to the left of the active pane" -.It Li "{right}" Ta "" Ta "The pane to the right of the active pane" +.It Li "{up-of}" Ta "" Ta "The pane above the active pane" +.It Li "{down-of}" Ta "" Ta "The pane below the active pane" +.It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane" +.It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane" .El .Pp The tokens From 46aa92420a4d3af7d273155aabb646d4bc91bb01 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 13 Aug 2015 15:02:23 +0000 Subject: [PATCH 680/949] right-up should be right-of, also rename the values too. --- cmd-find.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 4adfe681..87dacfb4 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -102,10 +102,10 @@ const char *cmd_find_pane_table[][2] = { { "{top-right}", "top-right" }, { "{bottom-left}", "bottom-left" }, { "{bottom-right}", "bottom-right" }, - { "{up-of}", "{up}" }, - { "{down-of}", "{down}" }, - { "{left-of}", "{left}" }, - { "{right-up}", "{right}" }, + { "{up-of}", "{up-of}" }, + { "{down-of}", "{down-of}" }, + { "{left-of}", "{left-of}" }, + { "{right-of}", "{right-of}" }, { NULL, NULL } }; @@ -699,22 +699,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) return (-1); fs->wp = fs->w->last; return (0); - } else if (strcmp(pane, "{up}") == 0) { + } else if (strcmp(pane, "{up-of}") == 0) { fs->wp = window_pane_find_up(fs->w->active); if (fs->wp == NULL) return (-1); return (0); - } else if (strcmp(pane, "{down}") == 0) { + } else if (strcmp(pane, "{down-of}") == 0) { fs->wp = window_pane_find_down(fs->w->active); if (fs->wp == NULL) return (-1); return (0); - } else if (strcmp(pane, "{left}") == 0) { + } else if (strcmp(pane, "{left-of}") == 0) { fs->wp = window_pane_find_left(fs->w->active); if (fs->wp == NULL) return (-1); return (0); - } else if (strcmp(pane, "{right}") == 0) { + } else if (strcmp(pane, "{right-of}") == 0) { fs->wp = window_pane_find_right(fs->w->active); if (fs->wp == NULL) return (-1); From f5357ed940bd62fefdcca9791a41ca762db42599 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 15 Aug 2015 09:53:19 +0100 Subject: [PATCH 681/949] Handle \ at EOL from Daniel Hahler. --- examples/tmux.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 0e9476ec..6f85db5b 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -266,7 +266,9 @@ syn match tmuxOptions /\s-\a\+/ display syn match tmuxVariable /\w\+=/ display syn match tmuxVariableExpansion /\${\=\w\+}\=/ display -syn region tmuxComment start=/#/ end=/$/ contains=tmuxTodo display oneline +" Comments can span multiple lines, when the newline is escaped +" (with a single) backslash at the end. +syn region tmuxComment start=/#/ skip=/\\\@ Date: Sun, 16 Aug 2015 08:57:34 +0000 Subject: [PATCH 682/949] Come out of copy mode when history is cleared. --- cmd-clear-history.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd-clear-history.c b/cmd-clear-history.c index e1342880..88dbbcf7 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -45,6 +45,9 @@ cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); gd = wp->base.grid; + if (wp->mode == &window_copy_mode) + window_pane_reset_mode(wp); + grid_move_lines(gd, 0, gd->hsize, gd->sy); gd->hsize = 0; From 3219e0314e3d1d39a57db330faa5693ce0264244 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Aug 2015 22:49:13 +0000 Subject: [PATCH 683/949] In grid_duplicate_lines, if the line is empty (cellsize == 0) then clear the destination celldata pointer rather than leaving a stale copy of the source pointer (which may later be freed). Fixes a crash found by Kuang-che Wu. --- grid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 7e086143..c339cdc5 100644 --- a/grid.c +++ b/grid.c @@ -652,7 +652,8 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, srcl->cellsize, sizeof *dstl->celldata); memcpy(dstl->celldata, srcl->celldata, srcl->cellsize * sizeof *dstl->celldata); - } + } else + dstl->celldata = NULL; sy++; dy++; From 2ffbd5b5f05dded1564ba32a6a00b0b417439b2f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 25 Aug 2015 15:00:05 +0000 Subject: [PATCH 684/949] When searching for tabs, start from screen width, fixes out-of-bounds read found by Kuang-che Wu. --- input.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index 095816c3..7a334810 100644 --- a/input.c +++ b/input.c @@ -1199,6 +1199,7 @@ input_csi_dispatch(struct input_ctx *ictx) struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; + u_int cx; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1217,12 +1218,16 @@ input_csi_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_CSI_CBT: /* Find the previous tab point, n times. */ + cx = s->cx; + if (cx > screen_size_x(s) - 1) + cx = screen_size_x(s) - 1; n = input_get(ictx, 0, 1, 1); - while (s->cx > 0 && n-- > 0) { + while (cx > 0 && n-- > 0) { do - s->cx--; - while (s->cx > 0 && !bit_test(s->tabs, s->cx)); + cx--; + while (cx > 0 && !bit_test(s->tabs, cx)); } + s->cx = cx; break; case INPUT_CSI_CUB: screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); From fc58e44f89b876aa051f849361bc215c1f965696 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 07:49:24 +0000 Subject: [PATCH 685/949] Only do the automatic-rename dance if the pane has changed (seen output, or new active pane). --- input.c | 2 ++ names.c | 4 ++++ tmux.h | 1 + window.c | 1 + 4 files changed, 8 insertions(+) diff --git a/input.c b/input.c index 7a334810..7ec35c88 100644 --- a/input.c +++ b/input.c @@ -844,6 +844,8 @@ input_parse(struct window_pane *wp) if (EVBUFFER_LENGTH(evb) == 0) return; + wp->flags |= PANE_CHANGED; + wp->window->flags |= WINDOW_ACTIVITY; wp->window->flags &= ~WINDOW_SILENCE; diff --git a/names.c b/names.c index 1ceb83c0..0a3af903 100644 --- a/names.c +++ b/names.c @@ -50,6 +50,10 @@ window_name_callback(unused int fd, unused short events, void *data) if (w->active == NULL) return; + if (~w->active->flags & PANE_CHANGED) + return; + w->active->flags &= ~PANE_CHANGED; + if (!options_get_number(&w->options, "automatic-rename")) { if (event_initialized(&w->name_timer)) event_del(&w->name_timer); diff --git a/tmux.h b/tmux.h index 0f9cb26e..2d0ccede 100644 --- a/tmux.h +++ b/tmux.h @@ -826,6 +826,7 @@ struct window_pane { #define PANE_RESIZE 0x8 #define PANE_FOCUSPUSH 0x10 #define PANE_INPUTOFF 0x20 +#define PANE_CHANGED 0x40 int argc; char **argv; diff --git a/window.c b/window.c index 6351eff5..395574f7 100644 --- a/window.c +++ b/window.c @@ -412,6 +412,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return (1); } w->active->active_point = next_active_point++; + w->active->flags |= PANE_CHANGED; return (1); } From 25faca41eb30bc7cba95da0103bfa6fdda4d9a8b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 07:55:43 +0000 Subject: [PATCH 686/949] Error messages should not have a trailing period. --- server-window.c | 2 +- window.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server-window.c b/server-window.c index 1b3938d3..c96c2602 100644 --- a/server-window.c +++ b/server-window.c @@ -147,7 +147,7 @@ server_window_check_silence(struct session *s, struct winlink *wl) * from this window. */ if (gettimeofday(&w->silence_timer, NULL) != 0) - fatal("gettimeofday failed."); + fatal("gettimeofday failed"); return (0); } diff --git a/window.c b/window.c index 395574f7..77c7a633 100644 --- a/window.c +++ b/window.c @@ -934,7 +934,7 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) */ wp->window->flags |= WINDOW_SILENCE; if (gettimeofday(&wp->window->silence_timer, NULL) != 0) - fatal("gettimeofday failed."); + fatal("gettimeofday failed"); return; start_timer: From ee9f708500c2a6987b7a3c0aba2ac5e88812d8aa Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 10:06:52 +0000 Subject: [PATCH 687/949] Allow environment variables in #{}. --- format.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index aff34abb..429f6185 100644 --- a/format.c +++ b/format.c @@ -322,6 +322,7 @@ format_find(struct format_tree *ft, const char *key) { struct format_entry *fe, fe_find; struct options_entry *o; + struct environ_entry *envent; static char s[16]; o = options_find(&global_options, key); @@ -347,9 +348,18 @@ format_find(struct format_tree *ft, const char *key) fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); - if (fe == NULL) - return (NULL); - return (fe->value); + if (fe != NULL) + return (fe->value); + + envent = NULL; + if (ft->s != NULL) + envent = environ_find(&ft->s->environ, key); + if (envent == NULL) + envent = environ_find(&global_environ, key); + if (envent != NULL) + return (envent->value); + + return (NULL); } /* @@ -371,7 +381,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, copy[keylen] = '\0'; /* Is there a length limit or whatnot? */ - if (!islower((u_char) *copy) && *copy != '@' && *copy != '?') { + if (!isalpha((u_char) *copy) && *copy != '@' && *copy != '?') { while (*copy != ':' && *copy != '\0') { switch (*copy) { case '=': From b6618b631b783f652ca06c9669b20e40ef30e336 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 11:38:27 +0000 Subject: [PATCH 688/949] Move format job cleanup onto its own timer. --- format.c | 17 ++++++++++++++++- server.c | 2 -- tmux.h | 1 - 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 429f6185..09b16e76 100644 --- a/format.c +++ b/format.c @@ -37,6 +37,7 @@ void format_job_callback(struct job *); const char *format_job_get(struct format_tree *, const char *); +void format_job_timer(int, short, void *); int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); @@ -63,6 +64,7 @@ struct format_job { }; /* Format job tree. */ +struct event format_job_event; int format_job_cmp(struct format_job *, struct format_job *); RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp); @@ -191,6 +193,8 @@ format_job_callback(struct job *job) server_status_client(c); fj->status = 0; } + + log_debug("%s: %s: %s", __func__, fj->cmd, fj->out); } /* Find a job. */ @@ -226,10 +230,11 @@ format_job_get(struct format_tree *ft, const char *cmd) /* Remove old jobs. */ void -format_clean(void) +format_job_timer(unused int fd, unused short events, unused void *arg) { struct format_job *fj, *fj1; time_t now; + struct timeval tv = { .tv_sec = 60 }; now = time(NULL); RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) { @@ -237,6 +242,8 @@ format_clean(void) continue; RB_REMOVE(format_job_tree, &format_jobs, fj); + log_debug("%s: %s", __func__, fj->cmd); + if (fj->job != NULL) job_free(fj->job); @@ -245,6 +252,9 @@ format_clean(void) free(fj); } + + evtimer_del(&format_job_event); + evtimer_add(&format_job_event, &tv); } /* Create a new tree. */ @@ -261,6 +271,11 @@ format_create_status(int status) struct format_tree *ft; char host[HOST_NAME_MAX + 1], *ptr; + if (!event_initialized(&format_job_event)) { + evtimer_set(&format_job_event, format_job_timer, NULL); + format_job_timer(-1, 0, NULL); + } + ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); ft->status = status; diff --git a/server.c b/server.c index d1c0bb3b..c4de510e 100644 --- a/server.c +++ b/server.c @@ -522,8 +522,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) server_client_status_timer(); - format_clean(); - evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/tmux.h b/tmux.h index 2d0ccede..5859cc53 100644 --- a/tmux.h +++ b/tmux.h @@ -1449,7 +1449,6 @@ void cfg_show_causes(struct session *); /* format.c */ struct format_tree; -void format_clean(void); struct format_tree *format_create(void); struct format_tree *format_create_status(int); void format_free(struct format_tree *); From 18d4802a7bcfc08948dccfd9084144b043ac591b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:15:54 +0000 Subject: [PATCH 689/949] Log time with message. --- log.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/log.c b/log.c index 0f42f66f..9a53775e 100644 --- a/log.c +++ b/log.c @@ -67,11 +67,13 @@ void log_vwrite(const char *msg, va_list ap) { char *fmt; + time_t t; if (log_file == NULL) return; - if (asprintf(&fmt, "%s\n", msg) == -1) + t = time(NULL); + if (asprintf(&fmt, "%lld %s\n", (long long)t, msg) == -1) exit(1); if (vfprintf(log_file, fmt, ap) == -1) exit(1); From 75d10058a41d8e95dda126d460a119a9037e9345 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:16:28 +0000 Subject: [PATCH 690/949] Run status update on a per-client timer at status-interval. --- cmd-attach-session.c | 2 ++ cmd-new-session.c | 1 + cmd-set-option.c | 5 +++- cmd-switch-client.c | 1 + format.c | 2 +- server-client.c | 38 ++---------------------------- server-fn.c | 1 + server.c | 2 -- status.c | 56 ++++++++++++++++++++++++++++++++++++++++---- tmux.h | 4 +++- 10 files changed, 67 insertions(+), 45 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index e7cde0f0..9f357d6e 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -133,6 +133,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s); server_redraw_client(c); @@ -177,6 +178,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s); server_redraw_client(c); diff --git a/cmd-new-session.c b/cmd-new-session.c index 1533e5f0..2125ee88 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -275,6 +275,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) else if (c->session != NULL) c->last_session = c->session; c->session = s; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s); server_redraw_client(c); diff --git a/cmd-set-option.c b/cmd-set-option.c index 761cee93..260961cb 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -176,7 +176,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - /* Start or stop timers when automatic-rename changed. */ + /* Start or stop timers when name or status options changed. */ if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) @@ -185,6 +185,9 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) evtimer_del(&w->name_timer); } } + if (strcmp(oe->name, "status") == 0 || + strcmp(oe->name, "status-interval") == 0) + status_timer_start_all(); /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 4bac540d..6a79c7b7 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -127,6 +127,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session != NULL) c->last_session = c->session; c->session = s; + status_timer_start(c); session_update_activity(s); recalculate_sizes(); diff --git a/format.c b/format.c index 09b16e76..39843674 100644 --- a/format.c +++ b/format.c @@ -247,7 +247,7 @@ format_job_timer(unused int fd, unused short events, unused void *arg) if (fj->job != NULL) job_free(fj->job); - free((void*)fj->cmd); + free((void *)fj->cmd); free(fj->out); free(fj); diff --git a/server-client.c b/server-client.c index f33018e4..82cec36c 100644 --- a/server-client.c +++ b/server-client.c @@ -188,6 +188,8 @@ server_client_lost(struct client *c) if (c->stderr_data != c->stdout_data) evbuffer_free(c->stderr_data); + if (event_initialized(&c->status_timer)) + evtimer_del(&c->status_timer); screen_free(&c->status); free(c->title); @@ -289,42 +291,6 @@ client_lost: server_client_lost(c); } -/* Handle client status timer. */ -void -server_client_status_timer(void) -{ - struct client *c; - struct session *s; - struct timeval tv; - int interval; - time_t difference; - - if (gettimeofday(&tv, NULL) != 0) - fatal("gettimeofday failed"); - - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL) - continue; - if (c->message_string != NULL || c->prompt_string != NULL) { - /* - * Don't need timed redraw for messages/prompts so bail - * now. The status timer isn't reset when they are - * redrawn anyway. - */ - continue; - } - s = c->session; - - if (!options_get_number(&s->options, "status")) - continue; - interval = options_get_number(&s->options, "status-interval"); - - difference = tv.tv_sec - c->status_timer.tv_sec; - if (interval != 0 && difference >= interval) - c->flags |= CLIENT_STATUS; - } -} - /* Check for mouse keys. */ int server_client_check_mouse(struct client *c) diff --git a/server-fn.c b/server-fn.c index 0e6e4d46..469dd82d 100644 --- a/server-fn.c +++ b/server-fn.c @@ -419,6 +419,7 @@ server_destroy_session(struct session *s) } else { c->last_session = NULL; c->session = s_new; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new); server_redraw_client(c); diff --git a/server.c b/server.c index c4de510e..cfd2dd34 100644 --- a/server.c +++ b/server.c @@ -520,8 +520,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) } } - server_client_status_timer(); - evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/status.c b/status.c index d9501f02..93cee2b1 100644 --- a/status.c +++ b/status.c @@ -37,6 +37,7 @@ char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); void status_message_callback(int, short, void *); +void status_timer_callback(int, short, void *); const char *status_prompt_up_history(u_int *); const char *status_prompt_down_history(u_int *); @@ -142,6 +143,55 @@ status_prompt_save_history(void) } +/* Status timer callback. */ +void +status_timer_callback(unused int fd, unused short events, void *arg) +{ + struct client *c = arg; + struct session *s = c->session; + struct timeval tv; + + evtimer_del(&c->status_timer); + + if (s == NULL) + return; + + if (c->message_string == NULL && c->prompt_string == NULL) + c->flags |= CLIENT_STATUS; + + timerclear(&tv); + tv.tv_sec = options_get_number(&s->options, "status-interval"); + + if (tv.tv_sec != 0) + evtimer_add(&c->status_timer, &tv); + log_debug("client %d, status interval %d", c->ibuf.fd, (int)tv.tv_sec); +} + +/* Start status timer for client. */ +void +status_timer_start(struct client *c) +{ + struct session *s = c->session; + + if (event_initialized(&c->status_timer)) + evtimer_del(&c->status_timer); + else + evtimer_set(&c->status_timer, status_timer_callback, c); + + if (s != NULL && options_get_number(&s->options, "status")) + status_timer_callback(-1, 0, c); +} + +/* Start status timer for all clients. */ +void +status_timer_start_all(void) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) + status_timer_start(c); +} + /* Get screen line of status line. -1 means off. */ int status_at_line(struct client *c) @@ -244,10 +294,8 @@ status_redraw(struct client *c) left = right = NULL; larrow = rarrow = 0; - /* Update status timer. */ - if (gettimeofday(&c->status_timer, NULL) != 0) - fatal("gettimeofday failed"); - t = c->status_timer.tv_sec; + /* Store current time. */ + t = time(NULL); /* Set up default colour. */ style_apply(&stdgc, &s->options, "status-style"); diff --git a/tmux.h b/tmux.h index 5859cc53..6b82e79c 100644 --- a/tmux.h +++ b/tmux.h @@ -1207,7 +1207,7 @@ struct client { struct event repeat_timer; - struct timeval status_timer; + struct event status_timer; struct screen status; #define CLIENT_TERMINAL 0x1 @@ -1893,6 +1893,8 @@ int server_set_stdin_callback(struct client *, void (*)(struct client *, void server_unzoom_window(struct window *); /* status.c */ +void status_timer_start(struct client *); +void status_timer_start_all(void); int status_at_line(struct client *); struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); From 6419f665237d4280966c298a38836b53746b8dcc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:25:42 +0000 Subject: [PATCH 691/949] Give clock mode its own timer. --- server.c | 9 --------- tmux.h | 1 - window-choose.c | 1 - window-clock.c | 50 +++++++++++++++++++++++++++++-------------------- window-copy.c | 1 - 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/server.c b/server.c index cfd2dd34..d39e3f56 100644 --- a/server.c +++ b/server.c @@ -504,8 +504,6 @@ server_child_stopped(pid_t pid, int status) void server_second_callback(unused int fd, unused short events, unused void *arg) { - struct window *w; - struct window_pane *wp; struct timeval tv; if (options_get_number(&global_s_options, "lock-server")) @@ -513,13 +511,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) else server_lock_sessions(); - RB_FOREACH(w, windows, &windows) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->mode != NULL && wp->mode->timer != NULL) - wp->mode->timer(wp); - } - } - evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/tmux.h b/tmux.h index 6b82e79c..9fae33bd 100644 --- a/tmux.h +++ b/tmux.h @@ -777,7 +777,6 @@ struct window_mode { void (*resize)(struct window_pane *, u_int, u_int); void (*key)(struct window_pane *, struct client *, struct session *, int, struct mouse_event *); - void (*timer)(struct window_pane *); }; /* Structures for choose mode. */ diff --git a/window-choose.c b/window-choose.c index c71fea3d..37baf0d7 100644 --- a/window-choose.c +++ b/window-choose.c @@ -57,7 +57,6 @@ const struct window_mode window_choose_mode = { window_choose_free, window_choose_resize, window_choose_key, - NULL, }; struct window_choose_mode_item { diff --git a/window-clock.c b/window-clock.c index 3cabd9e9..4a497891 100644 --- a/window-clock.c +++ b/window-clock.c @@ -29,8 +29,8 @@ void window_clock_free(struct window_pane *); void window_clock_resize(struct window_pane *, u_int, u_int); void window_clock_key(struct window_pane *, struct client *, struct session *, int, struct mouse_event *); -void window_clock_timer(struct window_pane *); +void window_clock_timer_callback(int, short, void *); void window_clock_draw_screen(struct window_pane *); const struct window_mode window_clock_mode = { @@ -38,12 +38,12 @@ const struct window_mode window_clock_mode = { window_clock_free, window_clock_resize, window_clock_key, - window_clock_timer, }; struct window_clock_mode_data { struct screen screen; time_t tim; + struct event timer; }; const char window_clock_table[14][5][5] = { @@ -119,15 +119,42 @@ const char window_clock_table[14][5][5] = { { 1,0,0,0,1 } }, }; +void +window_clock_timer_callback(unused int fd, unused short events, void *arg) +{ + struct window_pane *wp = arg; + struct window_clock_mode_data *data = wp->modedata; + struct tm now, then; + time_t t; + struct timeval tv = { .tv_sec = 1 }; + + evtimer_del(&data->timer); + evtimer_add(&data->timer, &tv); + + t = time(NULL); + gmtime_r(&t, &now); + gmtime_r(&data->tim, &then); + if (now.tm_min == then.tm_min) + return; + data->tim = t; + + window_clock_draw_screen(wp); + server_redraw_window(wp->window); +} + struct screen * window_clock_init(struct window_pane *wp) { struct window_clock_mode_data *data; struct screen *s; + struct timeval tv = { .tv_sec = 1 }; wp->modedata = data = xmalloc(sizeof *data); data->tim = time(NULL); + evtimer_set(&data->timer, window_clock_timer_callback, wp); + evtimer_add(&data->timer, &tv); + s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; @@ -142,6 +169,7 @@ window_clock_free(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; + evtimer_del(&data->timer); screen_free(&data->screen); free(data); } @@ -163,24 +191,6 @@ window_clock_key(struct window_pane *wp, unused struct client *c, window_pane_reset_mode(wp); } -void -window_clock_timer(struct window_pane *wp) -{ - struct window_clock_mode_data *data = wp->modedata; - struct tm now, then; - time_t t; - - t = time(NULL); - gmtime_r(&t, &now); - gmtime_r(&data->tim, &then); - if (now.tm_min == then.tm_min) - return; - data->tim = t; - - window_clock_draw_screen(wp); - server_redraw_window(wp->window); -} - void window_clock_draw_screen(struct window_pane *wp) { diff --git a/window-copy.c b/window-copy.c index 7d5bae4c..850b437a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -91,7 +91,6 @@ const struct window_mode window_copy_mode = { window_copy_free, window_copy_resize, window_copy_key, - NULL, }; enum window_copy_input_type { From 675def039652e69d8fd5f229eff2128116e1d328 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:31:55 +0000 Subject: [PATCH 692/949] Remove the lock-server option which is a bit redundant, it isn't that different without it. --- options-table.c | 5 ----- server.c | 27 +-------------------------- tmux.1 | 17 +---------------- 3 files changed, 2 insertions(+), 47 deletions(-) diff --git a/options-table.c b/options-table.c index 7f75d22c..b9431483 100644 --- a/options-table.c +++ b/options-table.c @@ -206,11 +206,6 @@ const struct options_table_entry session_options_table[] = { .default_str = "lock -np" }, - { .name = "lock-server", - .type = OPTIONS_TABLE_FLAG, - .default_num = 1 - }, - { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0, diff --git a/server.c b/server.c index d39e3f56..f9b0dc52 100644 --- a/server.c +++ b/server.c @@ -64,7 +64,6 @@ void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); void server_second_callback(int, short, void *); -void server_lock_server(void); void server_lock_sessions(void); /* Set marked pane. */ @@ -506,10 +505,7 @@ server_second_callback(unused int fd, unused short events, unused void *arg) { struct timeval tv; - if (options_get_number(&global_s_options, "lock-server")) - server_lock_server(); - else - server_lock_sessions(); + server_lock_sessions(); evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); @@ -517,27 +513,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) evtimer_add(&server_ev_second, &tv); } -/* Lock the server if ALL sessions have hit the time limit. */ -void -server_lock_server(void) -{ - struct session *s; - int timeout; - time_t t; - - t = time(NULL); - RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; - timeout = options_get_number(&s->options, "lock-after-time"); - if (timeout <= 0 || t <= s->activity_time.tv_sec + timeout) - return; /* not timed out */ - } - - server_lock(); - recalculate_sizes(); -} - /* Lock any sessions which have timed out. */ void server_lock_sessions(void) diff --git a/tmux.1 b/tmux.1 index 19f2af36..8e07fc2a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2561,9 +2561,7 @@ Lock the session (like the .Ic lock-session command) after .Ar number -seconds of inactivity, or the entire server (all sessions) if the -.Ic lock-server -option is set. +seconds of inactivity. The default is not to lock (set to 0). .It Ic lock-command Ar shell-command Command to run when locking each client. @@ -2571,19 +2569,6 @@ The default is to run .Xr lock 1 with .Fl np . -.It Xo Ic lock-server -.Op Ic on | off -.Xc -If this option is -.Ic on -(the default), -instead of each session locking individually as each has been -idle for -.Ic lock-after-time , -the entire server will lock after -.Em all -sessions would have locked. -This has no effect as a session option; it must be set as a global option. .It Ic message-command-style Ar style Set status line message command style, where .Ar style From 57cc4d45d52e0af562172ac4044f311e1bb00c64 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:01:03 +0000 Subject: [PATCH 693/949] Make session_update_activity more useful and use it in more places. --- cmd-attach-session.c | 4 ++-- cmd-new-session.c | 2 +- cmd-switch-client.c | 2 +- server-client.c | 10 +++++----- server-fn.c | 2 +- session.c | 21 +++++++++++++-------- tmux.h | 2 +- 7 files changed, 24 insertions(+), 19 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 9f357d6e..356bd4aa 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -135,7 +135,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->session = s; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s); + session_update_activity(s, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { @@ -180,7 +180,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->session = s; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s); + session_update_activity(s, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; diff --git a/cmd-new-session.c b/cmd-new-session.c index 2125ee88..fa4f1553 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -277,7 +277,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) c->session = s; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s); + session_update_activity(s, NULL); server_redraw_client(c); } recalculate_sizes(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 6a79c7b7..8bc8a3c5 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -128,7 +128,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) c->last_session = c->session; c->session = s; status_timer_start(c); - session_update_activity(s); + session_update_activity(s, NULL); recalculate_sizes(); server_check_unattached(); diff --git a/server-client.c b/server-client.c index 82cec36c..da24e222 100644 --- a/server-client.c +++ b/server-client.c @@ -561,9 +561,7 @@ server_client_handle_key(struct client *c, int key) /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); - memcpy(&s->last_activity_time, &s->activity_time, - sizeof s->last_activity_time); - memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); + session_update_activity(s, &c->activity_time); /* Number keys jump to pane in identify mode. */ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { @@ -981,6 +979,7 @@ server_client_msg_dispatch(struct client *c) struct msg_stdin_data stdindata; const char *data; ssize_t n, datalen; + struct session *s; if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) return (-1); @@ -1064,11 +1063,12 @@ server_client_msg_dispatch(struct client *c) if (c->tty.fd == -1) /* exited in the meantime */ break; + s = c->session; if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday"); - if (c->session != NULL) - session_update_activity(c->session); + if (s != NULL) + session_update_activity(s, &c->activity_time); tty_start_tty(&c->tty); server_redraw_client(c); diff --git a/server-fn.c b/server-fn.c index 469dd82d..cddd762f 100644 --- a/server-fn.c +++ b/server-fn.c @@ -421,7 +421,7 @@ server_destroy_session(struct session *s) c->session = s_new; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s_new); + session_update_activity(s_new, NULL); server_redraw_client(c); } } diff --git a/session.c b/session.c index 5061083a..7572ac21 100644 --- a/session.c +++ b/session.c @@ -112,10 +112,6 @@ session_create(const char *name, int argc, char **argv, const char *path, s->references = 1; s->flags = 0; - if (gettimeofday(&s->creation_time, NULL) != 0) - fatal("gettimeofday failed"); - session_update_activity(s); - s->cwd = dup(cwd); s->curw = NULL; @@ -133,6 +129,10 @@ session_create(const char *name, int argc, char **argv, const char *path, memcpy(s->tio, tio, sizeof *s->tio); } + if (gettimeofday(&s->creation_time, NULL) != 0) + fatal("gettimeofday failed"); + session_update_activity(s, &s->creation_time); + s->sx = sx; s->sy = sy; @@ -224,12 +224,17 @@ session_check_name(const char *name) return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } -/* Update session active time. */ +/* Update activity time. */ void -session_update_activity(struct session *s) +session_update_activity(struct session *s, struct timeval *from) { - if (gettimeofday(&s->activity_time, NULL) != 0) - fatal("gettimeofday"); + struct timeval *last = &s->last_activity_time; + + memcpy(last, &s->activity_time, sizeof *last); + if (from == NULL) + gettimeofday(&s->activity_time, NULL); + else + memcpy(&s->activity_time, from, sizeof s->activity_time); } /* Find the next usable session. */ diff --git a/tmux.h b/tmux.h index 9fae33bd..cc38dab8 100644 --- a/tmux.h +++ b/tmux.h @@ -2248,7 +2248,7 @@ struct session *session_create(const char *, int, char **, const char *, void session_destroy(struct session *); void session_unref(struct session *); int session_check_name(const char *); -void session_update_activity(struct session *); +void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, int, char **, From f6a0f8730efab9c52eaccbb62b5b1d27e8be7949 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:12:20 +0000 Subject: [PATCH 694/949] Per-session timers for locking, and remove the global one-second timer. --- cmd-set-option.c | 2 +- server.c | 45 ++------------------------------------------- session.c | 40 +++++++++++++++++++++++++++++++++++++++- tmux.h | 2 ++ 4 files changed, 44 insertions(+), 45 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 260961cb..56ca91e0 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -176,7 +176,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - /* Start or stop timers when name or status options changed. */ + /* Start or stop timers if necessary. */ if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) diff --git a/server.c b/server.c index f9b0dc52..0e7733c8 100644 --- a/server.c +++ b/server.c @@ -46,7 +46,6 @@ struct clients clients; int server_fd; int server_shutdown; struct event server_ev_accept; -struct event server_ev_second; struct session *marked_session; struct winlink *marked_winlink; @@ -163,9 +162,8 @@ server_create_socket(void) int server_start(int lockfd, char *lockfile) { - int pair[2]; - struct timeval tv; - char *cause; + int pair[2]; + char *cause; /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) @@ -243,11 +241,6 @@ server_start(int lockfd, char *lockfile) server_add_accept(0); - memset(&tv, 0, sizeof tv); - tv.tv_sec = 1; - evtimer_set(&server_ev_second, server_second_callback, NULL); - evtimer_add(&server_ev_second, &tv); - set_signals(server_signal_callback); server_loop(); status_prompt_save_history(); @@ -498,37 +491,3 @@ server_child_stopped(pid_t pid, int status) } } } - -/* Handle once-per-second timer events. */ -void -server_second_callback(unused int fd, unused short events, unused void *arg) -{ - struct timeval tv; - - server_lock_sessions(); - - evtimer_del(&server_ev_second); - memset(&tv, 0, sizeof tv); - tv.tv_sec = 1; - evtimer_add(&server_ev_second, &tv); -} - -/* Lock any sessions which have timed out. */ -void -server_lock_sessions(void) -{ - struct session *s; - int timeout; - time_t t; - - t = time(NULL); - RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; - timeout = options_get_number(&s->options, "lock-after-time"); - if (timeout > 0 && t > s->activity_time.tv_sec + timeout) { - server_lock_session(s); - recalculate_sizes(); - } - } -} diff --git a/session.c b/session.c index 7572ac21..041adf2d 100644 --- a/session.c +++ b/session.c @@ -33,6 +33,8 @@ struct session_groups session_groups; void session_free(int, short, void *); +void session_lock_timer(int, short, void *); + struct winlink *session_next_alert(struct winlink *); struct winlink *session_previous_alert(struct winlink *); @@ -108,7 +110,7 @@ session_create(const char *name, int argc, char **argv, const char *path, struct session *s; struct winlink *wl; - s = xmalloc(sizeof *s); + s = xcalloc(1, sizeof *s); s->references = 1; s->flags = 0; @@ -149,6 +151,10 @@ session_create(const char *name, int argc, char **argv, const char *path, } RB_INSERT(sessions, &sessions, s); + if (gettimeofday(&s->creation_time, NULL) != 0) + fatal("gettimeofday failed"); + session_update_activity(s, &s->creation_time); + if (argc >= 0) { wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); if (wl == NULL) { @@ -200,6 +206,9 @@ session_destroy(struct session *s) free(s->tio); + if (event_initialized(&s->lock_timer)) + event_del(&s->lock_timer); + session_group_remove(s); environ_free(&s->environ); options_free(&s->options); @@ -224,17 +233,46 @@ session_check_name(const char *name) return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } +/* Lock session if it has timed out. */ +void +session_lock_timer(unused int fd, unused short events, void *arg) +{ + struct session *s = arg; + + if (s->flags & SESSION_UNATTACHED) + return; + + log_debug("session %s locked, activity time %lld", s->name, + (long long)s->activity_time.tv_sec); + + server_lock_session(s); + recalculate_sizes(); +} + /* Update activity time. */ void session_update_activity(struct session *s, struct timeval *from) { struct timeval *last = &s->last_activity_time; + struct timeval tv; memcpy(last, &s->activity_time, sizeof *last); if (from == NULL) gettimeofday(&s->activity_time, NULL); else memcpy(&s->activity_time, from, sizeof s->activity_time); + + if (evtimer_initialized(&s->lock_timer)) + evtimer_del(&s->lock_timer); + else + evtimer_set(&s->lock_timer, session_lock_timer, s); + + if (~s->flags & SESSION_UNATTACHED) { + timerclear(&tv); + tv.tv_sec = options_get_number(&s->options, "lock-after-time"); + if (tv.tv_sec != 0) + evtimer_add(&s->lock_timer, &tv); + } } /* Find the next usable session. */ diff --git a/tmux.h b/tmux.h index cc38dab8..6ac0b578 100644 --- a/tmux.h +++ b/tmux.h @@ -993,6 +993,8 @@ struct session { struct timeval activity_time; struct timeval last_activity_time; + struct event lock_timer; + u_int sx; u_int sy; From ed2a486f46010559926f49b2f95fc3ceb61fe105 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:16:03 +0000 Subject: [PATCH 695/949] Don't leak name when freeing session, from Kuang-che Wu. --- session.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/session.c b/session.c index 041adf2d..c4e64e27 100644 --- a/session.c +++ b/session.c @@ -189,8 +189,10 @@ session_free(unused int fd, unused short events, void *arg) log_debug("sesson %s freed (%d references)", s->name, s->references); - if (s->references == 0) + if (s->references == 0) { + free(s->name); free(s); + } } /* Destroy a session. */ From f957db81d9c1c17468e57729f32aa67ffd3e23e0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:21:25 +0000 Subject: [PATCH 696/949] Remove unused prototypes. --- server.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/server.c b/server.c index 0e7733c8..d36dda12 100644 --- a/server.c +++ b/server.c @@ -62,8 +62,6 @@ void server_signal_callback(int, short, void *); void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); -void server_second_callback(int, short, void *); -void server_lock_sessions(void); /* Set marked pane. */ void From b0940bdf5460ec3324254b5df68b5386513641b2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:26:41 +0000 Subject: [PATCH 697/949] Check changed flag after restarting timer. --- names.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/names.c b/names.c index 0a3af903..aa0673ea 100644 --- a/names.c +++ b/names.c @@ -50,10 +50,6 @@ window_name_callback(unused int fd, unused short events, void *data) if (w->active == NULL) return; - if (~w->active->flags & PANE_CHANGED) - return; - w->active->flags &= ~PANE_CHANGED; - if (!options_get_number(&w->options, "automatic-rename")) { if (event_initialized(&w->name_timer)) event_del(&w->name_timer); @@ -61,6 +57,10 @@ window_name_callback(unused int fd, unused short events, void *data) } queue_window_name(w); + if (~w->active->flags & PANE_CHANGED) + return; + w->active->flags &= ~PANE_CHANGED; + name = format_window_name(w); if (strcmp(name, w->name) != 0) { window_set_name(w, name); From e2100c5f5f9c71185e2a3c410ebdd37f52d701a7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 15:51:48 +0000 Subject: [PATCH 698/949] We now only checking for name changes when the active pane has changed, but that can only happen when we have already been woken up by a read event, so there is no need for a timer, we can just check the changed flag on the end of that read event (we already loop over the windows to check for bells etc anyway). --- cmd-set-option.c | 4 +--- names.c | 32 +++++++------------------------- server-window.c | 1 + tmux.h | 3 +-- window.c | 5 ----- 5 files changed, 10 insertions(+), 35 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 56ca91e0..631a4d0e 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -180,9 +180,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); - else if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + w->active->flags |= PANE_CHANGED; } } if (strcmp(oe->name, "status") == 0 || diff --git a/names.c b/names.c index aa0673ea..00bc88a2 100644 --- a/names.c +++ b/names.c @@ -25,37 +25,16 @@ #include "tmux.h" -void window_name_callback(unused int, unused short, void *); - void -queue_window_name(struct window *w) +check_window_name(struct window *w) { - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = NAME_INTERVAL * 1000L; - - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); - evtimer_set(&w->name_timer, window_name_callback, w); - evtimer_add(&w->name_timer, &tv); -} - -void -window_name_callback(unused int fd, unused short events, void *data) -{ - struct window *w = data; - char *name; + char *name; if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) { - if (event_initialized(&w->name_timer)) - event_del(&w->name_timer); + if (!options_get_number(&w->options, "automatic-rename")) return; - } - queue_window_name(w); if (~w->active->flags & PANE_CHANGED) return; @@ -63,9 +42,12 @@ window_name_callback(unused int fd, unused short events, void *data) name = format_window_name(w); if (strcmp(name, w->name) != 0) { + log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); - } + } else + log_debug("@%u name not changed (still %s)", w->id, w->name); + free(name); } diff --git a/server-window.c b/server-window.c index c96c2602..be1dde2a 100644 --- a/server-window.c +++ b/server-window.c @@ -49,6 +49,7 @@ server_window_loop(void) server_status_session(s); } } + check_window_name(w); } } diff --git a/tmux.h b/tmux.h index 6ac0b578..395d3292 100644 --- a/tmux.h +++ b/tmux.h @@ -870,7 +870,6 @@ RB_HEAD(window_pane_tree, window_pane); struct window { u_int id; char *name; - struct event name_timer; struct timeval silence_timer; struct timeval activity_time; @@ -2209,7 +2208,7 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void queue_window_name(struct window *); +void check_window_name(struct window *); char *default_window_name(struct window *); char *format_window_name(struct window *); char *parse_window_name(const char *); diff --git a/window.c b/window.c index 77c7a633..bfd13b1c 100644 --- a/window.c +++ b/window.c @@ -299,8 +299,6 @@ window_create1(u_int sx, u_int sy) fatal("gettimeofday failed"); options_init(&w->options, &global_w_options); - if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); w->references = 0; @@ -349,9 +347,6 @@ window_destroy(struct window *w) layout_free_cell(w->saved_layout_root); free(w->old_layout); - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); - options_free(&w->options); window_destroy_panes(w); From 55b8d7456155de4e5d44136a140a599e2a2a755b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 16:10:46 +0000 Subject: [PATCH 699/949] Revert previous; we do need a timer, until I have a better idea. We can't do the name check every loop, because that is too expensive, and we can't make sure it only happens infrequently because we have no idea when the next change will happen. --- cmd-set-option.c | 4 +++- names.c | 32 +++++++++++++++++++++++++------- server-window.c | 1 - tmux.h | 3 ++- window.c | 5 +++++ 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 631a4d0e..56ca91e0 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -180,7 +180,9 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) - w->active->flags |= PANE_CHANGED; + queue_window_name(w); + else if (event_initialized(&w->name_timer)) + evtimer_del(&w->name_timer); } } if (strcmp(oe->name, "status") == 0 || diff --git a/names.c b/names.c index 00bc88a2..aa0673ea 100644 --- a/names.c +++ b/names.c @@ -25,16 +25,37 @@ #include "tmux.h" +void window_name_callback(unused int, unused short, void *); + void -check_window_name(struct window *w) +queue_window_name(struct window *w) { - char *name; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = NAME_INTERVAL * 1000L; + + if (event_initialized(&w->name_timer)) + evtimer_del(&w->name_timer); + evtimer_set(&w->name_timer, window_name_callback, w); + evtimer_add(&w->name_timer, &tv); +} + +void +window_name_callback(unused int fd, unused short events, void *data) +{ + struct window *w = data; + char *name; if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) + if (!options_get_number(&w->options, "automatic-rename")) { + if (event_initialized(&w->name_timer)) + event_del(&w->name_timer); return; + } + queue_window_name(w); if (~w->active->flags & PANE_CHANGED) return; @@ -42,12 +63,9 @@ check_window_name(struct window *w) name = format_window_name(w); if (strcmp(name, w->name) != 0) { - log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); - } else - log_debug("@%u name not changed (still %s)", w->id, w->name); - + } free(name); } diff --git a/server-window.c b/server-window.c index be1dde2a..c96c2602 100644 --- a/server-window.c +++ b/server-window.c @@ -49,7 +49,6 @@ server_window_loop(void) server_status_session(s); } } - check_window_name(w); } } diff --git a/tmux.h b/tmux.h index 395d3292..6ac0b578 100644 --- a/tmux.h +++ b/tmux.h @@ -870,6 +870,7 @@ RB_HEAD(window_pane_tree, window_pane); struct window { u_int id; char *name; + struct event name_timer; struct timeval silence_timer; struct timeval activity_time; @@ -2208,7 +2209,7 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void check_window_name(struct window *); +void queue_window_name(struct window *); char *default_window_name(struct window *); char *format_window_name(struct window *); char *parse_window_name(const char *); diff --git a/window.c b/window.c index bfd13b1c..77c7a633 100644 --- a/window.c +++ b/window.c @@ -299,6 +299,8 @@ window_create1(u_int sx, u_int sy) fatal("gettimeofday failed"); options_init(&w->options, &global_w_options); + if (options_get_number(&w->options, "automatic-rename")) + queue_window_name(w); w->references = 0; @@ -347,6 +349,9 @@ window_destroy(struct window *w) layout_free_cell(w->saved_layout_root); free(w->old_layout); + if (event_initialized(&w->name_timer)) + evtimer_del(&w->name_timer); + options_free(&w->options); window_destroy_panes(w); From 983ebb26898beaf66383c5f5aff56cb4c0f80ca4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 16:46:40 +0000 Subject: [PATCH 700/949] Allow formats to be specified as functions (in the code) so they are only evaluated on demand rather than each time a format tree is constructed. Use this for expensive formats like pane_current_command. --- format.c | 175 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 126 insertions(+), 49 deletions(-) diff --git a/format.c b/format.c index 39843674..a885bb85 100644 --- a/format.c +++ b/format.c @@ -35,14 +35,23 @@ * string. */ +struct format_entry; +typedef void (*format_cb)(struct format_tree *, struct format_entry *); + void format_job_callback(struct job *); const char *format_job_get(struct format_tree *, const char *); void format_job_timer(int, short, void *); +void format_cb_host(struct format_tree *, struct format_entry *); +void format_cb_host_short(struct format_tree *, struct format_entry *); +void format_cb_pid(struct format_tree *, struct format_entry *); +void format_cb_start_command(struct format_tree *, struct format_entry *); +void format_cb_current_command(struct format_tree *, struct format_entry *); + +void format_add_cb(struct format_tree *, const char *, format_cb); int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); char *format_time_string(time_t); -char *format_get_command(struct window_pane *); void format_defaults_pane_tabs(struct format_tree *, struct window_pane *); void format_defaults_session(struct format_tree *, struct session *); @@ -79,18 +88,19 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) /* Entry in format tree. */ struct format_entry { - char *key; - char *value; - - RB_ENTRY(format_entry) entry; + char *key; + char *value; + format_cb cb; + RB_ENTRY(format_entry) entry; }; /* Format entry tree. */ struct format_tree { - struct window *w; - struct session *s; + struct window *w; + struct session *s; + struct window_pane *wp; - int status; + int status; RB_HEAD(format_entry_tree, format_entry) tree; }; @@ -257,6 +267,75 @@ format_job_timer(unused int fd, unused short events, unused void *arg) evtimer_add(&format_job_event, &tv); } +/* Callback for host. */ +void +format_cb_host(unused struct format_tree *ft, struct format_entry *fe) +{ + char host[HOST_NAME_MAX + 1]; + + if (gethostname(host, sizeof host) != 0) + fe->value = xstrdup(""); + else + fe->value = xstrdup(host); +} + +/* Callback for host_short. */ +void +format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) +{ + char host[HOST_NAME_MAX + 1], *cp; + + if (gethostname(host, sizeof host) != 0) + fe->value = xstrdup(""); + else { + if ((cp = strchr(host, '.')) != NULL) + *cp = '\0'; + fe->value = xstrdup(host); + } +} + +/* Callback for pid. */ +void +format_cb_pid(unused struct format_tree *ft, struct format_entry *fe) +{ + xasprintf(&fe->value, "%ld", (long)getpid()); +} + +/* Callback for pane_start_command. */ +void +format_cb_start_command(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + + if (wp == NULL) + return; + + fe->value = cmd_stringify_argv(wp->argc, wp->argv); +} + +/* Callback for pane_current_command. */ +void +format_cb_current_command(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + char *cmd; + + if (wp == NULL) + return; + + cmd = get_proc_name(wp->fd, wp->tty); + if (cmd == NULL || *cmd == '\0') { + free(cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); + if (cmd == NULL || *cmd == '\0') { + free(cmd); + cmd = xstrdup(wp->shell); + } + } + fe->value = parse_window_name(cmd); + free(cmd); +} + /* Create a new tree. */ struct format_tree * format_create(void) @@ -269,7 +348,6 @@ struct format_tree * format_create_status(int status) { struct format_tree *ft; - char host[HOST_NAME_MAX + 1], *ptr; if (!event_initialized(&format_job_event)) { evtimer_set(&format_job_event, format_job_timer, NULL); @@ -280,13 +358,9 @@ format_create_status(int status) RB_INIT(&ft->tree); ft->status = status; - if (gethostname(host, sizeof host) == 0) { - format_add(ft, "host", "%s", host); - if ((ptr = strchr(host, '.')) != NULL) - *ptr = '\0'; - format_add(ft, "host_short", "%s", host); - } - format_add(ft, "pid", "%ld", (long) getpid()); + 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); return (ft); } @@ -318,17 +392,42 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); + if (fe_now != NULL) { + free(fe->key); + free(fe); + free(fe_now->value); + fe = fe_now; + } + + fe->cb = NULL; + va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); +} + +/* Add a key and function. */ +void +format_add_cb(struct format_tree *ft, const char *key, format_cb cb) +{ + struct format_entry *fe; + struct format_entry *fe_now; + + fe = xmalloc(sizeof *fe); + fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { - free(fe_now->value); - fe_now->value = fe->value; free(fe->key); free(fe); + free(fe_now->value); + fe = fe_now; } + + fe->cb = cb; + + fe->value = NULL; } /* Find a format entry. */ @@ -363,8 +462,11 @@ format_find(struct format_tree *ft, const char *key) fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); - if (fe != NULL) + if (fe != NULL) { + if (fe->value == NULL && fe->cb != NULL) + fe->cb(ft, fe); return (fe->value); + } envent = NULL; if (ft->s != NULL) @@ -506,7 +608,7 @@ char * format_expand(struct format_tree *ft, const char *fmt) { char *buf, *tmp, *cmd; - const char *ptr, *s; + const char *ptr, *s, *saved = fmt; size_t off, len, n, slen; int ch, brackets; @@ -610,29 +712,10 @@ format_expand(struct format_tree *ft, const char *fmt) } buf[off] = '\0'; + log_debug("format '%s' -> '%s'", saved, buf); return (buf); } -/* Get command name for format. */ -char * -format_get_command(struct window_pane *wp) -{ - char *cmd, *out; - - cmd = get_proc_name(wp->fd, wp->tty); - if (cmd == NULL || *cmd == '\0') { - free(cmd); - cmd = cmd_stringify_argv(wp->argc, wp->argv); - if (cmd == NULL || *cmd == '\0') { - free(cmd); - cmd = xstrdup(wp->shell); - } - } - out = parse_window_name(cmd); - free(cmd); - return (out); -} - /* Get time as a string. */ char * format_time_string(time_t t) @@ -863,11 +946,11 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) struct grid_line *gl; unsigned long long size; u_int i, idx; - char *cmd; int status; if (ft->w == NULL) ft->w = wp->window; + ft->wp = wp; size = 0; for (i = 0; i < gd->hsize; i++) { @@ -908,14 +991,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); - if ((cmd = cmd_stringify_argv(wp->argc, wp->argv)) != NULL) { - format_add(ft, "pane_start_command", "%s", cmd); - free(cmd); - } - if ((cmd = format_get_command(wp)) != NULL) { - format_add(ft, "pane_current_command", "%s", cmd); - free(cmd); - } + format_add_cb(ft, "pane_start_command", format_cb_start_command); + format_add_cb(ft, "pane_current_command", format_cb_current_command); format_add(ft, "cursor_x", "%u", wp->base.cx); format_add(ft, "cursor_y", "%u", wp->base.cy); From 5f122af55662a2af36f20493446dbbadfd24f7af Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 17:01:42 +0000 Subject: [PATCH 701/949] Make a few more expensive (ish) formats functions instead of inline. --- format.c | 169 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 106 insertions(+), 63 deletions(-) diff --git a/format.c b/format.c index a885bb85..d86e529d 100644 --- a/format.c +++ b/format.c @@ -45,8 +45,12 @@ void format_job_timer(int, short, void *); void format_cb_host(struct format_tree *, struct format_entry *); void format_cb_host_short(struct format_tree *, struct format_entry *); void format_cb_pid(struct format_tree *, struct format_entry *); +void format_cb_session_alerts(struct format_tree *, struct format_entry *); +void format_cb_window_layout(struct format_tree *, struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); +void format_cb_history_bytes(struct format_tree *, struct format_entry *); +void format_cb_pane_tabs(struct format_tree *, struct format_entry *); void format_add_cb(struct format_tree *, const char *, format_cb); int format_replace(struct format_tree *, const char *, size_t, char **, @@ -301,6 +305,51 @@ format_cb_pid(unused struct format_tree *ft, struct format_entry *fe) xasprintf(&fe->value, "%ld", (long)getpid()); } +/* Callback for session_alerts. */ +void +format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) +{ + struct session *s = ft->s; + struct winlink *wl; + char alerts[256], tmp[16]; + + if (s == NULL) + return; + + *alerts = '\0'; + RB_FOREACH(wl, winlinks, &s->windows) { + if ((wl->flags & WINLINK_ALERTFLAGS) == 0) + continue; + xsnprintf(tmp, sizeof tmp, "%u", wl->idx); + + if (*alerts != '\0') + strlcat(alerts, ",", sizeof alerts); + strlcat(alerts, tmp, sizeof alerts); + if (wl->flags & WINLINK_ACTIVITY) + strlcat(alerts, "#", sizeof alerts); + if (wl->flags & WINLINK_BELL) + strlcat(alerts, "!", sizeof alerts); + if (wl->flags & WINLINK_SILENCE) + strlcat(alerts, "~", sizeof alerts); + } + fe->value = xstrdup(alerts); +} + +/* Callback for window_layout. */ +void +format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->w; + + if (w == NULL) + return; + + if (w->saved_layout_root != NULL) + fe->value = layout_dump(w->saved_layout_root); + else + fe->value = layout_dump(w->layout_root); +} + /* Callback for pane_start_command. */ void format_cb_start_command(struct format_tree *ft, struct format_entry *fe) @@ -336,6 +385,56 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) free(cmd); } +/* Callback for history_bytes. */ +void +format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct grid *gd; + struct grid_line *gl; + unsigned long long size; + u_int i; + + if (wp == NULL) + return; + gd = wp->base.grid; + + size = 0; + for (i = 0; i < gd->hsize; i++) { + gl = &gd->linedata[i]; + size += gl->cellsize * sizeof *gl->celldata; + } + size += gd->hsize * sizeof *gd->linedata; + + xasprintf(&fe->value, "%llu", size); +} + +/* Callback for pane_tabs. */ +void +format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct evbuffer *buffer; + u_int i; + int size; + + if (wp == NULL) + return; + + buffer = evbuffer_new(); + for (i = 0; i < wp->base.grid->sx; i++) { + if (!bit_test(wp->base.tabs, i)) + continue; + + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%u", i); + } + size = EVBUFFER_LENGTH(buffer); + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Create a new tree. */ struct format_tree * format_create(void) @@ -756,8 +855,6 @@ format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; time_t t; - struct winlink *wl; - char alerts[256], tmp[16]; ft->s = s; @@ -783,23 +880,7 @@ 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); - *alerts = '\0'; - RB_FOREACH (wl, winlinks, &s->windows) { - if ((wl->flags & WINLINK_ALERTFLAGS) == 0) - continue; - xsnprintf(tmp, sizeof tmp, "%u", wl->idx); - - if (*alerts != '\0') - strlcat(alerts, ",", sizeof alerts); - strlcat(alerts, tmp, sizeof alerts); - if (wl->flags & WINLINK_ACTIVITY) - strlcat(alerts, "#", sizeof alerts); - if (wl->flags & WINLINK_BELL) - strlcat(alerts, "!", sizeof alerts); - if (wl->flags & WINLINK_SILENCE) - strlcat(alerts, "~", sizeof alerts); - } - format_add(ft, "session_alerts", "%s", alerts); + format_add_cb(ft, "session_alerts", format_cb_session_alerts); } /* Set default format keys for a client. */ @@ -858,16 +939,10 @@ format_defaults_client(struct format_tree *ft, struct client *c) void format_defaults_window(struct format_tree *ft, struct window *w) { - char *layout; time_t t; ft->w = w; - if (w->saved_layout_root != NULL) - layout = layout_dump(w->saved_layout_root); - else - layout = layout_dump(w->layout_root); - t = w->activity_time.tv_sec; format_add(ft, "window_activity", "%lld", (long long) t); format_add(ft, "window_activity_string", "%s", format_time_string(t)); @@ -876,12 +951,10 @@ 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_layout", "%s", layout); + format_add_cb(ft, "window_layout", format_cb_window_layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); - - free(layout); } /* Set default format keys for a winlink. */ @@ -916,51 +989,21 @@ format_defaults_winlink(struct format_tree *ft, struct session *s, free(flags); } -/* Add window pane tabs. */ -void -format_defaults_pane_tabs(struct format_tree *ft, struct window_pane *wp) -{ - struct evbuffer *buffer; - u_int i; - - buffer = evbuffer_new(); - for (i = 0; i < wp->base.grid->sx; i++) { - if (!bit_test(wp->base.tabs, i)) - continue; - - if (EVBUFFER_LENGTH(buffer) > 0) - evbuffer_add(buffer, ",", 1); - evbuffer_add_printf(buffer, "%u", i); - } - - format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer), - EVBUFFER_DATA(buffer)); - evbuffer_free(buffer); -} - /* Set default format keys for a window pane. */ void format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { - struct grid *gd = wp->base.grid; - struct grid_line *gl; - unsigned long long size; - u_int i, idx; - int status; + struct grid *gd = wp->base.grid; + u_int idx; + int status; if (ft->w == NULL) ft->w = wp->window; ft->wp = wp; - size = 0; - for (i = 0; i < gd->hsize; i++) { - gl = &gd->linedata[i]; - size += gl->cellsize * sizeof *gl->celldata; - } - size += gd->hsize * sizeof *gd->linedata; format_add(ft, "history_size", "%u", gd->hsize); format_add(ft, "history_limit", "%u", gd->hlimit); - format_add(ft, "history_bytes", "%llu", size); + format_add_cb(ft, "history_bytes", format_cb_history_bytes); if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); @@ -1023,7 +1066,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "mouse_utf8_flag", "%d", !!(wp->base.mode & MODE_MOUSE_UTF8)); - format_defaults_pane_tabs(ft, wp); + format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } /* Set default format keys for paste buffer. */ From d9b313332186b0c99ec6a9b7585db8188bb35cfc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 17:11:12 +0000 Subject: [PATCH 702/949] Only set default title to hostname on screens that are being used for a window pane, no point in calling gethostname() for temporary screens. --- screen.c | 8 +------- window.c | 4 ++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/screen.c b/screen.c index 1b841eed..f487cc67 100644 --- a/screen.c +++ b/screen.c @@ -31,14 +31,8 @@ void screen_resize_y(struct screen *, u_int); void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) { - char host[HOST_NAME_MAX+1]; - s->grid = grid_create(sx, sy, hlimit); - - if (gethostname(host, sizeof(host)) == 0) - s->title = xstrdup(host); - else - s->title = xstrdup(""); + s->title = xstrdup(""); s->cstyle = 0; s->ccolour = xstrdup(""); diff --git a/window.c b/window.c index 77c7a633..ff3ccfb8 100644 --- a/window.c +++ b/window.c @@ -704,6 +704,7 @@ struct window_pane * window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) { struct window_pane *wp; + char host[HOST_NAME_MAX + 1]; wp = xcalloc(1, sizeof *wp); wp->window = w; @@ -740,6 +741,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; + if (gethostname(host, sizeof host) == 0) + screen_set_title(&wp->base, host); + input_init(wp); return (wp); From 73bd8160768c5411bf96c8e2326c36f19cdd45c5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 00:24:44 +0000 Subject: [PATCH 703/949] Microseconds in log time. --- log.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/log.c b/log.c index 9a53775e..fa26eb4c 100644 --- a/log.c +++ b/log.c @@ -66,14 +66,15 @@ log_close(void) void log_vwrite(const char *msg, va_list ap) { - char *fmt; - time_t t; + char *fmt; + struct timeval tv; if (log_file == NULL) return; - t = time(NULL); - if (asprintf(&fmt, "%lld %s\n", (long long)t, msg) == -1) + gettimeofday(&tv, NULL); + if (asprintf(&fmt, "%lld.%06d %s\n", (long long)tv.tv_sec, + (int)tv.tv_usec, msg) == -1) exit(1); if (vfprintf(log_file, fmt, ap) == -1) exit(1); From b7861f34bae7a9e00446e0a8cf2f38e220c41c79 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 00:29:15 +0000 Subject: [PATCH 704/949] Better take on reducing the name timer. Again check for name changes in the main loop after events that may have changed the pane, but do so at most once every 500 millis. If the pane changed too soon, use a timer to ensure that a check happens later. --- cmd-set-option.c | 4 +-- names.c | 63 +++++++++++++++++++++++++++++++++++------------- server-window.c | 1 + tmux.h | 11 ++++++--- window.c | 6 ++--- 5 files changed, 57 insertions(+), 28 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 56ca91e0..631a4d0e 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -180,9 +180,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); - else if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + w->active->flags |= PANE_CHANGED; } } if (strcmp(oe->name, "status") == 0 || diff --git a/names.c b/names.c index aa0673ea..e880c577 100644 --- a/names.c +++ b/names.c @@ -25,47 +25,76 @@ #include "tmux.h" -void window_name_callback(unused int, unused short, void *); +void name_time_callback(int, short, void *); +int name_time_expired(struct window *, struct timeval *); void -queue_window_name(struct window *w) +name_time_callback(unused int fd, unused short events, void *arg) { - struct timeval tv; + struct window *w = arg; - tv.tv_sec = 0; - tv.tv_usec = NAME_INTERVAL * 1000L; + /* The event loop will call check_window_name for us on the way out. */ + log_debug("@%u name timer expired", w->id); +} - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); - evtimer_set(&w->name_timer, window_name_callback, w); - evtimer_add(&w->name_timer, &tv); +int +name_time_expired(struct window *w, struct timeval *tv) +{ + struct timeval offset; + + timersub(tv, &w->name_time, &offset); + if (offset.tv_sec != 0 || offset.tv_usec > NAME_INTERVAL) + return (0); + return (NAME_INTERVAL - offset.tv_usec); } void -window_name_callback(unused int fd, unused short events, void *data) +check_window_name(struct window *w) { - struct window *w = data; + struct timeval tv, next; char *name; + int left; if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) { - if (event_initialized(&w->name_timer)) - event_del(&w->name_timer); + if (!options_get_number(&w->options, "automatic-rename")) + return; + + if (~w->active->flags & PANE_CHANGED) { + log_debug("@%u active pane not changed", w->id); return; } - queue_window_name(w); + log_debug("@%u active pane changed", w->id); - if (~w->active->flags & PANE_CHANGED) + gettimeofday(&tv, NULL); + left = name_time_expired(w, &tv); + if (left != 0) { + if (!event_initialized(&w->name_event)) + evtimer_set(&w->name_event, name_time_callback, w); + if (!evtimer_pending(&w->name_event, NULL)) { + log_debug("@%u name timer queued (%d left)", w->id, left); + timerclear(&next); + next.tv_usec = left; + event_add(&w->name_event, &next); + } else + log_debug("@%u name timer already queued (%d left)", w->id, left); return; + } + memcpy(&w->name_time, &tv, sizeof w->name_time); + if (event_initialized(&w->name_event)) + evtimer_del(&w->name_event); + w->active->flags &= ~PANE_CHANGED; name = format_window_name(w); if (strcmp(name, w->name) != 0) { + log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); - } + } else + log_debug("@%u name not changed (still %s)", w->id, w->name); + free(name); } diff --git a/server-window.c b/server-window.c index c96c2602..be1dde2a 100644 --- a/server-window.c +++ b/server-window.c @@ -49,6 +49,7 @@ server_window_loop(void) server_status_session(s); } } + check_window_name(w); } } diff --git a/tmux.h b/tmux.h index 6ac0b578..5ae7225f 100644 --- a/tmux.h +++ b/tmux.h @@ -47,8 +47,8 @@ extern char **environ; */ #define PANE_MINIMUM 2 -/* Automatic name refresh interval, in milliseconds. */ -#define NAME_INTERVAL 500 +/* Automatic name refresh interval, in microseconds. Must be < 1 second. */ +#define NAME_INTERVAL 500000 /* * UTF-8 data size. This must be big enough to hold combined characters as well @@ -869,8 +869,11 @@ RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ struct window { u_int id; + char *name; - struct event name_timer; + struct event name_event; + struct timeval name_time; + struct timeval silence_timer; struct timeval activity_time; @@ -2209,7 +2212,7 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void queue_window_name(struct window *); +void check_window_name(struct window *); char *default_window_name(struct window *); char *format_window_name(struct window *); char *parse_window_name(const char *); diff --git a/window.c b/window.c index ff3ccfb8..f58d80ea 100644 --- a/window.c +++ b/window.c @@ -299,8 +299,6 @@ window_create1(u_int sx, u_int sy) fatal("gettimeofday failed"); options_init(&w->options, &global_w_options); - if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); w->references = 0; @@ -349,8 +347,8 @@ window_destroy(struct window *w) layout_free_cell(w->saved_layout_root); free(w->old_layout); - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + if (event_initialized(&w->name_event)) + evtimer_del(&w->name_event); options_free(&w->options); From 5267ce8ff41e8c5e69fa5d05da92b61e739c328a Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 00:39:18 +0000 Subject: [PATCH 705/949] Treat entering or leaving a mode as pane changed. --- window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window.c b/window.c index f58d80ea..c172b68e 100644 --- a/window.c +++ b/window.c @@ -1066,7 +1066,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) if ((s = wp->mode->init(wp)) != NULL) wp->screen = s; - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_CHANGED); return (0); } @@ -1080,7 +1080,7 @@ window_pane_reset_mode(struct window_pane *wp) wp->mode = NULL; wp->screen = &wp->base; - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_CHANGED); } void From b5aaefc727f303276a681d74f091f52a7e859d36 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 08:30:54 +0000 Subject: [PATCH 706/949] Move alerts onto events rather than checking every loop. --- Makefile | 4 +- server-window.c => alerts.c | 168 +++++++++++++++++++++++++----------- cmd-set-option.c | 2 + input.c | 9 +- server.c | 5 +- session.c | 1 + tmux.h | 11 ++- window.c | 23 ++--- 8 files changed, 150 insertions(+), 73 deletions(-) rename server-window.c => alerts.c (53%) diff --git a/Makefile b/Makefile index ac7e6e54..ad893d4b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ # $OpenBSD$ PROG= tmux -SRCS= arguments.c \ +SRCS= alerts.c \ + arguments.c \ attributes.c \ cfg.c \ client.c \ @@ -101,7 +102,6 @@ SRCS= arguments.c \ screen.c \ server-client.c \ server-fn.c \ - server-window.c \ server.c \ session.c \ signal.c \ diff --git a/server-window.c b/alerts.c similarity index 53% rename from server-window.c rename to alerts.c index be1dde2a..cfdd30e3 100644 --- a/server-window.c +++ b/alerts.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2015 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,43 +19,133 @@ #include #include -#include -#include #include "tmux.h" -int server_window_check_bell(struct session *, struct winlink *); -int server_window_check_activity(struct session *, struct winlink *); -int server_window_check_silence(struct session *, struct winlink *); -void ring_bell(struct session *); +int alerts_fired; + +void alerts_timer(int, short, void *); +int alerts_enabled(struct window *, int); +void alerts_callback(int, short, void *); +void alerts_reset(struct window *); + +int alerts_check_bell(struct session *, struct winlink *); +int alerts_check_activity(struct session *, struct winlink *); +int alerts_check_silence(struct session *, struct winlink *); +void alerts_ring_bell(struct session *); -/* Window functions that need to happen every loop. */ void -server_window_loop(void) +alerts_timer(unused int fd, unused short events, void *arg) +{ + struct window *w = arg; + + log_debug("@%u alerts timer expired", w->id); + alerts_reset(w); + alerts_queue(w, WINDOW_SILENCE); +} + +void +alerts_callback(unused int fd, unused short events, unused void *arg) { struct window *w; struct session *s; struct winlink *wl; + int flags, alerts; RB_FOREACH(w, windows, &windows) { RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window != w) continue; + flags = w->flags; - if (server_window_check_bell(s, wl) || - server_window_check_activity(s, wl) || - server_window_check_silence(s, wl)) + alerts = alerts_check_bell(s, wl); + alerts |= alerts_check_activity(s, wl); + alerts |= alerts_check_silence(s, wl); + if (alerts != 0) server_status_session(s); + + log_debug("%s:%d @%u alerts check, alerts %#x, " + "flags %#x", s->name, wl->idx, w->id, + alerts, flags); } } - check_window_name(w); } + alerts_fired = 0; } -/* Check for bell in window. */ int -server_window_check_bell(struct session *s, struct winlink *wl) +alerts_enabled(struct window *w, int flags) +{ + struct session *s; + + if (flags & WINDOW_ACTIVITY) { + if (options_get_number(&w->options, "monitor-activity")) + return (1); + } + if (flags & WINDOW_SILENCE) { + if (options_get_number(&w->options, "monitor-silence") != 0) + return (1); + } + if (~flags & WINDOW_BELL) + return (0); + RB_FOREACH(s, sessions, &sessions) { + if (!session_has(s, w)) + continue; + if (options_get_number(&s->options, "bell-action") != BELL_NONE) + return (1); + } + return (0); +} + +void +alerts_reset_all(void) +{ + struct window *w; + + RB_FOREACH(w, windows, &windows) + alerts_reset(w); +} + +void +alerts_reset(struct window *w) +{ + struct timeval tv; + + w->flags &= ~WINDOW_SILENCE; + event_del(&w->alerts_timer); + + timerclear(&tv); + tv.tv_sec = options_get_number(&w->options, "monitor-silence"); + + log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); + if (tv.tv_sec != 0) + event_add(&w->alerts_timer, &tv); +} + +void +alerts_queue(struct window *w, int flags) +{ + if (!event_initialized(&w->alerts_timer)) + evtimer_set(&w->alerts_timer, alerts_timer, w); + + if (w->flags & flags) + return; + w->flags |= flags; + log_debug("@%u alerts flags added %#x", w->id, flags); + + if (!alerts_fired && alerts_enabled(w, flags)) { + log_debug("alerts check queued (by @%u)", w->id); + event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); + alerts_fired = 1; + } + + if (flags & WINDOW_ACTIVITY) + alerts_reset(w); +} + +int +alerts_check_bell(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; @@ -70,10 +160,11 @@ server_window_check_bell(struct session *s, struct winlink *wl) if (s->curw->window == w) w->flags &= ~WINDOW_BELL; - visual = options_get_number(&s->options, "visual-bell"); action = options_get_number(&s->options, "bell-action"); if (action == BELL_NONE) return (0); + + visual = options_get_number(&s->options, "visual-bell"); TAILQ_FOREACH(c, &clients, entry) { if (c->session != s || c->flags & CLIENT_CONTROL) continue; @@ -93,12 +184,11 @@ server_window_check_bell(struct session *s, struct winlink *wl) status_message_set(c, "Bell in window %d", wl->idx); } - return (1); + return (WINDOW_BELL); } -/* Check for activity in window. */ int -server_window_check_activity(struct session *s, struct winlink *wl) +alerts_check_activity(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; @@ -115,7 +205,7 @@ server_window_check_activity(struct session *s, struct winlink *wl) return (0); if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); + alerts_ring_bell(s); wl->flags |= WINLINK_ACTIVITY; if (options_get_number(&s->options, "visual-activity")) { @@ -126,45 +216,28 @@ server_window_check_activity(struct session *s, struct winlink *wl) } } - return (1); + return (WINDOW_ACTIVITY); } -/* Check for silence in window. */ int -server_window_check_silence(struct session *s, struct winlink *wl) +alerts_check_silence(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; - struct timeval timer; - int silence_interval, timer_difference; + + if (s->curw->window == w) + w->flags &= ~WINDOW_SILENCE; if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) return (0); - - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) { - /* - * Reset the timer for this window if we've focused it. We - * don't want the timer tripping as soon as we've switched away - * from this window. - */ - if (gettimeofday(&w->silence_timer, NULL) != 0) - fatal("gettimeofday failed"); - - return (0); - } - - silence_interval = options_get_number(&w->options, "monitor-silence"); - if (silence_interval == 0) + if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); - if (gettimeofday(&timer, NULL) != 0) - fatal("gettimeofday"); - timer_difference = timer.tv_sec - w->silence_timer.tv_sec; - if (timer_difference <= silence_interval) + if (options_get_number(&w->options, "monitor-silence") == 0) return (0); if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); + alerts_ring_bell(s); wl->flags |= WINLINK_SILENCE; if (options_get_number(&s->options, "visual-silence")) { @@ -175,12 +248,11 @@ server_window_check_silence(struct session *s, struct winlink *wl) } } - return (1); + return (WINDOW_SILENCE); } -/* Ring terminal bell. */ void -ring_bell(struct session *s) +alerts_ring_bell(struct session *s) { struct client *c; diff --git a/cmd-set-option.c b/cmd-set-option.c index 631a4d0e..98ab2513 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -186,6 +186,8 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "status") == 0 || strcmp(oe->name, "status-interval") == 0) status_timer_start_all(); + if (strcmp(oe->name, "monitor-silence") == 0) + alerts_reset_all(); /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); diff --git a/input.c b/input.c index 7ec35c88..cfe6b4af 100644 --- a/input.c +++ b/input.c @@ -844,14 +844,9 @@ input_parse(struct window_pane *wp) if (EVBUFFER_LENGTH(evb) == 0) return; + window_update_activity(wp->window); wp->flags |= PANE_CHANGED; - wp->window->flags |= WINDOW_ACTIVITY; - wp->window->flags &= ~WINDOW_SILENCE; - - if (gettimeofday(&wp->window->activity_time, NULL) != 0) - fatal("gettimeofday failed"); - /* * Open the screen. Use NULL wp if there is a mode set as don't want to * update the tty. @@ -1081,7 +1076,7 @@ input_c0_dispatch(struct input_ctx *ictx) case '\000': /* NUL */ break; case '\007': /* BEL */ - wp->window->flags |= WINDOW_BELL; + alerts_queue(wp->window, WINDOW_BELL); break; case '\010': /* BS */ screen_write_backspace(sctx); diff --git a/server.c b/server.c index d36dda12..d21acbe9 100644 --- a/server.c +++ b/server.c @@ -249,10 +249,13 @@ server_start(int lockfd, char *lockfile) void server_loop(void) { + struct window *w; + while (!server_should_shutdown()) { event_loop(EVLOOP_ONCE); - server_window_loop(); + RB_FOREACH(w, windows, &windows) + check_window_name(w); server_client_loop(); } } diff --git a/session.c b/session.c index c4e64e27..56986497 100644 --- a/session.c +++ b/session.c @@ -519,6 +519,7 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_push(&s->lastw, s->curw); s->curw = wl; winlink_clear_flags(wl); + window_update_activity(wl->window); return (0); } diff --git a/tmux.h b/tmux.h index 5ae7225f..ae305a64 100644 --- a/tmux.h +++ b/tmux.h @@ -874,7 +874,8 @@ struct window { struct event name_event; struct timeval name_time; - struct timeval silence_timer; + struct event alerts_timer; + struct timeval activity_time; struct window_pane *active; @@ -1829,6 +1830,10 @@ void key_bindings_dispatch(struct key_binding *, struct client *, int key_string_lookup_string(const char *); const char *key_string_lookup_key(int); +/* alerts.c */ +void alerts_reset_all(void); +void alerts_queue(struct window *, int); + /* server.c */ extern struct clients clients; extern struct clients dead_clients; @@ -1856,9 +1861,6 @@ void server_client_callback(int, short, void *); void server_client_status_timer(void); void server_client_loop(void); -/* server-window.c */ -void server_window_loop(void); - /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); void server_write_ready(struct client *); @@ -2084,6 +2086,7 @@ void winlink_stack_push(struct winlink_stack *, struct winlink *); 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_create1(u_int, u_int); struct window *window_create(const char *, int, char **, const char *, const char *, int, struct environ *, struct termios *, diff --git a/window.c b/window.c index c172b68e..d8506774 100644 --- a/window.c +++ b/window.c @@ -277,6 +277,13 @@ window_find_by_id(u_int id) return (RB_FIND(windows, &windows, &w)); } +void +window_update_activity(struct window *w) +{ + gettimeofday(&w->activity_time, NULL); + alerts_queue(w, WINDOW_ACTIVITY); +} + struct window * window_create1(u_int sx, u_int sy) { @@ -295,9 +302,6 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; - if (gettimeofday(&w->activity_time, NULL) != 0) - fatal("gettimeofday failed"); - options_init(&w->options, &global_w_options); w->references = 0; @@ -305,6 +309,8 @@ window_create1(u_int sx, u_int sy) w->id = next_window_id++; RB_INSERT(windows, &windows, w); + window_update_activity(w); + return (w); } @@ -350,6 +356,9 @@ window_destroy(struct window *w) if (event_initialized(&w->name_event)) evtimer_del(&w->name_event); + if (event_initialized(&w->alerts_timer)) + evtimer_del(&w->alerts_timer); + options_free(&w->options); window_destroy_panes(w); @@ -929,14 +938,6 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) input_parse(wp); wp->pipe_off = EVBUFFER_LENGTH(evb); - - /* - * If we get here, we're not outputting anymore, so set the silence - * flag on the window. - */ - wp->window->flags |= WINDOW_SILENCE; - if (gettimeofday(&wp->window->silence_timer, NULL) != 0) - fatal("gettimeofday failed"); return; start_timer: From b9f0571780e7058a70a8cc3b6805b6f1a73061e0 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 08:54:41 +0000 Subject: [PATCH 707/949] We already loop over the windows in server_client_loop, so don't do it again in server_loop just to check names. --- server-client.c | 1 + server.c | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/server-client.c b/server-client.c index da24e222..8e298954 100644 --- a/server-client.c +++ b/server-client.c @@ -717,6 +717,7 @@ server_client_loop(void) } wp->flags &= ~PANE_REDRAW; } + check_window_name(w); } } diff --git a/server.c b/server.c index d21acbe9..9cddecd3 100644 --- a/server.c +++ b/server.c @@ -249,13 +249,11 @@ server_start(int lockfd, char *lockfile) void server_loop(void) { - struct window *w; - while (!server_should_shutdown()) { + log_debug("event dispatch enter"); event_loop(EVLOOP_ONCE); + log_debug("event dispatch exit"); - RB_FOREACH(w, windows, &windows) - check_window_name(w); server_client_loop(); } } From b56958500036970023c7a53264331cd10a5bbed2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 09:25:00 +0000 Subject: [PATCH 708/949] Move struct paste_buffer out of tmux.h. --- cmd-choose-buffer.c | 4 ++-- cmd-paste-buffer.c | 2 +- cmd-save-buffer.c | 21 +++++++++++---------- cmd-set-buffer.c | 39 +++++++++++++++++---------------------- format.c | 6 ++++-- paste.c | 34 ++++++++++++++++++++++++++++++++-- status.c | 16 ++++++++-------- tmux.h | 42 ++++++++++++++++-------------------------- window-copy.c | 32 +++++++++++++++++--------------- 9 files changed, 108 insertions(+), 88 deletions(-) diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index af178976..b4590306 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -65,7 +65,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); utf8flag = options_get_number(&wl->window->options, "utf8"); - if (paste_get_top() == NULL) + if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) @@ -85,7 +85,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cdata->ft_template = xstrdup(template); format_defaults_paste_buffer(cdata->ft, pb, utf8flag); - xasprintf(&action_data, "%s", pb->name); + xasprintf(&action_data, "%s", paste_buffer_name(pb)); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 6d5fb9fd..d4ff93d1 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -58,7 +58,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufname = args_get(args, 'b'); if (bufname == NULL) - pb = paste_get_top(); + pb = paste_get_top(NULL); else { pb = paste_get_name(bufname); if (pb == NULL) { diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 62c3989e..4644e689 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -57,14 +57,14 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *bufname; + const char *path, *bufname, *bufdata; char *start, *end, *msg; - size_t size, used, msglen; + size_t size, used, msglen, bufsize; int cwd, fd; FILE *f; if (!args_has(args, 'b')) { - if ((pb = paste_get_top()) == NULL) { + if ((pb = paste_get_top(NULL)) == NULL) { cmdq_error(cmdq, "no buffers"); return (CMD_RETURN_ERROR); } @@ -76,6 +76,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } } + bufdata = paste_buffer_data(pb, &bufsize); if (self->entry == &cmd_show_buffer_entry) path = "-"; @@ -114,7 +115,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } - if (fwrite(pb->data, 1, pb->size, f) != pb->size) { + if (fwrite(bufdata, 1, bufsize, f) != bufsize) { cmdq_error(cmdq, "%s: fwrite error", path); fclose(f); return (CMD_RETURN_ERROR); @@ -124,25 +125,25 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); do_stdout: - evbuffer_add(c->stdout_data, pb->data, pb->size); + evbuffer_add(c->stdout_data, bufdata, bufsize); server_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: - if (pb->size > (INT_MAX / 4) - 1) { + if (bufsize > (INT_MAX / 4) - 1) { cmdq_error(cmdq, "buffer too big"); return (CMD_RETURN_ERROR); } msg = NULL; used = 0; - while (used != pb->size) { - start = pb->data + used; - end = memchr(start, '\n', pb->size - used); + while (used != bufsize) { + start = bufdata + used; + end = memchr(start, '\n', bufsize - used); if (end != NULL) size = end - start; else - size = pb->size - used; + size = bufsize - used; msglen = size * 4 + 1; msg = xrealloc(msg, msglen); diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 0ec362b3..4fa7ca11 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -42,9 +42,9 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; - char *pdata, *cause; - const char *bufname; - size_t psize, newsize; + char *bufdata, *cause; + const char *bufname, *olddata; + size_t bufsize, newsize; bufname = NULL; @@ -58,12 +58,11 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufname = args_get(args, 'b'); if (bufname == NULL) { - pb = paste_get_top(); + pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); } - bufname = pb->name; } if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { @@ -79,37 +78,33 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "no data specified"); return (CMD_RETURN_ERROR); } - - psize = 0; - pdata = NULL; - pb = NULL; + bufsize = 0; + bufdata = NULL; + if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); if (args_has(args, 'b')) { bufname = args_get(args, 'b'); pb = paste_get_name(bufname); - } else if (args_has(args, 'a')) { - pb = paste_get_top(); - if (pb != NULL) - bufname = pb->name; - } + } else if (args_has(args, 'a')) + pb = paste_get_top(&bufname); if (args_has(args, 'a') && pb != NULL) { - psize = pb->size; - pdata = xmalloc(psize); - memcpy(pdata, pb->data, psize); + olddata = paste_buffer_data(pb, &bufsize); + bufdata = xmalloc(bufsize); + memcpy(bufdata, olddata, bufsize); } - pdata = xrealloc(pdata, psize + newsize); - memcpy(pdata + psize, args->argv[0], newsize); - psize += newsize; + bufdata = xrealloc(bufdata, bufsize + newsize); + memcpy(bufdata + bufsize, args->argv[0], newsize); + bufsize += newsize; - if (paste_set(pdata, psize, bufname, &cause) != 0) { + if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { cmdq_error(cmdq, "%s", cause); - free(pdata); + free(bufdata); free(cause); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index d86e529d..ae22a844 100644 --- a/format.c +++ b/format.c @@ -1074,10 +1074,12 @@ void format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, int utf8flag) { + size_t bufsize; char *s; - format_add(ft, "buffer_size", "%zu", pb->size); - format_add(ft, "buffer_name", "%s", pb->name); + paste_buffer_data(pb, &bufsize); + format_add(ft, "buffer_size", "%zu", bufsize); + format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); s = paste_make_sample(pb, utf8flag); format_add(ft, "buffer_sample", "%s", s); diff --git a/paste.c b/paste.c index 2ccc3cd2..923a06cf 100644 --- a/paste.c +++ b/paste.c @@ -30,6 +30,18 @@ * string! */ +struct paste_buffer { + char *data; + size_t size; + + char *name; + int automatic; + u_int order; + + RB_ENTRY(paste_buffer) name_entry; + RB_ENTRY(paste_buffer) time_entry; +}; + u_int paste_next_index; u_int paste_next_order; u_int paste_num_automatic; @@ -60,6 +72,22 @@ paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) return (0); } +/* Get paste buffer name. */ +const char * +paste_buffer_name(struct paste_buffer *pb) +{ + return (pb->name); +} + +/* Get paste buffer data. */ +const char * +paste_buffer_data(struct paste_buffer *pb, size_t *size) +{ + if (size != NULL) + *size = pb->size; + return (pb->data); +} + /* Walk paste buffers by name. */ struct paste_buffer * paste_walk(struct paste_buffer *pb) @@ -71,13 +99,15 @@ paste_walk(struct paste_buffer *pb) /* Get the most recent automatic buffer. */ struct paste_buffer * -paste_get_top(void) +paste_get_top(const char **name) { struct paste_buffer *pb; pb = RB_MIN(paste_time_tree, &paste_by_time); if (pb == NULL) return (NULL); + if (name != NULL) + *name = pb->name; return (pb); } @@ -87,7 +117,7 @@ paste_free_top(void) { struct paste_buffer *pb; - pb = paste_get_top(); + pb = paste_get_top(NULL); if (pb == NULL) return (-1); return (paste_free_name(pb->name)); diff --git a/status.c b/status.c index 93cee2b1..29cb686c 100644 --- a/status.c +++ b/status.c @@ -815,10 +815,9 @@ status_prompt_key(struct client *c, int key) struct options *oo = &sess->options; struct paste_buffer *pb; char *s, *first, *last, word[64], swapc; - const char *histstr; - const char *wsep = NULL; + const char *histstr, *bufdata, *wsep = NULL; u_char ch; - size_t size, n, off, idx; + size_t size, n, off, idx, bufsize; size = strlen(c->prompt_buffer); switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) { @@ -1067,24 +1066,25 @@ status_prompt_key(struct client *c, int key) c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PASTE: - if ((pb = paste_get_top()) == NULL) + if ((pb = paste_get_top(NULL)) == NULL) break; - for (n = 0; n < pb->size; n++) { - ch = (u_char) pb->data[n]; + bufdata = paste_buffer_data(pb, &bufsize); + for (n = 0; n < bufsize; n++) { + ch = (u_char)bufdata[n]; if (ch < 32 || ch == 127) break; } c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1); if (c->prompt_index == size) { - memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); c->prompt_index += n; c->prompt_buffer[c->prompt_index] = '\0'; } else { memmove(c->prompt_buffer + c->prompt_index + n, c->prompt_buffer + c->prompt_index, size + 1 - c->prompt_index); - memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); c->prompt_index += n; } diff --git a/tmux.h b/tmux.h index ae305a64..fc25be95 100644 --- a/tmux.h +++ b/tmux.h @@ -957,19 +957,6 @@ struct layout_cell { TAILQ_ENTRY(layout_cell) entry; }; -/* Paste buffer. */ -struct paste_buffer { - char *data; - size_t size; - - char *name; - int automatic; - u_int order; - - RB_ENTRY(paste_buffer) name_entry; - RB_ENTRY(paste_buffer) time_entry; -}; - /* Environment variable. */ struct environ_entry { char *name; @@ -1452,6 +1439,22 @@ void cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); +/* paste.c */ +struct paste_buffer; +const char *paste_buffer_name(struct paste_buffer *); +const char *paste_buffer_data(struct paste_buffer *, size_t *); +struct paste_buffer *paste_walk(struct paste_buffer *); +struct paste_buffer *paste_get_top(const char **); +struct paste_buffer *paste_get_name(const char *); +int paste_free_top(void); +int paste_free_name(const char *); +void paste_add(char *, size_t); +int paste_rename(const char *, const char *, char **); +int paste_set(char *, size_t, const char *, char **); +char *paste_make_sample(struct paste_buffer *, int); +void paste_send_pane(struct paste_buffer *, struct window_pane *, + const char *, int); + /* format.c */ struct format_tree; struct format_tree *format_create(void); @@ -1636,19 +1639,6 @@ void tty_keys_build(struct tty *); void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); -/* paste.c */ -struct paste_buffer *paste_walk(struct paste_buffer *); -struct paste_buffer *paste_get_top(void); -struct paste_buffer *paste_get_name(const char *); -int paste_free_top(void); -int paste_free_name(const char *); -void paste_add(char *, size_t); -int paste_rename(const char *, const char *, char **); -int paste_set(char *, size_t, const char *, char **); -char *paste_make_sample(struct paste_buffer *, int); -void paste_send_pane(struct paste_buffer *, struct window_pane *, - const char *, int); - /* arguments.c */ int args_cmp(struct args_entry *, struct args_entry *); RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); diff --git a/window-copy.c b/window-copy.c index 850b437a..460dd10b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -782,7 +782,8 @@ window_copy_key_input(struct window_pane *wp, int key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - size_t inputlen, n; + const char *bufdata; + size_t inputlen, n, bufsize; int np; struct paste_buffer *pb; u_char ch; @@ -800,17 +801,18 @@ window_copy_key_input(struct window_pane *wp, int key) *data->inputstr = '\0'; break; case MODEKEYEDIT_PASTE: - if ((pb = paste_get_top()) == NULL) + if ((pb = paste_get_top(NULL)) == NULL) break; - for (n = 0; n < pb->size; n++) { - ch = (u_char) pb->data[n]; + bufdata = paste_buffer_data(pb, &bufsize); + for (n = 0; n < bufsize; n++) { + ch = (u_char)bufdata[n]; if (ch < 32 || ch == 127) break; } inputlen = strlen(data->inputstr); data->inputstr = xrealloc(data->inputstr, inputlen + n + 1); - memcpy(data->inputstr + inputlen, pb->data, n); + memcpy(data->inputstr + inputlen, bufdata, n); data->inputstr[inputlen + n] = '\0'; break; case MODEKEYEDIT_ENTER: @@ -1491,7 +1493,8 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) { char *buf; struct paste_buffer *pb; - size_t len; + const char *bufdata; + size_t len, bufsize; struct screen_write_ctx ctx; buf = window_copy_get_selection(wp, &len); @@ -1504,17 +1507,16 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) screen_write_stop(&ctx); } - if (bufname == NULL || *bufname == '\0') { - pb = paste_get_top(); - if (pb != NULL) - bufname = pb->name; - } else + if (bufname == NULL || *bufname == '\0') + pb = paste_get_top(&bufname); + else pb = paste_get_name(bufname); if (pb != NULL) { - buf = xrealloc(buf, len + pb->size); - memmove(buf + pb->size, buf, len); - memcpy(buf, pb->data, pb->size); - len += pb->size; + bufdata = paste_buffer_data(pb, &bufsize); + buf = xrealloc(buf, len + bufsize); + memmove(buf + bufsize, buf, len); + memcpy(buf, bufdata, bufsize); + len += bufsize; } if (paste_set(buf, len, bufname, NULL) != 0) free(buf); From 373ef850e0a00dfb8180054625a91d046849261e Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 09:36:46 +0000 Subject: [PATCH 709/949] paste_send_pane can be merged into cmd-paste-buffer.c now. --- cmd-paste-buffer.c | 31 +++++++++++++++++++++++++++---- cmd-save-buffer.c | 4 ++-- paste.c | 29 ----------------------------- tmux.h | 2 -- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index d4ff93d1..cd3fc7d8 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -48,7 +48,9 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct paste_buffer *pb; - const char *sepstr, *bufname; + const char *sepstr, *bufname, *bufdata, *bufend, *line; + size_t seplen, bufsize; + int bracket = args_has(args, 'p'); if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); @@ -67,7 +69,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) } } - if (pb != NULL) { + if (pb != NULL && ~wp->flags & PANE_INPUTOFF) { sepstr = args_get(args, 's'); if (sepstr == NULL) { if (args_has(args, 'r')) @@ -75,10 +77,31 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else sepstr = "\r"; } - paste_send_pane(pb, wp, sepstr, args_has(args, 'p')); + seplen = strlen(sepstr); + + if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) + bufferevent_write(wp->event, "\033[200~", 6); + + bufdata = paste_buffer_data(pb, &bufsize); + bufend = bufdata + bufsize; + + for (;;) { + line = memchr(bufdata, '\n', bufend - bufdata); + if (line == NULL) + break; + + bufferevent_write(wp->event, bufdata, line - bufdata); + bufferevent_write(wp->event, sepstr, seplen); + + bufdata = line + 1; + } + if (bufdata != bufend) + bufferevent_write(wp->event, bufdata, bufend - bufdata); + + if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) + bufferevent_write(wp->event, "\033[201~", 6); } - /* Delete the buffer if -d. */ if (args_has(args, 'd')) { if (bufname == NULL) paste_free_top(); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 4644e689..5916cd4d 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -57,8 +57,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *bufname, *bufdata; - char *start, *end, *msg; + const char *path, *bufname, *bufdata, *start, *end; + char *msg; size_t size, used, msglen, bufsize; int cwd, fd; FILE *f; diff --git a/paste.c b/paste.c index 923a06cf..e7f752ce 100644 --- a/paste.c +++ b/paste.c @@ -319,32 +319,3 @@ paste_make_sample(struct paste_buffer *pb, int utf8flag) strlcpy(buf + width, "...", 4); return (buf); } - -/* Paste into a window pane, filtering '\n' according to separator. */ -void -paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, - const char *sep, int bracket) -{ - const char *data = pb->data, *end = data + pb->size, *lf; - size_t seplen; - - if (wp->flags & PANE_INPUTOFF) - return; - - if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) - bufferevent_write(wp->event, "\033[200~", 6); - - seplen = strlen(sep); - while ((lf = memchr(data, '\n', end - data)) != NULL) { - if (lf != data) - bufferevent_write(wp->event, data, lf - data); - bufferevent_write(wp->event, sep, seplen); - data = lf + 1; - } - - if (end != data) - bufferevent_write(wp->event, data, end - data); - - if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) - bufferevent_write(wp->event, "\033[201~", 6); -} diff --git a/tmux.h b/tmux.h index fc25be95..822bdc93 100644 --- a/tmux.h +++ b/tmux.h @@ -1452,8 +1452,6 @@ void paste_add(char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); char *paste_make_sample(struct paste_buffer *, int); -void paste_send_pane(struct paste_buffer *, struct window_pane *, - const char *, int); /* format.c */ struct format_tree; From 52bbac506c54cd378fb4493d218027dc36a7fce5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 23:19:52 +0000 Subject: [PATCH 710/949] struct args_entry can go into arguments.c. --- arguments.c | 6 ++++++ tmux.h | 7 +------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arguments.c b/arguments.c index 05ff97eb..54753de3 100644 --- a/arguments.c +++ b/arguments.c @@ -29,6 +29,12 @@ * Manipulate command arguments. */ +struct args_entry { + u_char flag; + char *value; + RB_ENTRY(args_entry) entry; +}; + struct args_entry *args_find(struct args *, u_char); RB_GENERATE(args_tree, args_entry, entry, args_cmp); diff --git a/tmux.h b/tmux.h index 822bdc93..6b462589 100644 --- a/tmux.h +++ b/tmux.h @@ -1257,13 +1257,8 @@ struct client { TAILQ_HEAD(clients, client); /* Parsed arguments structures. */ -struct args_entry { - u_char flag; - char *value; - RB_ENTRY(args_entry) entry; -}; +struct args_entry; RB_HEAD(args_tree, args_entry); - struct args { struct args_tree tree; int argc; From 5047670693e3bc861eb4ae95708c96041b12a759 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 23:55:55 +0000 Subject: [PATCH 711/949] Remove some old prototypes and unused functions. --- layout-set.c | 6 ------ tmux.h | 6 ------ window.c | 10 ---------- 3 files changed, 22 deletions(-) diff --git a/layout-set.c b/layout-set.c index 64b655a0..da94cff2 100644 --- a/layout-set.c +++ b/layout-set.c @@ -44,12 +44,6 @@ const struct { { "tiled", layout_set_tiled }, }; -const char * -layout_set_name(u_int layout) -{ - return (layout_sets[layout].name); -} - int layout_set_lookup(const char *name) { diff --git a/tmux.h b/tmux.h index 6b462589..10b161de 100644 --- a/tmux.h +++ b/tmux.h @@ -1841,7 +1841,6 @@ int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_callback(int, short, void *); -void server_client_status_timer(void); void server_client_loop(void); /* server-fn.c */ @@ -1863,7 +1862,6 @@ void server_status_window(struct window *); void server_lock(void); void server_lock_session(struct session *); void server_lock_client(struct client *); -int server_unlock(const char *); void server_kill_window(struct window *); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); @@ -2013,7 +2011,6 @@ void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); void screen_write_reverseindex(struct screen_write_ctx *); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_linefeed(struct screen_write_ctx *, int); -void screen_write_linefeedscreen(struct screen_write_ctx *, int); void screen_write_carriagereturn(struct screen_write_ctx *); void screen_write_clearendofscreen(struct screen_write_ctx *); void screen_write_clearstartofscreen(struct screen_write_ctx *); @@ -2076,7 +2073,6 @@ struct window *window_create(const char *, int, char **, const char *, u_int, u_int, u_int, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); -void window_set_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *); @@ -2157,12 +2153,10 @@ char *layout_dump(struct layout_cell *); int layout_parse(struct window *, const char *); /* layout-set.c */ -const char *layout_set_name(u_int); int layout_set_lookup(const char *); u_int layout_set_select(struct window *, u_int); u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); -void layout_set_active_changed(struct window *); /* window-clock.c */ extern const struct window_mode window_clock_mode; diff --git a/window.c b/window.c index d8506774..e5decdc4 100644 --- a/window.c +++ b/window.c @@ -440,16 +440,6 @@ window_get_active_at(struct window *w, u_int x, u_int y) return (NULL); } -void -window_set_active_at(struct window *w, u_int x, u_int y) -{ - struct window_pane *wp; - - wp = window_get_active_at(w, x, y); - if (wp != NULL && wp != w->active) - window_set_active_pane(w, wp); -} - struct window_pane * window_find_string(struct window *w, const char *s) { From b87dc608d9b5b470926aaf77c5956befdfb7bc7b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 15:43:40 +0000 Subject: [PATCH 712/949] Some style nits and dead assignments. --- cmd-show-environment.c | 2 +- cmd-swap-pane.c | 2 -- server-client.c | 7 ------- tmux.c | 4 ++-- xterm-keys.c | 2 ++ 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/cmd-show-environment.c b/cmd-show-environment.c index a61cf3f4..af24d91b 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -56,7 +56,7 @@ cmd_show_environment_escape(struct environ_entry *envent) } *out = '\0'; - return ret; + return (ret); } void diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index c4751c36..dc2b7246 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -58,12 +58,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); - src_wl = dst_wl; } else if (args_has(self->args, 'U')) { src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - src_wl = dst_wl; } else { src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, &src_wp); diff --git a/server-client.c b/server-client.c index 8e298954..08058ab4 100644 --- a/server-client.c +++ b/server-client.c @@ -551,13 +551,6 @@ server_client_handle_key(struct client *c, int key) w = s->curw->window; wp = w->active; - /* No session, do nothing. */ - if (c->session == NULL) - return; - s = c->session; - w = c->session->curw->window; - wp = w->active; - /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); diff --git a/tmux.c b/tmux.c index a3481461..e66d24c0 100644 --- a/tmux.c +++ b/tmux.c @@ -198,7 +198,7 @@ shell_exec(const char *shell, const char *shellcmd) fatal("execl failed"); } -const char* +const char * find_home(void) { struct passwd *pw; @@ -213,7 +213,7 @@ find_home(void) home = NULL; } - return home; + return (home); } int diff --git a/xterm-keys.c b/xterm-keys.c index 5ca75d89..10d51c78 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -120,6 +120,8 @@ xterm_keys_match(const char *template, const char *buf, size_t len, size_t pos; int retval; + *modifiers = 0; + if (len == 0) return (0); From 29f2120e5baabeccd8882be843907d3a8b42febf Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 30 Aug 2015 21:47:50 +0100 Subject: [PATCH 713/949] Linux: get_proc_name() -> osdep_get_name() --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 5f31fa86..5268a405 100644 --- a/format.c +++ b/format.c @@ -373,7 +373,7 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) if (wp == NULL) return; - cmd = get_proc_name(wp->fd, wp->tty); + cmd = osdep_get_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = cmd_stringify_argv(wp->argc, wp->argv); From dd92b6e83dbd4b5f24ad062c7944ca32add8d0e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 22:19:07 +0000 Subject: [PATCH 714/949] Event base does not need to be global. --- client.c | 10 +++++----- server.c | 4 ++-- tmux.c | 5 +---- tmux.h | 5 ++--- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/client.c b/client.c index 63432926..271568da 100644 --- a/client.c +++ b/client.c @@ -52,7 +52,7 @@ const char *client_exitsession; int client_attached; int client_get_lock(char *); -int client_connect(char *, int); +int client_connect(struct event_base *, char *, int); void client_send_identify(int); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); @@ -96,7 +96,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ int -client_connect(char *path, int start_server) +client_connect(struct event_base *base, char *path, int start_server) { struct sockaddr_un sa; size_t size; @@ -149,7 +149,7 @@ retry: close(lockfd); return (-1); } - fd = server_start(lockfd, lockfile); + fd = server_start(base, lockfd, lockfile); } if (locked) { free(lockfile); @@ -203,7 +203,7 @@ client_exit_message(void) /* Client main loop. */ int -client_main(int argc, char **argv, int flags) +client_main(struct event_base *base, int argc, char **argv, int flags) { struct cmd *cmd; struct cmd_list *cmdlist; @@ -252,7 +252,7 @@ client_main(int argc, char **argv, int flags) set_signals(client_signal); /* Initialize the client socket and start the server. */ - fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); + fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { if (errno == ECONNREFUSED) { fprintf(stderr, "no server running on %s\n", diff --git a/server.c b/server.c index 9cddecd3..8308367d 100644 --- a/server.c +++ b/server.c @@ -158,7 +158,7 @@ server_create_socket(void) /* Fork new server. */ int -server_start(int lockfd, char *lockfile) +server_start(struct event_base *base, int lockfd, char *lockfile) { int pair[2]; char *cause; @@ -188,7 +188,7 @@ server_start(int lockfd, char *lockfile) /* event_init() was called in our parent, need to reinit. */ clear_signals(0); - if (event_reinit(ev_base) != 0) + if (event_reinit(base) != 0) fatal("event_reinit failed"); logfile("server"); diff --git a/tmux.c b/tmux.c index e66d24c0..b984b903 100644 --- a/tmux.c +++ b/tmux.c @@ -41,8 +41,6 @@ struct options global_s_options; /* session options */ struct options global_w_options; /* window options */ struct environ global_environ; -struct event_base *ev_base; - char *cfg_file; char *shell_cmd; int debug_level; @@ -386,6 +384,5 @@ main(int argc, char **argv) setproctitle("%s (%s)", __progname, socket_path); /* Pass control to the client. */ - ev_base = event_init(); - exit(client_main(argc, argv, flags)); + exit(client_main(event_init(), argc, argv, flags)); } diff --git a/tmux.h b/tmux.h index 10b161de..3b67470f 100644 --- a/tmux.h +++ b/tmux.h @@ -1407,7 +1407,6 @@ extern struct options global_options; extern struct options global_s_options; extern struct options global_w_options; extern struct environ global_environ; -extern struct event_base *ev_base; extern char *cfg_file; extern char *shell_cmd; extern int debug_level; @@ -1792,7 +1791,7 @@ int cmd_string_parse(const char *, struct cmd_list **, const char *, void cmd_wait_for_flush(void); /* client.c */ -int client_main(int, char **, int); +int client_main(struct event_base *, int, char **, int); /* key-bindings.c */ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); @@ -1829,7 +1828,7 @@ void server_clear_marked(void); int server_is_marked(struct session *, struct winlink *, struct window_pane *); int server_check_marked(void); -int server_start(int, char *); +int server_start(struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); From c6e9160c676db48e12e3c38229870d1c020cf1d2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 22:40:25 +0000 Subject: [PATCH 715/949] Login shell can be a client flag, and move the exec code into client.c. --- client.c | 67 ++++++++++++++++++++++++++++++++++++++++++-------------- tmux.c | 36 +++++------------------------- tmux.h | 4 +--- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/client.c b/client.c index 271568da..55c5aa56 100644 --- a/client.c +++ b/client.c @@ -33,6 +33,7 @@ #include "tmux.h" +int client_flags; struct imsgbuf client_ibuf; struct event client_event; struct event client_stdin; @@ -51,9 +52,10 @@ enum msgtype client_exittype; const char *client_exitsession; int client_attached; +__dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); -void client_send_identify(int); +void client_send_identify(void); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -62,7 +64,7 @@ void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); void client_callback(int, short, void *); int client_dispatch_attached(void); -int client_dispatch_wait(void *); +int client_dispatch_wait(void); const char *client_exit_message(void); /* @@ -215,6 +217,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct termios tio, saved_tio; size_t size; + /* Save the flags. */ + client_flags = flags; + /* Set up the initial command. */ cmdflags = 0; if (shell_cmd != NULL) { @@ -266,13 +271,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Create imsg. */ imsg_init(&client_ibuf, fd); - event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); + event_set(&client_event, fd, EV_READ, client_callback, NULL); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); - if (flags & CLIENT_CONTROLCONTROL) { + if (client_flags & CLIENT_CONTROLCONTROL) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); @@ -291,7 +296,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(flags); + client_send_identify(); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -326,13 +331,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Print the exit message, if any, and exit. */ if (client_attached) { - if (client_exitreason != CLIENT_EXIT_NONE && !login_shell) + if (client_exitreason != CLIENT_EXIT_NONE) printf("[%s]\n", client_exit_message()); ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } else if (flags & CLIENT_CONTROLCONTROL) { + } else if (client_flags & CLIENT_CONTROLCONTROL) { if (client_exitreason != CLIENT_EXIT_NONE) printf("%%exit %s\n", client_exit_message()); else @@ -346,12 +351,12 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ void -client_send_identify(int flags) +client_send_identify(void) { const char *s; char **ss; size_t sslen; - int fd; + int fd, flags = client_flags; pid_t pid; client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); @@ -421,14 +426,13 @@ client_update_event(void) events = EV_READ; if (client_ibuf.w.queued > 0) events |= EV_WRITE; - event_set( - &client_event, client_ibuf.fd, events, client_callback, shell_cmd); + event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); event_add(&client_event, NULL); } /* Callback to handle signals in the client. */ void -client_signal(int sig, unused short events, unused void *data) +client_signal(int sig, unused short events, unused void *arg) { struct sigaction sigact; int status; @@ -470,7 +474,7 @@ client_signal(int sig, unused short events, unused void *data) /* Callback for client imsg read events. */ void -client_callback(unused int fd, short events, void *data) +client_callback(unused int fd, short events, unused void *arg) { ssize_t n; int retval; @@ -481,7 +485,7 @@ client_callback(unused int fd, short events, void *data) if (client_attached) retval = client_dispatch_attached(); else - retval = client_dispatch_wait(data); + retval = client_dispatch_wait(); if (retval != 0) { event_loopexit(NULL); return; @@ -504,7 +508,7 @@ lost_server: /* Callback for client stdin read events. */ void -client_stdin_callback(unused int fd, unused short events, unused void *data1) +client_stdin_callback(unused int fd, unused short events, unused void *arg) { struct msg_stdin_data data; @@ -536,9 +540,38 @@ client_write(int fd, const char *data, size_t size) } } +/* Run command in shell; used for -c. */ +__dead void +client_exec(const char *shell) +{ + const char *name, *ptr; + char *argv0; + + log_debug("shell %s, command %s", shell, shell_cmd); + + ptr = strrchr(shell, '/'); + if (ptr != NULL && *(ptr + 1) != '\0') + name = ptr + 1; + else + name = shell; + if (client_flags & CLIENT_LOGIN) + xasprintf(&argv0, "-%s", name); + else + xasprintf(&argv0, "%s", name); + setenv("SHELL", shell, 1); + + setblocking(STDIN_FILENO, 1); + setblocking(STDOUT_FILENO, 1); + setblocking(STDERR_FILENO, 1); + closefrom(STDERR_FILENO + 1); + + execl(shell, argv0, "-c", shell_cmd, (char *) NULL); + fatal("execl failed"); +} + /* Dispatch imsgs when in wait state (before MSG_READY). */ int -client_dispatch_wait(void *data0) +client_dispatch_wait(void) { struct imsg imsg; char *data; @@ -614,7 +647,7 @@ client_dispatch_wait(void *data0) fatalx("bad MSG_SHELL string"); clear_signals(0); - shell_exec(data, data0); + client_exec(data); /* NOTREACHED */ case MSG_DETACH: case MSG_DETACHKILL: diff --git a/tmux.c b/tmux.c index b984b903..1ef23915 100644 --- a/tmux.c +++ b/tmux.c @@ -46,7 +46,6 @@ char *shell_cmd; int debug_level; time_t start_time; char socket_path[PATH_MAX]; -int login_shell; char *environ_path; __dead void usage(void); @@ -170,32 +169,6 @@ setblocking(int fd, int state) } } -__dead void -shell_exec(const char *shell, const char *shellcmd) -{ - const char *shellname, *ptr; - char *argv0; - - ptr = strrchr(shell, '/'); - if (ptr != NULL && *(ptr + 1) != '\0') - shellname = ptr + 1; - else - shellname = shell; - if (login_shell) - xasprintf(&argv0, "-%s", shellname); - else - xasprintf(&argv0, "%s", shellname); - setenv("SHELL", shell, 1); - - setblocking(STDIN_FILENO, 1); - setblocking(STDOUT_FILENO, 1); - setblocking(STDERR_FILENO, 1); - closefrom(STDERR_FILENO + 1); - - execl(shell, argv0, "-c", shellcmd, (char *) NULL); - fatal("execl failed"); -} - const char * find_home(void) { @@ -229,9 +202,12 @@ main(int argc, char **argv) setlocale(LC_TIME, ""); - flags = 0; + if (**argv == '-') + flags = CLIENT_LOGIN; + else + flags = 0; + label = path = NULL; - login_shell = (**argv == '-'); while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { switch (opt) { case '2': @@ -252,7 +228,7 @@ main(int argc, char **argv) cfg_file = xstrdup(optarg); break; case 'l': - login_shell = 1; + flags |= CLIENT_LOGIN; break; case 'L': free(label); diff --git a/tmux.h b/tmux.h index 3b67470f..528c3e71 100644 --- a/tmux.h +++ b/tmux.h @@ -1203,7 +1203,7 @@ struct client { struct screen status; #define CLIENT_TERMINAL 0x1 -/* 0x2 unused */ +#define CLIENT_LOGIN 0x2 #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 @@ -1412,14 +1412,12 @@ extern char *shell_cmd; extern int debug_level; extern time_t start_time; extern char socket_path[PATH_MAX]; -extern int login_shell; extern char *environ_path; void logfile(const char *); const char *getshell(void); int checkshell(const char *); int areshell(const char *); void setblocking(int, int); -__dead void shell_exec(const char *, const char *); const char *find_home(void); /* cfg.c */ From 6a539c00dfe52f588376d997a75e6c7540db5e14 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 22:56:36 +0000 Subject: [PATCH 716/949] Path from $TMUX does not need to be global anymore. --- tmux.c | 26 +++++++++++++------------- tmux.h | 1 - 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/tmux.c b/tmux.c index 1ef23915..6eb2a301 100644 --- a/tmux.c +++ b/tmux.c @@ -46,7 +46,6 @@ char *shell_cmd; int debug_level; time_t start_time; char socket_path[PATH_MAX]; -char *environ_path; __dead void usage(void); char *makesocketpath(const char *); @@ -191,10 +190,8 @@ int main(int argc, char **argv) { char *s, *path, *label, **var, tmp[PATH_MAX]; - char in[256]; const char *home; - long long pid; - int opt, flags, keys, session; + int opt, flags, keys; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -321,11 +318,6 @@ main(int argc, char **argv) } } - /* Get path from environment. */ - s = getenv("TMUX"); - if (s != NULL && sscanf(s, "%255[^,],%lld,%d", in, &pid, &session) == 3) - environ_path = xstrdup(in); - /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. @@ -333,8 +325,15 @@ main(int argc, char **argv) if (path == NULL) { /* If no -L, use the environment. */ if (label == NULL) { - if (environ_path != NULL) - path = xstrdup(environ_path); + s = getenv("TMUX"); + if (s != NULL) { + path = xstrdup(s); + path[strcspn (path, ",")] = '\0'; + if (*path == '\0') { + free(path); + label = xstrdup("default"); + } + } else label = xstrdup("default"); } @@ -343,14 +342,15 @@ main(int argc, char **argv) if (label != NULL) { if ((path = makesocketpath(label)) == NULL) { fprintf(stderr, "can't create socket: %s\n", - strerror(errno)); + strerror(errno)); exit(1); } } } free(label); - if (strlcpy(socket_path, path, sizeof socket_path) >= sizeof socket_path) { + if (strlcpy(socket_path, path, sizeof socket_path) >= + sizeof socket_path) { fprintf(stderr, "socket path too long: %s\n", path); exit(1); } diff --git a/tmux.h b/tmux.h index 528c3e71..1bde93cd 100644 --- a/tmux.h +++ b/tmux.h @@ -1412,7 +1412,6 @@ extern char *shell_cmd; extern int debug_level; extern time_t start_time; extern char socket_path[PATH_MAX]; -extern char *environ_path; void logfile(const char *); const char *getshell(void); int checkshell(const char *); From 2c6ea705fd05966c00534623a9b8fa3431df98f5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 31 Aug 2015 19:57:37 +0100 Subject: [PATCH 717/949] Bring back pane_current_path. --- format.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/format.c b/format.c index 5268a405..bbb172af 100644 --- a/format.c +++ b/format.c @@ -50,6 +50,7 @@ void format_cb_session_alerts(struct format_tree *, struct format_entry *); void format_cb_window_layout(struct format_tree *, struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); +void format_cb_current_path(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); void format_cb_pane_tabs(struct format_tree *, struct format_entry *); @@ -386,6 +387,21 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) free(cmd); } +/* Callback for pane_current_path. */ +void +format_cb_current_path(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + char *cwd; + + if (wp == NULL) + return; + + cwd = osdep_get_cwd(wp->fd); + if (cwd != NULL) + fe->value = xstrdup(cwd); +} + /* Callback for history_bytes. */ void format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) @@ -1037,6 +1053,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_pid", "%ld", (long) wp->pid); format_add_cb(ft, "pane_start_command", format_cb_start_command); format_add_cb(ft, "pane_current_command", format_cb_current_command); + format_add_cb(ft, "pane_current_path", format_cb_current_path); format_add(ft, "cursor_x", "%u", wp->base.cx); format_add(ft, "cursor_y", "%u", wp->base.cy); From 2a836bc306c5635329bec2dd35ba9537a552ff9a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 09:48:34 +0000 Subject: [PATCH 718/949] All the cmd_*_entry declarations do not need to be in tmux.h. --- cmd.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tmux.h | 86 --------------------------------------------------------- 2 files changed, 87 insertions(+), 86 deletions(-) diff --git a/cmd.c b/cmd.c index e8ce932d..0c20d656 100644 --- a/cmd.c +++ b/cmd.c @@ -28,6 +28,93 @@ #include "tmux.h" +extern const struct cmd_entry cmd_attach_session_entry; +extern const struct cmd_entry cmd_bind_key_entry; +extern const struct cmd_entry cmd_break_pane_entry; +extern const struct cmd_entry cmd_capture_pane_entry; +extern const struct cmd_entry cmd_choose_buffer_entry; +extern const struct cmd_entry cmd_choose_client_entry; +extern const struct cmd_entry cmd_choose_session_entry; +extern const struct cmd_entry cmd_choose_tree_entry; +extern const struct cmd_entry cmd_choose_window_entry; +extern const struct cmd_entry cmd_clear_history_entry; +extern const struct cmd_entry cmd_clock_mode_entry; +extern const struct cmd_entry cmd_command_prompt_entry; +extern const struct cmd_entry cmd_confirm_before_entry; +extern const struct cmd_entry cmd_copy_mode_entry; +extern const struct cmd_entry cmd_delete_buffer_entry; +extern const struct cmd_entry cmd_detach_client_entry; +extern const struct cmd_entry cmd_display_message_entry; +extern const struct cmd_entry cmd_display_panes_entry; +extern const struct cmd_entry cmd_down_pane_entry; +extern const struct cmd_entry cmd_find_window_entry; +extern const struct cmd_entry cmd_has_session_entry; +extern const struct cmd_entry cmd_if_shell_entry; +extern const struct cmd_entry cmd_join_pane_entry; +extern const struct cmd_entry cmd_kill_pane_entry; +extern const struct cmd_entry cmd_kill_server_entry; +extern const struct cmd_entry cmd_kill_session_entry; +extern const struct cmd_entry cmd_kill_window_entry; +extern const struct cmd_entry cmd_last_pane_entry; +extern const struct cmd_entry cmd_last_window_entry; +extern const struct cmd_entry cmd_link_window_entry; +extern const struct cmd_entry cmd_list_buffers_entry; +extern const struct cmd_entry cmd_list_clients_entry; +extern const struct cmd_entry cmd_list_commands_entry; +extern const struct cmd_entry cmd_list_keys_entry; +extern const struct cmd_entry cmd_list_panes_entry; +extern const struct cmd_entry cmd_list_sessions_entry; +extern const struct cmd_entry cmd_list_windows_entry; +extern const struct cmd_entry cmd_load_buffer_entry; +extern const struct cmd_entry cmd_lock_client_entry; +extern const struct cmd_entry cmd_lock_server_entry; +extern const struct cmd_entry cmd_lock_session_entry; +extern const struct cmd_entry cmd_move_pane_entry; +extern const struct cmd_entry cmd_move_window_entry; +extern const struct cmd_entry cmd_new_session_entry; +extern const struct cmd_entry cmd_new_window_entry; +extern const struct cmd_entry cmd_next_layout_entry; +extern const struct cmd_entry cmd_next_window_entry; +extern const struct cmd_entry cmd_paste_buffer_entry; +extern const struct cmd_entry cmd_pipe_pane_entry; +extern const struct cmd_entry cmd_previous_layout_entry; +extern const struct cmd_entry cmd_previous_window_entry; +extern const struct cmd_entry cmd_refresh_client_entry; +extern const struct cmd_entry cmd_rename_session_entry; +extern const struct cmd_entry cmd_rename_window_entry; +extern const struct cmd_entry cmd_resize_pane_entry; +extern const struct cmd_entry cmd_respawn_pane_entry; +extern const struct cmd_entry cmd_respawn_window_entry; +extern const struct cmd_entry cmd_rotate_window_entry; +extern const struct cmd_entry cmd_run_shell_entry; +extern const struct cmd_entry cmd_save_buffer_entry; +extern const struct cmd_entry cmd_select_layout_entry; +extern const struct cmd_entry cmd_select_pane_entry; +extern const struct cmd_entry cmd_select_window_entry; +extern const struct cmd_entry cmd_send_keys_entry; +extern const struct cmd_entry cmd_send_prefix_entry; +extern const struct cmd_entry cmd_server_info_entry; +extern const struct cmd_entry cmd_set_buffer_entry; +extern const struct cmd_entry cmd_set_environment_entry; +extern const struct cmd_entry cmd_set_option_entry; +extern const struct cmd_entry cmd_set_window_option_entry; +extern const struct cmd_entry cmd_show_buffer_entry; +extern const struct cmd_entry cmd_show_environment_entry; +extern const struct cmd_entry cmd_show_messages_entry; +extern const struct cmd_entry cmd_show_options_entry; +extern const struct cmd_entry cmd_show_window_options_entry; +extern const struct cmd_entry cmd_source_file_entry; +extern const struct cmd_entry cmd_split_window_entry; +extern const struct cmd_entry cmd_start_server_entry; +extern const struct cmd_entry cmd_suspend_client_entry; +extern const struct cmd_entry cmd_swap_pane_entry; +extern const struct cmd_entry cmd_swap_window_entry; +extern const struct cmd_entry cmd_switch_client_entry; +extern const struct cmd_entry cmd_unbind_key_entry; +extern const struct cmd_entry cmd_unlink_window_entry; +extern const struct cmd_entry cmd_up_pane_entry; +extern const struct cmd_entry cmd_wait_for_entry; + const struct cmd_entry *cmd_table[] = { &cmd_attach_session_entry, &cmd_bind_key_entry, diff --git a/tmux.h b/tmux.h index 1bde93cd..4e880028 100644 --- a/tmux.h +++ b/tmux.h @@ -1671,92 +1671,6 @@ struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, struct winlink **); char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; -extern const struct cmd_entry cmd_attach_session_entry; -extern const struct cmd_entry cmd_bind_key_entry; -extern const struct cmd_entry cmd_break_pane_entry; -extern const struct cmd_entry cmd_capture_pane_entry; -extern const struct cmd_entry cmd_choose_buffer_entry; -extern const struct cmd_entry cmd_choose_client_entry; -extern const struct cmd_entry cmd_choose_session_entry; -extern const struct cmd_entry cmd_choose_tree_entry; -extern const struct cmd_entry cmd_choose_window_entry; -extern const struct cmd_entry cmd_clear_history_entry; -extern const struct cmd_entry cmd_clock_mode_entry; -extern const struct cmd_entry cmd_command_prompt_entry; -extern const struct cmd_entry cmd_confirm_before_entry; -extern const struct cmd_entry cmd_copy_mode_entry; -extern const struct cmd_entry cmd_delete_buffer_entry; -extern const struct cmd_entry cmd_detach_client_entry; -extern const struct cmd_entry cmd_display_message_entry; -extern const struct cmd_entry cmd_display_panes_entry; -extern const struct cmd_entry cmd_down_pane_entry; -extern const struct cmd_entry cmd_find_window_entry; -extern const struct cmd_entry cmd_has_session_entry; -extern const struct cmd_entry cmd_if_shell_entry; -extern const struct cmd_entry cmd_join_pane_entry; -extern const struct cmd_entry cmd_kill_pane_entry; -extern const struct cmd_entry cmd_kill_server_entry; -extern const struct cmd_entry cmd_kill_session_entry; -extern const struct cmd_entry cmd_kill_window_entry; -extern const struct cmd_entry cmd_last_pane_entry; -extern const struct cmd_entry cmd_last_window_entry; -extern const struct cmd_entry cmd_link_window_entry; -extern const struct cmd_entry cmd_list_buffers_entry; -extern const struct cmd_entry cmd_list_clients_entry; -extern const struct cmd_entry cmd_list_commands_entry; -extern const struct cmd_entry cmd_list_keys_entry; -extern const struct cmd_entry cmd_list_panes_entry; -extern const struct cmd_entry cmd_list_sessions_entry; -extern const struct cmd_entry cmd_list_windows_entry; -extern const struct cmd_entry cmd_load_buffer_entry; -extern const struct cmd_entry cmd_lock_client_entry; -extern const struct cmd_entry cmd_lock_server_entry; -extern const struct cmd_entry cmd_lock_session_entry; -extern const struct cmd_entry cmd_move_pane_entry; -extern const struct cmd_entry cmd_move_window_entry; -extern const struct cmd_entry cmd_new_session_entry; -extern const struct cmd_entry cmd_new_window_entry; -extern const struct cmd_entry cmd_next_layout_entry; -extern const struct cmd_entry cmd_next_window_entry; -extern const struct cmd_entry cmd_paste_buffer_entry; -extern const struct cmd_entry cmd_pipe_pane_entry; -extern const struct cmd_entry cmd_previous_layout_entry; -extern const struct cmd_entry cmd_previous_window_entry; -extern const struct cmd_entry cmd_refresh_client_entry; -extern const struct cmd_entry cmd_rename_session_entry; -extern const struct cmd_entry cmd_rename_window_entry; -extern const struct cmd_entry cmd_resize_pane_entry; -extern const struct cmd_entry cmd_respawn_pane_entry; -extern const struct cmd_entry cmd_respawn_window_entry; -extern const struct cmd_entry cmd_rotate_window_entry; -extern const struct cmd_entry cmd_run_shell_entry; -extern const struct cmd_entry cmd_save_buffer_entry; -extern const struct cmd_entry cmd_select_layout_entry; -extern const struct cmd_entry cmd_select_pane_entry; -extern const struct cmd_entry cmd_select_window_entry; -extern const struct cmd_entry cmd_send_keys_entry; -extern const struct cmd_entry cmd_send_prefix_entry; -extern const struct cmd_entry cmd_server_info_entry; -extern const struct cmd_entry cmd_set_buffer_entry; -extern const struct cmd_entry cmd_set_environment_entry; -extern const struct cmd_entry cmd_set_option_entry; -extern const struct cmd_entry cmd_set_window_option_entry; -extern const struct cmd_entry cmd_show_buffer_entry; -extern const struct cmd_entry cmd_show_environment_entry; -extern const struct cmd_entry cmd_show_messages_entry; -extern const struct cmd_entry cmd_show_options_entry; -extern const struct cmd_entry cmd_show_window_options_entry; -extern const struct cmd_entry cmd_source_file_entry; -extern const struct cmd_entry cmd_split_window_entry; -extern const struct cmd_entry cmd_start_server_entry; -extern const struct cmd_entry cmd_suspend_client_entry; -extern const struct cmd_entry cmd_swap_pane_entry; -extern const struct cmd_entry cmd_swap_window_entry; -extern const struct cmd_entry cmd_switch_client_entry; -extern const struct cmd_entry cmd_unbind_key_entry; -extern const struct cmd_entry cmd_unlink_window_entry; -extern const struct cmd_entry cmd_up_pane_entry; -extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, From 83157c02d6935d3ea4dcf6e39d6a531849a775d6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 10:01:56 +0000 Subject: [PATCH 719/949] Move initial conf load into cfg.c. --- cfg.c | 43 +++++++++++++++++++++++++++++++++++++------ server.c | 21 ++------------------- tmux.h | 3 +-- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/cfg.c b/cfg.c index 37474094..e15d048d 100644 --- a/cfg.c +++ b/cfg.c @@ -23,16 +23,47 @@ #include #include #include +#include #include #include "tmux.h" -struct cmd_q *cfg_cmd_q; -int cfg_finished; -int cfg_references; -char **cfg_causes; -u_int cfg_ncauses; -struct client *cfg_client; +struct cmd_q *cfg_cmd_q; +int cfg_finished; +int cfg_references; +char **cfg_causes; +u_int cfg_ncauses; +struct client *cfg_client; + +void cfg_default_done(struct cmd_q *); + +void +start_cfg(void) +{ + char *cause = NULL; + + cfg_cmd_q = cmdq_new(NULL); + cfg_cmd_q->emptyfn = cfg_default_done; + + cfg_finished = 0; + cfg_references = 1; + + cfg_client = TAILQ_FIRST(&clients); + if (cfg_client != NULL) + cfg_client->references++; + + if (access(TMUX_CONF, R_OK) == 0) { + if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) + cfg_add_cause("%s: %s", TMUX_CONF, cause); + } else if (errno != ENOENT) + cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); + + if (cfg_file != NULL && load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) + cfg_add_cause("%s: %s", cfg_file, cause); + free(cause); + + cmdq_continue(cfg_cmd_q); +} int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) diff --git a/server.c b/server.c index 8308367d..045daead 100644 --- a/server.c +++ b/server.c @@ -160,8 +160,7 @@ server_create_socket(void) int server_start(struct event_base *base, int lockfd, char *lockfile) { - int pair[2]; - char *cause; + int pair[2]; /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) @@ -217,24 +216,8 @@ server_start(struct event_base *base, int lockfd, char *lockfile) free(lockfile); close(lockfd); - cfg_cmd_q = cmdq_new(NULL); - cfg_cmd_q->emptyfn = cfg_default_done; - cfg_finished = 0; - cfg_references = 1; - cfg_client = TAILQ_FIRST(&clients); - if (cfg_client != NULL) - cfg_client->references++; + start_cfg(); - if (access(TMUX_CONF, R_OK) == 0) { - if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) - cfg_add_cause("%s: %s", TMUX_CONF, cause); - } else if (errno != ENOENT) - cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); - if (cfg_file != NULL) { - if (load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) - cfg_add_cause("%s: %s", cfg_file, cause); - } - cmdq_continue(cfg_cmd_q); status_prompt_load_history(); server_add_accept(0); diff --git a/tmux.h b/tmux.h index 4e880028..ced3cccf 100644 --- a/tmux.h +++ b/tmux.h @@ -1420,12 +1420,11 @@ void setblocking(int, int); const char *find_home(void); /* cfg.c */ -extern struct cmd_q *cfg_cmd_q; extern int cfg_finished; extern int cfg_references; extern struct client *cfg_client; +void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); -void cfg_default_done(struct cmd_q *); void cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); From 952ba846111fb91d9882846a4061955262f34212 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 10:10:59 +0000 Subject: [PATCH 720/949] Work out config file when needed not at startup. --- cfg.c | 18 +++++++++++++++++- tmux.c | 28 ++++++++-------------------- tmux.h | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/cfg.c b/cfg.c index e15d048d..ecb38fc0 100644 --- a/cfg.c +++ b/cfg.c @@ -28,6 +28,7 @@ #include "tmux.h" +char *cfg_file; struct cmd_q *cfg_cmd_q; int cfg_finished; int cfg_references; @@ -37,10 +38,18 @@ struct client *cfg_client; void cfg_default_done(struct cmd_q *); +void +set_cfg_file(const char *path) +{ + free(cfg_file); + cfg_file = xstrdup(path); +} + void start_cfg(void) { - char *cause = NULL; + char *cause = NULL; + const char *home; cfg_cmd_q = cmdq_new(NULL); cfg_cmd_q->emptyfn = cfg_default_done; @@ -58,6 +67,13 @@ start_cfg(void) } else if (errno != ENOENT) cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); + if (cfg_file == NULL && (home = find_home()) != NULL) { + xasprintf(&cfg_file, "%s/.tmux.conf", home); + if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { + free(cfg_file); + cfg_file = NULL; + } + } if (cfg_file != NULL && load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) cfg_add_cause("%s: %s", cfg_file, cause); free(cause); diff --git a/tmux.c b/tmux.c index 6eb2a301..69097bf9 100644 --- a/tmux.c +++ b/tmux.c @@ -41,7 +41,6 @@ struct options global_s_options; /* session options */ struct options global_w_options; /* window options */ struct environ global_environ; -char *cfg_file; char *shell_cmd; int debug_level; time_t start_time; @@ -171,8 +170,11 @@ setblocking(int fd, int state) const char * find_home(void) { - struct passwd *pw; - const char *home; + struct passwd *pw; + static const char *home; + + if (home != NULL) + return (home); home = getenv("HOME"); if (home == NULL || *home == '\0') { @@ -189,9 +191,8 @@ find_home(void) int main(int argc, char **argv) { - char *s, *path, *label, **var, tmp[PATH_MAX]; - const char *home; - int opt, flags, keys; + char *s, *path, *label, **var, tmp[PATH_MAX]; + int opt, flags, keys; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -221,8 +222,7 @@ main(int argc, char **argv) flags |= CLIENT_CONTROL; break; case 'f': - free(cfg_file); - cfg_file = xstrdup(optarg); + set_cfg_file(optarg); break; case 'l': flags |= CLIENT_LOGIN; @@ -306,18 +306,6 @@ main(int argc, char **argv) options_set_number(&global_w_options, "mode-keys", keys); } - /* Locate the configuration file. */ - if (cfg_file == NULL) { - home = find_home(); - if (home != NULL) { - xasprintf(&cfg_file, "%s/.tmux.conf", home); - if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { - free(cfg_file); - cfg_file = NULL; - } - } - } - /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. diff --git a/tmux.h b/tmux.h index ced3cccf..c2309f8a 100644 --- a/tmux.h +++ b/tmux.h @@ -1407,7 +1407,6 @@ extern struct options global_options; extern struct options global_s_options; extern struct options global_w_options; extern struct environ global_environ; -extern char *cfg_file; extern char *shell_cmd; extern int debug_level; extern time_t start_time; @@ -1425,6 +1424,7 @@ extern int cfg_references; extern struct client *cfg_client; void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); +void set_cfg_file(const char *); void cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); From 69a2d46ee55b5fc62862dde7cf74bc02556e7559 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 11:13:39 +0000 Subject: [PATCH 721/949] Remove dead_clients which is no longer used. --- tmux.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.h b/tmux.h index c2309f8a..a71d6c2a 100644 --- a/tmux.h +++ b/tmux.h @@ -1728,7 +1728,6 @@ void alerts_queue(struct window *, int); /* server.c */ extern struct clients clients; -extern struct clients dead_clients; extern struct session *marked_session; extern struct winlink *marked_winlink; extern struct window_pane *marked_window_pane; From fa3d4fab85e4c1f807808640f75f50a41bbb2c5b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 18:50:16 +0000 Subject: [PATCH 722/949] Fix a spelling error, sesson -> session. --- session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.c b/session.c index 56986497..a929cada 100644 --- a/session.c +++ b/session.c @@ -187,7 +187,7 @@ session_free(unused int fd, unused short events, void *arg) { struct session *s = arg; - log_debug("sesson %s freed (%d references)", s->name, s->references); + log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { free(s->name); From 364a885b0c8b3bc58396775603c7927e1fb19cee Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 19:14:43 +0000 Subject: [PATCH 723/949] Pass logging through vis(3). --- log.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/log.c b/log.c index fa26eb4c..ecb9698a 100644 --- a/log.c +++ b/log.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "tmux.h" @@ -66,19 +67,24 @@ log_close(void) void log_vwrite(const char *msg, va_list ap) { - char *fmt; + char *fmt, *out; struct timeval tv; if (log_file == NULL) return; - gettimeofday(&tv, NULL); - if (asprintf(&fmt, "%lld.%06d %s\n", (long long)tv.tv_sec, - (int)tv.tv_usec, msg) == -1) + if (vasprintf(&fmt, msg, ap) == -1) exit(1); - if (vfprintf(log_file, fmt, ap) == -1) + if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) + exit(1); + + gettimeofday(&tv, NULL); + if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, + (int)tv.tv_usec, out) == -1) exit(1); fflush(log_file); + + free(out); free(fmt); } From 66a2720c56328c186784345664ba283a4ca0748c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 19:16:00 +0000 Subject: [PATCH 724/949] Log the whole new input buffer once rather than each byte. --- input.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index cfe6b4af..25cfe971 100644 --- a/input.c +++ b/input.c @@ -862,10 +862,12 @@ input_parse(struct window_pane *wp) notify_input(wp, evb); off = 0; + log_debug("%s: %s, %zu bytes: %.*s", __func__, ictx->state->name, len, + (int)len, buf); + /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; - log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); /* Find the transition. */ itr = ictx->state->transitions; @@ -1070,7 +1072,7 @@ input_c0_dispatch(struct input_ctx *ictx) struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; - log_debug("%s: '%c", __func__, ictx->ch); + log_debug("%s: '%c'", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ From 93b946ee5076495ff815ad1a1d874341cbc70499 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 19:50:09 +0000 Subject: [PATCH 725/949] Tweak some error messages/comments. --- input.c | 2 +- server-client.c | 2 +- tty-term.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index 25cfe971..1b0c1752 100644 --- a/input.c +++ b/input.c @@ -878,7 +878,7 @@ input_parse(struct window_pane *wp) } if (itr->first == -1 || itr->last == -1) { /* No transition? Eh? */ - fatalx("No transition from state!"); + fatalx("no transition from state"); } /* diff --git a/server-client.c b/server-client.c index 08058ab4..70f5adcb 100644 --- a/server-client.c +++ b/server-client.c @@ -1060,7 +1060,7 @@ server_client_msg_dispatch(struct client *c) s = c->session; if (gettimeofday(&c->activity_time, NULL) != 0) - fatal("gettimeofday"); + fatal("gettimeofday failed"); if (s != NULL) session_update_activity(s, &c->activity_time); diff --git a/tty-term.c b/tty-term.c index 14339de1..f4fd91a8 100644 --- a/tty-term.c +++ b/tty-term.c @@ -560,7 +560,6 @@ tty_term_string(struct tty_term *term, enum tty_code_code code) return (term->codes[code].value.string); } -/* No vtparm. Fucking curses. */ const char * tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) { From 2ebef95994953e49ae84862a65f9ee649a72e200 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 1 Sep 2015 21:08:19 +0100 Subject: [PATCH 726/949] Sync up vis.* for stravis(). --- compat/vis.c | 56 ++++++++++++++++++++++++++++++++++++---------------- compat/vis.h | 10 +++++++++- configure.ac | 14 ++++++------- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/compat/vis.c b/compat/vis.c index 97eb5271..82b42be9 100644 --- a/compat/vis.c +++ b/compat/vis.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 millert Exp $ */ +/* $OpenBSD: vis.c,v 1.24 2015/07/20 01:52:28 millert Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -29,14 +29,17 @@ */ #include -#include +#include #include +#include #include +#include #include "tmux.h" #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') -#define isvisible(c) \ +#define isvisible(c,flag) \ + (((c) == '\\' || (flag & VIS_ALL) == 0) && \ (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ @@ -45,7 +48,7 @@ ((flag & VIS_NL) == 0 && (c) == '\n') || \ ((flag & VIS_SAFE) && ((c) == '\b' || \ (c) == '\007' || (c) == '\r' || \ - isgraph((u_char)(c))))) + isgraph((u_char)(c)))))) /* * vis - visually encode characters @@ -53,10 +56,11 @@ char * vis(char *dst, int c, int flag, int nextc) { - if (isvisible(c)) { - *dst++ = c; - if (c == '\\' && (flag & VIS_NOSLASH) == 0) + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) *dst++ = '\\'; + *dst++ = c; *dst = '\0'; return (dst); } @@ -136,10 +140,10 @@ done: /* * strvis, strnvis, strvisx - visually encode characters from src into dst - * + * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, - * is returned. + * is returned. * * Strnvis will write no more than siz-1 bytes (and will NULL terminate). * The number of bytes needed to fully encode the string is returned. @@ -168,19 +172,18 @@ strnvis(char *dst, const char *src, size_t siz, int flag) i = 0; for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { - if (isvisible(c)) { - i = 1; - *dst++ = c; - if (c == '\\' && (flag & VIS_NOSLASH) == 0) { + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) { /* need space for the extra '\\' */ - if (dst < end) - *dst++ = '\\'; - else { - dst--; + if (dst + 1 >= end) { i = 2; break; } + *dst++ = '\\'; } + i = 1; + *dst++ = c; src++; } else { i = vis(tbuf, c, flag, *++src) - tbuf; @@ -203,6 +206,25 @@ strnvis(char *dst, const char *src, size_t siz, int flag) return (dst - start); } +int +stravis(char **outp, const char *src, int flag) +{ + char *buf; + int len, serrno; + + buf = calloc(4, strlen(src) + 1); + if (buf == NULL) + return -1; + len = strvis(buf, src, flag); + serrno = errno; + *outp = realloc(buf, len + 1); + if (*outp == NULL) { + *outp = buf; + errno = serrno; + } + return (len); +} + int strvisx(char *dst, const char *src, size_t len, int flag) { diff --git a/compat/vis.h b/compat/vis.h index d6ff235d..6795139c 100644 --- a/compat/vis.h +++ b/compat/vis.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 millert Exp $ */ +/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ /*- @@ -50,6 +50,8 @@ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ +#define VIS_DQ 0x200 /* backslash-escape double quotes */ +#define VIS_ALL 0x400 /* encode all characters */ /* * other @@ -71,12 +73,18 @@ */ #define UNVIS_END 1 /* no more characters */ +#include + +__BEGIN_DECLS char *vis(char *, int, int, int); int strvis(char *, const char *, int); +int stravis(char **, const char *, int); int strnvis(char *, const char *, size_t, int); int strvisx(char *, const char *, size_t, int); int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t); +__END_DECLS + #endif /* !_VIS_H_ */ diff --git a/configure.ac b/configure.ac index de336fef..9d64a964 100644 --- a/configure.ac +++ b/configure.ac @@ -326,22 +326,22 @@ if test "x$found_strtonum" = xyes; then fi AM_CONDITIONAL(NO_STRTONUM, [test "x$found_strtonum" = xno]) -# Look for strnvis, compat/{vis,unvis}.c used if missing. -AC_CHECK_FUNC(strnvis, found_strnvis=yes, found_strnvis=no) -if test "x$found_strnvis" = xyes; then +# Look for stravis, compat/{vis,unvis}.c used if missing. +AC_CHECK_FUNC(stravis, found_stravis=yes, found_stravis=no) +if test "x$found_stravis" = xyes; then AC_MSG_CHECKING(if strnvis is broken) AC_EGREP_HEADER([strnvis\(char \*, const char \*, size_t, int\)], vis.h, AC_MSG_RESULT(no), - [found_strnvis=no]) - if test "x$found_strnvis" = xno; then + [found_stravis=no]) + if test "x$found_stravis" = xno; then AC_MSG_RESULT(yes) fi fi -if test "x$found_strnvis" = xyes; then +if test "x$found_stravis" = xyes; then AC_DEFINE(HAVE_VIS) fi -AM_CONDITIONAL(NO_VIS, [test "x$found_strnvis" = xno]) +AM_CONDITIONAL(NO_VIS, [test "x$found_stravis" = xno]) # Look for cfmakeraw, compat/cfmakeraw.c used if missing. AC_CHECK_FUNC(cfmakeraw, found_cfmakeraw=yes, found_cfmakeraw=no) From a45164f2e023f436d0c9b0f50580a71f2b4b8f87 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:12:07 +0000 Subject: [PATCH 727/949] Fix indentation of grid_string_cells_fg. --- grid.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/grid.c b/grid.c index c339cdc5..93fed05e 100644 --- a/grid.c +++ b/grid.c @@ -395,29 +395,29 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values) values[n++] = gc->fg; } else { switch (gc->fg) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - values[n++] = gc->fg + 30; - break; - case 8: - values[n++] = 39; - break; - case 90: - case 91: - case 92: - case 93: - case 94: - case 95: - case 96: - case 97: - values[n++] = gc->fg; - break; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + values[n++] = gc->fg + 30; + break; + case 8: + values[n++] = 39; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + values[n++] = gc->fg; + break; } } return (n); From 8121127606e8c354daa4802177763d2b1f8df81d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:37:54 +0000 Subject: [PATCH 728/949] We no longer need the terminal service class, so don't bother asking for it. --- tmux.h | 3 --- tty-keys.c | 69 ------------------------------------------------------ tty.c | 9 ------- 3 files changed, 81 deletions(-) diff --git a/tmux.h b/tmux.h index a71d6c2a..9e028194 100644 --- a/tmux.h +++ b/tmux.h @@ -1082,9 +1082,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; - char *path; - u_int class; u_int sx; u_int sy; @@ -1564,7 +1562,6 @@ void tty_putn(struct tty *, const void *, size_t, u_int); int tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); -void tty_set_class(struct tty *, u_int); void tty_start_tty(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); diff --git a/tty-keys.c b/tty-keys.c index 75e06526..c1de2ab7 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -41,7 +41,6 @@ struct tty_key *tty_keys_find1( struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); void tty_keys_callback(int, short, void *); int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); -int tty_keys_device(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { @@ -482,17 +481,6 @@ tty_keys_next(struct tty *tty) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); - /* Is this device attributes response? */ - switch (tty_keys_device(tty, buf, len, &size)) { - case 0: /* yes */ - key = KEYC_NONE; - 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)) { case 0: /* yes */ @@ -794,60 +782,3 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (0); } - -/* - * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for - * partial. - */ -int -tty_keys_device(struct tty *tty, const char *buf, size_t len, size_t *size) -{ - u_int i, class; - char tmp[64], *endptr; - - /* - * Primary device attributes are \033[?a;b and secondary are - * \033[>a;b;c. - */ - - *size = 0; - - /* 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] != '>' && buf[2] != '?') - return (-1); - if (len == 3) - return (1); - - /* Copy the rest up to a 'c'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { - if (3 + i == len) - return (1); - tmp[i] = buf[3 + i]; - } - if (i == (sizeof tmp) - 1) - return (-1); - tmp[i] = '\0'; - *size = 4 + i; - - /* Only primary is of interest. */ - if (buf[2] != '?') - return (0); - - /* Convert service class. */ - class = strtoul(tmp, &endptr, 10); - if (*endptr != ';') - class = 0; - - log_debug("received service class %u", class); - tty_set_class(tty, class); - - return (0); -} diff --git a/tty.c b/tty.c index 374fb8c6..105a24ed 100644 --- a/tty.c +++ b/tty.c @@ -233,7 +233,6 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - tty_puts(tty, "\033[c"); } tty->cx = UINT_MAX; @@ -253,14 +252,6 @@ tty_start_tty(struct tty *tty) tty->mouse_drag_release = NULL; } -void -tty_set_class(struct tty *tty, u_int class) -{ - if (tty->class != 0) - return; - tty->class = class; -} - void tty_stop_tty(struct tty *tty) { From 38e3baab2a8f46d910ea5104b48d4c71480d4814 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:43:25 +0000 Subject: [PATCH 729/949] A one line helper function is a little silly. --- alerts.c | 4 ++-- tmux.h | 1 - tty.c | 6 ------ 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/alerts.c b/alerts.c index cfdd30e3..5d52f7ad 100644 --- a/alerts.c +++ b/alerts.c @@ -174,7 +174,7 @@ alerts_check_bell(struct session *s, struct winlink *wl) (action == BELL_OTHER && c->session->curw->window != w) || action == BELL_ANY) - tty_bell(&c->tty); + tty_putcode(&c->tty, TTYC_BEL); continue; } if (action == BELL_CURRENT && c->session->curw->window == w) @@ -258,6 +258,6 @@ alerts_ring_bell(struct session *s) TAILQ_FOREACH(c, &clients, entry) { if (c->session == s && !(c->flags & CLIENT_CONTROL)) - tty_bell(&c->tty); + tty_putcode(&c->tty, TTYC_BEL); } } diff --git a/tmux.h b/tmux.h index 9e028194..0223d857 100644 --- a/tmux.h +++ b/tmux.h @@ -1596,7 +1596,6 @@ void tty_cmd_utf8character(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); -void tty_bell(struct tty *); /* tty-term.c */ extern struct tty_terms tty_terms; diff --git a/tty.c b/tty.c index 105a24ed..c4dfde1d 100644 --- a/tty.c +++ b/tty.c @@ -1712,9 +1712,3 @@ tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) } } } - -void -tty_bell(struct tty *tty) -{ - tty_putcode(tty, TTYC_BEL); -} From 6c10fc659a096d2d3514e23b1300c4bf51a122a4 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:52:57 +0000 Subject: [PATCH 730/949] Log pane which received input data. --- input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index 1b0c1752..ab56fc38 100644 --- a/input.c +++ b/input.c @@ -862,8 +862,8 @@ input_parse(struct window_pane *wp) notify_input(wp, evb); off = 0; - log_debug("%s: %s, %zu bytes: %.*s", __func__, ictx->state->name, len, - (int)len, buf); + log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, + ictx->state->name, len, (int)len, buf); /* Parse the input. */ while (off < len) { From 82326dcbe2e833476c2b8961d6b8b5c2cee69c0e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Sep 2015 14:30:23 +0000 Subject: [PATCH 731/949] A couple of style nits. --- format.c | 3 +-- tmux.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index ae22a844..20cf0adb 100644 --- a/format.c +++ b/format.c @@ -218,8 +218,7 @@ format_job_get(struct format_tree *ft, const char *cmd) struct format_job fj0, *fj; fj0.cmd = cmd; - if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) - { + if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) { fj = xcalloc(1, sizeof *fj); fj->cmd = xstrdup(cmd); fj->status = ft->status; diff --git a/tmux.c b/tmux.c index 69097bf9..875e8d72 100644 --- a/tmux.c +++ b/tmux.c @@ -321,8 +321,7 @@ main(int argc, char **argv) free(path); label = xstrdup("default"); } - } - else + } else label = xstrdup("default"); } From aceae73b9a401c0b124a3534079e9c0d998f0dbd Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 4 Sep 2015 12:02:44 +0000 Subject: [PATCH 732/949] Change wait-for to work when the signal comes before the wait, also use some helper functions and add some logging. --- cmd-wait-for.c | 105 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 35 deletions(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index a3e85856..0dce438b 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -41,6 +41,7 @@ const struct cmd_entry cmd_wait_for_entry = { struct wait_channel { const char *name; int locked; + int woken; TAILQ_HEAD(, cmd_q) waiters; TAILQ_HEAD(, cmd_q) lockers; @@ -69,8 +70,49 @@ enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, struct wait_channel *); +struct wait_channel *cmd_wait_for_add(const char *); +void cmd_wait_for_remove(struct wait_channel *wc); + +struct wait_channel * +cmd_wait_for_add(const char *name) +{ + struct wait_channel *wc; + + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + + wc->locked = 0; + wc->woken = 0; + + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + + RB_INSERT(wait_channels, &wait_channels, wc); + + log_debug("add wait channel %s", wc->name); + + return (wc); +} + +void +cmd_wait_for_remove(struct wait_channel *wc) +{ + + if (wc->locked) + return; + if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) + return; + + log_debug("remove wait channel %s", wc->name); + + RB_REMOVE(wait_channels, &wait_channels, wc); + + free((void *)wc->name); + free(wc); +} + enum cmd_retval -cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) { struct args *args = self->args; const char *name = args->argv[0]; @@ -89,15 +131,20 @@ cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, +cmd_wait_for_signal(unused struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq, *wq1; - if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { - cmdq_error(cmdq, "no waiting clients on %s", name); - return (CMD_RETURN_ERROR); + if (wc == NULL) + wc = cmd_wait_for_add(name); + + if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { + log_debug("signal wait channel %s, no waiters", wc->name); + wc->woken = 1; + return (CMD_RETURN_NORMAL); } + log_debug("signal wait channel %s, with waiters", wc->name); TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { TAILQ_REMOVE(&wc->waiters, wq, waitentry); @@ -105,12 +152,7 @@ cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, cmdq_continue(wq); } - if (!wc->locked) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void *)wc->name); - free(wc); - } - + cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } @@ -118,19 +160,23 @@ enum cmd_retval cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { - if (cmdq->client == NULL || cmdq->client->session != NULL) { + struct client *c = cmdq->client; + + if (c == NULL || c->session != NULL) { cmdq_error(cmdq, "not able to wait"); return (CMD_RETURN_ERROR); } - if (wc == NULL) { - wc = xmalloc(sizeof *wc); - wc->name = xstrdup(name); - wc->locked = 0; - TAILQ_INIT(&wc->waiters); - TAILQ_INIT(&wc->lockers); - RB_INSERT(wait_channels, &wait_channels, wc); + if (wc == NULL) + wc = cmd_wait_for_add(name); + + if (wc->woken) { + log_debug("wait channel %s already woken (client %d)", wc->name, + c->tty.fd); + cmd_wait_for_remove(wc); + return (CMD_RETURN_NORMAL); } + log_debug("wait channel %s not woken (client %d)", wc->name, c->tty.fd); TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; @@ -147,14 +193,8 @@ cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, return (CMD_RETURN_ERROR); } - if (wc == NULL) { - wc = xmalloc(sizeof *wc); - wc->name = xstrdup(name); - wc->locked = 0; - TAILQ_INIT(&wc->waiters); - TAILQ_INIT(&wc->lockers); - RB_INSERT(wait_channels, &wait_channels, wc); - } + if (wc == NULL) + wc = cmd_wait_for_add(name); if (wc->locked) { TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); @@ -183,11 +223,7 @@ cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, cmdq_continue(wq); } else { wc->locked = 0; - if (TAILQ_EMPTY(&wc->waiters)) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void *)wc->name); - free(wc); - } + cmd_wait_for_remove(wc); } return (CMD_RETURN_NORMAL); @@ -210,8 +246,7 @@ cmd_wait_for_flush(void) if (!cmdq_free(wq)) cmdq_continue(wq); } - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void *)wc->name); - free(wc); + wc->locked = 0; + cmd_wait_for_remove(wc); } } From fe536457cca384fdaa5fcf80236404cfaafc38f9 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 6 Sep 2015 21:29:36 +0100 Subject: [PATCH 733/949] Fix includes Let compat/ work out the includes; otherwise works on OpenBSD. --- cfg.c | 1 - log.c | 1 - 2 files changed, 2 deletions(-) diff --git a/cfg.c b/cfg.c index ecb38fc0..9548f582 100644 --- a/cfg.c +++ b/cfg.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "tmux.h" diff --git a/log.c b/log.c index 2acc6f0e..b0b22460 100644 --- a/log.c +++ b/log.c @@ -23,7 +23,6 @@ #include #include #include -#include #include "tmux.h" From 67ee995cc1ec0a9ae2247dae6bdde54211be8c5a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 9 Sep 2015 12:09:21 +0000 Subject: [PATCH 734/949] No need to keep global options around for client which doesn't use them. --- cfg.c | 2 +- client.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cfg.c b/cfg.c index ecb38fc0..2f457e6b 100644 --- a/cfg.c +++ b/cfg.c @@ -98,7 +98,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) } found = 0; - while ((buf = fparseln(f, NULL, &line, delim, 0))) { + while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) { log_debug("%s: %s", path, buf); /* Skip empty lines. */ diff --git a/client.c b/client.c index 55c5aa56..d36be86b 100644 --- a/client.c +++ b/client.c @@ -268,6 +268,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } + options_free(&global_options); + options_free(&global_s_options); + options_free(&global_w_options); + environ_free(&global_environ); /* Create imsg. */ imsg_init(&client_ibuf, fd); From cfabe30becba6f0c54035a29ee61a6a7f3d0cf60 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Sep 2015 08:58:14 +0000 Subject: [PATCH 735/949] Add session_last_attached time and format, from Sina Siadat. --- cmd-attach-session.c | 2 ++ cmd-new-session.c | 1 + cmd-switch-client.c | 1 + format.c | 7 +++++++ server-fn.c | 1 + tmux.1 | 2 ++ tmux.h | 1 + 7 files changed, 15 insertions(+) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 356bd4aa..a2ae49cb 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -136,6 +136,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { @@ -181,6 +182,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; diff --git a/cmd-new-session.c b/cmd-new-session.c index fa4f1553..7687398e 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -278,6 +278,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); } recalculate_sizes(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 8bc8a3c5..10171018 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -129,6 +129,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) c->session = s; status_timer_start(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); recalculate_sizes(); server_check_unattached(); diff --git a/format.c b/format.c index 20cf0adb..c401fa35 100644 --- a/format.c +++ b/format.c @@ -872,6 +872,13 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_created", "%lld", (long long) t); format_add(ft, "session_created_string", "%s", format_time_string(t)); + t = s->last_attached_time.tv_sec; + if (t != 0) { /* zero if never attached */ + format_add(ft, "session_last_attached", "%lld", (long long) t); + format_add(ft, "session_last_attached_string", "%s", + format_time_string(t)); + } + t = s->activity_time.tv_sec; format_add(ft, "session_activity", "%lld", (long long) t); format_add(ft, "session_activity_string", "%s", format_time_string(t)); diff --git a/server-fn.c b/server-fn.c index cddd762f..27908fde 100644 --- a/server-fn.c +++ b/server-fn.c @@ -422,6 +422,7 @@ server_destroy_session(struct session *s) status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new, NULL); + gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); } } diff --git a/tmux.1 b/tmux.1 index 8e07fc2a..a2f6cb96 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3401,6 +3401,8 @@ The following variables are available, where appropriate: .It Li "session_activity_string" Ta "" Ta "String time of session last activity" .It Li "session_created" Ta "" Ta "Integer time session created" .It Li "session_created_string" Ta "" Ta "String time session created" +.It Li "session_last_attached" Ta "" Ta "Integer time session last attached" +.It Li "session_last_attached_string" Ta "" Ta "String time session last attached" .It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_height" Ta "" Ta "Height of session" diff --git a/tmux.h b/tmux.h index 0223d857..8cd83e27 100644 --- a/tmux.h +++ b/tmux.h @@ -981,6 +981,7 @@ struct session { int cwd; struct timeval creation_time; + struct timeval last_attached_time; struct timeval activity_time; struct timeval last_activity_time; From 79e5b6290733567266a584d817fed48e1f2a52c0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Sep 2015 12:41:49 +0100 Subject: [PATCH 736/949] osdep_event_init not event_init. --- tmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.c b/tmux.c index 86ed6f7e..31ff24a0 100644 --- a/tmux.c +++ b/tmux.c @@ -354,5 +354,5 @@ main(int argc, char **argv) #endif /* Pass control to the client. */ - exit(client_main(event_init(), argc, argv, flags)); + exit(client_main(osdep_event_init(), argc, argv, flags)); } From 66c4ed98d62010421a47bcdb7a4a22a7cbcffff9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Sep 2015 14:59:16 +0100 Subject: [PATCH 737/949] Fix bad merge. --- tmux.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/tmux.h b/tmux.h index 1c643534..61027b98 100644 --- a/tmux.h +++ b/tmux.h @@ -39,9 +39,6 @@ extern char *__progname; extern char **environ; -extern char *__progname; -extern char **environ; - /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" From ef35c9f7659205659d6863058b9a7262b21440a5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 11 Sep 2015 13:16:35 +0100 Subject: [PATCH 738/949] Add --enable-coverage for gcov. --- Makefile.am | 4 ++++ configure.ac | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index e9a6bd33..982a88d8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,10 @@ CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign CPPFLAGS += -DDEBUG endif +if IS_COVERAGE +CFLAGS += -g -O0 --coverage +LDFLAGS += --coverage +endif CPPFLAGS += -iquote. endif diff --git a/configure.ac b/configure.ac index 9d64a964..f3fabd94 100644 --- a/configure.ac +++ b/configure.ac @@ -23,15 +23,23 @@ AC_PROG_INSTALL # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc -# Is this a debug build? +# Is this --enable-debug? found_debug=yes AC_ARG_ENABLE( debug, - AC_HELP_STRING(--enable-debug, create a debug build), + AC_HELP_STRING(--enable-debug, enable debug build flags), found_debug=$enable_debug ) AM_CONDITIONAL(IS_DEBUG, test "x$found_debug" = xyes) +# Is this --enable-coverage? +AC_ARG_ENABLE( + coverage, + AC_HELP_STRING(--enable-coverage, enable coverage build flags), + found_coverage=$enable_coverage +) +AM_CONDITIONAL(IS_COVERAGE, test "x$found_coverage" = xyes) + # Is this a static build? AC_ARG_ENABLE( static, From a3de5dbab1680528d622a5054075e3d8efced795 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Sep 2015 14:41:50 +0000 Subject: [PATCH 739/949] Merge delete-buffer into cmd-set-buffer.c and change the paste buffer API so it has one paste_free() rather than free_top and free_name (everywhere that uses it already has the right pointer). --- Makefile | 1 - cmd-delete-buffer.c | 57 --------------------------------------------- cmd-paste-buffer.c | 8 ++----- cmd-set-buffer.c | 52 ++++++++++++++++++++--------------------- paste.c | 37 ++++++----------------------- tmux.h | 3 +-- 6 files changed, 36 insertions(+), 122 deletions(-) delete mode 100644 cmd-delete-buffer.c diff --git a/Makefile b/Makefile index ad893d4b..653c8d85 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,6 @@ SRCS= alerts.c \ cmd-command-prompt.c \ cmd-confirm-before.c \ cmd-copy-mode.c \ - cmd-delete-buffer.c \ cmd-detach-client.c \ cmd-display-message.c \ cmd-display-panes.c \ diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c deleted file mode 100644 index 42268b78..00000000 --- a/cmd-delete-buffer.c +++ /dev/null @@ -1,57 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include - -#include "tmux.h" - -/* - * Delete a paste buffer. - */ - -enum cmd_retval cmd_delete_buffer_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_delete_buffer_entry = { - "delete-buffer", "deleteb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - cmd_delete_buffer_exec -}; - -enum cmd_retval -cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - const char *bufname; - - if (!args_has(args, 'b')) { - paste_free_top(); - return (CMD_RETURN_NORMAL); - } - bufname = args_get(args, 'b'); - - if (paste_free_name(bufname) != 0) { - cmdq_error(cmdq, "no buffer %s", bufname); - return (CMD_RETURN_ERROR); - } - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index cd3fc7d8..87f09ee6 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -102,12 +102,8 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufferevent_write(wp->event, "\033[201~", 6); } - if (args_has(args, 'd')) { - if (bufname == NULL) - paste_free_top(); - else - paste_free_name(bufname); - } + if (pb != NULL && args_has(args, 'd')) + paste_free(pb); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 4fa7ca11..01afa774 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -24,7 +24,7 @@ #include "tmux.h" /* - * Add, set, or append to a paste buffer. + * Add, set, append to or delete a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); @@ -37,6 +37,14 @@ const struct cmd_entry cmd_set_buffer_entry = { cmd_set_buffer_exec }; +const struct cmd_entry cmd_delete_buffer_entry = { + "delete-buffer", "deleteb", + "b:", 0, 0, + CMD_BUFFER_USAGE, + 0, + cmd_set_buffer_exec +}; + enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -46,31 +54,31 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) const char *bufname, *olddata; size_t bufsize, newsize; - bufname = NULL; + bufname = args_get(args, 'b'); + if (bufname == NULL) + pb = paste_get_top(&bufname); + else + pb = paste_get_name(bufname); - if (args_has(args, 'n')) { - if (args->argc > 0) { - cmdq_error(cmdq, "don't provide data with n flag"); + if (self->entry == &cmd_delete_buffer_entry) { + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); } + paste_free(pb); + return (CMD_RETURN_NORMAL); + } - if (args_has(args, 'b')) - bufname = args_get(args, 'b'); - - if (bufname == NULL) { - pb = paste_get_top(&bufname); - if (pb == NULL) { - cmdq_error(cmdq, "no buffer"); - return (CMD_RETURN_ERROR); - } + if (args_has(args, 'n')) { + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); + return (CMD_RETURN_ERROR); } - if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { cmdq_error(cmdq, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } - return (CMD_RETURN_NORMAL); } @@ -78,19 +86,11 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "no data specified"); return (CMD_RETURN_ERROR); } - pb = NULL; - - bufsize = 0; - bufdata = NULL; - if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); - if (args_has(args, 'b')) { - bufname = args_get(args, 'b'); - pb = paste_get_name(bufname); - } else if (args_has(args, 'a')) - pb = paste_get_top(&bufname); + bufsize = 0; + bufdata = NULL; if (args_has(args, 'a') && pb != NULL) { olddata = paste_buffer_data(pb, &bufsize); diff --git a/paste.c b/paste.c index e7f752ce..fa58f7c1 100644 --- a/paste.c +++ b/paste.c @@ -111,18 +111,6 @@ paste_get_top(const char **name) return (pb); } -/* Free the most recent buffer. */ -int -paste_free_top(void) -{ - struct paste_buffer *pb; - - pb = paste_get_top(NULL); - if (pb == NULL) - return (-1); - return (paste_free_name(pb->name)); -} - /* Get a paste buffer by name. */ struct paste_buffer * paste_get_name(const char *name) @@ -136,20 +124,10 @@ paste_get_name(const char *name) return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); } -/* Free a paste buffer by name. */ -int -paste_free_name(const char *name) +/* Free a paste buffer. */ +void +paste_free(struct paste_buffer *pb) { - struct paste_buffer *pb, pbfind; - - if (name == NULL || *name == '\0') - return (-1); - - pbfind.name = (char *)name; - pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); - if (pb == NULL) - return (-1); - RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb); if (pb->automatic) @@ -158,7 +136,6 @@ paste_free_name(const char *name) free(pb->data); free(pb->name); free(pb); - return (0); } /* @@ -179,7 +156,7 @@ paste_add(char *data, size_t size) if (paste_num_automatic < limit) break; if (pb->automatic) - paste_free_name(pb->name); + paste_free(pb); } pb = xmalloc(sizeof *pb); @@ -257,7 +234,7 @@ paste_rename(const char *oldname, const char *newname, char **cause) int paste_set(char *data, size_t size, const char *name, char **cause) { - struct paste_buffer *pb; + struct paste_buffer *pb, *old; if (cause != NULL) *cause = NULL; @@ -288,8 +265,8 @@ paste_set(char *data, size_t size, const char *name, char **cause) pb->automatic = 0; pb->order = paste_next_order++; - if (paste_get_name(name) != NULL) - paste_free_name(name); + if ((old = paste_get_name(name)) != NULL) + paste_free(old); RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); diff --git a/tmux.h b/tmux.h index 8cd83e27..a8cd8532 100644 --- a/tmux.h +++ b/tmux.h @@ -1435,8 +1435,7 @@ const char *paste_buffer_data(struct paste_buffer *, size_t *); struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_name(const char *); -int paste_free_top(void); -int paste_free_name(const char *); +void paste_free(struct paste_buffer *); void paste_add(char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); From ede0f2f633c2a0c2718004f0527bd5832041e966 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Sep 2015 10:45:55 +0000 Subject: [PATCH 740/949] Set woken flag when flushing so that the channel is freed, while here use the same loop construct for both loops. --- cmd-wait-for.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 0dce438b..e38ea8f1 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -97,7 +97,6 @@ cmd_wait_for_add(const char *name) void cmd_wait_for_remove(struct wait_channel *wc) { - if (wc->locked) return; if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) @@ -241,7 +240,8 @@ cmd_wait_for_flush(void) if (!cmdq_free(wq)) cmdq_continue(wq); } - while ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { + wc->woken = 1; + TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) { TAILQ_REMOVE(&wc->lockers, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); From 901c2eb20a7066b743c9c6cdde0766737f7c079f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Sep 2015 13:31:40 +0000 Subject: [PATCH 741/949] Add copy-mode -e to exit copy mode when scrolling off the bottom, useful for quick view of history, from Cam Hutchison. --- cmd-copy-mode.c | 6 +++--- tmux.1 | 12 +++++++++++- tmux.h | 2 +- window-copy.c | 26 ++++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index d729ada6..e04b561b 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { "copy-mode", NULL, - "Mt:u", 0, 0, - "[-Mu] " CMD_TARGET_PANE_USAGE, + "Met:u", 0, 0, + "[-Meu] " CMD_TARGET_PANE_USAGE, 0, cmd_copy_mode_exec }; @@ -66,7 +66,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) if (wp->mode != &window_copy_mode) { if (window_pane_set_mode(wp, &window_copy_mode) != 0) return (CMD_RETURN_NORMAL); - window_copy_init_from_pane(wp); + window_copy_init_from_pane(wp, args_has(self->args, 'e')); } if (args_has(args, 'M')) { if (wp->mode != NULL && wp->mode != &window_copy_mode) diff --git a/tmux.1 b/tmux.1 index a2f6cb96..43dce715 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1133,7 +1133,7 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl Mu +.Op Fl Meu .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1143,6 +1143,16 @@ 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 e +specifies that scrolling to the bottom of the history (to the visible screen) +should exit copy mode. +While in copy mode, pressing a key other than those used for scrolling will +disable this behaviour. +This is intended to allow fast scrolling through a pane's history, for +example with: +.Bd -literal -offset indent +bind PageUp copy-mode -eu +.Ed .El .Pp Each window displayed by diff --git a/tmux.h b/tmux.h index a8cd8532..080d670d 100644 --- a/tmux.h +++ b/tmux.h @@ -2068,7 +2068,7 @@ extern const char window_clock_table[14][5][5]; /* window-copy.c */ extern const struct window_mode window_copy_mode; -void window_copy_init_from_pane(struct window_pane *); +void window_copy_init_from_pane(struct window_pane *, u_int); void window_copy_init_for_output(struct window_pane *); void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); diff --git a/window-copy.c b/window-copy.c index 460dd10b..d18c0424 100644 --- a/window-copy.c +++ b/window-copy.c @@ -135,7 +135,8 @@ struct window_copy_mode_data { u_int selx; u_int sely; - u_int rectflag; /* are we in rectangle copy mode? */ + int rectflag; /* in rectangle copy mode? */ + int scroll_exit; /* exit on scroll to end? */ u_int cx; u_int cy; @@ -175,6 +176,7 @@ window_copy_init(struct window_pane *wp) data->backing_written = 0; data->rectflag = 0; + data->scroll_exit = 0; data->inputtype = WINDOW_COPY_OFF; data->inputprompt = NULL; @@ -206,7 +208,7 @@ window_copy_init(struct window_pane *wp) } void -window_copy_init_from_pane(struct window_pane *wp) +window_copy_init_from_pane(struct window_pane *wp, u_int scroll_exit) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -219,6 +221,7 @@ window_copy_init_from_pane(struct window_pane *wp) data->backing = &wp->base; data->cx = data->backing->cx; data->cy = data->backing->cy; + data->scroll_exit = scroll_exit; s->cx = data->cx; s->cy = data->cy; @@ -419,6 +422,13 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, } cmd = mode_key_lookup(&data->mdata, key, &arg); + if (cmd != MODEKEYCOPY_PREVIOUSPAGE && + cmd != MODEKEYCOPY_NEXTPAGE && + cmd != MODEKEYCOPY_SCROLLUP && + cmd != MODEKEYCOPY_SCROLLDOWN && + cmd != MODEKEYCOPY_HALFPAGEUP && + cmd != MODEKEYCOPY_HALFPAGEDOWN) + data->scroll_exit = 0; switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: if (sess != NULL) { @@ -461,6 +471,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, case MODEKEYCOPY_SCROLLDOWN: for (; np != 0; np--) window_copy_cursor_down(wp, 1); + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } break; case MODEKEYCOPY_PREVIOUSPAGE: for (; np != 0; np--) @@ -476,6 +490,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, else data->oy -= n; } + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; @@ -498,6 +516,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, else data->oy -= n; } + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; From 16efa8483888e326aed2c05a01b63b45a2b118ef Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 10:25:52 +0000 Subject: [PATCH 742/949] Make refresh-client force update of jobs, from Sina Siadat. --- cmd-refresh-client.c | 7 +++++-- format.c | 18 +++++++++++------- server-client.c | 3 ++- status.c | 5 ++++- tmux.h | 5 ++++- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index b6d5d624..5a45ec25 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -65,10 +65,13 @@ cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) } if (tty_set_size(&c->tty, w, h)) recalculate_sizes(); - } else if (args_has(args, 'S')) + } else if (args_has(args, 'S')) { + c->flags |= CLIENT_STATUSFORCE; server_status_client(c); - else + } else { + c->flags |= CLIENT_STATUSFORCE; server_redraw_client(c); + } return (CMD_RETURN_NORMAL); } diff --git a/format.c b/format.c index c401fa35..2fae8da0 100644 --- a/format.c +++ b/format.c @@ -104,7 +104,7 @@ struct format_tree { struct session *s; struct window_pane *wp; - int status; + int flags; RB_HEAD(format_entry_tree, format_entry) tree; }; @@ -216,27 +216,31 @@ const char * format_job_get(struct format_tree *ft, const char *cmd) { struct format_job fj0, *fj; + time_t t; fj0.cmd = cmd; if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) { fj = xcalloc(1, sizeof *fj); fj->cmd = xstrdup(cmd); - fj->status = ft->status; xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); RB_INSERT(format_job_tree, &format_jobs, fj); } - if (fj->job == NULL && fj->last != time(NULL)) { + t = time(NULL); + if (fj->job == NULL && ((ft->flags & FORMAT_FORCE) || fj->last != t)) { fj->job = job_run(fj->cmd, NULL, -1, format_job_callback, NULL, fj); if (fj->job == NULL) { free(fj->out); xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); } + fj->last = t; } - fj->last = time(NULL); + + if (ft->flags & FORMAT_STATUS) + fj->status = 1; return (fj->out); } @@ -438,12 +442,12 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) struct format_tree * format_create(void) { - return (format_create_status(0)); + return (format_create_flags(0)); } /* Create a new tree for the status line. */ struct format_tree * -format_create_status(int status) +format_create_flags(int flags) { struct format_tree *ft; @@ -454,7 +458,7 @@ format_create_status(int status) ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); - ft->status = status; + ft->flags = flags; format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host_short", format_cb_host_short); diff --git a/server-client.c b/server-client.c index 70f5adcb..6669bf05 100644 --- a/server-client.c +++ b/server-client.c @@ -937,7 +937,8 @@ server_client_check_redraw(struct client *c) tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; tty_update_mode(tty, tty->mode, NULL); - c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS); + c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS| + CLIENT_STATUSFORCE); } /* Set client title. */ diff --git a/status.c b/status.c index 29cb686c..7a1d2818 100644 --- a/status.c +++ b/status.c @@ -503,7 +503,10 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) if (fmt == NULL) return (xstrdup("")); - ft = format_create_status(1); + if (c->flags & CLIENT_STATUSFORCE) + ft = format_create_flags(FORMAT_STATUS|FORMAT_FORCE); + else + ft = format_create_flags(FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); diff --git a/tmux.h b/tmux.h index 080d670d..e5523418 100644 --- a/tmux.h +++ b/tmux.h @@ -1220,6 +1220,7 @@ struct client { #define CLIENT_UTF8 0x10000 #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 +#define CLIENT_STATUSFORCE 0x80000 int flags; struct key_table *keytable; @@ -1442,9 +1443,11 @@ int paste_set(char *, size_t, const char *, char **); char *paste_make_sample(struct paste_buffer *, int); /* format.c */ +#define FORMAT_STATUS 0x1 +#define FORMAT_FORCE 0x2 struct format_tree; struct format_tree *format_create(void); -struct format_tree *format_create_status(int); +struct format_tree *format_create_flags(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); From af16ce6ad9170e6a48e79e3af696f60daa2bae1d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 11:34:50 +0000 Subject: [PATCH 743/949] When the active pane changes, redraw panes if the style has changed. From Cam Hutchison. --- cmd-select-pane.c | 28 +++++++++++++++++----------- style.c | 12 ++++++++++++ tmux.h | 4 ++++ window.c | 24 ++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 5ea4bdc3..e76587cc 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -47,6 +47,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; + struct window *w; struct session *s; struct window_pane *wp, *lastwp, *markedwp; const char *style; @@ -55,21 +56,24 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); + w = wl->window; - if (wl->window->last == NULL) { + if (w->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'e')) - wl->window->last->flags &= ~PANE_INPUTOFF; + w->last->flags &= ~PANE_INPUTOFF; else if (args_has(self->args, 'd')) - wl->window->last->flags |= PANE_INPUTOFF; + w->last->flags |= PANE_INPUTOFF; else { - server_unzoom_window(wl->window); - window_set_active_pane(wl->window, wl->window->last); - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + server_unzoom_window(w); + window_redraw_active_switch(w, w->last); + if (window_set_active_pane(w, w->last)) { + server_status_window(w); + server_redraw_window_borders(w); + } } return (CMD_RETURN_NORMAL); @@ -77,6 +81,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + w = wl->window; if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) @@ -135,16 +140,17 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (wp == wl->window->active) + if (wp == w->active) return (CMD_RETURN_NORMAL); server_unzoom_window(wp->window); if (!window_pane_visible(wp)) { cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); } - if (window_set_active_pane(wl->window, wp)) { - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + window_redraw_active_switch(w, wp); + if (window_set_active_pane(w, wp)) { + server_status_window(w); + server_redraw_window_borders(w); } return (CMD_RETURN_NORMAL); diff --git a/style.c b/style.c index 9fafdd1d..c00b0fee 100644 --- a/style.c +++ b/style.c @@ -252,3 +252,15 @@ style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) if (gcp->attr != 0) gc->attr |= gcp->attr; } + +/* Check if two styles are the same. */ +int +style_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + return gc1->fg == gc2->fg && + gc1->bg == gc2->bg && + (gc1->flags & ~GRID_FLAG_PADDING) == + (gc2->flags & ~GRID_FLAG_PADDING) && + (gc1->attr & ~GRID_ATTR_CHARSET) == + (gc2->attr & ~GRID_ATTR_CHARSET); +} diff --git a/tmux.h b/tmux.h index e5523418..5b1f0f33 100644 --- a/tmux.h +++ b/tmux.h @@ -1983,6 +1983,8 @@ struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *); +void window_redraw_active_switch(struct window *, + struct window_pane *); struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); @@ -2210,5 +2212,7 @@ void style_apply(struct grid_cell *, struct options *, const char *); void style_apply_update(struct grid_cell *, struct options *, const char *); +int style_equal(const struct grid_cell *, + const struct grid_cell *); #endif /* TMUX_H */ diff --git a/window.c b/window.c index e5decdc4..9e77adcd 100644 --- a/window.c +++ b/window.c @@ -423,6 +423,30 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return (1); } +void +window_redraw_active_switch(struct window *w, struct window_pane *wp) +{ + const struct grid_cell *agc, *wgc; + + if (wp == w->active) + return; + + /* + * If window-style and window-active-style are the same, we don't need + * to redraw panes when switching active panes. Otherwise, if the + * active or inactive pane do not have a custom style, they will need + * to be redrawn. + */ + agc = options_get_style(&w->options, "window-active-style"); + wgc = options_get_style(&w->options, "window-style"); + if (style_equal(agc, wgc)) + return; + if (style_equal(&grid_default_cell, &w->active->colgc)) + w->active->flags |= PANE_REDRAW; + if (style_equal(&grid_default_cell, &wp->colgc)) + wp->flags |= PANE_REDRAW; +} + struct window_pane * window_get_active_at(struct window *w, u_int x, u_int y) { From 8da6de3e663ce23f434b6c90b49a95849f309063 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 11:57:22 +0000 Subject: [PATCH 744/949] Style nit, int for flags not u_int. --- tmux.h | 2 +- window-copy.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.h b/tmux.h index 5b1f0f33..6839be84 100644 --- a/tmux.h +++ b/tmux.h @@ -2073,7 +2073,7 @@ extern const char window_clock_table[14][5][5]; /* window-copy.c */ extern const struct window_mode window_copy_mode; -void window_copy_init_from_pane(struct window_pane *, u_int); +void window_copy_init_from_pane(struct window_pane *, int); void window_copy_init_for_output(struct window_pane *); void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); diff --git a/window-copy.c b/window-copy.c index d18c0424..c7d360de 100644 --- a/window-copy.c +++ b/window-copy.c @@ -208,7 +208,7 @@ window_copy_init(struct window_pane *wp) } void -window_copy_init_from_pane(struct window_pane *wp, u_int scroll_exit) +window_copy_init_from_pane(struct window_pane *wp, int scroll_exit) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; From 216ddf3da5798fe3e7246310ebe0b77e591ee34e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 12:12:24 +0000 Subject: [PATCH 745/949] Move tzset() from log_open to main. --- log.c | 2 -- tmux.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/log.c b/log.c index ecb9698a..8c2fdb07 100644 --- a/log.c +++ b/log.c @@ -48,8 +48,6 @@ log_open(const char *path) setvbuf(log_file, NULL, _IOLBF, 0); event_set_log_callback(log_event_cb); - - tzset(); } /* Close logging. */ diff --git a/tmux.c b/tmux.c index 875e8d72..0b8e6a91 100644 --- a/tmux.c +++ b/tmux.c @@ -199,6 +199,7 @@ main(int argc, char **argv) #endif setlocale(LC_TIME, ""); + tzset(); if (**argv == '-') flags = CLIENT_LOGIN; From 62bb6e37e0bdc2de2a20f7adc38993d2ccfd8c11 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 12:52:22 +0000 Subject: [PATCH 746/949] Should add buffer if no -b. --- cmd-set-buffer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 01afa774..e7f7627e 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -56,11 +56,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufname = args_get(args, 'b'); if (bufname == NULL) - pb = paste_get_top(&bufname); + pb = NULL; else pb = paste_get_name(bufname); if (self->entry == &cmd_delete_buffer_entry) { + if (pb == NULL) + pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); @@ -70,6 +72,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 'n')) { + if (pb == NULL) + pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); From 16ee4de5df3c31e362ed095e3feae1b504ddf1c5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 13:22:02 +0000 Subject: [PATCH 747/949] Remove some extra blank lines. --- cmd-find.c | 1 - cmd-set-option.c | 1 - paste.c | 1 - screen-write.c | 1 - window-clock.c | 1 - 5 files changed, 5 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 87dacfb4..ec883cbd 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -562,7 +562,6 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) return (0); } - /* Stop now if exact only. */ if (exact) return (-1); diff --git a/cmd-set-option.c b/cmd-set-option.c index 98ab2513..e0b07edb 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -263,7 +263,6 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, return (CMD_RETURN_NORMAL); } - /* Unset an option. */ int cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, diff --git a/paste.c b/paste.c index fa58f7c1..44f88d0b 100644 --- a/paste.c +++ b/paste.c @@ -254,7 +254,6 @@ paste_set(char *data, size_t size, const char *name, char **cause) return (-1); } - pb = xmalloc(sizeof *pb); pb->name = xstrdup(name); diff --git a/screen-write.c b/screen-write.c index 37e2b548..f80048b6 100644 --- a/screen-write.c +++ b/screen-write.c @@ -46,7 +46,6 @@ screen_write_stop(unused struct screen_write_ctx *ctx) { } - /* Reset screen state. */ void screen_write_reset(struct screen_write_ctx *ctx) diff --git a/window-clock.c b/window-clock.c index 4a497891..5bc546a9 100644 --- a/window-clock.c +++ b/window-clock.c @@ -233,7 +233,6 @@ window_clock_draw_screen(struct window_pane *wp) screen_write_puts(&ctx, &gc, "%s", tim); } - screen_write_stop(&ctx); return; } From d47789620b76a8a9b08b3f999211e25fcf8ee25f Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Mon, 14 Sep 2015 14:39:51 +0100 Subject: [PATCH 748/949] Add missing --- tmux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.c b/tmux.c index 94d97f92..5514a9af 100644 --- a/tmux.c +++ b/tmux.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "tmux.h" From 166aa97f75d01dababc94c78b8ca1192479166a7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 14 Sep 2015 15:59:21 +0100 Subject: [PATCH 749/949] No more $Id$. --- Makefile.am | 2 +- configure.ac | 2 +- tools/cmp-cvs.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 696ccb2b..8b39ccfc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -# $Id$ +# Makefile.am # Obvious program stuff. bin_PROGRAMS = tmux diff --git a/configure.ac b/configure.ac index f3fabd94..7874e2a7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# $Id$ +# configure.ac # Miscellaneous bits. AC_INIT(tmux, 2.1) diff --git a/tools/cmp-cvs.sh b/tools/cmp-cvs.sh index 104ded6b..5429d769 100644 --- a/tools/cmp-cvs.sh +++ b/tools/cmp-cvs.sh @@ -1,4 +1,4 @@ -# $Id$ +#!/bin/ksh rm diff.out touch diff.out From 54bd7612868341c47bd3bd237c9598d5860b3ef3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 15 Sep 2015 13:57:46 +0100 Subject: [PATCH 750/949] Add BCE to TODO. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 6f8d732c..f2bb4ce1 100644 --- a/TODO +++ b/TODO @@ -121,3 +121,5 @@ * multiline status line? * customizable command aliases * automatic pane logging + * BCE? We are halfway there (output side is done for pane backgrounds), + just need to change how screen/grid handles erase From a4b4b299875d833019cb829ca1bee41d71724f37 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:24:54 +0000 Subject: [PATCH 751/949] Rename cmd_q dead flag to a general flags bitmask (will be more flags later). --- cmd-if-shell.c | 2 +- cmd-queue.c | 9 ++++++--- cmd-run-shell.c | 2 +- server-client.c | 2 +- tmux.h | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a307bd2f..0271fdea 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -143,7 +143,7 @@ cmd_if_shell_callback(struct job *job) struct cmd_list *cmdlist; char *cause, *cmd; - if (cmdq->dead) + if (cmdq->flags & CMD_Q_DEAD) return; if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) diff --git a/cmd-queue.c b/cmd-queue.c index af987f59..217045fa 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -35,7 +35,7 @@ cmdq_new(struct client *c) cmdq = xcalloc(1, sizeof *cmdq); cmdq->references = 1; - cmdq->dead = 0; + cmdq->flags = 0; cmdq->client = c; cmdq->client_exit = -1; @@ -51,8 +51,11 @@ cmdq_new(struct client *c) int cmdq_free(struct cmd_q *cmdq) { - if (--cmdq->references != 0) - return (cmdq->dead); + if (--cmdq->references != 0) { + if (cmdq->flags & CMD_Q_DEAD) + return (1); + return (0); + } cmdq_flush(cmdq); free(cmdq); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 134cbeba..17ac0f76 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -131,7 +131,7 @@ cmd_run_shell_callback(struct job *job) int retcode; u_int lines; - if (cmdq->dead) + if (cmdq->flags & CMD_Q_DEAD) return; cmd = cdata->cmd; diff --git a/server-client.c b/server-client.c index 6669bf05..10768839 100644 --- a/server-client.c +++ b/server-client.c @@ -214,7 +214,7 @@ server_client_lost(struct client *c) free(c->prompt_string); free(c->prompt_buffer); - c->cmdq->dead = 1; + c->cmdq->flags |= CMD_Q_DEAD; cmdq_free(c->cmdq); c->cmdq = NULL; diff --git a/tmux.h b/tmux.h index 6839be84..3fd057e9 100644 --- a/tmux.h +++ b/tmux.h @@ -1305,7 +1305,8 @@ TAILQ_HEAD(cmd_q_items, cmd_q_item); /* Command queue. */ struct cmd_q { int references; - int dead; + int flags; +#define CMD_Q_DEAD 0x1 struct client *client; int client_exit; From d1b73be6e18a9576f4dc3bac73d7e1cc1a135938 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:29:30 +0000 Subject: [PATCH 752/949] Hoist some common code out of both branches of an if/else. --- cmd-attach-session.c | 54 +++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a2ae49cb..a7ef1cd9 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -93,6 +93,24 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, session_set_current(s, wl); } + if (cflag != NULL) { + ft = format_create(); + format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, + NULL, NULL); + cp = format_expand(ft, cflag); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + close(s->cwd); + s->cwd = fd; + } + if (c->session != NULL) { if (dflag) { /* @@ -108,24 +126,6 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } } - if (cflag != NULL) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, - NULL, NULL); - cp = format_expand(ft, cflag); - format_free(ft); - - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - close(s->cwd); - s->cwd = fd; - } - if (!Eflag) { update = options_get_string(&s->options, "update-environment"); @@ -146,24 +146,6 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, return (CMD_RETURN_ERROR); } - if (cflag != NULL) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, - NULL, NULL); - cp = format_expand(ft, cflag); - format_free(ft); - - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - close(s->cwd); - s->cwd = fd; - } - if (rflag) c->flags |= CLIENT_READONLY; From ecb257f0efad988660b1a3f27bc9e641275cd1f2 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:40:05 +0000 Subject: [PATCH 753/949] A few minor style nits. --- cmd-list-panes.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/cmd-list-panes.c b/cmd-list-panes.c index bd34344f..0af391c5 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -19,7 +19,6 @@ #include #include -#include #include "tmux.h" @@ -30,10 +29,10 @@ enum cmd_retval cmd_list_panes_exec(struct cmd *, struct cmd_q *); void cmd_list_panes_server(struct cmd *, struct cmd_q *); -void cmd_list_panes_session( - struct cmd *, struct session *, struct cmd_q *, int); -void cmd_list_panes_window(struct cmd *, - struct session *, struct winlink *, struct cmd_q *, int); +void cmd_list_panes_session(struct cmd *, struct session *, struct cmd_q *, + int); +void cmd_list_panes_window(struct cmd *, struct session *, struct winlink *, + struct cmd_q *, int); const struct cmd_entry cmd_list_panes_entry = { "list-panes", "lsp", @@ -77,8 +76,8 @@ cmd_list_panes_server(struct cmd *self, struct cmd_q *cmdq) } void -cmd_list_panes_session( - struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) +cmd_list_panes_session(struct cmd *self, struct session *s, struct cmd_q *cmdq, + int type) { struct winlink *wl; @@ -87,8 +86,8 @@ cmd_list_panes_session( } void -cmd_list_panes_window(struct cmd *self, - struct session *s, struct winlink *wl, struct cmd_q *cmdq, int type) +cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, + struct cmd_q *cmdq, int type) { struct args *args = self->args; struct window_pane *wp; @@ -115,9 +114,9 @@ cmd_list_panes_window(struct cmd *self, "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; case 2: - template = "#{session_name}:#{window_index}.#{pane_index}: " - "[#{pane_width}x#{pane_height}] [history " - "#{history_size}/#{history_limit}, " + template = "#{session_name}:#{window_index}." + "#{pane_index}: [#{pane_width}x#{pane_height}] " + "[history #{history_size}/#{history_limit}, " "#{history_bytes} bytes] #{pane_id}" "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; From 232a0ffc34bd1c4c3dbb006369b8c87b9ee55c9a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:40:27 +0000 Subject: [PATCH 754/949] Give some variables less silly names. --- cmd-kill-session.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd-kill-session.c b/cmd-kill-session.c index d7e2a219..74843eb6 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -41,16 +41,16 @@ enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s, *s2, *s3; + struct session *s, *sloop, *stmp; if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'a')) { - RB_FOREACH_SAFE(s2, sessions, &sessions, s3) { - if (s != s2) { - server_destroy_session(s2); - session_destroy(s2); + RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { + if (sloop != s) { + server_destroy_session(sloop); + session_destroy(sloop); } } } else { From c1d0b6a6eed2fd393ea30d257ce147e8f6b5e6c8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:41:00 +0000 Subject: [PATCH 755/949] Log when cmdq_continue is called. --- cmd-queue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd-queue.c b/cmd-queue.c index 217045fa..1fe34dce 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -195,6 +195,7 @@ cmdq_continue_one(struct cmd_q *cmdq) int cmdq_continue(struct cmd_q *cmdq) { + struct client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; int empty; @@ -202,6 +203,9 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->references++; notify_disable(); + log_debug("continuing cmdq %p: flags=%#x, client=%d", cmdq, cmdq->flags, + c != NULL ? c->ibuf.fd : -1); + empty = TAILQ_EMPTY(&cmdq->queue); if (empty) goto empty; From 8b5d5dca9f8fcb7832c48baeef10a0320776b30c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Sep 2015 14:11:55 +0000 Subject: [PATCH 756/949] Redraw both src and dst sessions in break-pane. --- cmd-break-pane.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index c7af7865..2aa5c5b7 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -43,7 +43,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; - struct session *s; + struct session *src_s; + struct session *dst_s; struct window_pane *wp; struct window *w; char *name; @@ -53,28 +54,28 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; char *cp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 's'), &s, &wp)) == NULL) + wl = cmd_find_pane(cmdq, args_get(args, 's'), &src_s, &wp); + if (wl == NULL) return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst_s)) == -2) return (CMD_RETURN_ERROR); - if (idx != -1 && winlink_find_by_index(&s->windows, idx) != NULL) { + if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { cmdq_error(cmdq, "index %d already in use", idx); return (CMD_RETURN_ERROR); } + w = wl->window; - if (window_count_panes(wl->window) == 1) { + if (window_count_panes(w) == 1) { cmdq_error(cmdq, "can't break with only one pane"); return (CMD_RETURN_ERROR); } - - w = wl->window; server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); window_lost_pane(w, wp); layout_close_pane(wp); - w = wp->window = window_create1(s->sx, s->sy); + w = wp->window = window_create1(dst_s->sx, dst_s->sy); TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; name = default_window_name(w); @@ -83,20 +84,25 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) layout_init(w, wp); if (idx == -1) - idx = -1 - options_get_number(&s->options, "base-index"); - wl = session_attach(s, w, idx, &cause); /* can't fail */ + idx = -1 - options_get_number(&dst_s->options, "base-index"); + wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) - session_select(s, wl->idx); + session_select(dst_s, wl->idx); - server_redraw_session(s); - server_status_session_group(s); + server_redraw_session(src_s); + if (src_s != dst_s) + server_redraw_session(dst_s); + server_status_session_group(src_s); + if (src_s != dst_s) + server_status_session_group(dst_s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, wp); + format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, + wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); From 6b709e655e52a302f537d6995affdbaf4e8ef0f1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 18 Sep 2015 09:55:22 +0000 Subject: [PATCH 757/949] -l should apply to the new not the old pane with -b, from "MadMaverick9" on GitHub. --- layout.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/layout.c b/layout.c index bb1bbf8d..266d1f39 100644 --- a/layout.c +++ b/layout.c @@ -686,6 +686,8 @@ layout_split_pane( case LAYOUT_LEFTRIGHT: if (size < 0) size2 = ((sx + 1) / 2) - 1; + else if (insert_before) + size2 = sx - size - 1; else size2 = size; if (size2 < PANE_MINIMUM) @@ -699,6 +701,8 @@ layout_split_pane( case LAYOUT_TOPBOTTOM: if (size < 0) size2 = ((sy + 1) / 2) - 1; + else if (insert_before) + size2 = sy - size - 1; else size2 = size; if (size2 < PANE_MINIMUM) From d5f223a3fec0b0ccf69f17e3515a20c79ec28e6e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 21 Sep 2015 09:34:52 +0000 Subject: [PATCH 758/949] Reset the alerts timer always on activity, from Thomas Adam. --- alerts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alerts.c b/alerts.c index 5d52f7ad..806e565b 100644 --- a/alerts.c +++ b/alerts.c @@ -126,6 +126,9 @@ alerts_reset(struct window *w) void alerts_queue(struct window *w, int flags) { + if (w->flags & WINDOW_ACTIVITY) + alerts_reset(w); + if (!event_initialized(&w->alerts_timer)) evtimer_set(&w->alerts_timer, alerts_timer, w); @@ -139,9 +142,6 @@ alerts_queue(struct window *w, int flags) event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); alerts_fired = 1; } - - if (flags & WINDOW_ACTIVITY) - alerts_reset(w); } int From dc66795e353e1d84c23cb87f4120480a152b43d9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Sep 2015 21:56:16 +0000 Subject: [PATCH 759/949] Don't update last session when the session is unchanged, from Sina Siadat. --- cmd-switch-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 10171018..3a72886a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -124,7 +124,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) environ_update(update, &c->environ, &s->environ); } - if (c->session != NULL) + if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; status_timer_start(c); From 1caebaa49a0a418b21724c3651e993224bfc12eb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 23 Sep 2015 14:26:53 +0100 Subject: [PATCH 760/949] Add to TODO. --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index f2bb4ce1..78d566a4 100644 --- a/TODO +++ b/TODO @@ -62,6 +62,7 @@ * paste w/o trailing whitespace * command to toggle selection not to move it in copy-mode * regex searching + * copy-pipe should have -x as well - layout stuff * way to tag a layout as a number/name From ddb2d1221b3a824114e7c456251c4cf983b0b330 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Sep 2015 07:02:18 +0000 Subject: [PATCH 761/949] Assign flag not number for flag types (we got away with it so far because that are a union). From Filipe Brandenburger. --- tty-term.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-term.c b/tty-term.c index f4fd91a8..76626ddf 100644 --- a/tty-term.c +++ b/tty-term.c @@ -451,7 +451,7 @@ tty_term_find(char *name, int fd, char **cause) if (n == -1) break; code->type = TTYCODE_FLAG; - code->value.number = n; + code->value.flag = n; break; } } From 69ea6b9373c0cc992932499b89330c26e27d6510 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Sep 2015 12:03:58 +0000 Subject: [PATCH 762/949] Do not leak log file descriptor. --- log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/log.c b/log.c index 8c2fdb07..711b9b8a 100644 --- a/log.c +++ b/log.c @@ -42,6 +42,9 @@ log_event_cb(unused int severity, const char *msg) void log_open(const char *path) { + if (log_file != NULL) + fclose(log_file); + log_file = fopen(path, "w"); if (log_file == NULL) return; From 2a62917444783e62ea5b8c13069aecb77b47ff07 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Sep 2015 12:06:20 +0000 Subject: [PATCH 763/949] Don't leak fd and path on failure. --- client.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client.c b/client.c index d36be86b..da7bb314 100644 --- a/client.c +++ b/client.c @@ -153,15 +153,19 @@ retry: } fd = server_start(base, lockfd, lockfile); } + if (locked) { free(lockfile); close(lockfd); } - setblocking(fd, 0); return (fd); failed: + if (locked) { + free(lockfile); + close(lockfd); + } close(fd); return (-1); } From 28f23f18e9d79405a60348c4f7aeded33da9135b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Sep 2015 15:53:07 +0000 Subject: [PATCH 764/949] Free the history when it is cleared, based on a diff from Carlo Cannas. --- cmd-clear-history.c | 4 +--- grid.c | 20 ++++++++++++++++---- tmux.h | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 88dbbcf7..63e9d548 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -47,9 +47,7 @@ cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) if (wp->mode == &window_copy_mode) window_pane_reset_mode(wp); - - grid_move_lines(gd, 0, gd->hsize, gd->sy); - gd->hsize = 0; + grid_clear_history(gd); return (CMD_RETURN_NORMAL); } diff --git a/grid.c b/grid.c index 93fed05e..99dafab2 100644 --- a/grid.c +++ b/grid.c @@ -170,6 +170,18 @@ grid_scroll_history(struct grid *gd) gd->hsize++; } +/* Clear the history. */ +void +grid_clear_history(struct grid *gd) +{ + grid_clear_lines(gd, 0, gd->hsize); + grid_move_lines(gd, 0, gd->hsize, gd->sy); + + gd->hsize = 0; + gd->linedata = xreallocarray(gd->linedata, gd->sy, + sizeof *gd->linedata); +} + /* Scroll a region up, moving the top line into the history. */ void grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) @@ -344,8 +356,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) grid_clear_lines(gd, yy, 1); } - memmove( - &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); + memmove(&gd->linedata[dy], &gd->linedata[py], + ny * (sizeof *gd->linedata)); /* Wipe any lines that have been moved (without freeing them). */ for (yy = py; yy < py + ny; yy++) { @@ -371,8 +383,8 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) grid_expand_line(gd, py, px + nx); grid_expand_line(gd, py, dx + nx); - memmove( - &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); + memmove(&gl->celldata[dx], &gl->celldata[px], + nx * sizeof *gl->celldata); /* Wipe any cells that have been moved. */ for (xx = px; xx < px + nx; xx++) { diff --git a/tmux.h b/tmux.h index 3fd057e9..5bf9b39b 100644 --- a/tmux.h +++ b/tmux.h @@ -1842,6 +1842,7 @@ int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *); void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); +void grid_clear_history(struct grid *); void grid_expand_line(struct grid *, u_int, u_int); const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int); From 03d7dba5d86afa797eac49e75d37554590ef66c3 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Sep 2015 23:30:12 +0000 Subject: [PATCH 765/949] If the terminal has colors=256, only try to use setaf/setab if they exist, reported by Filipe Brandenburger. --- tty.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tty.c b/tty.c index c4dfde1d..7be952c8 100644 --- a/tty.c +++ b/tty.c @@ -1648,14 +1648,19 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) char s[32]; /* - * If the terminfo entry has 256 colours, assume that setaf and setab - * work correctly. + * If the terminfo entry has 256 colours and setaf and setab exist, + * assume that they work correctly. */ if (tty->term->flags & TERM_256COLOURS) { - if (*type == '3') + if (*type == '3') { + if (!tty_term_has(tty->term, TTYC_SETAF)) + goto fallback; tty_putcode1(tty, TTYC_SETAF, colour); - else + } else { + if (!tty_term_has(tty->term, TTYC_SETAB)) + goto fallback; tty_putcode1(tty, TTYC_SETAB, colour); + } return (0); } @@ -1663,13 +1668,15 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) * If the user has specified -2 to the client, setaf and setab may not * work, so send the usual sequence. */ - if (tty->term_flags & TERM_256COLOURS) { - xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); - tty_puts(tty, s); - return (0); - } + if (tty->term_flags & TERM_256COLOURS) + goto fallback; return (-1); + +fallback: + xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); + tty_puts(tty, s); + return (0); } void From 695a591f8e9bce3fe3dd22a23a337f0fa548b543 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Sep 2015 23:30:24 +0000 Subject: [PATCH 766/949] Adding colors=256 to *256color* was always pretty stupid and now it won't work (without adding setaf@:setab@ too). --- options-table.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index b9431483..cfec138d 100644 --- a/options-table.c +++ b/options-table.c @@ -107,8 +107,7 @@ const struct options_table_entry server_options_table[] = { { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, - .default_str = "*256col*:colors=256" - ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" + .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" }, From 7340d5adfdc8cc6d845a373f3e0d59bfd10a45d1 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Oct 2015 09:52:58 +0000 Subject: [PATCH 767/949] Couple of memory leaks in error paths, from Frederik Vanderstraeten. --- cmd-capture-pane.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index a348e155..bd9ecebb 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -196,6 +196,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (c == NULL || (c->session != NULL && !(c->flags & CLIENT_CONTROL))) { cmdq_error(cmdq, "can't write to stdout"); + free(buf); return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); @@ -210,11 +211,12 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (paste_set(buf, len, bufname, &cause) != 0) { cmdq_error(cmdq, "%s", cause); - free(buf); free(cause); + free(buf); return (CMD_RETURN_ERROR); } } + free(buf); return (CMD_RETURN_NORMAL); } From 241fd72f754388c140036eb1b826a07700f5be3b Mon Sep 17 00:00:00 2001 From: guenther Date: Sun, 11 Oct 2015 00:26:23 +0000 Subject: [PATCH 768/949] Userspace doesn't need to use SUN_LEN(): connect() and bind() must accept sizeof(struct sockaddr_un), so do the simple, portable thing ok beck@ deraadt@ --- client.c | 2 +- server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index da7bb314..2eb48a49 100644 --- a/client.c +++ b/client.c @@ -119,7 +119,7 @@ retry: fatal("socket failed"); log_debug("trying connect"); - if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { + if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; diff --git a/server.c b/server.c index 045daead..fd77ff38 100644 --- a/server.c +++ b/server.c @@ -145,7 +145,7 @@ server_create_socket(void) return (-1); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); - if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) return (-1); umask(mask); From f199fb6a2b53c99fb92de3db5a7e03ac73fb0db4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Oct 2015 09:24:25 +0100 Subject: [PATCH 769/949] Fix available_fds when there is no AF_INET, reported by Mathieu Arnold. --- compat/imsg.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compat/imsg.c b/compat/imsg.c index 982ee069..6c9bee68 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -51,8 +51,12 @@ available_fds(unsigned int n) for (i = 0; i < n; i++) { fds[i] = -1; if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - ret = 1; - break; + if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) + fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); + if (fds[i] < 0) { + ret = 1; + break; + } } } From c06c14fb299549adeb32c5021032374686e5d682 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Oct 2015 21:42:17 +0100 Subject: [PATCH 770/949] Some header fixes. --- job.c | 2 +- log.c | 2 +- screen.c | 1 - tmux.c | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/job.c b/job.c index ff21b3b6..dc11c4c8 100644 --- a/job.c +++ b/job.c @@ -20,10 +20,10 @@ #include #include +#include #include #include #include -#include #include "tmux.h" diff --git a/log.c b/log.c index c622d894..adb65702 100644 --- a/log.c +++ b/log.c @@ -17,12 +17,12 @@ */ #include -#include #include #include #include #include +#include #include "tmux.h" diff --git a/screen.c b/screen.c index 01314330..f487cc67 100644 --- a/screen.c +++ b/screen.c @@ -18,7 +18,6 @@ #include -#include #include #include #include diff --git a/tmux.c b/tmux.c index 5514a9af..dff7952c 100644 --- a/tmux.c +++ b/tmux.c @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include #include "tmux.h" From cf89abb01367c9f0d7e848ef0058465e8eddf09c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 16 Oct 2015 07:43:29 +0000 Subject: [PATCH 771/949] Don't free after calling paste_set but do after evbuffer_add, from Theo Buehler. --- cmd-capture-pane.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index bd9ecebb..8958a12d 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -200,11 +200,11 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); + free(buf); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); } else { - bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); @@ -217,6 +217,5 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) } } - free(buf); return (CMD_RETURN_NORMAL); } From e0527d773144fef667addb5b4f24045c7d8aaa99 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 17 Oct 2015 18:48:22 +0100 Subject: [PATCH 772/949] time.h is not needed now tzset() is not in log.c. --- log.c | 1 - 1 file changed, 1 deletion(-) diff --git a/log.c b/log.c index adb65702..536af173 100644 --- a/log.c +++ b/log.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "tmux.h" From 9c601ebde81a3965541161499ebce0b5e5f1122f Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 17 Oct 2015 18:30:43 +0000 Subject: [PATCH 773/949] Add pledge "stdio unix sendfd proc exec tty" to tmux client process, "sendfd" is dropped after first message from the server. --- client.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/client.c b/client.c index 2eb48a49..c3803ed5 100644 --- a/client.c +++ b/client.c @@ -55,7 +55,7 @@ int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); -void client_send_identify(void); +void client_send_identify(const char *, int); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -214,7 +214,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct cmd *cmd; struct cmd_list *cmdlist; struct msg_command_data *data; - int cmdflags, fd, i; + int cmdflags, fd, i, cwd; + const char* ttynam; pid_t ppid; enum msgtype msg; char *cause; @@ -272,6 +273,26 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } + + /* Save these before pledge(). */ + if ((cwd = open(".", O_RDONLY)) == -1) + cwd = open("/", O_RDONLY); + if ((ttynam = ttyname(STDIN_FILENO)) == NULL) + ttynam = ""; + + /* + * Drop privileges for client. "proc exec" is needed for -c and for + * locking (which uses system(3)). + * + * "tty" is needed to restore termios(4) and also for some reason -CC + * does not work properly without it (input is not recognised). + * + * "sendfd" is dropped later in client_dispatch_wait(). + */ + if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) + fatal("pledge failed"); + + /* Free stuff that is not used in the client. */ options_free(&global_options); options_free(&global_s_options); options_free(&global_w_options); @@ -304,7 +325,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(); + client_send_identify(ttynam, cwd); /* closes cwd */ /* Send first command. */ if (msg == MSG_COMMAND) { @@ -359,7 +380,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ void -client_send_identify(void) +client_send_identify(const char *ttynam, int cwd) { const char *s; char **ss; @@ -373,13 +394,8 @@ client_send_identify(void) s = ""; client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - if ((s = ttyname(STDIN_FILENO)) == NULL) - s = ""; - client_write_one(MSG_IDENTIFY_TTYNAME, -1, s, strlen(s) + 1); - - if ((fd = open(".", O_RDONLY)) == -1) - fd = open("/", O_RDONLY); - client_write_one(MSG_IDENTIFY_CWD, fd, NULL, 0); + client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); + client_write_one(MSG_IDENTIFY_CWD, cwd, NULL, 0); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); @@ -395,8 +411,6 @@ client_send_identify(void) } client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); - - client_update_event(); } /* Helper to send one message. */ @@ -587,6 +601,19 @@ client_dispatch_wait(void) struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; + static int pledge_applied; + + /* + * "sendfd" is no longer required once all of the identify messages + * have been sent. We know the server won't send us anything until that + * point (because we don't ask it to), so we can drop "sendfd" once we + * get the first message from the server. + */ + if (!pledge_applied) { + if (pledge("stdio unix proc exec tty", NULL) != 0) + fatal("pledge failed"); + pledge_applied = 1; + }; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) From 6c3ade76dfae9d9fe6f838a11dd23cd7109505de Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 17 Oct 2015 20:16:12 +0100 Subject: [PATCH 774/949] __OpenBSD__ around pledge(). --- client.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client.c b/client.c index 612d4202..1e7492d9 100644 --- a/client.c +++ b/client.c @@ -276,6 +276,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; +#ifdef __OpenBSD__ /* * Drop privileges for client. "proc exec" is needed for -c and for * locking (which uses system(3)). @@ -287,6 +288,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) */ if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); +#endif /* Free stuff that is not used in the client. */ options_free(&global_options); @@ -605,6 +607,7 @@ client_dispatch_wait(void) struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; +#ifdef __OpenBSD__ static int pledge_applied; /* @@ -618,6 +621,7 @@ client_dispatch_wait(void) fatal("pledge failed"); pledge_applied = 1; }; +#endif for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) From 310f0a960ca64fa3809545badc629c0c166c6cd2 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 18 Oct 2015 18:10:43 +0100 Subject: [PATCH 775/949] Update for 2.1 release. --- CHANGES | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index cf695936..db7be25b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,54 @@ +CHANGES FROM 2.0 to 2.1 18 October 2015 + +Incompatible Changes +==================== + +* Mouse-mode has been rewritten. There's now no longer options for: + - mouse-resize-pane + - mouse-select-pane + - mouse-select-window + - mode-mouse + + Instead there is just one option: 'mouse' which turns on mouse support + entirely. +* 'default-terminal' is now a session option. Furthermore, if this is set + to 'screen-*' then emulate what screen does. If italics are wanted, this + can be set to 'tmux' but this is still new and not necessarily supported + on all platforms with older ncurses installs. +* The c0-* options for rate-limiting have been removed. Instead, a backoff + approach is used. + +Normal Changes +============== + +* New formats: + - session_activity + - window_linked + - window_activity_format + - session_alerts + - session_last_attached + - client_pid + - pid +* 'copy-selection', 'append-selection', 'start-named-buffer' now understand + an '-x' flag to prevent it exiting copying mode. +* 'select-pane' now understands '-P' to set window/pane background colours. +* 'renumber-windows' now understands windows which are unlinked. +* 'bind' now understands multiple key tables. Allows for key-chaining. +* 'select-layout' understands '-o' to undo the last layout change. +* The environment is updated when switching sessions as well as attaching. +* 'select-pane' now understands '-M' for marking a pane. This marked pane + can then be used with commands which understand src-pane specifiers + automatically. +* If a session/window target is prefixed with '=' then only an exact match + is considered. +* 'move-window' understands '-a'. +* 'update-environment' understands '-E' when attach-session is used on an + already attached client. +* 'show-environment' understands '-s' to output Bourne-compatible commands. +* New option: 'history-file' to save/restore command prompt history. +* Copy mode is exited if the history is cleared whilst in copy-mode. +* 'copy-mode' learned '-e' to exit copy-mode when scrolling to end. + CHANGES FROM 1.9a to 2.0 6 March 2015 Incompatible Changes diff --git a/configure.ac b/configure.ac index 7874e2a7..93aae20d 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ AC_PROG_INSTALL test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? -found_debug=yes +#found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, enable debug build flags), From 7c78b2b756a207896406a6e6a857e8cedb851c7e Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 18 Oct 2015 18:19:08 +0100 Subject: [PATCH 776/949] Start working on tmux 2.2 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 93aae20d..a2b9e054 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # configure.ac # Miscellaneous bits. -AC_INIT(tmux, 2.1) +AC_INIT(tmux, 2.2) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -24,7 +24,7 @@ AC_PROG_INSTALL test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? -#found_debug=yes +found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, enable debug build flags), From 174a2ad731055f97838290226d656813143620ca Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 18 Oct 2015 20:42:42 +0000 Subject: [PATCH 777/949] Pass current directory as a string rather than a file descriptor because pledge doesn't let us pass directory file descriptors. --- client.c | 18 +++++++++--------- server-client.c | 7 ++++--- tmux.h | 3 ++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/client.c b/client.c index c3803ed5..73d871bc 100644 --- a/client.c +++ b/client.c @@ -55,7 +55,7 @@ int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); -void client_send_identify(const char *, int); +void client_send_identify(const char *, const char *); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -214,11 +214,11 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct cmd *cmd; struct cmd_list *cmdlist; struct msg_command_data *data; - int cmdflags, fd, i, cwd; - const char* ttynam; + int cmdflags, fd, i; + const char *ttynam, *cwd; pid_t ppid; enum msgtype msg; - char *cause; + char *cause, path[PATH_MAX]; struct termios tio, saved_tio; size_t size; @@ -275,8 +275,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Save these before pledge(). */ - if ((cwd = open(".", O_RDONLY)) == -1) - cwd = open("/", O_RDONLY); + if ((cwd = getcwd(path, sizeof path)) == NULL) + cwd = "/"; if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; @@ -325,7 +325,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(ttynam, cwd); /* closes cwd */ + client_send_identify(ttynam, cwd); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -380,7 +380,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ void -client_send_identify(const char *ttynam, int cwd) +client_send_identify(const char *ttynam, const char *cwd) { const char *s; char **ss; @@ -395,7 +395,7 @@ client_send_identify(const char *ttynam, int cwd) client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); - client_write_one(MSG_IDENTIFY_CWD, cwd, NULL, 0); + client_write_one(MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); diff --git a/server-client.c b/server-client.c index 10768839..52ced4d4 100644 --- a/server-client.c +++ b/server-client.c @@ -1166,9 +1166,10 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) c->ttyname = xstrdup(data); break; case MSG_IDENTIFY_CWD: - if (datalen != 0) - fatalx("bad MSG_IDENTIFY_CWD size"); - c->cwd = imsg->fd; + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_CWD string"); + if ((c->cwd = open(data, O_RDONLY)) == -1) + c->cwd = open("/", O_RDONLY); break; case MSG_IDENTIFY_STDIN: if (datalen != 0) diff --git a/tmux.h b/tmux.h index 5bf9b39b..72495100 100644 --- a/tmux.h +++ b/tmux.h @@ -395,11 +395,12 @@ enum msgtype { MSG_IDENTIFY_FLAGS = 100, MSG_IDENTIFY_TERM, MSG_IDENTIFY_TTYNAME, - MSG_IDENTIFY_CWD, + MSG_IDENTIFY_OLDCWD, /* unused */ MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, MSG_IDENTIFY_CLIENTPID, + MSG_IDENTIFY_CWD, MSG_COMMAND = 200, MSG_DETACH, From 8c8cddbe022af5ece9949804cacffbee6b8972fa Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 20 Oct 2015 14:19:27 +0000 Subject: [PATCH 778/949] The table could change when retrying so don't save it at start of server_client_handle_key. --- server-client.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server-client.c b/server-client.c index 52ced4d4..320389af 100644 --- a/server-client.c +++ b/server-client.c @@ -541,7 +541,7 @@ server_client_handle_key(struct client *c, int key) struct window *w; struct window_pane *wp; struct timeval tv; - struct key_table *table = c->keytable; + struct key_table *table; struct key_binding bd_find, *bd; int xtimeout; @@ -607,7 +607,7 @@ server_client_handle_key(struct client *c, int key) retry: /* Try to see if there is a key binding in the current table. */ bd_find.key = key; - bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + bd = RB_FIND(key_bindings, &c->keytable->key_bindings, &bd_find); if (bd != NULL) { /* * Key was matched in this table. If currently repeating but a @@ -625,6 +625,7 @@ retry: * Take a reference to this table to make sure the key binding * doesn't disappear. */ + table = c->keytable; table->references++; /* From 076034345afe0dbfef3fa1a8116a64dc7d990b51 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 20 Oct 2015 21:12:08 +0000 Subject: [PATCH 779/949] Use client pointer not file descriptor in logging. --- cmd-queue.c | 4 ++-- cmd-wait-for.c | 5 ++--- server-client.c | 10 +++++----- server-fn.c | 2 +- status.c | 2 +- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 1fe34dce..ff8c69cb 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -203,8 +203,8 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->references++; notify_disable(); - log_debug("continuing cmdq %p: flags=%#x, client=%d", cmdq, cmdq->flags, - c != NULL ? c->ibuf.fd : -1); + log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags, + c); empty = TAILQ_EMPTY(&cmdq->queue); if (empty) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index e38ea8f1..79e0b55e 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -170,12 +170,11 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, wc = cmd_wait_for_add(name); if (wc->woken) { - log_debug("wait channel %s already woken (client %d)", wc->name, - c->tty.fd); + log_debug("wait channel %s already woken (%p)", wc->name, c); cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } - log_debug("wait channel %s not woken (client %d)", wc->name, c->tty.fd); + log_debug("wait channel %s not woken (%p)", wc->name, c); TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; diff --git a/server-client.c b/server-client.c index 320389af..41ecc141 100644 --- a/server-client.c +++ b/server-client.c @@ -131,7 +131,7 @@ server_client_create(int fd) evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); TAILQ_INSERT_TAIL(&clients, c, entry); - log_debug("new client %d", fd); + log_debug("new client %p", c); } /* Open client terminal if needed. */ @@ -172,7 +172,7 @@ server_client_lost(struct client *c) c->stdin_callback(c, 1, c->stdin_callback_data); TAILQ_REMOVE(&clients, c, entry); - log_debug("lost client %d", c->ibuf.fd); + log_debug("lost client %p", c); /* * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called @@ -238,7 +238,7 @@ server_client_lost(struct client *c) void server_client_unref(struct client *c) { - log_debug("unref client %d (%d references)", c->ibuf.fd, c->references); + log_debug("unref client %p (%d references)", c, c->references); c->references--; if (c->references == 0) @@ -251,7 +251,7 @@ server_client_free(unused int fd, unused short events, void *arg) { struct client *c = arg; - log_debug("free client %d (%d references)", c->ibuf.fd, c->references); + log_debug("free client %p (%d references)", c, c->references); if (c->references == 0) free(c); @@ -998,7 +998,7 @@ server_client_msg_dispatch(struct client *c) continue; } - log_debug("got %u from client %d", imsg.hdr.type, c->ibuf.fd); + log_debug("got %u from client %p", imsg.hdr.type, c); switch (imsg.hdr.type) { case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_TERM: diff --git a/server-fn.c b/server-fn.c index 27908fde..a31a3772 100644 --- a/server-fn.c +++ b/server-fn.c @@ -64,7 +64,7 @@ server_write_client(struct client *c, enum msgtype type, const void *buf, if (c->flags & CLIENT_BAD) return (-1); - log_debug("writing %d to client %d", type, c->ibuf.fd); + log_debug("writing %d to client %p", type, c); error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len); if (error == 1) diff --git a/status.c b/status.c index 7a1d2818..e17c1f5d 100644 --- a/status.c +++ b/status.c @@ -164,7 +164,7 @@ status_timer_callback(unused int fd, unused short events, void *arg) if (tv.tv_sec != 0) evtimer_add(&c->status_timer, &tv); - log_debug("client %d, status interval %d", c->ibuf.fd, (int)tv.tv_sec); + log_debug("client %p, status interval %d", c, (int)tv.tv_sec); } /* Start status timer for client. */ From ddbc4a0f6ca0c6486a038ce91971212785d44744 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 21 Oct 2015 11:13:47 +0000 Subject: [PATCH 780/949] By popular demand add a default binding for mouse wheel up to scroll into history (if the mouse is, on of course). --- key-bindings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/key-bindings.c b/key-bindings.c index 3dc3a054..cc1a1c6e 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -225,6 +225,7 @@ key_bindings_init(void) "bind -n MouseDown1Status select-window -t=", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDown3Pane select-pane -mt=", + "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -e\"'", }; u_int i; struct cmd_list *cmdlist; From 60ca29df64a96412ad6f78a294e0c68b01bed3d6 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 21 Oct 2015 13:14:36 +0000 Subject: [PATCH 781/949] client_key_table was missing. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index 43dce715..1e85a898 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3357,6 +3357,7 @@ The following variables are available, where appropriate: .It Li "client_created_string" Ta "" Ta "String time client created" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_height" Ta "" Ta "Height of client" +.It Li "client_key_table" Ta "" Ta "Current key table" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" From 31fd071faa55638bfaa337d4b62c08e34a24c8f8 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 10:46:24 +0000 Subject: [PATCH 782/949] Rename shutdown to exit. --- server.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server.c b/server.c index fd77ff38..2aed3dbb 100644 --- a/server.c +++ b/server.c @@ -44,7 +44,7 @@ struct clients clients; int server_fd; -int server_shutdown; +int server_exit; struct event server_ev_accept; struct session *marked_session; @@ -55,8 +55,8 @@ struct layout_cell *marked_layout_cell; int server_create_socket(void); void server_loop(void); -int server_should_shutdown(void); -void server_send_shutdown(void); +int server_should_exit(void); +void server_send_exit(void); void server_accept_callback(int, short, void *); void server_signal_callback(int, short, void *); void server_child_signal(void); @@ -232,7 +232,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) void server_loop(void) { - while (!server_should_shutdown()) { + while (!server_should_exit()) { log_debug("event dispatch enter"); event_loop(EVLOOP_ONCE); log_debug("event dispatch exit"); @@ -243,7 +243,7 @@ server_loop(void) /* Check if the server should exit (no more clients or sessions). */ int -server_should_shutdown(void) +server_should_exit(void) { struct client *c; @@ -268,9 +268,9 @@ server_should_shutdown(void) return (1); } -/* Shutdown the server by killing all clients and windows. */ +/* Exit the server by killing all clients and windows. */ void -server_send_shutdown(void) +server_send_exit(void) { struct client *c, *c1; struct session *s, *s1; @@ -281,7 +281,7 @@ server_send_shutdown(void) if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) server_client_lost(c); else - server_write_client(c, MSG_SHUTDOWN, NULL, 0); + server_write_client(c, MSG_EXIT, NULL, 0); c->session = NULL; } @@ -348,7 +348,7 @@ server_accept_callback(int fd, short events, unused void *data) } fatal("accept failed"); } - if (server_shutdown) { + if (server_exit) { close(newfd); return; } @@ -386,8 +386,8 @@ server_signal_callback(int sig, unused short events, unused void *data) switch (sig) { case SIGTERM: - server_shutdown = 1; - server_send_shutdown(); + server_exit = 1; + server_send_exit(); break; case SIGCHLD: server_child_signal(); From 515dfea4b79a09513d1ede5dbb8a89c3e18b8393 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 10:48:30 +0000 Subject: [PATCH 783/949] This should not be changed. --- server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.c b/server.c index 2aed3dbb..ff38a353 100644 --- a/server.c +++ b/server.c @@ -281,7 +281,7 @@ server_send_exit(void) if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) server_client_lost(c); else - server_write_client(c, MSG_EXIT, NULL, 0); + server_write_client(c, MSG_SHUTDOWN, NULL, 0); c->session = NULL; } From c2c2d44c72a6e693c22bb48d54b0b337a568b70b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 11:00:51 +0000 Subject: [PATCH 784/949] Log identify messages. --- server-client.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server-client.c b/server-client.c index 41ecc141..435e8b1a 100644 --- a/server-client.c +++ b/server-client.c @@ -1155,38 +1155,45 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) fatalx("bad MSG_IDENTIFY_FLAGS size"); memcpy(&flags, data, sizeof flags); c->flags |= flags; + log_debug("client %p IDENTIFY_FLAGS %#x", c, flags); break; case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); c->term = xstrdup(data); + log_debug("client %p IDENTIFY_TERM %s", c, data); break; case MSG_IDENTIFY_TTYNAME: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TTYNAME string"); c->ttyname = xstrdup(data); + log_debug("client %p IDENTIFY_TTYNAME %s", c, data); break; case MSG_IDENTIFY_CWD: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_CWD string"); if ((c->cwd = open(data, O_RDONLY)) == -1) c->cwd = open("/", O_RDONLY); + log_debug("client %p IDENTIFY_CWD %s", c, data); break; case MSG_IDENTIFY_STDIN: if (datalen != 0) fatalx("bad MSG_IDENTIFY_STDIN size"); c->fd = imsg->fd; + log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); break; case MSG_IDENTIFY_ENVIRON: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) environ_put(&c->environ, data); + log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: if (datalen != sizeof c->pid) fatalx("bad MSG_IDENTIFY_CLIENTPID size"); memcpy(&c->pid, data, sizeof c->pid); + log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid); break; default: break; From a05c27a7e1c4d43709817d6746a510f16c960b4b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 11:19:31 +0000 Subject: [PATCH 785/949] Unzoom before -LRUD, reported by Andy Weidenbaum. --- cmd-select-pane.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e76587cc..7986e98c 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -120,14 +120,19 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'L')) + if (args_has(self->args, 'L')) { + server_unzoom_window(wp->window); wp = window_pane_find_left(wp); - else if (args_has(self->args, 'R')) + } else if (args_has(self->args, 'R')) { + server_unzoom_window(wp->window); wp = window_pane_find_right(wp); - else if (args_has(self->args, 'U')) + } else if (args_has(self->args, 'U')) { + server_unzoom_window(wp->window); wp = window_pane_find_up(wp); - else if (args_has(self->args, 'D')) + } else if (args_has(self->args, 'D')) { + server_unzoom_window(wp->window); wp = window_pane_find_down(wp); + } if (wp == NULL) return (CMD_RETURN_NORMAL); From 3ebcf25149d75977ea97e9d4f786e0508d1a0d5e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 11:23:00 +0000 Subject: [PATCH 786/949] If the pane is still on all_window_panes but not actually connected to window or session (which can happen if it is killed during a command sequence and something else has a reference), fall back to the best effort. Fixes "tmux killw\; detach" for Rudis Muiznieks. --- cmd-find.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index ec883cbd..376d9041 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -255,24 +255,35 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) wp = NULL; /* Not running in a pane. We know nothing. Find the best session. */ - if (wp == NULL) { - fs->s = cmd_find_best_session(NULL, 0, fs->flags); - if (fs->s == NULL) - return (-1); - fs->wl = fs->s->curw; - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - fs->wp = fs->w->active; - return (0); - } + if (wp == NULL) + goto unknown_pane; /* We now know the window and pane. */ fs->w = wp->window; fs->wp = wp; /* Find the best session and winlink. */ - if (cmd_find_best_session_with_window(fs) != 0) + if (cmd_find_best_session_with_window(fs) != 0) { + if (wp != NULL) { + /* + * The window may have been destroyed but the pane + * still on all_window_panes due to something else + * holding a reference. + */ + goto unknown_pane; + } return (-1); + } + return (0); + +unknown_pane: + fs->s = cmd_find_best_session(NULL, 0, fs->flags); + if (fs->s == NULL) + return (-1); + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; return (0); } From 63a3fd3c0fa41fec6cbc15dbd2bf08ceb3ccb208 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 15:52:54 +0000 Subject: [PATCH 787/949] Use tty_term_flag not _has for XT, and make -2 force direct use of 256-colour escape sequences (so setaf/setab can be bypassed if needed). --- tty.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tty.c b/tty.c index 7be952c8..5d23ee82 100644 --- a/tty.c +++ b/tty.c @@ -228,7 +228,7 @@ tty_start_tty(struct tty *tty) 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_XT)) { + 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"); @@ -293,7 +293,7 @@ tty_stop_tty(struct tty *tty) 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_XT)) { + if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { tty->flags &= ~TTY_FOCUS; tty_raw(tty, "\033[?1004l"); @@ -1647,6 +1647,13 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) { char s[32]; + /* + * If the user has specified -2 to the client, setaf and setab may not + * work (or they may not want to use them), so send the usual sequence. + */ + if (tty->term_flags & TERM_256COLOURS) + goto fallback; + /* * If the terminfo entry has 256 colours and setaf and setab exist, * assume that they work correctly. @@ -1664,13 +1671,6 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) return (0); } - /* - * If the user has specified -2 to the client, setaf and setab may not - * work, so send the usual sequence. - */ - if (tty->term_flags & TERM_256COLOURS) - goto fallback; - return (-1); fallback: From 14da99940806dd63c2180ad238886fcb6735db04 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:02:21 +0000 Subject: [PATCH 788/949] Format for scroll position, from Jorge Morante. --- format.c | 6 +++++- tmux.1 | 1 + tmux.h | 1 + window-copy.c | 10 ++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 2fae8da0..1d80fbf7 100644 --- a/format.c +++ b/format.c @@ -1005,7 +1005,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { struct grid *gd = wp->base.grid; u_int idx; - int status; + int status, scroll_position; if (ft->w == NULL) ft->w = wp->window; @@ -1052,6 +1052,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); + scroll_position = window_copy_scroll_position(wp); + if (scroll_position != -1) + format_add(ft, "scroll_position", "%d", scroll_position); + format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); diff --git a/tmux.1 b/tmux.1 index 1e85a898..7744785a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3406,6 +3406,7 @@ The following variables are available, where appropriate: .It Li "pid" Ta "" Ta "Server PID" .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 "scroll_position" Ta "" Ta "Scroll position in copy mode" .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_activity" Ta "" Ta "Integer time of session last activity" diff --git a/tmux.h b/tmux.h index 72495100..528c22c7 100644 --- a/tmux.h +++ b/tmux.h @@ -2082,6 +2082,7 @@ void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *); void window_copy_start_drag(struct client *, struct mouse_event *); +int window_copy_scroll_position(struct window_pane *); /* window-choose.c */ extern const struct window_mode window_choose_mode; diff --git a/window-copy.c b/window-copy.c index c7d360de..f81a2a16 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2219,6 +2219,16 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny) screen_write_stop(&ctx); } +int +window_copy_scroll_position(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + if (wp->mode != &window_copy_mode) + return (-1); + return (data->oy); +} + void window_copy_rectangle_toggle(struct window_pane *wp) { From 5383b047d1dd0d71f724d8f9cac4e45994506d35 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:07:29 +0000 Subject: [PATCH 789/949] tmux can call pledge() in main with large set and then reduce it slightly in the server to "stdio rpath wpath cpath fattr unix recvfd proc exec tty ps". --- server.c | 4 ++++ tmux.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/server.c b/server.c index ff38a353..7ce1c99d 100644 --- a/server.c +++ b/server.c @@ -178,6 +178,10 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); + if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " + "ps", NULL) != 0) + fatal("pledge failed"); + /* * Must daemonise before loading configuration as the PID changes so * $TMUX would be wrong for sessions created in the config file. diff --git a/tmux.c b/tmux.c index 0b8e6a91..b2530285 100644 --- a/tmux.c +++ b/tmux.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -254,6 +255,10 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); + if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " + "proc exec tty ps", NULL) != 0) + err(1, "pledge"); + if (!(flags & CLIENT_UTF8)) { /* * If the user has set whichever of LC_ALL, LC_CTYPE or LANG From 1a4ddfa8a7c699e48980dd8fa70024ffe0b67f57 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:29:07 +0000 Subject: [PATCH 790/949] If $TMUX is set, and we are unsure about the session, use it. --- cmd-find.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index 376d9041..4173e202 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "tmux.h" @@ -51,6 +52,7 @@ struct cmd_find_state { int idx; }; +struct session *cmd_find_try_TMUX(struct client *, struct window *); int cmd_find_client_better(struct client *, struct client *); struct client *cmd_find_best_client(struct client **, u_int); int cmd_find_session_better(struct session *, struct session *, @@ -109,6 +111,33 @@ const char *cmd_find_pane_table[][2] = { { NULL, NULL } }; +/* Get session from TMUX if present. */ +struct session * +cmd_find_try_TMUX(struct client *c, struct window *w) +{ + struct environ_entry *envent; + char tmp[256]; + long long pid; + u_int session; + struct session *s; + + envent = environ_find(&c->environ, "TMUX"); + if (envent == NULL) + return (NULL); + + if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3) + return (NULL); + if (pid != getpid()) + return (NULL); + log_debug("client %d TMUX is %s (session @%u)", c->ibuf.fd, + envent->value, session); + + s = session_find_by_id(session); + if (s == NULL || (w != NULL && !session_has(s, w))) + return (NULL); + return (s); +} + /* Is this client better? */ int cmd_find_client_better(struct client *c, struct client *than) @@ -192,6 +221,12 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) u_int ssize; struct session *s; + if (fs->cmdq->client != NULL) { + fs->s = cmd_find_try_TMUX(fs->cmdq->client, fs->w); + if (fs->s != NULL) + return (cmd_find_best_winlink_with_window(fs)); + } + ssize = 0; RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, fs->w)) @@ -277,7 +312,9 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) return (0); unknown_pane: - fs->s = cmd_find_best_session(NULL, 0, fs->flags); + fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); + if (fs->s == NULL) + fs->s = cmd_find_best_session(NULL, 0, fs->flags); if (fs->s == NULL) return (-1); fs->wl = fs->s->curw; From 26a55ddcf99b9631af28d8273dac2bafa968adff Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:30:15 +0000 Subject: [PATCH 791/949] Remove some unnecessary checks before free(). --- cmd-new-session.c | 2 +- cmd-new-window.c | 2 +- cmd-split-window.c | 2 +- tty.c | 6 ++---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 7687398e..b3bf3c74 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -133,7 +133,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) strerror(errno)); return (CMD_RETURN_ERROR); } - } else if (cp != NULL) + } else free(cp); cwd = fd; } else if (c != NULL && c->session == NULL) diff --git a/cmd-new-window.c b/cmd-new-window.c index 9cead449..893fe6e2 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -107,7 +107,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) strerror(errno)); return (CMD_RETURN_ERROR); } - } else if (cp != NULL) + } else free(cp); cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) diff --git a/cmd-split-window.c b/cmd-split-window.c index 4e85a0f3..f397113f 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -101,7 +101,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) strerror(errno)); return (CMD_RETURN_ERROR); } - } else if (cp != NULL) + } else free(cp); cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) diff --git a/tty.c b/tty.c index 5d23ee82..3d6f29ca 100644 --- a/tty.c +++ b/tty.c @@ -338,10 +338,8 @@ tty_free(struct tty *tty) tty_close(tty); free(tty->ccolour); - if (tty->path != NULL) - free(tty->path); - if (tty->termname != NULL) - free(tty->termname); + free(tty->path); + free(tty->termname); } void From 2e2b8a95bd13dab848a61113c9974c9ac936f9d6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 23:46:36 +0000 Subject: [PATCH 792/949] Pasting mouse escape sequences is unlikely, so skip them when working out whether the user is pasting. --- server-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 435e8b1a..dbff9212 100644 --- a/server-client.c +++ b/server-client.c @@ -598,7 +598,7 @@ server_client_handle_key(struct client *c, int key) m->valid = 0; /* Treat everything as a regular key when pasting is detected. */ - if (server_client_assume_paste(s)) { + if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) { if (!(c->flags & CLIENT_READONLY)) window_pane_key(wp, c, s, key, m); return; From 3034a71488ce5a76105b5777848dd8578a7aa376 Mon Sep 17 00:00:00 2001 From: deraadt Date: Sun, 25 Oct 2015 07:48:16 +0000 Subject: [PATCH 793/949] Let's see if anyone screams about not being able to specify $TMPDIR for their tmux sockets. (Over the years, I have seen $TMPDIR set up worse than /tmp many times, and don't know how this practice infected other parts of the system. Nothing uses tmpdir(3), nor a huge-temporary-file program like sort.) ok nicm --- tmux.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tmux.c b/tmux.c index b2530285..8b3d0bfe 100644 --- a/tmux.c +++ b/tmux.c @@ -128,8 +128,6 @@ makesocketpath(const char *label) uid = getuid(); if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); - else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); else xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); From 3faa51a0caebb56746988ae7f5e9e9f649dc42b6 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 25 Oct 2015 08:59:26 +0000 Subject: [PATCH 794/949] Pass output from jobs through format_expand() so they are expanded again (this was the previous behaviour). --- format.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/format.c b/format.c index 1d80fbf7..f5f036c2 100644 --- a/format.c +++ b/format.c @@ -39,7 +39,7 @@ struct format_entry; typedef void (*format_cb)(struct format_tree *, struct format_entry *); void format_job_callback(struct job *); -const char *format_job_get(struct format_tree *, const char *); +char *format_job_get(struct format_tree *, const char *); void format_job_timer(int, short, void *); void format_cb_host(struct format_tree *, struct format_entry *); @@ -212,7 +212,7 @@ format_job_callback(struct job *job) } /* Find a job. */ -const char * +char * format_job_get(struct format_tree *ft, const char *cmd) { struct format_job fj0, *fj; @@ -242,7 +242,7 @@ format_job_get(struct format_tree *ft, const char *cmd) if (ft->flags & FORMAT_STATUS) fj->status = 1; - return (fj->out); + return (format_expand(ft, fj->out)); } /* Remove old jobs. */ @@ -709,9 +709,9 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *tmp, *cmd; + char *buf, *tmp, *cmd, *out; const char *ptr, *s, *saved = fmt; - size_t off, len, n, slen; + size_t off, len, n, outlen; int ch, brackets; if (fmt == NULL) @@ -751,18 +751,20 @@ format_expand(struct format_tree *ft, const char *fmt) tmp[n] = '\0'; cmd = format_expand(ft, tmp); - s = format_job_get(ft, cmd); - slen = strlen(s); + out = format_job_get(ft, cmd); + outlen = strlen(out); free(cmd); free(tmp); - while (len - off < slen + 1) { + while (len - off < outlen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; } - memcpy(buf + off, s, slen); - off += slen; + memcpy(buf + off, out, outlen); + off += outlen; + + free(out); fmt += n + 1; continue; From 91f53d590bba4282754a36632e04ddc97170f7ae Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 25 Oct 2015 09:31:07 +0000 Subject: [PATCH 795/949] __OpenBSD__ around pledge() --- server.c | 3 ++- tmux.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server.c b/server.c index 139505d4..7dcad380 100644 --- a/server.c +++ b/server.c @@ -177,10 +177,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); - +#endif /* * Must daemonise before loading configuration as the PID changes so * $TMUX would be wrong for sessions created in the config file. diff --git a/tmux.c b/tmux.c index b8373ab7..18da1abe 100644 --- a/tmux.c +++ b/tmux.c @@ -259,9 +259,11 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); +#endif if (!(flags & CLIENT_UTF8)) { /* From 7930cb54c0cd6105899ba51be767697e1210aec2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 25 Oct 2015 09:31:46 +0000 Subject: [PATCH 796/949] ifdef __OpenBSD__ around pledge(). --- server.c | 2 ++ tmux.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/server.c b/server.c index 139505d4..d3be5cc1 100644 --- a/server.c +++ b/server.c @@ -177,9 +177,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); +#endif /* * Must daemonise before loading configuration as the PID changes so diff --git a/tmux.c b/tmux.c index b8373ab7..18da1abe 100644 --- a/tmux.c +++ b/tmux.c @@ -259,9 +259,11 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); +#endif if (!(flags & CLIENT_UTF8)) { /* From ad437f13d55b62d9a635fb85ec67c25c10ab7d22 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 25 Oct 2015 09:34:56 +0000 Subject: [PATCH 797/949] Add missing headers for getpid() --- cmd-find.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-find.c b/cmd-find.c index 3d129875..ad03445d 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "tmux.h" From c14fb5b633e63cc5f20d1f67fe071e4d4404e48e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 25 Oct 2015 09:38:08 +0000 Subject: [PATCH 798/949] -sys/types.h --- cmd-find.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index ad03445d..01aaeb1f 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "tmux.h" From e65306d8e7bd6db99bd0746cd16a21d2c066b8db Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 25 Oct 2015 22:29:17 +0000 Subject: [PATCH 799/949] Extend the modifiers allowed before formats: as well as the existing #{=10:...} length limit, add #{t:...} to convert a time_t format to a string, #{b:...} for basename and #{d:...} for dirname. Remove all the foo_string time formats as they can now be replaced by "t:", for example #{window_activity_string} becomes #{t:window_activity}. --- cmd-choose-client.c | 2 +- cmd-list-sessions.c | 2 +- format.c | 243 +++++++++++++++++++++++++++----------------- tmux.1 | 26 +++-- tmux.h | 1 - 5 files changed, 168 insertions(+), 106 deletions(-) diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 93ac28a8..93a141ee 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -31,7 +31,7 @@ "#{client_tty}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \ - "(last used #{client_activity_string})" + "(last used #{t:client_activity})" enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_q *); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 8ad55d03..49ef9467 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -30,7 +30,7 @@ #define LIST_SESSIONS_TEMPLATE \ "#{session_name}: #{session_windows} windows " \ - "(created #{session_created_string}) " \ + "(created #{t:session_created}) " \ "[#{session_width}x#{session_height}]" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ diff --git a/format.c b/format.c index f5f036c2..7ff11b59 100644 --- a/format.c +++ b/format.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -52,7 +53,9 @@ void format_cb_current_command(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); void format_cb_pane_tabs(struct format_tree *, struct format_entry *); +char *format_find(struct format_tree *, const char *, int); void format_add_cb(struct format_tree *, const char *, format_cb); +void format_add_tv(struct format_tree *, const char *, struct timeval *); int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); char *format_time_string(time_t); @@ -90,10 +93,16 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) return (strcmp(fj1->cmd, fj2->cmd)); } +/* Format modifiers. */ +#define FORMAT_TIMESTRING 0x1 +#define FORMAT_BASENAME 0x2 +#define FORMAT_DIRNAME 0x4 + /* Entry in format tree. */ struct format_entry { char *key; char *value; + time_t t; format_cb cb; RB_ENTRY(format_entry) entry; }; @@ -503,12 +512,37 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) } fe->cb = NULL; + fe->t = 0; va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); } +/* Add a key and time. */ +void +format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) +{ + struct format_entry *fe; + struct format_entry *fe_now; + + fe = xmalloc(sizeof *fe); + fe->key = xstrdup(key); + + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); + if (fe_now != NULL) { + free(fe->key); + free(fe); + free(fe_now->value); + fe = fe_now; + } + + fe->cb = NULL; + fe->t = tv->tv_sec; + + fe->value = NULL; +} + /* Add a key and function. */ void format_add_cb(struct format_tree *ft, const char *key, format_cb cb) @@ -528,57 +562,99 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb) } fe->cb = cb; + fe->t = 0; fe->value = NULL; } /* Find a format entry. */ -const char * -format_find(struct format_tree *ft, const char *key) +char * +format_find(struct format_tree *ft, const char *key, int modifiers) { struct format_entry *fe, fe_find; struct options_entry *o; struct environ_entry *envent; - static char s[16]; + static char s[64]; + const char *found; + char *copy, *saved; - o = options_find(&global_options, key); - if (o == NULL && ft->w != NULL) - o = options_find(&ft->w->options, key); - if (o == NULL) - o = options_find(&global_w_options, key); - if (o == NULL && ft->s != NULL) - o = options_find(&ft->s->options, key); - if (o == NULL) - o = options_find(&global_s_options, key); - if (o != NULL) { - switch (o->type) { - case OPTIONS_STRING: - return (o->str); - case OPTIONS_NUMBER: - xsnprintf(s, sizeof s, "%lld", o->num); - return (s); - case OPTIONS_STYLE: - return (style_tostring(&o->style)); + found = NULL; + + if (~modifiers & FORMAT_TIMESTRING) { + o = options_find(&global_options, key); + if (o == NULL && ft->w != NULL) + o = options_find(&ft->w->options, key); + if (o == NULL) + o = options_find(&global_w_options, key); + if (o == NULL && ft->s != NULL) + o = options_find(&ft->s->options, key); + if (o == NULL) + o = options_find(&global_s_options, key); + if (o != NULL) { + switch (o->type) { + case OPTIONS_STRING: + found = o->str; + goto found; + case OPTIONS_NUMBER: + xsnprintf(s, sizeof s, "%lld", o->num); + found = s; + goto found; + case OPTIONS_STYLE: + found = style_tostring(&o->style); + goto found; + } } } fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { + if (modifiers & FORMAT_TIMESTRING) { + if (fe->t == 0) + return (NULL); + ctime_r(&fe->t, s); + s[strcspn(s, "\n")] = '\0'; + found = s; + goto found; + } + if (fe->t != 0) { + xsnprintf(s, sizeof s, "%lld", (long long)fe->t); + found = s; + goto found; + } if (fe->value == NULL && fe->cb != NULL) fe->cb(ft, fe); - return (fe->value); + found = fe->value; + goto found; } - envent = NULL; - if (ft->s != NULL) - envent = environ_find(&ft->s->environ, key); - if (envent == NULL) - envent = environ_find(&global_environ, key); - if (envent != NULL) - return (envent->value); + if (~modifiers & FORMAT_TIMESTRING) { + envent = NULL; + if (ft->s != NULL) + envent = environ_find(&ft->s->environ, key); + if (envent == NULL) + envent = environ_find(&global_environ, key); + if (envent != NULL) { + found = envent->value; + goto found; + } + } return (NULL); + +found: + copy = xstrdup(found); + if (modifiers & FORMAT_BASENAME) { + saved = copy; + copy = xstrdup(basename(saved)); + free(saved); + } + if (modifiers & FORMAT_DIRNAME) { + saved = copy; + copy = xstrdup(dirname(saved)); + free(saved); + } + return (copy); } /* @@ -589,10 +665,10 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr, *saved, *trimmed; - const char *value; + char *copy, *copy0, *endptr, *ptr, *saved, *trimmed, *value; size_t valuelen; u_long limit = 0; + int modifiers = 0; /* Make a copy of the key. */ copy0 = copy = xmalloc(keylen + 1); @@ -600,24 +676,34 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, copy[keylen] = '\0'; /* Is there a length limit or whatnot? */ - if (!isalpha((u_char) *copy) && *copy != '@' && *copy != '?') { - while (*copy != ':' && *copy != '\0') { - switch (*copy) { - case '=': - errno = 0; - limit = strtoul(copy + 1, &endptr, 10); - if (errno == ERANGE && limit == ULONG_MAX) - goto fail; - copy = endptr; - break; - default: - copy++; - break; - } - } - if (*copy != ':') - goto fail; - copy++; + switch (copy[0]) { + case '=': + errno = 0; + limit = strtoul(copy + 1, &endptr, 10); + if (errno == ERANGE && limit == ULONG_MAX) + break; + if (*endptr != ':') + break; + copy = endptr + 1; + break; + case 'b': + if (copy[1] != ':') + break; + modifiers |= FORMAT_BASENAME; + copy += 2; + break; + case 'd': + if (copy[1] != ':') + break; + modifiers |= FORMAT_DIRNAME; + copy += 2; + break; + case 't': + if (copy[1] != ':') + break; + modifiers |= FORMAT_TIMESTRING; + copy += 2; + break; } /* @@ -630,7 +716,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; *ptr = '\0'; - value = format_find(ft, copy + 1); + value = saved = format_find(ft, copy + 1, modifiers); if (value != NULL && *value != '\0' && (value[0] != '0' || value[1] != '\0')) { value = ptr + 1; @@ -644,13 +730,13 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; value = ptr + 1; } - saved = format_expand(ft, value); - value = saved; + value = format_expand(ft, value); + free(saved); + saved = value; } else { - value = format_find(ft, copy); + saved = value = format_find(ft, copy, modifiers); if (value == NULL) - value = ""; - saved = NULL; + saved = value = xstrdup(""); } /* Truncate the value if needed. */ @@ -820,18 +906,6 @@ format_expand(struct format_tree *ft, const char *fmt) return (buf); } -/* Get time as a string. */ -char * -format_time_string(time_t t) -{ - char *tim; - - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - - return (tim); -} - /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, @@ -859,7 +933,6 @@ void format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; - time_t t; ft->s = s; @@ -874,20 +947,9 @@ format_defaults_session(struct format_tree *ft, struct session *s) if (sg != NULL) format_add(ft, "session_group", "%u", session_group_index(sg)); - t = s->creation_time.tv_sec; - format_add(ft, "session_created", "%lld", (long long) t); - format_add(ft, "session_created_string", "%s", format_time_string(t)); - - t = s->last_attached_time.tv_sec; - if (t != 0) { /* zero if never attached */ - format_add(ft, "session_last_attached", "%lld", (long long) t); - format_add(ft, "session_last_attached_string", "%s", - format_time_string(t)); - } - - t = s->activity_time.tv_sec; - format_add(ft, "session_activity", "%lld", (long long) t); - format_add(ft, "session_activity_string", "%s", format_time_string(t)); + format_add_tv(ft, "session_created", &s->creation_time); + format_add_tv(ft, "session_last_attached", &s->last_attached_time); + format_add_tv(ft, "session_activity", &s->activity_time); format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); @@ -900,7 +962,6 @@ void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; - time_t t; if (ft->s == NULL) ft->s = c->session; @@ -915,13 +976,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); - t = c->creation_time.tv_sec; - format_add(ft, "client_created", "%lld", (long long) t); - format_add(ft, "client_created_string", "%s", format_time_string(t)); - - t = c->activity_time.tv_sec; - format_add(ft, "client_activity", "%lld", (long long) t); - format_add(ft, "client_activity_string", "%s", format_time_string(t)); + format_add_tv(ft, "client_created", &c->creation_time); + format_add_tv(ft, "client_activity", &c->activity_time); if (strcmp(c->keytable->name, "root") == 0) format_add(ft, "client_prefix", "%d", 0); @@ -951,14 +1007,9 @@ format_defaults_client(struct format_tree *ft, struct client *c) void format_defaults_window(struct format_tree *ft, struct window *w) { - time_t t; - ft->w = w; - t = w->activity_time.tv_sec; - format_add(ft, "window_activity", "%lld", (long long) t); - format_add(ft, "window_activity_string", "%s", format_time_string(t)); - + format_add_tv(ft, "window_activity", &w->activity_time); format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); diff --git a/tmux.1 b/tmux.1 index 7744785a..39172f34 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3319,12 +3319,31 @@ if is enabled, or .Ql no if not. +.Pp A limit may be placed on the length of the resultant string by prefixing it by an .Ql = , a number and a colon, so .Ql #{=10:pane_title} will include at most the first 10 characters of the pane title. +Prefixing a time variable with +.Ql t: +will convert it to a string, so if +.Ql #{window_activity} +gives +.Ql 1445765102, +.Ql #{t:window_activity} +gives +.Ql Sun Oct 25 09:25:02 2015 . +The +.Ql b: +and +.Ql d: +prefixes are +.Xr basename 3 +and +.Xr dirname 3 +of the variable respectively. .Pp In addition, the first line of a shell command's output may be inserted using .Ql #() . @@ -3352,9 +3371,7 @@ 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 "Integer time client last had activity" -.It Li "client_activity_string" Ta "" Ta "String time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" -.It Li "client_created_string" Ta "" Ta "String time client created" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_key_table" Ta "" Ta "Current key table" @@ -3410,11 +3427,8 @@ The following variables are available, where appropriate: .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_activity" Ta "" Ta "Integer time of session last activity" -.It Li "session_activity_string" Ta "" Ta "String time of session last activity" .It Li "session_created" Ta "" Ta "Integer time session created" -.It Li "session_created_string" Ta "" Ta "String time session created" .It Li "session_last_attached" Ta "" Ta "Integer time session last attached" -.It Li "session_last_attached_string" Ta "" Ta "String time session last attached" .It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_height" Ta "" Ta "Height of session" @@ -3424,9 +3438,7 @@ The following variables are available, where appropriate: .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "window_activity" Ta "" Ta "Integer time of window last activity" -.It Li "window_activity_string" Ta "" Ta "String time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" -.It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" .It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" .It Li "window_flags" Ta "#F" Ta "Window flags" diff --git a/tmux.h b/tmux.h index 528c22c7..97238865 100644 --- a/tmux.h +++ b/tmux.h @@ -1453,7 +1453,6 @@ struct format_tree *format_create_flags(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); -const char *format_find(struct format_tree *, const char *); char *format_expand_time(struct format_tree *, const char *, time_t); char *format_expand(struct format_tree *, const char *); void format_defaults(struct format_tree *, struct client *, From c582f7d177ed14d7d1bbe5ecdd76d9732910fbbf Mon Sep 17 00:00:00 2001 From: jmc Date: Mon, 26 Oct 2015 00:15:37 +0000 Subject: [PATCH 800/949] space before punctuation; --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 39172f34..a9f3cdc9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3331,7 +3331,7 @@ Prefixing a time variable with will convert it to a string, so if .Ql #{window_activity} gives -.Ql 1445765102, +.Ql 1445765102 , .Ql #{t:window_activity} gives .Ql Sun Oct 25 09:25:02 2015 . From a22fe33aa00d4e29e8d71b58b8f728d83e5d23f3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 17:17:06 +0000 Subject: [PATCH 801/949] Some extra logging of where keys are actually going. --- input-keys.c | 5 +++-- server-client.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index d812a906..6e49dd3b 100644 --- a/input-keys.c +++ b/input-keys.c @@ -145,7 +145,8 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) char *out; u_char ch; - log_debug("writing key 0x%x (%s)", key, key_string_lookup_key(key)); + log_debug("writing key 0x%x (%s) to %%%u", key, + key_string_lookup_key(key), wp->id); /* If this is a mouse key, pass off to mouse function. */ if (KEYC_IS_MOUSE(key)) { @@ -251,6 +252,6 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) buf[len++] = x + 33; buf[len++] = y + 33; } - log_debug("writing mouse %.*s", (int)len, buf); + log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); bufferevent_write(wp->event, buf, len); } diff --git a/server-client.c b/server-client.c index dbff9212..24f0250a 100644 --- a/server-client.c +++ b/server-client.c @@ -369,6 +369,8 @@ server_client_check_mouse(struct client *c) wp = window_get_active_at(s->curw->window, x, y); if (wp != NULL) where = PANE; + log_debug("mouse at %u,%u is on pane %%%u", x, y, + wp->id); } if (where == NOWHERE) return (KEYC_NONE); From b85be36d1cb96e0328b3f1dc9388288989d3b8e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 22:03:04 +0000 Subject: [PATCH 802/949] Handle unknown keys more gracefully, return a string instead of NULL. --- cmd-list-keys.c | 8 -------- key-string.c | 6 ++++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index e2d5dc52..d26486bd 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -77,8 +77,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); - if (key == NULL) - continue; if (bd->can_repeat) repeat = 1; @@ -97,8 +95,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); - if (key == NULL) - continue; if (!repeat) r = ""; @@ -140,8 +136,6 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) any_mode = 0; RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); - if (key == NULL) - continue; if (mbind->mode != 0) any_mode = 1; @@ -153,8 +147,6 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); - if (key == NULL) - continue; mode = ""; if (mbind->mode != 0) diff --git a/key-string.c b/key-string.c index b6474c4f..88cd2a32 100644 --- a/key-string.c +++ b/key-string.c @@ -238,8 +238,10 @@ key_string_lookup_key(int key) } /* Invalid keys are errors. */ - if (key == 127 || key > 255) - return (NULL); + if (key == 127 || key > 255) { + snprintf(out, sizeof out, "", key); + return (out); + } /* Check for standard or control key. */ if (key >= 0 && key <= 32) { From 380a1ea8ef8c4d47eda53214f0e283bdad857a6d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 23:06:18 +0000 Subject: [PATCH 803/949] Default bindings for mouse wheel on status line to change window (like we had before), from Patrick Palka. --- key-bindings.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/key-bindings.c b/key-bindings.c index cc1a1c6e..2d739e84 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -223,6 +223,8 @@ key_bindings_init(void) "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", + "bind -n WheelDownStatus next-window", + "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDown3Pane select-pane -mt=", "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -e\"'", From 640c6fdd5fd859cffecf461647440a437d25f879 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 23:16:18 +0000 Subject: [PATCH 804/949] If a mouse event has no key binding, pass it through to the pane it happened in, not the active pane like normal key presses. Fixes problems seen by Enrico Ghirardi. --- server-client.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/server-client.c b/server-client.c index 24f0250a..0e657ae8 100644 --- a/server-client.c +++ b/server-client.c @@ -551,7 +551,6 @@ server_client_handle_key(struct client *c, int key) if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; w = s->curw->window; - wp = w->active; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) @@ -592,19 +591,14 @@ server_client_handle_key(struct client *c, int key) m->valid = 1; m->key = key; - if (!options_get_number(&s->options, "mouse")) { - window_pane_key(wp, c, s, key, m); - return; - } + if (!options_get_number(&s->options, "mouse")) + goto forward; } else m->valid = 0; /* Treat everything as a regular key when pasting is detected. */ - if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) { - if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); - return; - } + if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) + goto forward; retry: /* Try to see if there is a key binding in the current table. */ @@ -680,7 +674,17 @@ retry: key == options_get_number(&s->options, "prefix2")) { server_client_key_table(c, "prefix"); server_status_client(c); - } else if (!(c->flags & CLIENT_READONLY)) + return; + } + +forward: + if (c->flags & CLIENT_READONLY) + return; + if (KEYC_IS_MOUSE(key)) + wp = cmd_mouse_pane(m, NULL, NULL); + else + wp = w->active; + if (wp != NULL) window_pane_key(wp, c, s, key, m); } From 3fc001d0a2e2456fb266f689964fe116d58c2227 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 09:15:21 +0000 Subject: [PATCH 805/949] Use copy-mode -et= in WheelUpPane binding, from Patrick Palka. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index 2d739e84..e12dd62c 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -227,7 +227,7 @@ key_bindings_init(void) "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDown3Pane select-pane -mt=", - "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -e\"'", + "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", }; u_int i; struct cmd_list *cmdlist; From 17c2c4219df2a8fec4c2f46f718a3fbbbfebe50a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 09:18:06 +0000 Subject: [PATCH 806/949] The format callback may not always succeed, so we need to check for NULL. From Patrick Palka. --- format.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/format.c b/format.c index 7ff11b59..0d7d7c20 100644 --- a/format.c +++ b/format.c @@ -643,6 +643,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers) return (NULL); found: + if (found == NULL) + return (NULL); copy = xstrdup(found); if (modifiers & FORMAT_BASENAME) { saved = copy; From 9952201ca72b42819d64a9174fa7b5b898215668 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 09:28:31 +0000 Subject: [PATCH 807/949] Count brackets in #{?...} so that nested conditional formats work, from Daniel De Graaf. --- format.c | 34 ++++++++++++++++++++-------------- screen.c | 2 -- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/format.c b/format.c index 0d7d7c20..285bfadb 100644 --- a/format.c +++ b/format.c @@ -670,7 +670,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, char *copy, *copy0, *endptr, *ptr, *saved, *trimmed, *value; size_t valuelen; u_long limit = 0; - int modifiers = 0; + int modifiers = 0, brackets; /* Make a copy of the key. */ copy0 = copy = xmalloc(keylen + 1); @@ -718,20 +718,26 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; *ptr = '\0'; - value = saved = format_find(ft, copy + 1, modifiers); - if (value != NULL && *value != '\0' && - (value[0] != '0' || value[1] != '\0')) { - value = ptr + 1; - ptr = strchr(value, ','); - if (ptr == NULL) - goto fail; - *ptr = '\0'; - } else { - ptr = strchr(ptr + 1, ','); - if (ptr == NULL) - goto fail; - value = ptr + 1; + value = ptr + 1; + saved = format_find(ft, copy + 1, modifiers); + + brackets = 0; + for (ptr = ptr + 1; *ptr != '\0'; ptr++) { + if (*ptr == '{') + brackets++; + if (*ptr == '}') + brackets--; + if (*ptr == ',' && brackets == 0) + break; } + if (*ptr == '\0') + goto fail; + + if (saved != NULL && *saved != '\0' && + (saved[0] != '0' || saved[1] != '\0')) { + *ptr = '\0'; + } else + value = ptr + 1; value = format_expand(ft, value); free(saved); saved = value; diff --git a/screen.c b/screen.c index f487cc67..5551bb93 100644 --- a/screen.c +++ b/screen.c @@ -194,8 +194,6 @@ screen_resize_y(struct screen *s, u_int sy) * Now just increase the history size, if possible, to take * over the lines which are left. If history is off, delete * lines from the top. - * - * XXX Should apply history limit? */ available = s->cy; if (gd->flags & GRID_HISTORY) From 07b0ea03c33893bd2b104db5ea4e1397f92e0477 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 13:23:24 +0000 Subject: [PATCH 808/949] Break the common process set up, event loop and imsg dispatch code between server and client out into a separate internal API. This will make it easier to add another process. --- Makefile | 1 + client.c | 509 ++++++++++++++++++------------------------- cmd-attach-session.c | 18 +- cmd-detach-client.c | 13 +- cmd-find.c | 14 +- cmd-new-session.c | 7 +- proc.c | 249 +++++++++++++++++++++ server-client.c | 266 ++++++++++------------ server-fn.c | 64 +----- server.c | 72 ++---- signal.c | 14 +- tmux.c | 4 +- tmux.h | 31 ++- 13 files changed, 648 insertions(+), 614 deletions(-) create mode 100644 proc.c diff --git a/Makefile b/Makefile index 653c8d85..89c8c5c8 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,7 @@ SRCS= alerts.c \ options-table.c \ options.c \ paste.c \ + proc.c \ procname.c \ resize.c \ screen-redraw.c \ diff --git a/client.c b/client.c index 73d871bc..606feefe 100644 --- a/client.c +++ b/client.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -33,10 +34,10 @@ #include "tmux.h" -int client_flags; -struct imsgbuf client_ibuf; -struct event client_event; -struct event client_stdin; +struct tmuxproc *client_proc; +struct tmuxpeer *client_peer; +int client_flags; +struct event client_stdin; enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -47,24 +48,21 @@ enum { CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, } client_exitreason = CLIENT_EXIT_NONE; -int client_exitval; -enum msgtype client_exittype; -const char *client_exitsession; -int client_attached; +int client_exitval; +enum msgtype client_exittype; +const char *client_exitsession; +int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); void client_send_identify(const char *, const char *); -int client_write_one(enum msgtype, int, const void *, size_t); -int client_write_server(enum msgtype, const void *, size_t); -void client_update_event(void); -void client_signal(int, short, void *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); -void client_callback(int, short, void *); -int client_dispatch_attached(void); -int client_dispatch_wait(void); +void client_signal(int); +void client_dispatch(struct imsg *, void *); +void client_dispatch_attached(struct imsg *); +void client_dispatch_wait(struct imsg *); const char *client_exit_message(void); /* @@ -222,6 +220,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct termios tio, saved_tio; size_t size; + /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ + signal(SIGCHLD, SIG_IGN); + /* Save the flags. */ client_flags = flags; @@ -254,13 +255,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) cmd_list_free(cmdlist); } - /* Set process title, log and signals now this is the client. */ - setproctitle("client (%s)", socket_path); - logfile("client"); - - /* Establish signal handlers. */ - set_signals(client_signal); - /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { @@ -274,6 +268,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags) return (1); } + /* Build process state. */ + client_proc = proc_start("client", base, 0, client_signal); + client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); + /* Save these before pledge(). */ if ((cwd = getcwd(path, sizeof path)) == NULL) cwd = "/"; @@ -298,10 +296,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) options_free(&global_w_options); environ_free(&global_environ); - /* Create imsg. */ - imsg_init(&client_ibuf, fd); - event_set(&client_event, fd, EV_READ, client_callback, NULL); - /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, @@ -345,18 +339,17 @@ client_main(struct event_base *base, int argc, char **argv, int flags) size += sizeof *data; /* Send the command. */ - if (client_write_server(msg, data, size) != 0) { + if (proc_send(client_peer, msg, -1, data, size) != 0) { fprintf(stderr, "failed to send command\n"); free(data); return (1); } free(data); } else if (msg == MSG_SHELL) - client_write_server(msg, NULL, 0); + proc_send(client_peer, msg, -1, NULL, 0); - /* Set the event and dispatch. */ - client_update_event(); - event_dispatch(); + /* Start main loop. */ + proc_loop(client_proc, NULL); /* Print the exit message, if any, and exit. */ if (client_attached) { @@ -388,144 +381,29 @@ client_send_identify(const char *ttynam, const char *cwd) int fd, flags = client_flags; pid_t pid; - client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); + proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); if ((s = getenv("TERM")) == NULL) s = ""; - client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); - client_write_one(MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); + proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); + proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); - client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); pid = getpid(); - client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); + proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) - client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } - client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); -} - -/* Helper to send one message. */ -int -client_write_one(enum msgtype type, int fd, const void *buf, size_t len) -{ - int retval; - - retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd, - (void *)buf, len); - if (retval != 1) - return (-1); - return (0); -} - -/* Write a message to the server without a file descriptor. */ -int -client_write_server(enum msgtype type, const void *buf, size_t len) -{ - int retval; - - retval = client_write_one(type, -1, buf, len); - if (retval == 0) - client_update_event(); - return (retval); -} - -/* Update client event based on whether it needs to read or read and write. */ -void -client_update_event(void) -{ - short events; - - event_del(&client_event); - events = EV_READ; - if (client_ibuf.w.queued > 0) - events |= EV_WRITE; - event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); - event_add(&client_event, NULL); -} - -/* Callback to handle signals in the client. */ -void -client_signal(int sig, unused short events, unused void *arg) -{ - struct sigaction sigact; - int status; - - if (sig == SIGCHLD) - waitpid(WAIT_ANY, &status, WNOHANG); - else if (!client_attached) { - if (sig == SIGTERM) - event_loopexit(NULL); - } else { - switch (sig) { - case SIGHUP: - client_exitreason = CLIENT_EXIT_LOST_TTY; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGTERM: - client_exitreason = CLIENT_EXIT_TERMINATED; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGWINCH: - client_write_server(MSG_RESIZE, NULL, 0); - break; - case SIGCONT: - memset(&sigact, 0, sizeof sigact); - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_RESTART; - sigact.sa_handler = SIG_IGN; - if (sigaction(SIGTSTP, &sigact, NULL) != 0) - fatal("sigaction failed"); - client_write_server(MSG_WAKEUP, NULL, 0); - break; - } - } - - client_update_event(); -} - -/* Callback for client imsg read events. */ -void -client_callback(unused int fd, short events, unused void *arg) -{ - ssize_t n; - int retval; - - if (events & EV_READ) { - if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) - goto lost_server; - if (client_attached) - retval = client_dispatch_attached(); - else - retval = client_dispatch_wait(); - if (retval != 0) { - event_loopexit(NULL); - return; - } - } - - if (events & EV_WRITE) { - if (msgbuf_write(&client_ibuf.w) <= 0 && errno != EAGAIN) - goto lost_server; - } - - client_update_event(); - return; - -lost_server: - client_exitreason = CLIENT_EXIT_LOST_SERVER; - client_exitval = 1; - event_loopexit(NULL); + proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } /* Callback for client stdin read events. */ @@ -538,10 +416,9 @@ client_stdin_callback(unused int fd, unused short events, unused void *arg) if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) return; - client_write_server(MSG_STDIN, &data, sizeof data); + proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); if (data.size <= 0) event_del(&client_stdin); - client_update_event(); } /* Force write to file descriptor. */ @@ -591,13 +468,65 @@ client_exec(const char *shell) fatal("execl failed"); } -/* Dispatch imsgs when in wait state (before MSG_READY). */ -int -client_dispatch_wait(void) +/* Callback to handle signals in the client. */ +void +client_signal(int sig) +{ + struct sigaction sigact; + int status; + + if (sig == SIGCHLD) + waitpid(WAIT_ANY, &status, WNOHANG); + else if (!client_attached) { + if (sig == SIGTERM) + proc_exit(client_proc); + } else { + switch (sig) { + case SIGHUP: + client_exitreason = CLIENT_EXIT_LOST_TTY; + client_exitval = 1; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGTERM: + client_exitreason = CLIENT_EXIT_TERMINATED; + client_exitval = 1; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGWINCH: + proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); + break; + case SIGCONT: + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); + break; + } + } +} + +/* Callback for client read events. */ +void +client_dispatch(struct imsg *imsg, unused void *arg) +{ + if (imsg == NULL) { + client_exitreason = CLIENT_EXIT_LOST_SERVER; + client_exitval = 1; + } else if (client_attached) + client_dispatch_attached(imsg); + else + client_dispatch_wait(imsg); +} + +/* Dispatch imsgs when in wait state (before MSG_READY). */ +void +client_dispatch_wait(struct imsg *imsg) { - struct imsg imsg; char *data; - ssize_t n, datalen; + ssize_t datalen; struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; @@ -615,163 +544,141 @@ client_dispatch_wait(void) pledge_applied = 1; }; - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - log_debug("got %u from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_EXIT: - case MSG_SHUTDOWN: - if (datalen != sizeof retval && datalen != 0) - fatalx("bad MSG_EXIT size"); - if (datalen == sizeof retval) { - memcpy(&retval, data, sizeof retval); - client_exitval = retval; - } - imsg_free(&imsg); - return (-1); - case MSG_READY: - if (datalen != 0) - fatalx("bad MSG_READY size"); - - event_del(&client_stdin); - client_attached = 1; - client_write_server(MSG_RESIZE, 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"); - - fprintf(stderr, "protocol version mismatch " - "(client %d, server %u)\n", PROTOCOL_VERSION, - imsg.hdr.peerid); - client_exitval = 1; - - imsg_free(&imsg); - return (-1); - case MSG_SHELL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_SHELL string"); - - clear_signals(0); - client_exec(data); - /* NOTREACHED */ - case MSG_DETACH: - case MSG_DETACHKILL: - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXITED: - imsg_free(&imsg); - return (-1); + switch (imsg->hdr.type) { + case MSG_EXIT: + case MSG_SHUTDOWN: + if (datalen != sizeof retval && datalen != 0) + fatalx("bad MSG_EXIT size"); + if (datalen == sizeof retval) { + memcpy(&retval, data, sizeof retval); + client_exitval = retval; } + proc_exit(client_proc); + break; + case MSG_READY: + if (datalen != 0) + fatalx("bad MSG_READY size"); - imsg_free(&imsg); + 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"); + + fprintf(stderr, "protocol version mismatch " + "(client %d, server %u)\n", PROTOCOL_VERSION, + imsg->hdr.peerid); + client_exitval = 1; + proc_exit(client_proc); + break; + case MSG_SHELL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_SHELL string"); + + clear_signals(0); + client_exec(data); + /* NOTREACHED */ + case MSG_DETACH: + case MSG_DETACHKILL: + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXITED: + proc_exit(client_proc); + break; } } /* Dispatch imsgs in attached state (after MSG_READY). */ -int -client_dispatch_attached(void) +void +client_dispatch_attached(struct imsg *imsg) { - struct imsg imsg; struct sigaction sigact; char *data; - ssize_t n, datalen; + ssize_t datalen; - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + switch (imsg->hdr.type) { + case MSG_DETACH: + case MSG_DETACHKILL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_DETACH string"); - log_debug("got %u from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_DETACH: - case MSG_DETACHKILL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_DETACH string"); + client_exitsession = xstrdup(data); + client_exittype = imsg->hdr.type; + if (imsg->hdr.type == MSG_DETACHKILL) + client_exitreason = CLIENT_EXIT_DETACHED_HUP; + else + client_exitreason = CLIENT_EXIT_DETACHED; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXIT: + if (datalen != 0 && datalen != sizeof (int)) + fatalx("bad MSG_EXIT size"); - client_exitsession = xstrdup(data); - client_exittype = imsg.hdr.type; - if (imsg.hdr.type == MSG_DETACHKILL) - client_exitreason = CLIENT_EXIT_DETACHED_HUP; - else - client_exitreason = CLIENT_EXIT_DETACHED; - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXIT: - if (datalen != 0 && datalen != sizeof (int)) - fatalx("bad MSG_EXIT size"); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_EXITED; + break; + case MSG_EXITED: + if (datalen != 0) + fatalx("bad MSG_EXITED size"); - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_EXITED; - break; - case MSG_EXITED: - if (datalen != 0) - fatalx("bad MSG_EXITED size"); + proc_exit(client_proc); + break; + case MSG_SHUTDOWN: + if (datalen != 0) + fatalx("bad MSG_SHUTDOWN size"); - imsg_free(&imsg); - return (-1); - case MSG_SHUTDOWN: - if (datalen != 0) - fatalx("bad MSG_SHUTDOWN size"); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_SERVER_EXITED; + client_exitval = 1; + break; + case MSG_SUSPEND: + if (datalen != 0) + fatalx("bad MSG_SUSPEND size"); - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_SERVER_EXITED; - client_exitval = 1; - break; - case MSG_SUSPEND: - if (datalen != 0) - fatalx("bad MSG_SUSPEND size"); + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + kill(getpid(), SIGTSTP); + break; + case MSG_LOCK: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_LOCK string"); - memset(&sigact, 0, sizeof sigact); - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_RESTART; - sigact.sa_handler = SIG_DFL; - if (sigaction(SIGTSTP, &sigact, NULL) != 0) - fatal("sigaction failed"); - kill(getpid(), SIGTSTP); - break; - case MSG_LOCK: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_LOCK string"); - - system(data); - client_write_server(MSG_UNLOCK, NULL, 0); - break; - } - - imsg_free(&imsg); + system(data); + proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); + break; } } diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a7ef1cd9..4e390323 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -113,16 +113,10 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (c->session != NULL) { if (dflag) { - /* - * Can't use server_write_session in case attaching to - * the same session as currently attached to. - */ TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_write_client(c, MSG_DETACH, - c_loop->session->name, - strlen(c_loop->session->name) + 1); + proc_send_s(c->peer, MSG_DETACH, s->name); } } @@ -150,8 +144,11 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->flags |= CLIENT_READONLY; if (dflag) { - server_write_session(s, MSG_DETACH, s->name, - strlen(s->name) + 1); + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c_loop) + continue; + proc_send_s(c->peer, MSG_DETACH, s->name); + } } if (!Eflag) { @@ -168,7 +165,8 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; - server_write_ready(c); + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 4bae9997..813ac032 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -57,7 +57,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_SUSPEND, NULL, 0); + proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); return (CMD_RETURN_NORMAL); } @@ -74,9 +74,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session != s) continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + proc_send_s(cloop->peer, msgtype, cloop->session->name); } return (CMD_RETURN_STOP); } @@ -89,14 +87,11 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session == NULL || cloop == c) continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + proc_send_s(cloop->peer, msgtype, cloop->session->name); } return (CMD_RETURN_NORMAL); } - server_write_client(c, msgtype, c->session->name, - strlen(c->session->name) + 1); + proc_send_s(c->peer, msgtype, c->session->name); return (CMD_RETURN_STOP); } diff --git a/cmd-find.c b/cmd-find.c index 4173e202..fbc06fb7 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -129,8 +129,8 @@ cmd_find_try_TMUX(struct client *c, struct window *w) return (NULL); if (pid != getpid()) return (NULL); - log_debug("client %d TMUX is %s (session @%u)", c->ibuf.fd, - envent->value, session); + log_debug("client %p TMUX is %s (session @%u)", c, envent->value, + session); s = session_find_by_id(session); if (s == NULL || (w != NULL && !session_has(s, w))) @@ -333,6 +333,8 @@ cmd_find_current_session(struct cmd_find_state *fs) { /* If we know the current client, use it. */ if (fs->cmdq->client != NULL) { + log_debug("%s: have client %p%s", __func__, fs->cmdq->client, + fs->cmdq->client->session == NULL ? "" : " (with session)"); if (fs->cmdq->client->session == NULL) return (cmd_find_current_session_with_client(fs)); fs->s = fs->cmdq->client->session; @@ -365,8 +367,11 @@ cmd_find_current_client(struct cmd_q *cmdq) u_int csize; /* If the queue client has a session, use it. */ - if (cmdq->client != NULL && cmdq->client->session != NULL) + if (cmdq->client != NULL && cmdq->client->session != NULL) { + log_debug("%s: using cmdq %p client %p", __func__, cmdq, + cmdq->client); return (cmdq->client); + } /* Otherwise find the current session. */ cmd_find_clear_state(¤t, cmdq, 0); @@ -375,6 +380,7 @@ cmd_find_current_client(struct cmd_q *cmdq) /* If it is attached, find the best of it's clients. */ s = current.s; + log_debug("%s: current session $%u %s", __func__, s->id, s->name); if (~s->flags & SESSION_UNATTACHED) { csize = 0; TAILQ_FOREACH(c, &clients, entry) { @@ -1220,6 +1226,7 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) c = cmd_find_current_client(cmdq); if (c == NULL && !quiet) cmdq_error(cmdq, "no current client"); + log_debug("%s: no target, return %p", __func__, c); return (c); } copy = xstrdup(target); @@ -1251,6 +1258,7 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) cmdq_error(cmdq, "can't find client %s", copy); free(copy); + log_debug("%s: target %s, return %p", __func__, target, c); return (c); } diff --git a/cmd-new-session.c b/cmd-new-session.c index b3bf3c74..f1a6167a 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -270,9 +270,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { - if (!already_attached) - server_write_ready(c); - else if (c->session != NULL) + if (!already_attached) { + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); + } else if (c->session != NULL) c->last_session = c->session; c->session = s; status_timer_start(c); diff --git a/proc.c b/proc.c new file mode 100644 index 00000000..413b2125 --- /dev/null +++ b/proc.c @@ -0,0 +1,249 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +struct tmuxproc { + const char *name; + int exit; + + void (*signalcb)(int); +}; + +struct tmuxpeer { + struct tmuxproc *parent; + + struct imsgbuf ibuf; + struct event event; + + int flags; +#define PEER_BAD 0x1 + + void (*dispatchcb)(struct imsg *, void *); + void *arg; +}; + +static void proc_update_event(struct tmuxpeer *); + +static void +proc_event_cb(unused int fd, short events, void *arg) +{ + struct tmuxpeer *peer = arg; + ssize_t n; + struct imsg imsg; + int v; + + if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { + if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + for (;;) { + if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { + peer->dispatchcb(NULL, peer->arg); + return; + } + if (n == 0) + break; + log_debug("peer %p message %d", peer, imsg.hdr.type); + + v = imsg.hdr.peerid; + if (imsg.hdr.type != MSG_VERSION && + v != PROTOCOL_VERSION) { + log_debug("peer %p bad version %d", peer, v); + + proc_send(peer, MSG_VERSION, -1, NULL, 0); + peer->flags |= PEER_BAD; + + if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + break; + } + + peer->dispatchcb(&imsg, peer->arg); + imsg_free(&imsg); + } + } + + if (events & EV_WRITE) { + if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { + peer->dispatchcb(NULL, peer->arg); + return; + } + } + + if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + + proc_update_event(peer); +} + +static void +proc_signal_cb(int signo, unused short events, void *arg) +{ + struct tmuxproc *tp = arg; + + tp->signalcb(signo); +} + +static void +proc_update_event(struct tmuxpeer *peer) +{ + short events; + + event_del(&peer->event); + + events = EV_READ; + if (peer->ibuf.w.queued > 0) + events |= EV_WRITE; + event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); + + event_add(&peer->event, NULL); +} + +int +proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, + size_t len) +{ + struct imsgbuf *ibuf = &peer->ibuf; + void *vp = (void *)buf; + int retval; + + if (peer->flags & PEER_BAD) + return (-1); + log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); + + retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); + if (retval != 1) + return (-1); + proc_update_event(peer); + return (0); +} + +int +proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s) +{ + return (proc_send(peer, type, -1, s, strlen(s) + 1)); +} + +struct tmuxproc * +proc_start(const char *name, struct event_base *base, int forkflag, + void (*signalcb)(int)) +{ + struct tmuxproc *tp; + + if (forkflag) { + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + return (NULL); + } + if (daemon(1, 0) != 0) + fatal("daemon failed"); + + clear_signals(0); + if (event_reinit(base) != 0) + fatalx("event_reinit failed"); + } + + logfile(name); + setproctitle("%s (%s)", name, socket_path); + + log_debug("%s started (%ld): socket %s, protocol %d", name, + (long)getpid(), socket_path, PROTOCOL_VERSION); + + tp = xcalloc(1, sizeof *tp); + tp->name = xstrdup(name); + + tp->signalcb = signalcb; + set_signals(proc_signal_cb, tp); + + return (tp); +} + +void +proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) +{ + log_debug("%s loop enter", tp->name); + do + event_loop(EVLOOP_ONCE); + while (!tp->exit && (loopcb == NULL || !loopcb ())); + log_debug("%s loop exit", tp->name); +} + +void +proc_exit(struct tmuxproc *tp) +{ + tp->exit = 1; +} + +struct tmuxpeer * +proc_add_peer(struct tmuxproc *tp, int fd, + void (*dispatchcb)(struct imsg *, void *), void *arg) +{ + struct tmuxpeer *peer; + + peer = xcalloc(1, sizeof *peer); + peer->parent = tp; + + peer->dispatchcb = dispatchcb; + peer->arg = arg; + + imsg_init(&peer->ibuf, fd); + event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + + log_debug("add peer %p: %d (%p)", peer, fd, arg); + + proc_update_event(peer); + return (peer); +} + +void +proc_remove_peer(struct tmuxpeer *peer) +{ + log_debug("remove peer %p", peer); + + event_del(&peer->event); + imsg_clear(&peer->ibuf); + + close(peer->ibuf.fd); + free(peer); +} + +void +proc_kill_peer(struct tmuxpeer *peer) +{ + peer->flags |= PEER_BAD; +} diff --git a/server-client.c b/server-client.c index 0e657ae8..13f905b2 100644 --- a/server-client.c +++ b/server-client.c @@ -18,10 +18,12 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -42,10 +44,10 @@ void server_client_set_title(struct client *); void server_client_reset_state(struct client *); int server_client_assume_paste(struct session *); -int server_client_msg_dispatch(struct client *); -void server_client_msg_command(struct client *, struct imsg *); -void server_client_msg_identify(struct client *, struct imsg *); -void server_client_msg_shell(struct client *); +void server_client_dispatch(struct imsg *, void *); +void server_client_dispatch_command(struct client *, struct imsg *); +void server_client_dispatch_identify(struct client *, struct imsg *); +void server_client_dispatch_shell(struct client *); /* Check if this client is inside this server. */ int @@ -87,8 +89,7 @@ server_client_create(int fd) c = xcalloc(1, sizeof *c); c->references = 1; - imsg_init(&c->ibuf, fd); - server_update_event(c); + c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c); if (gettimeofday(&c->creation_time, NULL) != 0) fatal("gettimeofday failed"); @@ -220,10 +221,8 @@ server_client_lost(struct client *c) environ_free(&c->environ); - close(c->ibuf.fd); - imsg_clear(&c->ibuf); - if (event_initialized(&c->event)) - event_del(&c->event); + proc_remove_peer(c->peer); + c->peer = NULL; server_client_unref(c); @@ -257,40 +256,6 @@ server_client_free(unused int fd, unused short events, void *arg) free(c); } -/* Process a single client event. */ -void -server_client_callback(int fd, short events, void *data) -{ - struct client *c = data; - - if (c->flags & CLIENT_DEAD) - return; - - if (fd == c->ibuf.fd) { - if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) <= 0 && - errno != EAGAIN) - goto client_lost; - - if (c->flags & CLIENT_BAD) { - if (c->ibuf.w.queued == 0) - goto client_lost; - return; - } - - if (events & EV_READ && server_client_msg_dispatch(c) != 0) - goto client_lost; - } - - server_push_stdout(c); - server_push_stderr(c); - - server_update_event(c); - return; - -client_lost: - server_client_lost(c); -} - /* Check for mouse keys. */ int server_client_check_mouse(struct client *c) @@ -880,7 +845,7 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; - server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval); + proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); c->flags &= ~CLIENT_EXIT; } @@ -974,123 +939,112 @@ server_client_set_title(struct client *c) } /* Dispatch message from client. */ -int -server_client_msg_dispatch(struct client *c) +void +server_client_dispatch(struct imsg *imsg, void *arg) { - struct imsg imsg; + struct client *c = arg; struct msg_stdin_data stdindata; const char *data; - ssize_t n, datalen; + ssize_t datalen; struct session *s; - if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) - return (-1); + if (c->flags & CLIENT_DEAD) + return; - for (;;) { - if ((n = imsg_get(&c->ibuf, &imsg)) == -1) - return (-1); - if (n == 0) - return (0); - - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - if (imsg.hdr.peerid != PROTOCOL_VERSION) { - server_write_client(c, MSG_VERSION, NULL, 0); - c->flags |= CLIENT_BAD; - if (imsg.fd != -1) - close(imsg.fd); - imsg_free(&imsg); - continue; - } - - log_debug("got %u from client %p", imsg.hdr.type, c); - switch (imsg.hdr.type) { - case MSG_IDENTIFY_FLAGS: - case MSG_IDENTIFY_TERM: - case MSG_IDENTIFY_TTYNAME: - case MSG_IDENTIFY_CWD: - case MSG_IDENTIFY_STDIN: - case MSG_IDENTIFY_ENVIRON: - case MSG_IDENTIFY_CLIENTPID: - case MSG_IDENTIFY_DONE: - server_client_msg_identify(c, &imsg); - break; - case MSG_COMMAND: - server_client_msg_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; - if (tty_resize(&c->tty)) { - recalculate_sizes(); - server_redraw_client(c); - } - break; - case MSG_EXITING: - if (datalen != 0) - fatalx("bad MSG_EXITING size"); - - c->session = NULL; - tty_close(&c->tty); - server_write_client(c, MSG_EXITED, NULL, 0); - break; - case MSG_WAKEUP: - case MSG_UNLOCK: - if (datalen != 0) - fatalx("bad MSG_WAKEUP size"); - - if (!(c->flags & CLIENT_SUSPENDED)) - break; - c->flags &= ~CLIENT_SUSPENDED; - - if (c->tty.fd == -1) /* exited in the meantime */ - break; - s = c->session; - - if (gettimeofday(&c->activity_time, NULL) != 0) - fatal("gettimeofday failed"); - if (s != NULL) - session_update_activity(s, &c->activity_time); - - tty_start_tty(&c->tty); - server_redraw_client(c); - recalculate_sizes(); - break; - case MSG_SHELL: - if (datalen != 0) - fatalx("bad MSG_SHELL size"); - - server_client_msg_shell(c); - break; - } - - imsg_free(&imsg); + if (imsg == NULL) { + server_client_lost(c); + return; } + + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_IDENTIFY_FLAGS: + case MSG_IDENTIFY_TERM: + case MSG_IDENTIFY_TTYNAME: + case MSG_IDENTIFY_CWD: + case MSG_IDENTIFY_STDIN: + case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_CLIENTPID: + case MSG_IDENTIFY_DONE: + server_client_dispatch_identify(c, imsg); + break; + 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; + if (tty_resize(&c->tty)) { + recalculate_sizes(); + server_redraw_client(c); + } + break; + case MSG_EXITING: + if (datalen != 0) + fatalx("bad MSG_EXITING size"); + + c->session = NULL; + tty_close(&c->tty); + proc_send(c->peer, MSG_EXITED, -1, NULL, 0); + break; + case MSG_WAKEUP: + case MSG_UNLOCK: + if (datalen != 0) + fatalx("bad MSG_WAKEUP size"); + + if (!(c->flags & CLIENT_SUSPENDED)) + break; + c->flags &= ~CLIENT_SUSPENDED; + + if (c->tty.fd == -1) /* exited in the meantime */ + break; + s = c->session; + + if (gettimeofday(&c->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + if (s != NULL) + session_update_activity(s, &c->activity_time); + + tty_start_tty(&c->tty); + server_redraw_client(c); + recalculate_sizes(); + break; + case MSG_SHELL: + if (datalen != 0) + fatalx("bad MSG_SHELL size"); + + server_client_dispatch_shell(c); + break; + } + + server_push_stdout(c); + server_push_stderr(c); } /* Handle command message. */ void -server_client_msg_command(struct client *c, struct imsg *imsg) +server_client_dispatch_command(struct client *c, struct imsg *imsg) { struct msg_command_data data; char *buf; @@ -1143,7 +1097,7 @@ error: /* Handle identify message. */ void -server_client_msg_identify(struct client *c, struct imsg *imsg) +server_client_dispatch_identify(struct client *c, struct imsg *imsg) { const char *data; size_t datalen; @@ -1217,7 +1171,7 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (c->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); - server_write_client(c, MSG_STDIN, NULL, 0); + proc_send(c->peer, MSG_STDIN, -1, NULL, 0); c->tty.fd = -1; c->tty.log_fd = -1; @@ -1248,14 +1202,14 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) /* Handle shell message. */ void -server_client_msg_shell(struct client *c) +server_client_dispatch_shell(struct client *c) { const char *shell; shell = options_get_string(&global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1); + proc_send_s(c->peer, MSG_SHELL, shell); - c->flags |= CLIENT_BAD; /* it will die after exec */ + proc_kill_peer(c->peer); } diff --git a/server-fn.c b/server-fn.c index a31a3772..095535ab 100644 --- a/server-fn.c +++ b/server-fn.c @@ -17,7 +17,10 @@ */ #include +#include +#include +#include #include #include #include @@ -47,43 +50,6 @@ server_fill_environ(struct session *s, struct environ *env) environ_set(env, "TMUX", var); } -void -server_write_ready(struct client *c) -{ - if (c->flags & CLIENT_CONTROL) - return; - server_write_client(c, MSG_READY, NULL, 0); -} - -int -server_write_client(struct client *c, enum msgtype type, const void *buf, - size_t len) -{ - struct imsgbuf *ibuf = &c->ibuf; - int error; - - if (c->flags & CLIENT_BAD) - return (-1); - log_debug("writing %d to client %p", type, c); - error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, - (void *) buf, len); - if (error == 1) - server_update_event(c); - return (error == 1 ? 0 : -1); -} - -void -server_write_session(struct session *s, enum msgtype type, const void *buf, - size_t len) -{ - struct client *c; - - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == s) - server_write_client(c, type, buf, len); - } -} - void server_redraw_client(struct client *c) { @@ -227,7 +193,7 @@ server_lock_client(struct client *c) tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_LOCK, cmd, strlen(cmd) + 1); + proc_send_s(c->peer, MSG_LOCK, cmd); } void @@ -484,22 +450,6 @@ server_callback_identify(unused int fd, unused short events, void *data) server_clear_identify(c); } -void -server_update_event(struct client *c) -{ - short events; - - events = 0; - if (!(c->flags & CLIENT_BAD)) - events |= EV_READ; - if (c->ibuf.w.queued > 0) - events |= EV_WRITE; - if (event_initialized(&c->event)) - event_del(&c->event); - event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); - event_add(&c->event, NULL); -} - /* Push stdout to client if possible. */ void server_push_stdout(struct client *c) @@ -516,7 +466,7 @@ server_push_stdout(struct client *c) memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); data.size = size; - if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) + if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) == 0) evbuffer_drain(c->stdout_data, size); } @@ -540,7 +490,7 @@ server_push_stderr(struct client *c) memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); data.size = size; - if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) + if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) == 0) evbuffer_drain(c->stderr_data, size); } @@ -570,7 +520,7 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, if (c->stdin_closed) c->stdin_callback(c, 1, c->stdin_callback_data); - server_write_client(c, MSG_STDIN, NULL, 0); + proc_send(c->peer, MSG_STDIN, -1, NULL, 0); return (0); } diff --git a/server.c b/server.c index 7ce1c99d..a36e9a73 100644 --- a/server.c +++ b/server.c @@ -43,6 +43,7 @@ struct clients clients; +struct tmuxproc *server_proc; int server_fd; int server_exit; struct event server_ev_accept; @@ -54,11 +55,11 @@ struct window_pane *marked_window_pane; struct layout_cell *marked_layout_cell; int server_create_socket(void); -void server_loop(void); +int server_loop(void); int server_should_exit(void); void server_send_exit(void); -void server_accept_callback(int, short, void *); -void server_signal_callback(int, short, void *); +void server_accept(int, short, void *); +void server_signal(int); void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); @@ -162,17 +163,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) { int pair[2]; - /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); - log_debug("starting server"); - switch (fork()) { - case -1: - fatal("fork failed"); - case 0: - break; - default: + server_proc = proc_start("server", base, 1, server_signal); + if (server_proc == NULL) { close(pair[1]); return (pair[0]); } @@ -182,21 +177,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) "ps", NULL) != 0) fatal("pledge failed"); - /* - * Must daemonise before loading configuration as the PID changes so - * $TMUX would be wrong for sessions created in the config file. - */ - if (daemon(1, 0) != 0) - fatal("daemon failed"); - - /* event_init() was called in our parent, need to reinit. */ - clear_signals(0); - if (event_reinit(base) != 0) - fatal("event_reinit failed"); - - logfile("server"); - log_debug("server started, pid %ld", (long) getpid()); - RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); @@ -207,8 +187,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) utf8_build(); start_time = time(NULL); - log_debug("socket path %s", socket_path); - setproctitle("server (%s)", socket_path); server_fd = server_create_socket(); if (server_fd == -1) @@ -226,31 +204,19 @@ server_start(struct event_base *base, int lockfd, char *lockfile) server_add_accept(0); - set_signals(server_signal_callback); - server_loop(); + proc_loop(server_proc, server_loop); status_prompt_save_history(); exit(0); } -/* Main server loop. */ -void +/* Server loop callback. */ +int server_loop(void) -{ - while (!server_should_exit()) { - log_debug("event dispatch enter"); - event_loop(EVLOOP_ONCE); - log_debug("event dispatch exit"); - - server_client_loop(); - } -} - -/* Check if the server should exit (no more clients or sessions). */ -int -server_should_exit(void) { struct client *c; + server_client_loop(); + if (!options_get_number(&global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); @@ -282,10 +248,10 @@ server_send_exit(void) cmd_wait_for_flush(); TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { - if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) + if (c->flags & CLIENT_SUSPENDED) server_client_lost(c); else - server_write_client(c, MSG_SHUTDOWN, NULL, 0); + proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); c->session = NULL; } @@ -331,7 +297,7 @@ server_update_socket(void) /* Callback for server socket. */ void -server_accept_callback(int fd, short events, unused void *data) +server_accept(int fd, short events, unused void *data) { struct sockaddr_storage sa; socklen_t slen = sizeof sa; @@ -372,19 +338,19 @@ server_add_accept(int timeout) event_del(&server_ev_accept); if (timeout == 0) { - event_set(&server_ev_accept, - server_fd, EV_READ, server_accept_callback, NULL); + event_set(&server_ev_accept, server_fd, EV_READ, server_accept, + NULL); event_add(&server_ev_accept, NULL); } else { - event_set(&server_ev_accept, - server_fd, EV_TIMEOUT, server_accept_callback, NULL); + event_set(&server_ev_accept, server_fd, EV_TIMEOUT, + server_accept, NULL); event_add(&server_ev_accept, &tv); } } /* Signal handler. */ void -server_signal_callback(int sig, unused short events, unused void *data) +server_signal(int sig) { int fd; diff --git a/signal.c b/signal.c index 7e6268a7..9a4d58c2 100644 --- a/signal.c +++ b/signal.c @@ -32,7 +32,7 @@ struct event ev_sigusr1; struct event ev_sigwinch; void -set_signals(void(*handler)(int, short, unused void *)) +set_signals(void (*handler)(int, short, void *), void *arg) { struct sigaction sigact; @@ -49,17 +49,17 @@ set_signals(void(*handler)(int, short, unused void *)) if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); - signal_set(&ev_sighup, SIGHUP, handler, NULL); + signal_set(&ev_sighup, SIGHUP, handler, arg); signal_add(&ev_sighup, NULL); - signal_set(&ev_sigchld, SIGCHLD, handler, NULL); + signal_set(&ev_sigchld, SIGCHLD, handler, arg); signal_add(&ev_sigchld, NULL); - signal_set(&ev_sigcont, SIGCONT, handler, NULL); + signal_set(&ev_sigcont, SIGCONT, handler, arg); signal_add(&ev_sigcont, NULL); - signal_set(&ev_sigterm, SIGTERM, handler, NULL); + signal_set(&ev_sigterm, SIGTERM, handler, arg); signal_add(&ev_sigterm, NULL); - signal_set(&ev_sigusr1, SIGUSR1, handler, NULL); + signal_set(&ev_sigusr1, SIGUSR1, handler, arg); signal_add(&ev_sigusr1, NULL); - signal_set(&ev_sigwinch, SIGWINCH, handler, NULL); + signal_set(&ev_sigwinch, SIGWINCH, handler, arg); signal_add(&ev_sigwinch, NULL); } diff --git a/tmux.c b/tmux.c index 8b3d0bfe..01e73f5d 100644 --- a/tmux.c +++ b/tmux.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "tmux.h" @@ -347,9 +348,6 @@ main(int argc, char **argv) } free(path); - /* Set process title. */ - setproctitle("%s (%s)", __progname, socket_path); - /* Pass control to the client. */ exit(client_main(event_init(), argc, argv, flags)); } diff --git a/tmux.h b/tmux.h index 97238865..c4b12402 100644 --- a/tmux.h +++ b/tmux.h @@ -24,11 +24,9 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -1170,8 +1168,10 @@ struct message_entry { }; /* Client connection. */ +struct tmuxproc; +struct tmuxpeer; struct client { - struct imsgbuf ibuf; + struct tmuxpeer *peer; pid_t pid; int fd; @@ -1209,7 +1209,7 @@ struct client { #define CLIENT_STATUS 0x10 #define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 -#define CLIENT_BAD 0x80 +/* 0x80 unused */ #define CLIENT_IDENTIFY 0x100 #define CLIENT_DEAD 0x200 #define CLIENT_BORDERS 0x400 @@ -1420,6 +1420,19 @@ int areshell(const char *); void setblocking(int, int); const char *find_home(void); +/* proc.c */ +struct imsg; +int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t); +int proc_send_s(struct tmuxpeer *, enum msgtype, const char *); +struct tmuxproc *proc_start(const char *, struct event_base *, int, + void (*)(int)); +void proc_loop(struct tmuxproc *, int (*)(void)); +void proc_exit(struct tmuxproc *); +struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, + void (*)(struct imsg *, void *), void *); +void proc_remove_peer(struct tmuxpeer *); +void proc_kill_peer(struct tmuxpeer *); + /* cfg.c */ extern int cfg_finished; extern int cfg_references; @@ -1727,6 +1740,7 @@ void alerts_reset_all(void); void alerts_queue(struct window *, int); /* server.c */ +extern struct tmuxproc *server_proc; extern struct clients clients; extern struct session *marked_session; extern struct winlink *marked_winlink; @@ -1748,16 +1762,10 @@ void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); -void server_client_callback(int, short, void *); void server_client_loop(void); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); -void server_write_ready(struct client *); -int server_write_client(struct client *, enum msgtype, const void *, - size_t); -void server_write_session(struct session *, enum msgtype, const void *, - size_t); void server_redraw_client(struct client *); void server_status_client(struct client *); void server_redraw_session(struct session *); @@ -1780,7 +1788,6 @@ void server_destroy_session(struct session *); void server_check_unattached(void); void server_set_identify(struct client *); void server_clear_identify(struct client *); -void server_update_event(struct client *); void server_push_stdout(struct client *); void server_push_stderr(struct client *); int server_set_stdin_callback(struct client *, void (*)(struct client *, @@ -2110,7 +2117,7 @@ char *format_window_name(struct window *); char *parse_window_name(const char *); /* signal.c */ -void set_signals(void(*)(int, short, void *)); +void set_signals(void(*)(int, short, void *), void *); void clear_signals(int); /* control.c */ From 67c3a014b960b8c1d7931d3c99f570610b1d4d3f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 14:51:35 +0000 Subject: [PATCH 809/949] No more TMPDIR. --- tmux.1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index a9f3cdc9..1fb99252 100644 --- a/tmux.1 +++ b/tmux.1 @@ -143,11 +143,10 @@ session created, and continues to process the rest of the configuration file. .It Fl L Ar socket-name .Nm stores the server socket in a directory under -.Ev TMUX_TMPDIR , -.Ev TMPDIR -if it is unset, or +.Ev TMUX_TMPDIR +or .Pa /tmp -if both are unset. +if it is unset. The default socket is named .Em default . This option allows a different socket name to be specified, allowing several From 44657bf932b068aff5ce1019a4e8a2e7b00b5321 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 15:58:42 +0000 Subject: [PATCH 810/949] Move struct options into options.c. --- alerts.c | 24 +++++++++---------- client.c | 6 ++--- cmd-attach-session.c | 4 ++-- cmd-break-pane.c | 2 +- cmd-choose-buffer.c | 2 +- cmd-move-window.c | 2 +- cmd-new-session.c | 10 ++++---- cmd-new-window.c | 4 ++-- cmd-rename-window.c | 2 +- cmd-send-keys.c | 4 ++-- cmd-set-option.c | 24 +++++++++---------- cmd-show-options.c | 14 ++++++----- cmd-split-window.c | 6 ++--- cmd-switch-client.c | 2 +- format.c | 12 +++++----- input-keys.c | 2 +- input.c | 6 ++--- layout-set.c | 8 +++---- names.c | 4 ++-- options.c | 28 ++++++++++++++++++++-- paste.c | 2 +- resize.c | 8 +++---- screen-redraw.c | 6 ++--- server-client.c | 20 ++++++++-------- server-fn.c | 16 ++++++------- server.c | 2 +- session.c | 19 ++++++++------- status.c | 56 ++++++++++++++++++++++---------------------- tmux.c | 31 ++++++++++++------------ tmux.h | 22 +++++++---------- tty-keys.c | 2 +- tty-term.c | 2 +- tty.c | 8 +++---- window-choose.c | 6 ++--- window-clock.c | 4 ++-- window-copy.c | 34 +++++++++++++-------------- window.c | 20 ++++++++-------- 37 files changed, 223 insertions(+), 201 deletions(-) diff --git a/alerts.c b/alerts.c index 806e565b..f1477030 100644 --- a/alerts.c +++ b/alerts.c @@ -80,11 +80,11 @@ alerts_enabled(struct window *w, int flags) struct session *s; if (flags & WINDOW_ACTIVITY) { - if (options_get_number(&w->options, "monitor-activity")) + if (options_get_number(w->options, "monitor-activity")) return (1); } if (flags & WINDOW_SILENCE) { - if (options_get_number(&w->options, "monitor-silence") != 0) + if (options_get_number(w->options, "monitor-silence") != 0) return (1); } if (~flags & WINDOW_BELL) @@ -92,7 +92,7 @@ alerts_enabled(struct window *w, int flags) RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, w)) continue; - if (options_get_number(&s->options, "bell-action") != BELL_NONE) + if (options_get_number(s->options, "bell-action") != BELL_NONE) return (1); } return (0); @@ -116,7 +116,7 @@ alerts_reset(struct window *w) event_del(&w->alerts_timer); timerclear(&tv); - tv.tv_sec = options_get_number(&w->options, "monitor-silence"); + tv.tv_sec = options_get_number(w->options, "monitor-silence"); log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); if (tv.tv_sec != 0) @@ -160,11 +160,11 @@ alerts_check_bell(struct session *s, struct winlink *wl) if (s->curw->window == w) w->flags &= ~WINDOW_BELL; - action = options_get_number(&s->options, "bell-action"); + action = options_get_number(s->options, "bell-action"); if (action == BELL_NONE) return (0); - visual = options_get_number(&s->options, "visual-bell"); + visual = options_get_number(s->options, "visual-bell"); TAILQ_FOREACH(c, &clients, entry) { if (c->session != s || c->flags & CLIENT_CONTROL) continue; @@ -201,14 +201,14 @@ alerts_check_activity(struct session *s, struct winlink *wl) if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); - if (!options_get_number(&w->options, "monitor-activity")) + if (!options_get_number(w->options, "monitor-activity")) return (0); - if (options_get_number(&s->options, "bell-on-alert")) + if (options_get_number(s->options, "bell-on-alert")) alerts_ring_bell(s); wl->flags |= WINLINK_ACTIVITY; - if (options_get_number(&s->options, "visual-activity")) { + if (options_get_number(s->options, "visual-activity")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; @@ -233,14 +233,14 @@ alerts_check_silence(struct session *s, struct winlink *wl) if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); - if (options_get_number(&w->options, "monitor-silence") == 0) + if (options_get_number(w->options, "monitor-silence") == 0) return (0); - if (options_get_number(&s->options, "bell-on-alert")) + if (options_get_number(s->options, "bell-on-alert")) alerts_ring_bell(s); wl->flags |= WINLINK_SILENCE; - if (options_get_number(&s->options, "visual-silence")) { + if (options_get_number(s->options, "visual-silence")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; diff --git a/client.c b/client.c index 606feefe..aa453538 100644 --- a/client.c +++ b/client.c @@ -291,9 +291,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) fatal("pledge failed"); /* Free stuff that is not used in the client. */ - options_free(&global_options); - options_free(&global_s_options); - options_free(&global_w_options); + options_free(global_options); + options_free(global_s_options); + options_free(global_w_options); environ_free(&global_environ); /* Create stdin handler. */ diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 4e390323..a3623ec4 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -121,7 +121,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (!Eflag) { - update = options_get_string(&s->options, + update = options_get_string(s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } @@ -152,7 +152,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (!Eflag) { - update = options_get_string(&s->options, + update = options_get_string(s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 2aa5c5b7..39179cc7 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -84,7 +84,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) layout_init(w, wp); if (idx == -1) - idx = -1 - options_get_number(&dst_s->options, "base-index"); + idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(dst_s, wl->idx); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index b4590306..e790de6b 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -63,7 +63,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); - utf8flag = options_get_number(&wl->window->options, "utf8"); + utf8flag = options_get_number(wl->window->options, "utf8"); if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); diff --git a/cmd-move-window.c b/cmd-move-window.c index b15df4f6..1bd46ab2 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -95,7 +95,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) * session already has the correct winlink id to us, either * automatically or specified by -s. */ - if (!sflag && options_get_number(&src->options, "renumber-windows")) + if (!sflag && options_get_number(src->options, "renumber-windows")) session_renumber_windows(src); recalculate_sizes(); diff --git a/cmd-new-session.c b/cmd-new-session.c index f1a6167a..48828e54 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -197,7 +197,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) goto error; } } - if (sy > 0 && options_get_number(&global_s_options, "status")) + if (sy > 0 && options_get_number(global_s_options, "status")) sy--; if (sx == 0) sx = 1; @@ -211,7 +211,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) argc = args->argc; argv = args->argv; } else if (target == NULL) { - cmd = options_get_string(&global_s_options, "default-command"); + cmd = options_get_string(global_s_options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = &cmd; @@ -232,13 +232,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Construct the environment. */ environ_init(&env); if (c != NULL && !args_has(args, 'E')) { - update = options_get_string(&global_s_options, + update = options_get_string(global_s_options, "update-environment"); environ_update(update, &c->environ, &env); } /* Create the new session. */ - idx = -1 - options_get_number(&global_s_options, "base-index"); + idx = -1 - options_get_number(global_s_options, "base-index"); s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, sy, &cause); if (s == NULL) { @@ -252,7 +252,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (argc >= 0 && args_has(args, 'n')) { w = s->curw->window; window_set_name(w, args_get(args, 'n')); - options_set_number(&w->options, "automatic-rename", 0); + options_set_number(w->options, "automatic-rename", 0); } /* diff --git a/cmd-new-window.c b/cmd-new-window.c index 893fe6e2..a3712d76 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -71,7 +71,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) detached = args_has(args, 'd'); if (args->argc == 0) { - cmd = options_get_string(&s->options, "default-command"); + cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; @@ -136,7 +136,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } if (idx == -1) - idx = -1 - options_get_number(&s->options, "base-index"); + idx = -1 - options_get_number(s->options, "base-index"); wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, &cause); if (wl == NULL) { diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 2f677a48..bc85d96b 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -47,7 +47,7 @@ cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); window_set_name(wl->window, args->argv[0]); - options_set_number(&wl->window->options, "automatic-rename", 0); + options_set_number(wl->window->options, "automatic-rename", 0); server_status_window(wl->window); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index b1f8d672..dd796d60 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -70,9 +70,9 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (self->entry == &cmd_send_prefix_entry) { if (args_has(args, '2')) - key = options_get_number(&s->options, "prefix2"); + key = options_get_number(s->options, "prefix2"); else - key = options_get_number(&s->options, "prefix"); + key = options_get_number(s->options, "prefix"); window_pane_key(wp, NULL, s, key, NULL); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-option.c b/cmd-set-option.c index e0b07edb..6762e6ad 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -126,10 +126,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Work out the tree from the table. */ if (table == server_options_table) - oo = &global_options; + oo = global_options; else if (table == window_options_table) { if (args_has(self->args, 'g')) - oo = &global_w_options; + oo = global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) { @@ -139,11 +139,11 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) 'g')) ? " need target window or -g" : ""); return (CMD_RETURN_ERROR); } - oo = &wl->window->options; + oo = wl->window->options; } } else if (table == session_options_table) { if (args_has(self->args, 'g')) - oo = &global_s_options; + oo = global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) { @@ -153,7 +153,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) 'g')) ? " need target session or -g" : ""); return (CMD_RETURN_ERROR); } - oo = &s->options; + oo = s->options; } } else { cmdq_error(cmdq, "unknown table"); @@ -179,7 +179,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Start or stop timers if necessary. */ if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { - if (options_get_number(&w->options, "automatic-rename")) + if (options_get_number(w->options, "automatic-rename")) w->active->flags |= PANE_CHANGED; } } @@ -210,25 +210,25 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, struct options *oo; if (args_has(args, 's')) - oo = &global_options; + oo = global_options; else if (args_has(self->args, 'w') || self->entry == &cmd_set_window_option_entry) { if (args_has(self->args, 'g')) - oo = &global_w_options; + oo = global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); - oo = &wl->window->options; + oo = wl->window->options; } } else { if (args_has(self->args, 'g')) - oo = &global_s_options; + oo = global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - oo = &s->options; + oo = s->options; } } @@ -276,7 +276,7 @@ cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, return (-1); } - if (args_has(args, 'g') || oo == &global_options) { + if (args_has(args, 'g') || oo == global_options) { switch (oe->type) { case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); diff --git a/cmd-show-options.c b/cmd-show-options.c index a5011e72..e99d665f 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -61,28 +61,28 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) int quiet; if (args_has(self->args, 's')) { - oo = &global_options; + oo = global_options; table = server_options_table; } else if (args_has(self->args, 'w') || self->entry == &cmd_show_window_options_entry) { table = window_options_table; if (args_has(self->args, 'g')) - oo = &global_w_options; + oo = global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); - oo = &wl->window->options; + oo = wl->window->options; } } else { table = session_options_table; if (args_has(self->args, 'g')) - oo = &global_s_options; + oo = global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - oo = &s->options; + oo = s->options; } } @@ -151,13 +151,15 @@ cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, struct options_entry *o; const char *optval; - RB_FOREACH(o, options_tree, &oo->tree) { + o = options_first(oo); + while (o != NULL) { if (*o->name == '@') { if (args_has(self->args, 'v')) cmdq_print(cmdq, "%s", o->str); else cmdq_print(cmdq, "%s \"%s\"", o->name, o->str); } + o = options_next(o); } for (oe = table; oe->name != NULL; oe++) { diff --git a/cmd-split-window.c b/cmd-split-window.c index f397113f..60b04d77 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -73,7 +73,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) server_fill_environ(s, &env); if (args->argc == 0) { - cmd = options_get_string(&s->options, "default-command"); + cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; @@ -135,9 +135,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) else size = (wp->sx * percentage) / 100; } - hlimit = options_get_number(&s->options, "history-limit"); + hlimit = options_get_number(s->options, "history-limit"); - shell = options_get_string(&s->options, "default-shell"); + shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 3a72886a..5a8c8b08 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -120,7 +120,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } if (c != NULL && !args_has(args, 'E')) { - update = options_get_string(&s->options, "update-environment"); + update = options_get_string(s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } diff --git a/format.c b/format.c index 285bfadb..0add71e5 100644 --- a/format.c +++ b/format.c @@ -581,15 +581,15 @@ format_find(struct format_tree *ft, const char *key, int modifiers) found = NULL; if (~modifiers & FORMAT_TIMESTRING) { - o = options_find(&global_options, key); + o = options_find(global_options, key); if (o == NULL && ft->w != NULL) - o = options_find(&ft->w->options, key); + o = options_find(ft->w->options, key); if (o == NULL) - o = options_find(&global_w_options, key); + o = options_find(global_w_options, key); if (o == NULL && ft->s != NULL) - o = options_find(&ft->s->options, key); + o = options_find(ft->s->options, key); if (o == NULL) - o = options_find(&global_s_options, key); + o = options_find(global_s_options, key); if (o != NULL) { switch (o->type) { case OPTIONS_STRING: @@ -1101,7 +1101,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); format_add(ft, "pane_synchronized", "%d", - !!options_get_number(&wp->window->options, "synchronize-panes")); + !!options_get_number(wp->window->options, "synchronize-panes")); format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); diff --git a/input-keys.c b/input-keys.c index 6e49dd3b..579c0f39 100644 --- a/input-keys.c +++ b/input-keys.c @@ -171,7 +171,7 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) * Then try to look this up as an xterm key, if the flag to output them * is set. */ - if (options_get_number(&wp->window->options, "xterm-keys")) { + if (options_get_number(wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { bufferevent_write(wp->event, out, strlen(out)); free(out); diff --git a/input.c b/input.c index ab56fc38..3a02b0ce 100644 --- a/input.c +++ b/input.c @@ -1907,12 +1907,12 @@ input_exit_rename(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; - if (!options_get_number(&ictx->wp->window->options, "allow-rename")) + if (!options_get_number(ictx->wp->window->options, "allow-rename")) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); window_set_name(ictx->wp->window, ictx->input_buf); - options_set_number(&ictx->wp->window->options, "automatic-rename", 0); + options_set_number(ictx->wp->window->options, "automatic-rename", 0); server_status_window(ictx->wp->window); } @@ -1921,7 +1921,7 @@ input_exit_rename(struct input_ctx *ictx) int input_utf8_open(struct input_ctx *ictx) { - if (!options_get_number(&ictx->wp->window->options, "utf8")) { + if (!options_get_number(ictx->wp->window->options, "utf8")) { /* Print, and do not switch state. */ input_print(ictx); return (-1); diff --git a/layout-set.c b/layout-set.c index da94cff2..852ec0f6 100644 --- a/layout-set.c +++ b/layout-set.c @@ -245,10 +245,10 @@ layout_set_main_h(struct window *w) width = (w->sx - (n - 1)) / columns; /* Get the main pane height and add one for separator line. */ - mainheight = options_get_number(&w->options, "main-pane-height") + 1; + mainheight = options_get_number(w->options, "main-pane-height") + 1; /* Get the optional other pane height and add one for separator line. */ - otherheight = options_get_number(&w->options, "other-pane-height") + 1; + otherheight = options_get_number(w->options, "other-pane-height") + 1; /* * If an other pane height was specified, honour it so long as it @@ -366,10 +366,10 @@ layout_set_main_v(struct window *w) height = (w->sy - (n - 1)) / rows; /* Get the main pane width and add one for separator line. */ - mainwidth = options_get_number(&w->options, "main-pane-width") + 1; + mainwidth = options_get_number(w->options, "main-pane-width") + 1; /* Get the optional other pane width and add one for separator line. */ - otherwidth = options_get_number(&w->options, "other-pane-width") + 1; + otherwidth = options_get_number(w->options, "other-pane-width") + 1; /* * If an other pane width was specified, honour it so long as it diff --git a/names.c b/names.c index e880c577..0e806ca0 100644 --- a/names.c +++ b/names.c @@ -58,7 +58,7 @@ check_window_name(struct window *w) if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) + if (!options_get_number(w->options, "automatic-rename")) return; if (~w->active->flags & PANE_CHANGED) { @@ -122,7 +122,7 @@ format_window_name(struct window *w) format_defaults_window(ft, w); format_defaults_pane(ft, w->active); - fmt = options_get_string(&w->options, "automatic-rename-format"); + fmt = options_get_string(w->options, "automatic-rename-format"); name = format_expand(ft, fmt); format_free(ft); diff --git a/options.c b/options.c index 030cfd51..487918fd 100644 --- a/options.c +++ b/options.c @@ -29,6 +29,13 @@ * a red-black tree. */ +struct options { + RB_HEAD(options_tree, options_entry) tree; + struct options *parent; +}; + +int options_cmp(struct options_entry *, struct options_entry *); +RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); RB_GENERATE(options_tree, options_entry, entry, options_cmp); int @@ -37,11 +44,15 @@ options_cmp(struct options_entry *o1, struct options_entry *o2) return (strcmp(o1->name, o2->name)); } -void -options_init(struct options *oo, struct options *parent) +struct options * +options_create(struct options *parent) { + struct options *oo; + + oo = xcalloc(1, sizeof *oo); RB_INIT(&oo->tree); oo->parent = parent; + return (oo); } void @@ -57,6 +68,19 @@ options_free(struct options *oo) free(o->str); free(o); } + free(oo); +} + +struct options_entry * +options_first(struct options *oo) +{ + return (RB_MIN(options_tree, &oo->tree)); +} + +struct options_entry * +options_next(struct options_entry *o) +{ + return (RB_NEXT(options_tree, &oo->tree, o)); } struct options_entry * diff --git a/paste.c b/paste.c index 44f88d0b..ad3b56b5 100644 --- a/paste.c +++ b/paste.c @@ -151,7 +151,7 @@ paste_add(char *data, size_t size) if (size == 0) return; - limit = options_get_number(&global_options, "buffer-limit"); + limit = options_get_number(global_options, "buffer-limit"); RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { if (paste_num_automatic < limit) break; diff --git a/resize.c b/resize.c index 3606bfeb..c7805e05 100644 --- a/resize.c +++ b/resize.c @@ -53,7 +53,7 @@ recalculate_sizes(void) int flag, has_status, is_zoomed, forced; RB_FOREACH(s, sessions, &sessions) { - has_status = options_get_number(&s->options, "status"); + has_status = options_get_number(s->options, "status"); s->attached = 0; ssx = ssy = UINT_MAX; @@ -94,7 +94,7 @@ recalculate_sizes(void) RB_FOREACH(w, windows, &windows) { if (w->active == NULL) continue; - flag = options_get_number(&w->options, "aggressive-resize"); + flag = options_get_number(w->options, "aggressive-resize"); ssx = ssy = UINT_MAX; RB_FOREACH(s, sessions, &sessions) { @@ -115,12 +115,12 @@ recalculate_sizes(void) continue; forced = 0; - limit = options_get_number(&w->options, "force-width"); + limit = options_get_number(w->options, "force-width"); if (limit >= PANE_MINIMUM && ssx > limit) { ssx = limit; forced |= WINDOW_FORCEWIDTH; } - limit = options_get_number(&w->options, "force-height"); + limit = options_get_number(w->options, "force-height"); if (limit >= PANE_MINIMUM && ssy > limit) { ssy = limit; forced |= WINDOW_FORCEHEIGHT; diff --git a/screen-redraw.c b/screen-redraw.c index 799f5c55..fd4536d0 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -222,7 +222,7 @@ void screen_redraw_screen(struct client *c, int draw_panes, int draw_status, int draw_borders) { - struct options *oo = &c->session->options; + struct options *oo = c->session->options; struct tty *tty = &c->tty; u_int top; int status, spos; @@ -276,7 +276,7 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) { struct session *s = c->session; struct window *w = s->curw->window; - struct options *oo = &w->options; + struct options *oo = w->options; struct tty *tty = &c->tty; struct window_pane *wp; struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; @@ -392,7 +392,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) { struct tty *tty = &c->tty; struct session *s = c->session; - struct options *oo = &s->options; + struct options *oo = s->options; struct window *w = wp->window; struct grid_cell gc; u_int idx, px, py, i, j, xoff, yoff; diff --git a/server-client.c b/server-client.c index 13f905b2..8d6eefa0 100644 --- a/server-client.c +++ b/server-client.c @@ -490,7 +490,7 @@ server_client_assume_paste(struct session *s) struct timeval tv; int t; - if ((t = options_get_number(&s->options, "assume-paste-time")) == 0) + if ((t = options_get_number(s->options, "assume-paste-time")) == 0) return (0); timersub(&s->activity_time, &s->last_activity_time, &tv); @@ -556,7 +556,7 @@ server_client_handle_key(struct client *c, int key) m->valid = 1; m->key = key; - if (!options_get_number(&s->options, "mouse")) + if (!options_get_number(s->options, "mouse")) goto forward; } else m->valid = 0; @@ -593,7 +593,7 @@ retry: * If this is a repeating key, start the timer. Otherwise reset * the client back to the root table. */ - xtimeout = options_get_number(&s->options, "repeat-time"); + xtimeout = options_get_number(s->options, "repeat-time"); if (xtimeout != 0 && bd->can_repeat) { c->flags |= CLIENT_REPEAT; @@ -635,8 +635,8 @@ retry: * No match, but in the root table. Prefix switches to the prefix table * and everything else is passed through. */ - if (key == options_get_number(&s->options, "prefix") || - key == options_get_number(&s->options, "prefix2")) { + if (key == options_get_number(s->options, "prefix") || + key == options_get_number(s->options, "prefix2")) { server_client_key_table(c, "prefix"); server_status_client(c); return; @@ -713,7 +713,7 @@ server_client_check_focus(struct window_pane *wp) int push; /* Are focus events off? */ - if (!options_get_number(&global_options, "focus-events")) + if (!options_get_number(global_options, "focus-events")) return; /* Do we need to push the focus state? */ @@ -773,7 +773,7 @@ server_client_reset_state(struct client *c) struct window *w = c->session->curw->window; struct window_pane *wp = w->active; struct screen *s = wp->screen; - struct options *oo = &c->session->options; + struct options *oo = c->session->options; int status, mode, o; if (c->flags & CLIENT_SUSPENDED) @@ -862,7 +862,7 @@ server_client_check_redraw(struct client *c) return; if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { - if (options_get_number(&s->options, "set-titles")) + if (options_get_number(s->options, "set-titles")) server_client_set_title(c); if (c->message_string != NULL) @@ -922,7 +922,7 @@ server_client_set_title(struct client *c) char *title; struct format_tree *ft; - template = options_get_string(&s->options, "set-titles-string"); + template = options_get_string(s->options, "set-titles-string"); ft = format_create(); format_defaults(ft, c, NULL, NULL, NULL); @@ -1206,7 +1206,7 @@ server_client_dispatch_shell(struct client *c) { const char *shell; - shell = options_get_string(&global_s_options, "default-shell"); + shell = options_get_string(global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; proc_send_s(c->peer, MSG_SHELL, shell); diff --git a/server-fn.c b/server-fn.c index 095535ab..e9a04cb5 100644 --- a/server-fn.c +++ b/server-fn.c @@ -39,7 +39,7 @@ server_fill_environ(struct session *s, struct environ *env) long pid; if (s != NULL) { - term = options_get_string(&global_options, "default-terminal"); + term = options_get_string(global_options, "default-terminal"); environ_set(env, "TERM", term); idx = s->id; @@ -183,7 +183,7 @@ server_lock_client(struct client *c) if (c->flags & CLIENT_SUSPENDED) return; - cmd = options_get_string(&c->session->options, "lock-command"); + cmd = options_get_string(c->session->options, "lock-command"); if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) return; @@ -219,7 +219,7 @@ server_kill_window(struct window *w) server_redraw_session_group(s); } - if (options_get_number(&s->options, "renumber-windows")) { + if (options_get_number(s->options, "renumber-windows")) { if ((sg = session_group_find(s)) != NULL) { TAILQ_FOREACH(target_s, &sg->sessions, gentry) session_renumber_windows(target_s); @@ -272,7 +272,7 @@ server_link_window(struct session *src, struct winlink *srcwl, } if (dstidx == -1) - dstidx = -1 - options_get_number(&dst->options, "base-index"); + dstidx = -1 - options_get_number(dst->options, "base-index"); dstwl = session_attach(dst, srcwl->window, dstidx, cause); if (dstwl == NULL) return (-1); @@ -308,7 +308,7 @@ server_destroy_pane(struct window_pane *wp) wp->fd = -1; } - if (options_get_number(&w->options, "remain-on-exit")) { + if (options_get_number(w->options, "remain-on-exit")) { if (old_fd == -1) return; screen_write_start(&ctx, wp, &wp->base); @@ -371,7 +371,7 @@ server_destroy_session(struct session *s) struct client *c; struct session *s_new; - if (!options_get_number(&s->options, "detach-on-destroy")) + if (!options_get_number(s->options, "detach-on-destroy")) s_new = server_next_session(s); else s_new = NULL; @@ -407,7 +407,7 @@ server_check_unattached(void) RB_FOREACH(s, sessions, &sessions) { if (!(s->flags & SESSION_UNATTACHED)) continue; - if (options_get_number (&s->options, "destroy-unattached")) + if (options_get_number (s->options, "destroy-unattached")) session_destroy(s); } } @@ -418,7 +418,7 @@ server_set_identify(struct client *c) struct timeval tv; int delay; - delay = options_get_number(&c->session->options, "display-panes-time"); + delay = options_get_number(c->session->options, "display-panes-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; diff --git a/server.c b/server.c index a36e9a73..2af2c442 100644 --- a/server.c +++ b/server.c @@ -217,7 +217,7 @@ server_loop(void) server_client_loop(); - if (!options_get_number(&global_options, "exit-unattached")) { + if (!options_get_number(global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } diff --git a/session.c b/session.c index a929cada..6b641d48 100644 --- a/session.c +++ b/session.c @@ -120,10 +120,10 @@ session_create(const char *name, int argc, char **argv, const char *path, TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); - options_init(&s->options, &global_s_options); environ_init(&s->environ); if (env != NULL) environ_copy(env, &s->environ); + s->options = options_create(global_s_options); s->tio = NULL; if (tio != NULL) { @@ -190,6 +190,9 @@ session_free(unused int fd, unused short events, void *arg) log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { + environ_free(&s->environ); + options_free(s->options); + free(s->name); free(s); } @@ -212,8 +215,6 @@ session_destroy(struct session *s) event_del(&s->lock_timer); session_group_remove(s); - environ_free(&s->environ); - options_free(&s->options); while (!TAILQ_EMPTY(&s->lastw)) winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); @@ -271,7 +272,7 @@ session_update_activity(struct session *s, struct timeval *from) if (~s->flags & SESSION_UNATTACHED) { timerclear(&tv); - tv.tv_sec = options_get_number(&s->options, "lock-after-time"); + tv.tv_sec = options_get_number(s->options, "lock-after-time"); if (tv.tv_sec != 0) evtimer_add(&s->lock_timer, &tv); } @@ -332,11 +333,11 @@ session_new(struct session *s, const char *name, int argc, char **argv, environ_copy(&s->environ, &env); server_fill_environ(s, &env); - shell = options_get_string(&s->options, "default-shell"); + shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - hlimit = options_get_number(&s->options, "history-limit"); + hlimit = options_get_number(s->options, "history-limit"); w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause); if (w == NULL) { @@ -348,8 +349,8 @@ session_new(struct session *s, const char *name, int argc, char **argv, notify_window_linked(s, w); environ_free(&env); - if (options_get_number(&s->options, "set-remain-on-exit")) - options_set_number(&w->options, "remain-on-exit", 1); + if (options_get_number(s->options, "set-remain-on-exit")) + options_set_number(w->options, "remain-on-exit", 1); session_group_synchronize_from(s); return (wl); @@ -712,7 +713,7 @@ session_renumber_windows(struct session *s) RB_INIT(&s->windows); /* Start renumbering from the base-index if it's set. */ - new_idx = options_get_number(&s->options, "base-index"); + new_idx = options_get_number(s->options, "base-index"); new_curw_idx = 0; /* Go through the winlinks and assign new indexes. */ diff --git a/status.c b/status.c index e17c1f5d..a2f46b95 100644 --- a/status.c +++ b/status.c @@ -61,7 +61,7 @@ status_prompt_find_history_file(void) const char *home, *history_file; char *path; - history_file = options_get_string(&global_options, "history-file"); + history_file = options_get_string(global_options, "history-file"); if (*history_file == '\0') return (NULL); if (*history_file == '/') @@ -160,7 +160,7 @@ status_timer_callback(unused int fd, unused short events, void *arg) c->flags |= CLIENT_STATUS; timerclear(&tv); - tv.tv_sec = options_get_number(&s->options, "status-interval"); + tv.tv_sec = options_get_number(s->options, "status-interval"); if (tv.tv_sec != 0) evtimer_add(&c->status_timer, &tv); @@ -178,7 +178,7 @@ status_timer_start(struct client *c) else evtimer_set(&c->status_timer, status_timer_callback, c); - if (s != NULL && options_get_number(&s->options, "status")) + if (s != NULL && options_get_number(s->options, "status")) status_timer_callback(-1, 0, c); } @@ -198,10 +198,10 @@ status_at_line(struct client *c) { struct session *s = c->session; - if (!options_get_number(&s->options, "status")) + if (!options_get_number(s->options, "status")) return (-1); - if (options_get_number(&s->options, "status-position") == 0) + if (options_get_number(s->options, "status-position") == 0) return (0); return (c->tty.sy - 1); } @@ -216,12 +216,12 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag, char *left; size_t leftlen; - style_apply_update(gc, &s->options, "status-left-style"); + style_apply_update(gc, s->options, "status-left-style"); - template = options_get_string(&s->options, "status-left"); + template = options_get_string(s->options, "status-left"); left = status_replace(c, NULL, template, t); - *size = options_get_number(&s->options, "status-left-length"); + *size = options_get_number(s->options, "status-left-length"); leftlen = screen_write_cstrlen(utf8flag, "%s", left); if (leftlen < *size) *size = leftlen; @@ -238,12 +238,12 @@ status_redraw_get_right(struct client *c, time_t t, int utf8flag, char *right; size_t rightlen; - style_apply_update(gc, &s->options, "status-right-style"); + style_apply_update(gc, s->options, "status-right-style"); - template = options_get_string(&s->options, "status-right"); + template = options_get_string(s->options, "status-right"); right = status_replace(c, NULL, template, t); - *size = options_get_number(&s->options, "status-right-length"); + *size = options_get_number(s->options, "status-right-length"); rightlen = screen_write_cstrlen(utf8flag, "%s", right); if (rightlen < *size) *size = rightlen; @@ -261,7 +261,7 @@ status_get_window_at(struct client *c, u_int x) x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { - oo = &wl->window->options; + oo = wl->window->options; len = strlen(options_get_string(oo, "window-status-separator")); if (x < wl->status_width) @@ -289,7 +289,7 @@ status_redraw(struct client *c) int larrow, rarrow, utf8flag; /* No status line? */ - if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) + if (c->tty.sy == 0 || !options_get_number(s->options, "status")) return (1); left = right = NULL; larrow = rarrow = 0; @@ -298,7 +298,7 @@ status_redraw(struct client *c) t = time(NULL); /* Set up default colour. */ - style_apply(&stdgc, &s->options, "status-style"); + style_apply(&stdgc, s->options, "status-style"); /* Create the target screen. */ memcpy(&old_status, &c->status, sizeof old_status); @@ -313,7 +313,7 @@ status_redraw(struct client *c) goto out; /* Get UTF-8 flag. */ - utf8flag = options_get_number(&s->options, "status-utf8"); + utf8flag = options_get_number(s->options, "status-utf8"); /* Work out left and right strings. */ memcpy(&lgc, &stdgc, sizeof lgc); @@ -346,7 +346,7 @@ status_redraw(struct client *c) if (wl == s->curw) wloffset = wlwidth; - oo = &wl->window->options; + oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); seplen = screen_write_strlen(utf8flag, "%s", sep); wlwidth += wl->status_width + seplen; @@ -361,7 +361,7 @@ status_redraw(struct client *c) screen_write_cnputs(&ctx, -1, &wl->status_cell, utf8flag, "%s", wl->status_text); - oo = &wl->window->options; + oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep); } @@ -461,7 +461,7 @@ draw: else wloffset = 0; if (wlwidth < wlavailable) { - switch (options_get_number(&s->options, "status-justify")) { + switch (options_get_number(s->options, "status-justify")) { case 1: /* centred */ wloffset += (wlavailable - wlwidth) / 2; break; @@ -520,7 +520,7 @@ char * status_print(struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc) { - struct options *oo = &wl->window->options; + struct options *oo = wl->window->options; struct session *s = c->session; const char *fmt; char *text; @@ -553,7 +553,7 @@ status_message_set(struct client *c, const char *fmt, ...) int delay; u_int first, limit; - limit = options_get_number(&global_options, "message-limit"); + limit = options_get_number(global_options, "message-limit"); status_prompt_clear(c); status_message_clear(c); @@ -577,7 +577,7 @@ status_message_set(struct client *c, const char *fmt, ...) free(msg); } - delay = options_get_number(&c->session->options, "display-time"); + delay = options_get_number(c->session->options, "display-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; @@ -631,13 +631,13 @@ status_message_redraw(struct client *c) memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(&s->options, "status-utf8"); + utf8flag = options_get_number(s->options, "status-utf8"); len = screen_write_strlen(utf8flag, "%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; - style_apply(&gc, &s->options, "message-style"); + style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); @@ -686,7 +686,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, c->prompt_flags = flags; - keys = options_get_number(&c->session->options, "status-keys"); + keys = options_get_number(c->session->options, "status-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit); else @@ -761,7 +761,7 @@ status_prompt_redraw(struct client *c) memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(&s->options, "status-utf8"); + utf8flag = options_get_number(s->options, "status-utf8"); len = screen_write_strlen(utf8flag, "%s", c->prompt_string); if (len > c->tty.sx) @@ -770,9 +770,9 @@ status_prompt_redraw(struct client *c) /* Change colours for command mode. */ if (c->prompt_mdata.mode == 1) - style_apply(&gc, &s->options, "message-command-style"); + style_apply(&gc, s->options, "message-command-style"); else - style_apply(&gc, &s->options, "message-style"); + style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); @@ -815,7 +815,7 @@ void status_prompt_key(struct client *c, int key) { struct session *sess = c->session; - struct options *oo = &sess->options; + struct options *oo = sess->options; struct paste_buffer *pb; char *s, *first, *last, word[64], swapc; const char *histstr, *bufdata, *wsep = NULL; diff --git a/tmux.c b/tmux.c index 01e73f5d..bdc426f9 100644 --- a/tmux.c +++ b/tmux.c @@ -38,9 +38,9 @@ extern char *malloc_options; #endif -struct options global_options; /* server options */ -struct options global_s_options; /* session options */ -struct options global_w_options; /* window options */ +struct options *global_options; /* server options */ +struct options *global_s_options; /* session options */ +struct options *global_w_options; /* window options */ struct environ global_environ; char *shell_cmd; @@ -281,22 +281,21 @@ main(int argc, char **argv) if (getcwd(tmp, sizeof tmp) != NULL) environ_set(&global_environ, "PWD", tmp); - options_init(&global_options, NULL); - options_table_populate_tree(server_options_table, &global_options); + global_options = options_create(NULL); + options_table_populate_tree(server_options_table, global_options); - options_init(&global_s_options, NULL); - options_table_populate_tree(session_options_table, &global_s_options); - options_set_string(&global_s_options, "default-shell", "%s", - getshell()); + global_s_options = options_create(NULL); + options_table_populate_tree(session_options_table, global_s_options); + options_set_string(global_s_options, "default-shell", "%s", getshell()); - options_init(&global_w_options, NULL); - options_table_populate_tree(window_options_table, &global_w_options); + global_w_options = options_create(NULL); + options_table_populate_tree(window_options_table, global_w_options); /* Enable UTF-8 if the first client is on UTF-8 terminal. */ if (flags & CLIENT_UTF8) { - options_set_number(&global_s_options, "status-utf8", 1); - options_set_number(&global_s_options, "mouse-utf8", 1); - options_set_number(&global_w_options, "utf8", 1); + options_set_number(global_s_options, "status-utf8", 1); + options_set_number(global_s_options, "mouse-utf8", 1); + options_set_number(global_w_options, "utf8", 1); } /* Override keys to vi if VISUAL or EDITOR are set. */ @@ -307,8 +306,8 @@ main(int argc, char **argv) keys = MODEKEY_VI; else keys = MODEKEY_EMACS; - options_set_number(&global_s_options, "status-keys", keys); - options_set_number(&global_w_options, "mode-keys", keys); + options_set_number(global_s_options, "status-keys", keys); + options_set_number(global_w_options, "mode-keys", keys); } /* diff --git a/tmux.h b/tmux.h index c4b12402..3b2d2d10 100644 --- a/tmux.h +++ b/tmux.h @@ -680,11 +680,6 @@ struct options_entry { RB_ENTRY(options_entry) entry; }; -struct options { - RB_HEAD(options_tree, options_entry) tree; - struct options *parent; -}; - /* Scheduled job. */ struct job { enum { @@ -866,6 +861,7 @@ TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ +struct options; struct window { u_int id; @@ -899,7 +895,7 @@ struct window { #define WINDOW_FORCEHEIGHT 0x4000 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) - struct options options; + struct options *options; u_int references; @@ -993,7 +989,7 @@ struct session { struct winlink_stack lastw; struct winlinks windows; - struct options options; + struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; @@ -1405,9 +1401,9 @@ struct options_table_entry { #define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ -extern struct options global_options; -extern struct options global_s_options; -extern struct options global_w_options; +extern struct options *global_options; +extern struct options *global_s_options; +extern struct options *global_w_options; extern struct environ global_environ; extern char *shell_cmd; extern int debug_level; @@ -1509,10 +1505,10 @@ void notify_session_created(struct session *); void notify_session_closed(struct session *); /* options.c */ -int options_cmp(struct options_entry *, struct options_entry *); -RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); -void options_init(struct options *, struct options *); +struct options *options_create(struct options *); void options_free(struct options *); +struct options_entry *options_first(struct options *); +struct options_entry *options_next(struct options_entry *); struct options_entry *options_find1(struct options *, const char *); struct options_entry *options_find(struct options *, const char *); void options_remove(struct options *, const char *); diff --git a/tty-keys.c b/tty-keys.c index c1de2ab7..309e8c04 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -564,7 +564,7 @@ partial_key: } /* Get the time period. */ - delay = options_get_number(&global_options, "escape-time"); + delay = options_get_number(global_options, "escape-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; diff --git a/tty-term.c b/tty-term.c index 76626ddf..c6147559 100644 --- a/tty-term.c +++ b/tty-term.c @@ -457,7 +457,7 @@ tty_term_find(char *name, int fd, char **cause) } /* Apply terminal overrides. */ - s = options_get_string(&global_options, "terminal-overrides"); + s = options_get_string(global_options, "terminal-overrides"); tty_term_override(term, s); /* Delete curses data. */ diff --git a/tty.c b/tty.c index 3d6f29ca..6b6343c8 100644 --- a/tty.c +++ b/tty.c @@ -229,7 +229,7 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); if (tty_term_flag(tty->term, TTYC_XT)) { - if (options_get_number(&global_options, "focus-events")) { + if (options_get_number(global_options, "focus-events")) { tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } @@ -457,7 +457,7 @@ tty_set_italics(struct tty *tty) const char *s; if (tty_term_has(tty->term, TTYC_SITM)) { - s = options_get_string(&global_options, "default-terminal"); + s = options_get_string(global_options, "default-terminal"); if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { tty_putcode(tty, TTYC_SITM); return; @@ -1686,8 +1686,8 @@ tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) return; pgc = &wp->colgc; - agc = options_get_style(&wp->window->options, "window-active-style"); - wgc = options_get_style(&wp->window->options, "window-style"); + agc = options_get_style(wp->window->options, "window-active-style"); + wgc = options_get_style(wp->window->options, "window-style"); if (gc->fg == 8 && !(gc->flags & GRID_FLAG_FG256)) { if (pgc->fg != 8 || (pgc->flags & GRID_FLAG_FG256)) { diff --git a/window-choose.c b/window-choose.c index 37baf0d7..f1c3f94a 100644 --- a/window-choose.c +++ b/window-choose.c @@ -169,7 +169,7 @@ window_choose_init(struct window_pane *wp) screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_choice); else @@ -748,7 +748,7 @@ window_choose_write_line( { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct screen *s = &data->screen; struct grid_cell gc; size_t last, xoff = 0; @@ -759,7 +759,7 @@ window_choose_write_line( fatalx("called before callback assigned"); last = screen_size_y(s) - 1; - utf8flag = options_get_number(&wp->window->options, "utf8"); + utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) style_apply(&gc, oo, "mode-style"); diff --git a/window-clock.c b/window-clock.c index 5bc546a9..e366714b 100644 --- a/window-clock.c +++ b/window-clock.c @@ -204,8 +204,8 @@ window_clock_draw_screen(struct window_pane *wp) struct tm *tm; u_int i, j, x, y, idx; - colour = options_get_number(&wp->window->options, "clock-mode-colour"); - style = options_get_number(&wp->window->options, "clock-mode-style"); + colour = options_get_number(wp->window->options, "clock-mode-colour"); + style = options_get_number(wp->window->options, "clock-mode-style"); screen_write_start(&ctx, NULL, s); diff --git a/window-copy.c b/window-copy.c index f81a2a16..3ba27c80 100644 --- a/window-copy.c +++ b/window-copy.c @@ -195,7 +195,7 @@ window_copy_init(struct window_pane *wp) s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else @@ -286,7 +286,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) if (backing == &wp->base) return; - utf8flag = options_get_number(&wp->window->options, "utf8"); + utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); @@ -629,13 +629,13 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, break; case MODEKEYCOPY_NEXTWORD: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word(wp, word_separators); break; case MODEKEYCOPY_NEXTWORDEND: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word_end(wp, word_separators); break; @@ -645,7 +645,7 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, break; case MODEKEYCOPY_PREVIOUSWORD: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_previous_word(wp, word_separators); break; @@ -777,7 +777,7 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, return; input_on: - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); else @@ -787,7 +787,7 @@ input_on: return; input_off: - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else @@ -1026,8 +1026,8 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) if (*searchstr == '\0') return; - utf8flag = options_get_number(&wp->window->options, "utf8"); - wrapflag = options_get_number(&wp->window->options, "wrap-search"); + utf8flag = options_get_number(wp->window->options, "utf8"); + wrapflag = options_get_number(wp->window->options, "wrap-search"); searchlen = screen_write_strlen(utf8flag, "%s", searchstr); screen_init(&ss, searchlen, 1, 0); @@ -1093,8 +1093,8 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) if (*searchstr == '\0') return; - utf8flag = options_get_number(&wp->window->options, "utf8"); - wrapflag = options_get_number(&wp->window->options, "wrap-search"); + utf8flag = options_get_number(wp->window->options, "utf8"); + wrapflag = options_get_number(wp->window->options, "wrap-search"); searchlen = screen_write_strlen(utf8flag, "%s", searchstr); screen_init(&ss, searchlen, 1, 0); @@ -1168,7 +1168,7 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct grid_cell gc; char hdr[512]; size_t last, xoff = 0, size = 0, limit; @@ -1301,7 +1301,7 @@ window_copy_update_selection(struct window_pane *wp, int may_redraw) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct grid_cell gc; u_int sx, sy, ty, cy; @@ -1401,7 +1401,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) * bottom-right-most, regardless of copy direction. If it is vi, also * keep bottom-right-most character. */ - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (data->rectflag) { /* * Need to ignore the column with the cursor in it, which for @@ -1460,7 +1460,7 @@ window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, { struct screen_write_ctx ctx; - if (options_get_number(&global_options, "set-clipboard")) { + if (options_get_number(global_options, "set-clipboard")) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); @@ -1523,7 +1523,7 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) if (buf == NULL) return; - if (options_get_number(&global_options, "set-clipboard")) { + if (options_get_number(global_options, "set-clipboard")) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); @@ -2074,7 +2074,7 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) { struct window_copy_mode_data *data = wp->modedata; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; int keys, expected = 1; diff --git a/window.c b/window.c index 9e77adcd..baaaa0f8 100644 --- a/window.c +++ b/window.c @@ -302,7 +302,7 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; - options_init(&w->options, &global_w_options); + w->options = options_create(global_w_options); w->references = 0; @@ -335,7 +335,7 @@ window_create(const char *name, int argc, char **argv, const char *path, w->active = TAILQ_FIRST(&w->panes); if (name != NULL) { w->name = xstrdup(name); - options_set_number(&w->options, "automatic-rename", 0); + options_set_number(w->options, "automatic-rename", 0); } else w->name = default_window_name(w); @@ -359,7 +359,7 @@ window_destroy(struct window *w) if (event_initialized(&w->alerts_timer)) evtimer_del(&w->alerts_timer); - options_free(&w->options); + options_free(w->options); window_destroy_panes(w); @@ -437,8 +437,8 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) * active or inactive pane do not have a custom style, they will need * to be redrawn. */ - agc = options_get_style(&w->options, "window-active-style"); - wgc = options_get_style(&w->options, "window-style"); + agc = options_get_style(w->options, "window-active-style"); + wgc = options_get_style(w->options, "window-style"); if (style_equal(agc, wgc)) return; if (style_equal(&grid_default_cell, &w->active->colgc)) @@ -598,7 +598,7 @@ window_pane_at_index(struct window *w, u_int idx) struct window_pane *wp; u_int n; - n = options_get_number(&w->options, "pane-base-index"); + n = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wp, &w->panes, entry) { if (n == idx) return (wp); @@ -636,7 +636,7 @@ window_pane_index(struct window_pane *wp, u_int *i) struct window_pane *wq; struct window *w = wp->window; - *i = options_get_number(&w->options, "pane-base-index"); + *i = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wq, &w->panes, entry) { if (wp == wq) { return (0); @@ -1002,7 +1002,7 @@ window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid != NULL) return; - if (!options_get_number(&wp->window->options, "alternate-screen")) + if (!options_get_number(wp->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -1032,7 +1032,7 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid == NULL) return; - if (!options_get_number(&wp->window->options, "alternate-screen")) + if (!options_get_number(wp->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -1120,7 +1120,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, if (KEYC_IS_MOUSE(key)) return; - if (options_get_number(&wp->window->options, "synchronize-panes")) { + if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; From bf9c933caed5c74be3c9c4da02d7c57a9dcf091d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 28 Oct 2015 09:51:55 +0000 Subject: [PATCH 811/949] Like options, move the environ struct into environ.c. --- client.c | 2 +- cmd-attach-session.c | 4 ++-- cmd-find.c | 2 +- cmd-new-session.c | 14 +++++++------- cmd-new-window.c | 4 ++-- cmd-respawn-pane.c | 20 ++++++++++---------- cmd-respawn-window.c | 20 ++++++++++---------- cmd-set-environment.c | 4 ++-- cmd-show-environment.c | 9 ++++++--- cmd-split-window.c | 20 ++++++++++---------- cmd-string.c | 6 +++--- cmd-switch-client.c | 2 +- environ.c | 30 +++++++++++++++++++++++++----- format.c | 4 ++-- job.c | 18 +++++++++--------- server-client.c | 8 ++++---- session.c | 22 +++++++++++----------- tmux.c | 8 ++++---- tmux.h | 29 +++++++++++++++-------------- 19 files changed, 125 insertions(+), 101 deletions(-) diff --git a/client.c b/client.c index aa453538..97382f63 100644 --- a/client.c +++ b/client.c @@ -294,7 +294,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) options_free(global_options); options_free(global_s_options); options_free(global_w_options); - environ_free(&global_environ); + environ_free(global_environ); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a3623ec4..c570358c 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -123,7 +123,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (!Eflag) { update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + environ_update(update, c->environ, s->environ); } c->session = s; @@ -154,7 +154,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (!Eflag) { update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + environ_update(update, c->environ, s->environ); } c->session = s; diff --git a/cmd-find.c b/cmd-find.c index fbc06fb7..609297aa 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -121,7 +121,7 @@ cmd_find_try_TMUX(struct client *c, struct window *w) u_int session; struct session *s; - envent = environ_find(&c->environ, "TMUX"); + envent = environ_find(c->environ, "TMUX"); if (envent == NULL) return (NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 48828e54..7b637bc6 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -60,7 +60,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client, *c0; struct session *s, *groupwith; struct window *w; - struct environ env; + struct environ *env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; const char *path; @@ -223,30 +223,30 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (c != NULL && c->session == NULL) - envent = environ_find(&c->environ, "PATH"); + envent = environ_find(c->environ, "PATH"); else - envent = environ_find(&global_environ, "PATH"); + envent = environ_find(global_environ, "PATH"); if (envent != NULL) path = envent->value; /* Construct the environment. */ - environ_init(&env); + env = environ_create(); if (c != NULL && !args_has(args, 'E')) { update = options_get_string(global_s_options, "update-environment"); - environ_update(update, &c->environ, &env); + environ_update(update, c->environ, env); } /* Create the new session. */ idx = -1 - options_get_number(global_s_options, "base-index"); - s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, + s = session_create(newname, argc, argv, path, cwd, env, tiop, idx, sx, sy, &cause); + environ_free(env); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); goto error; } - environ_free(&env); /* Set the initial window name if one given. */ if (argc >= 0 && args_has(args, 'n')) { diff --git a/cmd-new-window.c b/cmd-new-window.c index a3712d76..70e70c68 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -86,9 +86,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 6575e8e4..864b45b8 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -46,7 +46,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp; struct session *s; - struct environ env; + struct environ *env; const char *path; char *cause; u_int idx; @@ -64,10 +64,10 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); window_pane_reset_mode(wp); screen_reinit(&wp->base); @@ -75,22 +75,22 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); - environ_free(&env); + environ_free(env); return (CMD_RETURN_ERROR); } wp->flags |= PANE_REDRAW; server_status_window(w); - environ_free(&env); + environ_free(env); return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 06102ed0..8d8332b1 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -45,7 +45,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp; struct session *s; - struct environ env; + struct environ *env; const char *path; char *cause; struct environ_entry *envent; @@ -64,10 +64,10 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) } } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); @@ -78,17 +78,17 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); - environ_free(&env); + environ_free(env); server_destroy_pane(wp); return (CMD_RETURN_ERROR); } @@ -101,6 +101,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); server_redraw_window(w); - environ_free(&env); + environ_free(env); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 83e63b48..864e1d9b 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -61,11 +61,11 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) value = args->argv[1]; if (args_has(self->args, 'g')) - env = &global_environ; + env = global_environ; else { if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); - env = &s->environ; + env = s->environ; } if (args_has(self->args, 'u')) { diff --git a/cmd-show-environment.c b/cmd-show-environment.c index af24d91b..96cfa289 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -91,12 +91,12 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) struct environ_entry *envent; if (args_has(self->args, 'g')) - env = &global_environ; + env = global_environ; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - env = &s->environ; + env = s->environ; } if (args->argc != 0) { @@ -109,7 +109,10 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - RB_FOREACH(envent, environ, env) + envent = environ_first(env); + while (envent != NULL) { cmd_show_environment_print(self, cmdq, envent); + envent = environ_next(envent); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 60b04d77..75b8eeb6 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -52,7 +52,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; struct window *w; struct window_pane *wp, *new_wp = NULL; - struct environ env; + struct environ *env; const char *cmd, *path, *shell, *template; char **argv, *cause, *new_cause, *cp; u_int hlimit; @@ -67,10 +67,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) w = wl->window; server_unzoom_window(w); - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); if (args->argc == 0) { cmd = options_get_string(s->options, "default-command"); @@ -151,13 +151,13 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, &env, + if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env, s->tio, &cause) != 0) goto error; @@ -170,7 +170,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } else server_status_session(s); - environ_free(&env); + environ_free(env); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) @@ -193,7 +193,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); error: - environ_free(&env); + environ_free(env); if (new_wp != NULL) { layout_close_pane(new_wp); window_remove_pane(w, new_wp); diff --git a/cmd-string.c b/cmd-string.c index db1723cb..51554800 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -126,7 +126,7 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, whitespace = argv[0] + strcspn(argv[0], " \t"); if (equals == NULL || equals > whitespace) break; - environ_put(&global_environ, argv[0]); + environ_put(global_environ, argv[0]); argc--; memmove(argv, argv + 1, argc * (sizeof *argv)); } @@ -303,7 +303,7 @@ cmd_string_variable(const char *s, size_t *p) buf = xrealloc(buf, len + 1); buf[len] = '\0'; - envent = environ_find(&global_environ, buf); + envent = environ_find(global_environ, buf); free(buf); if (envent == NULL) return (xstrdup("")); @@ -326,7 +326,7 @@ cmd_string_expand_tilde(const char *s, size_t *p) last = cmd_string_getc(s, p); if (last == EOF || last == '/' || last == ' '|| last == '\t') { - envent = environ_find(&global_environ, "HOME"); + envent = environ_find(global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') home = envent->value; else if ((pw = getpwuid(getuid())) != NULL) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 5a8c8b08..dbdc5b63 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -121,7 +121,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && !args_has(args, 'E')) { update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + environ_update(update, c->environ, s->environ); } if (c->session != NULL && c->session != s) diff --git a/environ.c b/environ.c index 11b8c849..43a9ce01 100644 --- a/environ.c +++ b/environ.c @@ -27,6 +27,9 @@ * Environment - manipulate a set of environment variables. */ +RB_HEAD(environ, environ_entry); +int environ_cmp(struct environ_entry *, struct environ_entry *); +RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); RB_GENERATE(environ, environ_entry, entry, environ_cmp); int @@ -36,25 +39,42 @@ environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) } /* Initialise the environment. */ -void -environ_init(struct environ *env) +struct environ * +environ_create(void) { + struct environ *env; + + env = xcalloc(1, sizeof *env); RB_INIT(env); + + return (env); } /* Free an environment. */ void environ_free(struct environ *env) { - struct environ_entry *envent; + struct environ_entry *envent, *envent1; - while (!RB_EMPTY(env)) { - envent = RB_ROOT(env); + RB_FOREACH_SAFE(envent, environ, env, envent1) { RB_REMOVE(environ, env, envent); free(envent->name); free(envent->value); free(envent); } + free(env); +} + +struct environ_entry * +environ_first(struct environ *env) +{ + return (RB_MIN(environ, env)); +} + +struct environ_entry * +environ_next(struct environ_entry *envent) +{ + return (RB_NEXT(environ, env, envent)); } /* Copy one environment into another. */ diff --git a/format.c b/format.c index 0add71e5..0d9fbc7f 100644 --- a/format.c +++ b/format.c @@ -631,9 +631,9 @@ format_find(struct format_tree *ft, const char *key, int modifiers) if (~modifiers & FORMAT_TIMESTRING) { envent = NULL; if (ft->s != NULL) - envent = environ_find(&ft->s->environ, key); + envent = environ_find(ft->s->environ, key); if (envent == NULL) - envent = environ_find(&global_environ, key); + envent = environ_find(global_environ, key); if (envent != NULL) { found = envent->value; goto found; diff --git a/job.c b/job.c index 0b348eda..8492fb52 100644 --- a/job.c +++ b/job.c @@ -45,22 +45,22 @@ job_run(const char *cmd, struct session *s, int cwd, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; - struct environ env; + struct environ *env; pid_t pid; int nullfd, out[2]; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); - environ_init(&env); - environ_copy(&global_environ, &env); + env = environ_create(); + environ_copy(global_environ, env); if (s != NULL) - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + environ_copy(s->environ, env); + server_fill_environ(s, env); switch (pid = fork()) { case -1: - environ_free(&env); + environ_free(env); close(out[0]); close(out[1]); return (NULL); @@ -70,8 +70,8 @@ job_run(const char *cmd, struct session *s, int cwd, if (cwd != -1 && fchdir(cwd) != 0) chdir("/"); - environ_push(&env); - environ_free(&env); + environ_push(env); + environ_free(env); if (dup2(out[1], STDIN_FILENO) == -1) fatal("dup2 failed"); @@ -96,7 +96,7 @@ job_run(const char *cmd, struct session *s, int cwd, } /* parent */ - environ_free(&env); + environ_free(env); close(out[1]); job = xmalloc(sizeof *job); diff --git a/server-client.c b/server-client.c index 8d6eefa0..7ae74c09 100644 --- a/server-client.c +++ b/server-client.c @@ -59,7 +59,7 @@ server_client_check_nested(struct client *c) if (c->tty.path == NULL) return (0); - envent = environ_find(&c->environ, "TMUX"); + envent = environ_find(c->environ, "TMUX"); if (envent == NULL || *envent->value == '\0') return (0); @@ -95,7 +95,7 @@ server_client_create(int fd) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); - environ_init(&c->environ); + c->environ = environ_create(); c->fd = -1; c->cwd = -1; @@ -219,7 +219,7 @@ server_client_lost(struct client *c) cmdq_free(c->cmdq); c->cmdq = NULL; - environ_free(&c->environ); + environ_free(c->environ); proc_remove_peer(c->peer); c->peer = NULL; @@ -1146,7 +1146,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) - environ_put(&c->environ, data); + environ_put(c->environ, data); log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: diff --git a/session.c b/session.c index 6b641d48..217b5272 100644 --- a/session.c +++ b/session.c @@ -120,9 +120,9 @@ session_create(const char *name, int argc, char **argv, const char *path, TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); - environ_init(&s->environ); + s->environ = environ_create(); if (env != NULL) - environ_copy(env, &s->environ); + environ_copy(env, s->environ); s->options = options_create(global_s_options); s->tio = NULL; @@ -190,7 +190,7 @@ session_free(unused int fd, unused short events, void *arg) log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { - environ_free(&s->environ); + environ_free(s->environ); options_free(s->options); free(s->name); @@ -319,7 +319,7 @@ session_new(struct session *s, const char *name, int argc, char **argv, { struct window *w; struct winlink *wl; - struct environ env; + struct environ *env; const char *shell; u_int hlimit; @@ -328,26 +328,26 @@ session_new(struct session *s, const char *name, int argc, char **argv, return (NULL); } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; hlimit = options_get_number(s->options, "history-limit"); - w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, + w = window_create(name, argc, argv, path, shell, cwd, env, s->tio, s->sx, s->sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); - environ_free(&env); + environ_free(env); return (NULL); } winlink_set_window(wl, w); notify_window_linked(s, w); - environ_free(&env); + environ_free(env); if (options_get_number(s->options, "set-remain-on-exit")) options_set_number(w->options, "remain-on-exit", 1); diff --git a/tmux.c b/tmux.c index bdc426f9..758479b8 100644 --- a/tmux.c +++ b/tmux.c @@ -41,7 +41,7 @@ extern char *malloc_options; struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ -struct environ global_environ; +struct environ *global_environ; char *shell_cmd; int debug_level; @@ -275,11 +275,11 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; } - environ_init(&global_environ); + global_environ = environ_create(); for (var = environ; *var != NULL; var++) - environ_put(&global_environ, *var); + environ_put(global_environ, *var); if (getcwd(tmp, sizeof tmp) != NULL) - environ_set(&global_environ, "PWD", tmp); + environ_set(global_environ, "PWD", tmp); global_options = options_create(NULL); options_table_populate_tree(server_options_table, global_options); diff --git a/tmux.h b/tmux.h index 3b2d2d10..9a55a1b1 100644 --- a/tmux.h +++ b/tmux.h @@ -36,6 +36,15 @@ extern char *__progname; extern char **environ; +struct client; +struct environ; +struct input_ctx; +struct mouse_event; +struct options; +struct session; +struct tmuxpeer; +struct tmuxproc; + /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" @@ -762,9 +771,6 @@ struct screen_write_ctx { * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ -struct client; -struct session; -struct mouse_event; struct window_mode { struct screen *(*init)(struct window_pane *); void (*free)(struct window_pane *); @@ -796,7 +802,6 @@ struct window_choose_data { }; /* Child window structure. */ -struct input_ctx; struct window_pane { u_int id; u_int active_point; @@ -861,7 +866,6 @@ TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ -struct options; struct window { u_int id; @@ -959,7 +963,6 @@ struct environ_entry { RB_ENTRY(environ_entry) entry; }; -RB_HEAD(environ, environ_entry); /* Client session. */ struct session_group { @@ -998,7 +1001,7 @@ struct session { struct termios *tio; - struct environ environ; + struct environ *environ; int references; @@ -1164,8 +1167,6 @@ struct message_entry { }; /* Client connection. */ -struct tmuxproc; -struct tmuxpeer; struct client { struct tmuxpeer *peer; @@ -1177,7 +1178,7 @@ struct client { struct timeval creation_time; struct timeval activity_time; - struct environ environ; + struct environ *environ; char *title; int cwd; @@ -1404,7 +1405,7 @@ struct options_table_entry { extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; -extern struct environ global_environ; +extern struct environ *global_environ; extern char *shell_cmd; extern int debug_level; extern time_t start_time; @@ -1541,10 +1542,10 @@ void job_free(struct job *); void job_died(struct job *, int); /* environ.c */ -int environ_cmp(struct environ_entry *, struct environ_entry *); -RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); -void environ_init(struct environ *); +struct environ *environ_create(void); void environ_free(struct environ *); +struct environ_entry *environ_first(struct environ *); +struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); void environ_set(struct environ *, const char *, const char *); From 45f3cea263d1f99912cd6b353c91ccb872c26a71 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 29 Oct 2015 09:35:31 +0000 Subject: [PATCH 812/949] Break version check into a separate function, and limit version to 8 bits. --- proc.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/proc.c b/proc.c index 413b2125..12e50e2c 100644 --- a/proc.c +++ b/proc.c @@ -49,7 +49,8 @@ struct tmuxpeer { void *arg; }; -static void proc_update_event(struct tmuxpeer *); +static int peer_check_version(struct tmuxpeer *, struct imsg *); +static void proc_update_event(struct tmuxpeer *); static void proc_event_cb(unused int fd, short events, void *arg) @@ -57,7 +58,6 @@ proc_event_cb(unused int fd, short events, void *arg) struct tmuxpeer *peer = arg; ssize_t n; struct imsg imsg; - int v; if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { @@ -73,14 +73,7 @@ proc_event_cb(unused int fd, short events, void *arg) break; log_debug("peer %p message %d", peer, imsg.hdr.type); - v = imsg.hdr.peerid; - if (imsg.hdr.type != MSG_VERSION && - v != PROTOCOL_VERSION) { - log_debug("peer %p bad version %d", peer, v); - - proc_send(peer, MSG_VERSION, -1, NULL, 0); - peer->flags |= PEER_BAD; - + if (peer_check_version(peer, &imsg) != 0) { if (imsg.fd != -1) close(imsg.fd); imsg_free(&imsg); @@ -115,6 +108,24 @@ proc_signal_cb(int signo, unused short events, void *arg) tp->signalcb(signo); } +static int +peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) +{ + int version; + + version = imsg->hdr.peerid & 0xff; + if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) { + log_debug("peer %p bad version %d", peer, version); + + proc_send(peer, MSG_VERSION, -1, NULL, 0); + peer->flags |= PEER_BAD; + + return (-1); + } + imsg->hdr.peerid >>= 8; + return (0); +} + static void proc_update_event(struct tmuxpeer *peer) { From 01defc9f4965bb174e1d1295754d5a8695683054 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 08:13:58 +0000 Subject: [PATCH 813/949] Because pledge(2) does not allow us to pass directory file descriptors around, we can't use file descriptors for the working directory because we will be unable to pass it to a privileged process to tell it where to read or write files or spawn children. So move tmux back to using strings for the current working directory. We try to check it exists with access() when it is set but ultimately fall back to ~ if it fails at time of use (or / if that fails too). --- client.c | 6 ++++-- cmd-attach-session.c | 14 ++++++-------- cmd-if-shell.c | 4 ++-- cmd-load-buffer.c | 23 +++++++++++++---------- cmd-new-session.c | 39 ++++++++++++++++----------------------- cmd-new-window.c | 32 ++++++++++++++------------------ cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- cmd-run-shell.c | 4 ++-- cmd-save-buffer.c | 35 +++++++++++++++++------------------ cmd-split-window.c | 33 +++++++++++++++------------------ format.c | 2 +- job.c | 9 ++++++--- server-client.c | 14 +++++++++----- session.c | 10 +++++----- tmux.h | 20 ++++++++++---------- window-copy.c | 2 +- window.c | 24 +++++++++++++----------- 18 files changed, 136 insertions(+), 139 deletions(-) diff --git a/client.c b/client.c index 97382f63..008bd631 100644 --- a/client.c +++ b/client.c @@ -273,8 +273,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags) client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); /* Save these before pledge(). */ - if ((cwd = getcwd(path, sizeof path)) == NULL) - cwd = "/"; + if ((cwd = getcwd(path, sizeof path)) == NULL) { + if ((cwd = find_home()) == NULL) + cwd = "/"; + } if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; diff --git a/cmd-attach-session.c b/cmd-attach-session.c index c570358c..b339b890 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -51,9 +51,8 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, struct window_pane *wp = NULL; const char *update; char *cause; - int fd; struct format_tree *ft; - char *cp; + char *cwd; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); @@ -97,18 +96,17 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cp = format_expand(ft, cflag); + cwd = format_expand(ft, cflag); format_free(ft); - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { + if (access(cwd, X_OK) != 0) { + free((void *)cwd); cmdq_error(cmdq, "bad working directory: %s", strerror(errno)); return (CMD_RETURN_ERROR); } - close(s->cwd); - s->cwd = fd; + free((void *)s->cwd); + s->cwd = cwd; } if (c->session != NULL) { diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 0271fdea..a9c84261 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -66,7 +66,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; - int cwd; + const char *cwd; if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); @@ -83,7 +83,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) else if (s != NULL) cwd = s->cwd; else - cwd = -1; + cwd = NULL; } ft = format_create(); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 897807d0..c55e6c11 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -49,10 +49,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path, *bufname; - char *pdata, *new_pdata, *cause; + const char *path, *bufname, *cwd; + char *pdata, *new_pdata, *cause, *file, resolved[PATH_MAX]; size_t psize; - int ch, error, cwd, fd; + int ch, error; bufname = NULL; if (args_has(args, 'b')) @@ -75,13 +75,16 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else if ((s = cmd_find_current(cmdq)) != NULL) cwd = s->cwd; else - cwd = AT_FDCWD; + cwd = "."; - if ((fd = openat(cwd, path, O_RDONLY)) == -1 || - (f = fdopen(fd, "rb")) == NULL) { - if (fd != -1) - close(fd); - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) + f = NULL; + else + f = fopen(resolved, "rb"); + free(file); + if (f == NULL) { + cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); return (CMD_RETURN_ERROR); } @@ -97,7 +100,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) pdata[psize++] = ch; } if (ferror(f)) { - cmdq_error(cmdq, "%s: read error", path); + cmdq_error(cmdq, "%s: read error", resolved); goto error; } if (pdata != NULL) diff --git a/cmd-new-session.c b/cmd-new-session.c index 7b637bc6..90bb2e0e 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -63,10 +63,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ *env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; - const char *path; + const char *path, *cwd, *to_free; char **argv, *cmd, *cause, *cp; - int detached, already_attached, idx, cwd, fd = -1; - int argc; + int detached, already_attached, idx, argc; u_int sx, sy; struct format_tree *ft; struct environ_entry *envent; @@ -118,32 +117,26 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) already_attached = 1; /* Get the new session working directory. */ + to_free = NULL; if (args_has(args, 'c')) { ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, NULL); - cp = format_expand(ft, args_get(args, 'c')); + to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else - free(cp); - cwd = fd; + if (access(cwd, X_OK) != 0) { + free((void *)cwd); + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } } else if (c != NULL && c->session == NULL) cwd = c->cwd; else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) cwd = c0->session->cwd; - else { - fd = open(".", O_RDONLY); - cwd = fd; - } + else + cwd = "."; /* * If this is a new client, check for nesting and save the termios @@ -311,12 +304,12 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (!detached) cmdq->client_exit = 0; - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/cmd-new-window.c b/cmd-new-window.c index 70e70c68..a5085fff 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -49,9 +49,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct session *s; struct winlink *wl; - const char *cmd, *path, *template; + const char *cmd, *path, *template, *cwd, *to_free; char **argv, *cause, *cp; - int argc, idx, detached, cwd, fd = -1; + int argc, idx, detached; struct format_tree *ft; struct environ_entry *envent; @@ -92,24 +92,20 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; + to_free = NULL; if (args_has(args, 'c')) { ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cp = format_expand(ft, args_get(args, 'c')); + cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else - free(cp); - cwd = fd; + if (access(cwd, X_OK) != 0) { + free((void *)cwd); + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else @@ -165,12 +161,12 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 864b45b8..a50b9d06 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -81,7 +81,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 8d8332b1..93af1b68 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -84,7 +84,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 17ac0f76..def3ef01 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -80,7 +80,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; - int cwd; + const char *cwd; if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); @@ -97,7 +97,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) else if (s != NULL) cwd = s->cwd; else - cwd = -1; + cwd = NULL; } ft = format_create(); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 5916cd4d..dca2c8ef 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -57,10 +57,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *bufname, *bufdata, *start, *end; - char *msg; + const char *path, *bufname, *bufdata, *start, *end, *cwd; + const char *flags; + char *msg, *file, resolved[PATH_MAX]; size_t size, used, msglen, bufsize; - int cwd, fd; FILE *f; if (!args_has(args, 'b')) { @@ -97,26 +97,25 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else if ((s = cmd_find_current(cmdq)) != NULL) cwd = s->cwd; else - cwd = AT_FDCWD; + cwd = "."; - f = NULL; - if (args_has(self->args, 'a')) { - fd = openat(cwd, path, O_CREAT|O_RDWR|O_APPEND, 0600); - if (fd != -1) - f = fdopen(fd, "ab"); - } else { - fd = openat(cwd, path, O_CREAT|O_RDWR|O_TRUNC, 0600); - if (fd != -1) - f = fdopen(fd, "wb"); - } + flags = "wb"; + if (args_has(self->args, 'a')) + flags = "ab"; + + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) + f = NULL; + else + f = fopen(resolved, flags); + free(file); if (f == NULL) { - if (fd != -1) - close(fd); - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); return (CMD_RETURN_ERROR); } + if (fwrite(bufdata, 1, bufsize, f) != bufsize) { - cmdq_error(cmdq, "%s: fwrite error", path); + cmdq_error(cmdq, "%s: write error", resolved); fclose(f); return (CMD_RETURN_ERROR); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 75b8eeb6..6adb19a8 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -53,10 +53,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp, *new_wp = NULL; struct environ *env; - const char *cmd, *path, *shell, *template; + const char *cmd, *path, *shell, *template, *cwd, *to_free; char **argv, *cause, *new_cause, *cp; u_int hlimit; - int argc, size, percentage, cwd, fd = -1; + int argc, size, percentage; enum layout_type type; struct layout_cell *lc; struct format_tree *ft; @@ -86,24 +86,20 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) argv = args->argv; } + to_free = NULL; if (args_has(args, 'c')) { ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cp = format_expand(ft, args_get(args, 'c')); + to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else - free(cp); - cwd = fd; + if (access(cwd, X_OK) != 0) { + free((void *)cwd); + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else @@ -188,8 +184,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } notify_window_layout_changed(w); - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: @@ -200,7 +196,8 @@ error: } cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); - if (fd != -1) - close(fd); + + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 0d9fbc7f..847ba08a 100644 --- a/format.c +++ b/format.c @@ -239,7 +239,7 @@ format_job_get(struct format_tree *ft, const char *cmd) t = time(NULL); if (fj->job == NULL && ((ft->flags & FORMAT_FORCE) || fj->last != t)) { - fj->job = job_run(fj->cmd, NULL, -1, format_job_callback, + fj->job = job_run(fj->cmd, NULL, NULL, format_job_callback, NULL, fj); if (fj->job == NULL) { free(fj->out); diff --git a/job.c b/job.c index 8492fb52..0aee7b05 100644 --- a/job.c +++ b/job.c @@ -41,13 +41,14 @@ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * -job_run(const char *cmd, struct session *s, int cwd, +job_run(const char *cmd, struct session *s, const char *cwd, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; struct environ *env; pid_t pid; int nullfd, out[2]; + const char *home; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); @@ -67,8 +68,10 @@ job_run(const char *cmd, struct session *s, int cwd, case 0: /* child */ clear_signals(1); - if (cwd != -1 && fchdir(cwd) != 0) - chdir("/"); + if (cwd == NULL || chdir(cwd) != 0) { + if ((home = find_home()) == NULL || chdir(home) != 0) + chdir("/"); + } environ_push(env); environ_free(env); diff --git a/server-client.c b/server-client.c index 7ae74c09..264e5e4a 100644 --- a/server-client.c +++ b/server-client.c @@ -98,7 +98,7 @@ server_client_create(int fd) c->environ = environ_create(); c->fd = -1; - c->cwd = -1; + c->cwd = NULL; c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; @@ -194,7 +194,7 @@ server_client_lost(struct client *c) screen_free(&c->status); free(c->title); - close(c->cwd); + free((void *)c->cwd); evtimer_del(&c->repeat_timer); @@ -1099,7 +1099,7 @@ error: void server_client_dispatch_identify(struct client *c, struct imsg *imsg) { - const char *data; + const char *data, *home; size_t datalen; int flags; @@ -1132,8 +1132,12 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) case MSG_IDENTIFY_CWD: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_CWD string"); - if ((c->cwd = open(data, O_RDONLY)) == -1) - c->cwd = open("/", O_RDONLY); + if (access(data, X_OK) == 0) + c->cwd = xstrdup(data); + else if ((home = find_home()) != NULL) + c->cwd = xstrdup(home); + else + c->cwd = xstrdup("/"); log_debug("client %p IDENTIFY_CWD %s", c, data); break; case MSG_IDENTIFY_STDIN: diff --git a/session.c b/session.c index 217b5272..cbaadcee 100644 --- a/session.c +++ b/session.c @@ -104,8 +104,8 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * session_create(const char *name, int argc, char **argv, const char *path, - int cwd, struct environ *env, struct termios *tio, int idx, u_int sx, - u_int sy, char **cause) + const char *cwd, struct environ *env, struct termios *tio, int idx, + u_int sx, u_int sy, char **cause) { struct session *s; struct winlink *wl; @@ -114,7 +114,7 @@ session_create(const char *name, int argc, char **argv, const char *path, s->references = 1; s->flags = 0; - s->cwd = dup(cwd); + s->cwd = xstrdup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); @@ -224,7 +224,7 @@ session_destroy(struct session *s) winlink_remove(&s->windows, wl); } - close(s->cwd); + free((void *)s->cwd); session_unref(s); } @@ -315,7 +315,7 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * session_new(struct session *s, const char *name, int argc, char **argv, - const char *path, int cwd, int idx, char **cause) + const char *path, const char *cwd, int idx, char **cause) { struct window *w; struct winlink *wl; diff --git a/tmux.h b/tmux.h index 9a55a1b1..f26f1c8c 100644 --- a/tmux.h +++ b/tmux.h @@ -829,7 +829,7 @@ struct window_pane { int argc; char **argv; char *shell; - int cwd; + const char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; @@ -976,7 +976,7 @@ struct session { u_int id; char *name; - int cwd; + const char *cwd; struct timeval creation_time; struct timeval last_attached_time; @@ -1181,7 +1181,7 @@ struct client { struct environ *environ; char *title; - int cwd; + const char *cwd; char *term; char *ttyname; @@ -1536,7 +1536,7 @@ int options_table_find(const char *, const struct options_table_entry **, /* job.c */ extern struct joblist all_jobs; -struct job *job_run(const char *, struct session *, int, +struct job *job_run(const char *, struct session *, const char *, void (*)(struct job *), void (*)(void *), void *); void job_free(struct job *); void job_died(struct job *, int); @@ -1982,8 +1982,8 @@ struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, int, char **, const char *, - const char *, int, struct environ *, struct termios *, - u_int, u_int, u_int, char **); + const char *, const char *, struct environ *, + struct termios *, u_int, u_int, u_int, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); @@ -2010,7 +2010,7 @@ struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); int window_pane_spawn(struct window_pane *, int, char **, - const char *, const char *, int, struct environ *, + const char *, const char *, const char *, struct environ *, struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, @@ -2144,8 +2144,8 @@ struct session *session_find(const char *); struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, int, char **, const char *, - int, struct environ *, struct termios *, int, u_int, - u_int, char **); + const char *, struct environ *, struct termios *, int, + u_int, u_int, char **); void session_destroy(struct session *); void session_unref(struct session *); int session_check_name(const char *); @@ -2153,7 +2153,7 @@ void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, int, char **, - const char *, int, int, char **); + const char *, const char *, int, char **); struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); diff --git a/window-copy.c b/window-copy.c index 3ba27c80..c5c053bf 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1488,7 +1488,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); - job = job_run(expanded, sess, -1, NULL, NULL, NULL); + job = job_run(expanded, sess, NULL, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); free(expanded); diff --git a/window.c b/window.c index baaaa0f8..49408297 100644 --- a/window.c +++ b/window.c @@ -316,8 +316,8 @@ window_create1(u_int sx, u_int sy) struct window * window_create(const char *name, int argc, char **argv, const char *path, - const char *shell, int cwd, struct environ *env, struct termios *tio, - u_int sx, u_int sy, u_int hlimit, char **cause) + const char *shell, const char *cwd, struct environ *env, + struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; struct window_pane *wp; @@ -736,7 +736,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->argc = 0; wp->argv = NULL; wp->shell = NULL; - wp->cwd = -1; + wp->cwd = NULL; wp->fd = -1; wp->event = NULL; @@ -796,7 +796,7 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); - close(wp->cwd); + free((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); free(wp); @@ -804,12 +804,12 @@ window_pane_destroy(struct window_pane *wp) int window_pane_spawn(struct window_pane *wp, int argc, char **argv, - const char *path, const char *shell, int cwd, struct environ *env, + const char *path, const char *shell, const char *cwd, struct environ *env, struct termios *tio, char **cause) { struct winsize ws; char *argv0, *cmd, **argvp, paneid[16]; - const char *ptr, *first; + const char *ptr, *first, *home; struct termios tio2; int i; @@ -826,9 +826,9 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, free(wp->shell); wp->shell = xstrdup(shell); } - if (cwd != -1) { - close(wp->cwd); - wp->cwd = dup(cwd); + if (cwd != NULL) { + free((void *)wp->cwd); + wp->cwd = xstrdup(cwd); } cmd = cmd_stringify_argv(wp->argc, wp->argv); @@ -847,8 +847,10 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, free(cmd); return (-1); case 0: - if (fchdir(wp->cwd) != 0) - chdir("/"); + if (chdir(wp->cwd) != 0) { + if ((home = find_home()) == NULL || chdir(home) != 0) + chdir("/"); + } if (tcgetattr(STDIN_FILENO, &tio2) != 0) fatal("tcgetattr failed"); From abb4e9e2fa68c38f71fedc6d1ce2e78fcaa59495 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 13:12:03 +0000 Subject: [PATCH 814/949] The output log is only useful once and it means creating a file, so open it once at startup instead of in every call to tty_open. --- server-client.c | 1 - server.c | 2 ++ tmux.h | 3 +-- tty.c | 43 ++++++++++++++++++++----------------------- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/server-client.c b/server-client.c index 264e5e4a..f1e2deff 100644 --- a/server-client.c +++ b/server-client.c @@ -1178,7 +1178,6 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) proc_send(c->peer, MSG_STDIN, -1, NULL, 0); c->tty.fd = -1; - c->tty.log_fd = -1; close(c->fd); c->fd = -1; diff --git a/server.c b/server.c index 2af2c442..bc3fa51d 100644 --- a/server.c +++ b/server.c @@ -173,6 +173,8 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); + if (debug_level > 3) + tty_create_log(); if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); diff --git a/tmux.h b/tmux.h index f26f1c8c..25b51350 100644 --- a/tmux.h +++ b/tmux.h @@ -1102,8 +1102,6 @@ struct tty { int fd; struct bufferevent *event; - int log_fd; - struct termios tio; struct grid_cell cell; @@ -1555,6 +1553,7 @@ void environ_update(const char *, struct environ *, struct environ *); void environ_push(struct environ *); /* tty.c */ +void tty_create_log(void); void tty_init_termios(int, struct termios *, struct bufferevent *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, diff --git a/tty.c b/tty.c index 6b6343c8..c0ae79bb 100644 --- a/tty.c +++ b/tty.c @@ -31,6 +31,8 @@ #include "tmux.h" +static int tty_log_fd = -1; + void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); @@ -59,6 +61,18 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) +void +tty_create_log(void) +{ + char name[64]; + + xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); + + tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); +} + int tty_init(struct tty *tty, struct client *c, int fd, char *term) { @@ -68,7 +82,6 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) return (-1); memset(tty, 0, sizeof *tty); - tty->log_fd = -1; if (term == NULL || *term == '\0') tty->termname = xstrdup("unknown"); @@ -139,17 +152,6 @@ tty_set_size(struct tty *tty, u_int sx, u_int sy) { int tty_open(struct tty *tty, char **cause) { - char out[64]; - int fd; - - if (debug_level > 3) { - xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid()); - fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - fatal("fcntl failed"); - tty->log_fd = fd; - } - tty->term = tty_term_find(tty->termname, tty->fd, cause); if (tty->term == NULL) { tty_close(tty); @@ -308,11 +310,6 @@ tty_stop_tty(struct tty *tty) void tty_close(struct tty *tty) { - if (tty->log_fd != -1) { - close(tty->log_fd); - tty->log_fd = -1; - } - if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty_stop_tty(tty); @@ -406,8 +403,8 @@ tty_puts(struct tty *tty, const char *s) return; bufferevent_write(tty->event, s, strlen(s)); - if (tty->log_fd != -1) - write(tty->log_fd, s, strlen(s)); + if (tty_log_fd != -1) + write(tty_log_fd, s, strlen(s)); } void @@ -438,16 +435,16 @@ tty_putc(struct tty *tty, u_char ch) tty->cx++; } - if (tty->log_fd != -1) - write(tty->log_fd, &ch, 1); + if (tty_log_fd != -1) + write(tty_log_fd, &ch, 1); } void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { bufferevent_write(tty->event, buf, len); - if (tty->log_fd != -1) - write(tty->log_fd, buf, len); + if (tty_log_fd != -1) + write(tty_log_fd, buf, len); tty->cx += width; } From b0a99e85b66c8d74022525908a9584ce1b002da4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 13:43:38 +0000 Subject: [PATCH 815/949] Don't shift version out of peerid, it is needed later. --- client.c | 2 +- proc.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client.c b/client.c index 008bd631..bc934d33 100644 --- a/client.c +++ b/client.c @@ -596,7 +596,7 @@ client_dispatch_wait(struct imsg *imsg) fprintf(stderr, "protocol version mismatch " "(client %d, server %u)\n", PROTOCOL_VERSION, - imsg->hdr.peerid); + imsg->hdr.peerid & 0xff); client_exitval = 1; proc_exit(client_proc); break; diff --git a/proc.c b/proc.c index 12e50e2c..6b84fc00 100644 --- a/proc.c +++ b/proc.c @@ -122,7 +122,6 @@ peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) return (-1); } - imsg->hdr.peerid >>= 8; return (0); } From ba7fb49fb9b972a03547381783abe91be3fcfa37 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 14:51:15 +0000 Subject: [PATCH 816/949] Fall back silently to ~ or / rather than checking -c with access(), this was the old behaviour. --- cmd-attach-session.c | 6 ------ cmd-new-session.c | 7 ------- cmd-new-window.c | 7 ------- cmd-split-window.c | 7 ------- 4 files changed, 27 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index b339b890..e5589277 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -99,12 +99,6 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, cwd = format_expand(ft, cflag); format_free(ft); - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } free((void *)s->cwd); s->cwd = cwd; } diff --git a/cmd-new-session.c b/cmd-new-session.c index 90bb2e0e..65dc6cf5 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -124,13 +124,6 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } } else if (c != NULL && c->session == NULL) cwd = c->cwd; else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) diff --git a/cmd-new-window.c b/cmd-new-window.c index a5085fff..fb89e24f 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -99,13 +99,6 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) NULL); cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else diff --git a/cmd-split-window.c b/cmd-split-window.c index 6adb19a8..fb766d3b 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -93,13 +93,6 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else From 455284f1c02af1a4e0d68555e285563ce7fffb8f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 3 Nov 2015 15:07:36 +0000 Subject: [PATCH 817/949] Detach the client we are looping over, from Thomas Adam. --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index e5589277..cb1f589e 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -139,7 +139,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c->peer, MSG_DETACH, s->name); + proc_send_s(c_loop->peer, MSG_DETACH, s->name); } } From 5577535891e709f87f88311a1adef55dd6c6de85 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 5 Nov 2015 11:05:30 +0000 Subject: [PATCH 818/949] Pass through right click if mouse is on, from Patrick Palka. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index e12dd62c..83b94ac1 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -226,7 +226,7 @@ key_bindings_init(void) "bind -n WheelDownStatus next-window", "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", - "bind -n MouseDown3Pane select-pane -mt=", + "bind -n MouseDown3Pane if-shell -Ft= '#{mouse_any_flag}' 'select-pane -t=; send-keys -M' 'select-pane -mt='", "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", }; u_int i; From e9b58d9de4d0161e9446017c5681298c776089a5 Mon Sep 17 00:00:00 2001 From: schwarze Date: Thu, 5 Nov 2015 16:44:25 +0000 Subject: [PATCH 819/949] Update the internal wcwidth(3) table of tmux(1) to match the data in /usr/src/share/locale/ctype/en_US.UTF-8.src, with one single exception: Keep U+00AD SOFT HYPHEN at width 1 rather than moving it to width 0, a tradition already observed in the old https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c . While here, manually rebalance the btree for optimal lookup speed. OK nicm@ --- utf8.c | 455 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 303 insertions(+), 152 deletions(-) diff --git a/utf8.c b/utf8.c index 12a0b29e..9a06c186 100644 --- a/utf8.c +++ b/utf8.c @@ -34,164 +34,315 @@ struct utf8_width_entry { struct utf8_width_entry *right; }; -/* Random order. Not optimal but it'll do for now... */ +/* Sorted, then repeatedly split in the middle to balance the tree. */ struct utf8_width_entry utf8_width_table[] = { - { 0x00951, 0x00954, 0, NULL, NULL }, - { 0x00ccc, 0x00ccd, 0, NULL, NULL }, - { 0x0fff9, 0x0fffb, 0, NULL, NULL }, - { 0x20000, 0x2fffd, 2, NULL, NULL }, - { 0x00ebb, 0x00ebc, 0, NULL, NULL }, - { 0x01932, 0x01932, 0, NULL, NULL }, - { 0x0070f, 0x0070f, 0, NULL, NULL }, - { 0x00a70, 0x00a71, 0, NULL, NULL }, - { 0x02329, 0x02329, 2, NULL, NULL }, - { 0x00acd, 0x00acd, 0, NULL, NULL }, - { 0x00ac7, 0x00ac8, 0, NULL, NULL }, - { 0x00a3c, 0x00a3c, 0, NULL, NULL }, - { 0x009cd, 0x009cd, 0, NULL, NULL }, - { 0x00591, 0x005bd, 0, NULL, NULL }, - { 0x01058, 0x01059, 0, NULL, NULL }, - { 0x0ffe0, 0x0ffe6, 2, NULL, NULL }, - { 0x01100, 0x0115f, 2, NULL, NULL }, - { 0x0fe20, 0x0fe23, 0, NULL, NULL }, - { 0x0302a, 0x0302f, 0, NULL, NULL }, - { 0x01772, 0x01773, 0, NULL, NULL }, - { 0x005bf, 0x005bf, 0, NULL, NULL }, - { 0x006ea, 0x006ed, 0, NULL, NULL }, - { 0x00bc0, 0x00bc0, 0, NULL, NULL }, - { 0x00962, 0x00963, 0, NULL, NULL }, - { 0x01732, 0x01734, 0, NULL, NULL }, - { 0x00d41, 0x00d43, 0, NULL, NULL }, - { 0x01b42, 0x01b42, 0, NULL, NULL }, - { 0x00a41, 0x00a42, 0, NULL, NULL }, - { 0x00eb4, 0x00eb9, 0, NULL, NULL }, - { 0x00b01, 0x00b01, 0, NULL, NULL }, - { 0x00e34, 0x00e3a, 0, NULL, NULL }, - { 0x03040, 0x03098, 2, NULL, NULL }, - { 0x0093c, 0x0093c, 0, NULL, NULL }, - { 0x00c4a, 0x00c4d, 0, NULL, NULL }, - { 0x01032, 0x01032, 0, NULL, NULL }, - { 0x00f37, 0x00f37, 0, NULL, NULL }, - { 0x00901, 0x00902, 0, NULL, NULL }, - { 0x00cbf, 0x00cbf, 0, NULL, NULL }, - { 0x0a806, 0x0a806, 0, NULL, NULL }, - { 0x00dd2, 0x00dd4, 0, NULL, NULL }, - { 0x00f71, 0x00f7e, 0, NULL, NULL }, - { 0x01752, 0x01753, 0, NULL, NULL }, - { 0x1d242, 0x1d244, 0, NULL, NULL }, - { 0x005c1, 0x005c2, 0, NULL, NULL }, - { 0x0309b, 0x0a4cf, 2, NULL, NULL }, - { 0xe0100, 0xe01ef, 0, NULL, NULL }, - { 0x017dd, 0x017dd, 0, NULL, NULL }, - { 0x00600, 0x00603, 0, NULL, NULL }, - { 0x009e2, 0x009e3, 0, NULL, NULL }, - { 0x00cc6, 0x00cc6, 0, NULL, NULL }, - { 0x0a80b, 0x0a80b, 0, NULL, NULL }, - { 0x01712, 0x01714, 0, NULL, NULL }, - { 0x00b3c, 0x00b3c, 0, NULL, NULL }, - { 0x01b00, 0x01b03, 0, NULL, NULL }, - { 0x007eb, 0x007f3, 0, NULL, NULL }, - { 0xe0001, 0xe0001, 0, NULL, NULL }, - { 0x1d185, 0x1d18b, 0, NULL, NULL }, - { 0x0feff, 0x0feff, 0, NULL, NULL }, - { 0x01b36, 0x01b3a, 0, NULL, NULL }, - { 0x01920, 0x01922, 0, NULL, NULL }, - { 0x00670, 0x00670, 0, NULL, NULL }, - { 0x00f90, 0x00f97, 0, NULL, NULL }, - { 0x01927, 0x01928, 0, NULL, NULL }, - { 0x0200b, 0x0200f, 0, NULL, NULL }, - { 0x0ff00, 0x0ff60, 2, NULL, NULL }, - { 0x0f900, 0x0faff, 2, NULL, NULL }, - { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, - { 0x00cbc, 0x00cbc, 0, NULL, NULL }, - { 0x00eb1, 0x00eb1, 0, NULL, NULL }, - { 0x10a38, 0x10a3a, 0, NULL, NULL }, - { 0x007a6, 0x007b0, 0, NULL, NULL }, - { 0x00f80, 0x00f84, 0, NULL, NULL }, + { 0x00b41, 0x00b44, 0, NULL, NULL }, + { 0x008e4, 0x00902, 0, NULL, NULL }, + { 0x006d6, 0x006dd, 0, NULL, NULL }, { 0x005c4, 0x005c5, 0, NULL, NULL }, - { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, - { 0x017c9, 0x017d3, 0, NULL, NULL }, - { 0x00d4d, 0x00d4d, 0, NULL, NULL }, - { 0x1d167, 0x1d169, 0, NULL, NULL }, - { 0x01036, 0x01037, 0, NULL, NULL }, - { 0xe0020, 0xe007f, 0, NULL, NULL }, - { 0x00f35, 0x00f35, 0, NULL, NULL }, - { 0x017b4, 0x017b5, 0, NULL, NULL }, - { 0x0206a, 0x0206f, 0, NULL, NULL }, - { 0x00c46, 0x00c48, 0, NULL, NULL }, - { 0x01939, 0x0193b, 0, NULL, NULL }, - { 0x01dc0, 0x01dca, 0, NULL, NULL }, - { 0x10a0c, 0x10a0f, 0, NULL, NULL }, - { 0x0102d, 0x01030, 0, NULL, NULL }, - { 0x017c6, 0x017c6, 0, NULL, NULL }, - { 0x00ec8, 0x00ecd, 0, NULL, NULL }, - { 0x00b41, 0x00b43, 0, NULL, NULL }, - { 0x017b7, 0x017bd, 0, NULL, NULL }, - { 0x1d173, 0x1d182, 0, NULL, NULL }, - { 0x00a47, 0x00a48, 0, NULL, NULL }, - { 0x0232a, 0x0232a, 2, NULL, NULL }, - { 0x01b3c, 0x01b3c, 0, NULL, NULL }, - { 0x10a01, 0x10a03, 0, NULL, NULL }, - { 0x00ae2, 0x00ae3, 0, NULL, NULL }, - { 0x00483, 0x00486, 0, NULL, NULL }, - { 0x0135f, 0x0135f, 0, NULL, NULL }, - { 0x01a17, 0x01a18, 0, NULL, NULL }, - { 0x006e7, 0x006e8, 0, NULL, NULL }, - { 0x03099, 0x0309a, 0, NULL, NULL }, - { 0x00b4d, 0x00b4d, 0, NULL, NULL }, - { 0x00ce2, 0x00ce3, 0, NULL, NULL }, - { 0x00bcd, 0x00bcd, 0, NULL, NULL }, - { 0x00610, 0x00615, 0, NULL, NULL }, - { 0x00f99, 0x00fbc, 0, NULL, NULL }, - { 0x009c1, 0x009c4, 0, NULL, NULL }, - { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x00591, 0x005bd, 0, NULL, NULL }, { 0x00300, 0x0036f, 0, NULL, NULL }, - { 0x03030, 0x0303e, 2, NULL, NULL }, - { 0x01b34, 0x01b34, 0, NULL, NULL }, - { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, - { 0x00dca, 0x00dca, 0, NULL, NULL }, - { 0x006d6, 0x006e4, 0, NULL, NULL }, - { 0x00f86, 0x00f87, 0, NULL, NULL }, - { 0x00b3f, 0x00b3f, 0, NULL, NULL }, - { 0x0fe30, 0x0fe6f, 2, NULL, NULL }, - { 0x01039, 0x01039, 0, NULL, NULL }, - { 0x0094d, 0x0094d, 0, NULL, NULL }, - { 0x00c55, 0x00c56, 0, NULL, NULL }, - { 0x00488, 0x00489, 0, NULL, NULL }, - { 0x00e47, 0x00e4e, 0, NULL, NULL }, - { 0x00a81, 0x00a82, 0, NULL, NULL }, - { 0x00ac1, 0x00ac5, 0, NULL, NULL }, - { 0x0202a, 0x0202e, 0, NULL, NULL }, - { 0x00dd6, 0x00dd6, 0, NULL, NULL }, - { 0x018a9, 0x018a9, 0, NULL, NULL }, - { 0x0064b, 0x0065e, 0, NULL, NULL }, - { 0x00abc, 0x00abc, 0, NULL, NULL }, - { 0x00b82, 0x00b82, 0, NULL, NULL }, - { 0x00f39, 0x00f39, 0, NULL, NULL }, - { 0x020d0, 0x020ef, 0, NULL, NULL }, - { 0x01dfe, 0x01dff, 0, NULL, NULL }, - { 0x30000, 0x3fffd, 2, NULL, NULL }, - { 0x00711, 0x00711, 0, NULL, NULL }, - { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, - { 0x0180b, 0x0180d, 0, NULL, NULL }, - { 0x10a3f, 0x10a3f, 0, NULL, NULL }, - { 0x00981, 0x00981, 0, NULL, NULL }, - { 0x0a825, 0x0a826, 0, NULL, NULL }, - { 0x00941, 0x00948, 0, NULL, NULL }, - { 0x01b6b, 0x01b73, 0, NULL, NULL }, - { 0x00e31, 0x00e31, 0, NULL, NULL }, - { 0x0fe10, 0x0fe19, 2, NULL, NULL }, - { 0x00a01, 0x00a02, 0, NULL, NULL }, - { 0x00a4b, 0x00a4d, 0, NULL, NULL }, - { 0x00f18, 0x00f19, 0, NULL, NULL }, - { 0x00fc6, 0x00fc6, 0, NULL, NULL }, - { 0x02e80, 0x03029, 2, NULL, NULL }, - { 0x00b56, 0x00b56, 0, NULL, NULL }, - { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x00483, 0x00489, 0, NULL, NULL }, + { 0x005bf, 0x005bf, 0, NULL, NULL }, + { 0x005c1, 0x005c2, 0, NULL, NULL }, + { 0x00610, 0x0061a, 0, NULL, NULL }, + { 0x00600, 0x00605, 0, NULL, NULL }, { 0x005c7, 0x005c7, 0, NULL, NULL }, - { 0x02060, 0x02063, 0, NULL, NULL }, + { 0x0064b, 0x0065f, 0, NULL, NULL }, + { 0x0061c, 0x0061c, 0, NULL, NULL }, + { 0x00670, 0x00670, 0, NULL, NULL }, + { 0x007a6, 0x007b0, 0, NULL, NULL }, + { 0x006ea, 0x006ed, 0, NULL, NULL }, + { 0x006df, 0x006e4, 0, NULL, NULL }, + { 0x006e7, 0x006e8, 0, NULL, NULL }, + { 0x00711, 0x00711, 0, NULL, NULL }, + { 0x0070f, 0x0070f, 0, NULL, NULL }, + { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x0081b, 0x00823, 0, NULL, NULL }, + { 0x007eb, 0x007f3, 0, NULL, NULL }, + { 0x00816, 0x00819, 0, NULL, NULL }, + { 0x00829, 0x0082d, 0, NULL, NULL }, + { 0x00825, 0x00827, 0, NULL, NULL }, + { 0x00859, 0x0085b, 0, NULL, NULL }, + { 0x00a41, 0x00a42, 0, NULL, NULL }, + { 0x00981, 0x00981, 0, NULL, NULL }, + { 0x00941, 0x00948, 0, NULL, NULL }, + { 0x0093a, 0x0093a, 0, NULL, NULL }, + { 0x0093c, 0x0093c, 0, NULL, NULL }, + { 0x00951, 0x00957, 0, NULL, NULL }, + { 0x0094d, 0x0094d, 0, NULL, NULL }, + { 0x00962, 0x00963, 0, NULL, NULL }, + { 0x009e2, 0x009e3, 0, NULL, NULL }, + { 0x009c1, 0x009c4, 0, NULL, NULL }, + { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x009cd, 0x009cd, 0, NULL, NULL }, + { 0x00a01, 0x00a02, 0, NULL, NULL }, + { 0x00a3c, 0x00a3c, 0, NULL, NULL }, + { 0x00ac1, 0x00ac5, 0, NULL, NULL }, + { 0x00a70, 0x00a71, 0, NULL, NULL }, + { 0x00a4b, 0x00a4d, 0, NULL, NULL }, + { 0x00a47, 0x00a48, 0, NULL, NULL }, + { 0x00a51, 0x00a51, 0, NULL, NULL }, + { 0x00a81, 0x00a82, 0, NULL, NULL }, + { 0x00a75, 0x00a75, 0, NULL, NULL }, + { 0x00abc, 0x00abc, 0, NULL, NULL }, + { 0x00ae2, 0x00ae3, 0, NULL, NULL }, + { 0x00ac7, 0x00ac8, 0, NULL, NULL }, + { 0x00acd, 0x00acd, 0, NULL, NULL }, + { 0x00b3c, 0x00b3c, 0, NULL, NULL }, + { 0x00b01, 0x00b01, 0, NULL, NULL }, + { 0x00b3f, 0x00b3f, 0, NULL, NULL }, + { 0x03190, 0x031ba, 2, NULL, NULL }, + { 0x017c9, 0x017d3, 0, NULL, NULL }, + { 0x00ec8, 0x00ecd, 0, NULL, NULL }, + { 0x00cc6, 0x00cc6, 0, NULL, NULL }, { 0x00c3e, 0x00c40, 0, NULL, NULL }, + { 0x00b82, 0x00b82, 0, NULL, NULL }, + { 0x00b56, 0x00b56, 0, NULL, NULL }, + { 0x00b4d, 0x00b4d, 0, NULL, NULL }, + { 0x00b62, 0x00b63, 0, NULL, NULL }, + { 0x00bcd, 0x00bcd, 0, NULL, NULL }, + { 0x00bc0, 0x00bc0, 0, NULL, NULL }, + { 0x00c00, 0x00c00, 0, NULL, NULL }, + { 0x00c62, 0x00c63, 0, NULL, NULL }, + { 0x00c4a, 0x00c4d, 0, NULL, NULL }, + { 0x00c46, 0x00c48, 0, NULL, NULL }, + { 0x00c55, 0x00c56, 0, NULL, NULL }, + { 0x00cbc, 0x00cbc, 0, NULL, NULL }, + { 0x00c81, 0x00c81, 0, NULL, NULL }, + { 0x00cbf, 0x00cbf, 0, NULL, NULL }, + { 0x00dd2, 0x00dd4, 0, NULL, NULL }, + { 0x00d41, 0x00d44, 0, NULL, NULL }, + { 0x00ce2, 0x00ce3, 0, NULL, NULL }, + { 0x00ccc, 0x00ccd, 0, NULL, NULL }, + { 0x00d01, 0x00d01, 0, NULL, NULL }, + { 0x00d62, 0x00d63, 0, NULL, NULL }, + { 0x00d4d, 0x00d4d, 0, NULL, NULL }, + { 0x00dca, 0x00dca, 0, NULL, NULL }, + { 0x00e47, 0x00e4e, 0, NULL, NULL }, + { 0x00e31, 0x00e31, 0, NULL, NULL }, + { 0x00dd6, 0x00dd6, 0, NULL, NULL }, + { 0x00e34, 0x00e3a, 0, NULL, NULL }, + { 0x00eb4, 0x00eb9, 0, NULL, NULL }, + { 0x00eb1, 0x00eb1, 0, NULL, NULL }, + { 0x00ebb, 0x00ebc, 0, NULL, NULL }, + { 0x0105e, 0x01060, 0, NULL, NULL }, + { 0x00f8d, 0x00f97, 0, NULL, NULL }, + { 0x00f39, 0x00f39, 0, NULL, NULL }, + { 0x00f35, 0x00f35, 0, NULL, NULL }, + { 0x00f18, 0x00f19, 0, NULL, NULL }, + { 0x00f37, 0x00f37, 0, NULL, NULL }, + { 0x00f80, 0x00f84, 0, NULL, NULL }, + { 0x00f71, 0x00f7e, 0, NULL, NULL }, + { 0x00f86, 0x00f87, 0, NULL, NULL }, + { 0x01032, 0x01037, 0, NULL, NULL }, + { 0x00fc6, 0x00fc6, 0, NULL, NULL }, + { 0x00f99, 0x00fbc, 0, NULL, NULL }, + { 0x0102d, 0x01030, 0, NULL, NULL }, + { 0x0103d, 0x0103e, 0, NULL, NULL }, + { 0x01039, 0x0103a, 0, NULL, NULL }, + { 0x01058, 0x01059, 0, NULL, NULL }, + { 0x0135d, 0x0135f, 0, NULL, NULL }, + { 0x01085, 0x01086, 0, NULL, NULL }, + { 0x01071, 0x01074, 0, NULL, NULL }, + { 0x01082, 0x01082, 0, NULL, NULL }, + { 0x0109d, 0x0109d, 0, NULL, NULL }, + { 0x0108d, 0x0108d, 0, NULL, NULL }, + { 0x01100, 0x011ff, 2, NULL, NULL }, + { 0x01772, 0x01773, 0, NULL, NULL }, + { 0x01732, 0x01734, 0, NULL, NULL }, + { 0x01712, 0x01714, 0, NULL, NULL }, + { 0x01752, 0x01753, 0, NULL, NULL }, + { 0x017b7, 0x017bd, 0, NULL, NULL }, + { 0x017b4, 0x017b5, 0, NULL, NULL }, + { 0x017c6, 0x017c6, 0, NULL, NULL }, + { 0x01c2c, 0x01c33, 0, NULL, NULL }, + { 0x01a7f, 0x01a7f, 0, NULL, NULL }, + { 0x01a17, 0x01a18, 0, NULL, NULL }, + { 0x01920, 0x01922, 0, NULL, NULL }, + { 0x0180b, 0x0180e, 0, NULL, NULL }, + { 0x017dd, 0x017dd, 0, NULL, NULL }, + { 0x018a9, 0x018a9, 0, NULL, NULL }, + { 0x01932, 0x01932, 0, NULL, NULL }, + { 0x01927, 0x01928, 0, NULL, NULL }, + { 0x01939, 0x0193b, 0, NULL, NULL }, + { 0x01a60, 0x01a60, 0, NULL, NULL }, + { 0x01a56, 0x01a56, 0, NULL, NULL }, + { 0x01a1b, 0x01a1b, 0, NULL, NULL }, + { 0x01a58, 0x01a5e, 0, NULL, NULL }, + { 0x01a65, 0x01a6c, 0, NULL, NULL }, + { 0x01a62, 0x01a62, 0, NULL, NULL }, + { 0x01a73, 0x01a7c, 0, NULL, NULL }, + { 0x01b80, 0x01b81, 0, NULL, NULL }, + { 0x01b36, 0x01b3a, 0, NULL, NULL }, + { 0x01b00, 0x01b03, 0, NULL, NULL }, + { 0x01ab0, 0x01abe, 0, NULL, NULL }, + { 0x01b34, 0x01b34, 0, NULL, NULL }, + { 0x01b42, 0x01b42, 0, NULL, NULL }, + { 0x01b3c, 0x01b3c, 0, NULL, NULL }, + { 0x01b6b, 0x01b73, 0, NULL, NULL }, + { 0x01be6, 0x01be6, 0, NULL, NULL }, + { 0x01ba8, 0x01ba9, 0, NULL, NULL }, + { 0x01ba2, 0x01ba5, 0, NULL, NULL }, + { 0x01bab, 0x01bad, 0, NULL, NULL }, + { 0x01bed, 0x01bed, 0, NULL, NULL }, + { 0x01be8, 0x01be9, 0, NULL, NULL }, + { 0x01bef, 0x01bf1, 0, NULL, NULL }, + { 0x02329, 0x0232a, 2, NULL, NULL }, + { 0x01dc0, 0x01df5, 0, NULL, NULL }, + { 0x01ce2, 0x01ce8, 0, NULL, NULL }, + { 0x01cd0, 0x01cd2, 0, NULL, NULL }, + { 0x01c36, 0x01c37, 0, NULL, NULL }, + { 0x01cd4, 0x01ce0, 0, NULL, NULL }, + { 0x01cf4, 0x01cf4, 0, NULL, NULL }, + { 0x01ced, 0x01ced, 0, NULL, NULL }, + { 0x01cf8, 0x01cf9, 0, NULL, NULL }, + { 0x02060, 0x02064, 0, NULL, NULL }, + { 0x0200b, 0x0200f, 0, NULL, NULL }, + { 0x01dfc, 0x01dff, 0, NULL, NULL }, + { 0x0202a, 0x0202e, 0, NULL, NULL }, + { 0x02066, 0x0206f, 0, NULL, NULL }, + { 0x020d0, 0x020f0, 0, NULL, NULL }, + { 0x03001, 0x03029, 2, NULL, NULL }, + { 0x02e80, 0x02e99, 2, NULL, NULL }, + { 0x02d7f, 0x02d7f, 0, NULL, NULL }, + { 0x02cef, 0x02cf1, 0, NULL, NULL }, + { 0x02de0, 0x02dff, 0, NULL, NULL }, + { 0x02f00, 0x02fd5, 2, NULL, NULL }, + { 0x02e9b, 0x02ef3, 2, NULL, NULL }, + { 0x02ff0, 0x02ffb, 2, NULL, NULL }, + { 0x03099, 0x0309a, 0, NULL, NULL }, + { 0x0302e, 0x0303e, 2, NULL, NULL }, + { 0x0302a, 0x0302d, 0, NULL, NULL }, + { 0x03041, 0x03096, 2, NULL, NULL }, + { 0x03105, 0x0312d, 2, NULL, NULL }, + { 0x0309b, 0x030ff, 2, NULL, NULL }, + { 0x03131, 0x0318e, 2, NULL, NULL }, + { 0x10a3f, 0x10a3f, 0, NULL, NULL }, + { 0x0aa4c, 0x0aa4c, 0, NULL, NULL }, + { 0x0a825, 0x0a826, 0, NULL, NULL }, + { 0x0a490, 0x0a4c6, 2, NULL, NULL }, + { 0x03250, 0x032fe, 2, NULL, NULL }, + { 0x031f0, 0x0321e, 2, NULL, NULL }, + { 0x031c0, 0x031e3, 2, NULL, NULL }, + { 0x03220, 0x03247, 2, NULL, NULL }, + { 0x04e00, 0x09fcc, 2, NULL, NULL }, + { 0x03300, 0x04db5, 2, NULL, NULL }, + { 0x0a000, 0x0a48c, 2, NULL, NULL }, + { 0x0a6f0, 0x0a6f1, 0, NULL, NULL }, + { 0x0a674, 0x0a67d, 0, NULL, NULL }, + { 0x0a66f, 0x0a672, 0, NULL, NULL }, + { 0x0a69f, 0x0a69f, 0, NULL, NULL }, + { 0x0a806, 0x0a806, 0, NULL, NULL }, + { 0x0a802, 0x0a802, 0, NULL, NULL }, + { 0x0a80b, 0x0a80b, 0, NULL, NULL }, + { 0x0a9b6, 0x0a9b9, 0, NULL, NULL }, + { 0x0a947, 0x0a951, 0, NULL, NULL }, + { 0x0a8e0, 0x0a8f1, 0, NULL, NULL }, + { 0x0a8c4, 0x0a8c4, 0, NULL, NULL }, + { 0x0a926, 0x0a92d, 0, NULL, NULL }, + { 0x0a980, 0x0a982, 0, NULL, NULL }, + { 0x0a960, 0x0a97c, 2, NULL, NULL }, + { 0x0a9b3, 0x0a9b3, 0, NULL, NULL }, + { 0x0aa29, 0x0aa2e, 0, NULL, NULL }, + { 0x0a9bc, 0x0a9bc, 0, NULL, NULL }, + { 0x0a9e5, 0x0a9e5, 0, NULL, NULL }, + { 0x0aa35, 0x0aa36, 0, NULL, NULL }, + { 0x0aa31, 0x0aa32, 0, NULL, NULL }, + { 0x0aa43, 0x0aa43, 0, NULL, NULL }, + { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, + { 0x0aaf6, 0x0aaf6, 0, NULL, NULL }, + { 0x0aab7, 0x0aab8, 0, NULL, NULL }, + { 0x0aab0, 0x0aab0, 0, NULL, NULL }, + { 0x0aa7c, 0x0aa7c, 0, NULL, NULL }, + { 0x0aab2, 0x0aab4, 0, NULL, NULL }, + { 0x0aac1, 0x0aac1, 0, NULL, NULL }, + { 0x0aabe, 0x0aabf, 0, NULL, NULL }, + { 0x0aaec, 0x0aaed, 0, NULL, NULL }, + { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, + { 0x0abe8, 0x0abe8, 0, NULL, NULL }, + { 0x0abe5, 0x0abe5, 0, NULL, NULL }, + { 0x0abed, 0x0abed, 0, NULL, NULL }, + { 0x0f900, 0x0fa6d, 2, NULL, NULL }, + { 0x0d800, 0x0f8ff, 0, NULL, NULL }, + { 0x0fa70, 0x0fad9, 2, NULL, NULL }, + { 0x0fff9, 0x0fffb, 0, NULL, NULL }, + { 0x0fe30, 0x0fe52, 2, NULL, NULL }, + { 0x0fe10, 0x0fe19, 2, NULL, NULL }, + { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, + { 0x0fe20, 0x0fe2d, 0, NULL, NULL }, + { 0x0fe68, 0x0fe6b, 2, NULL, NULL }, + { 0x0fe54, 0x0fe66, 2, NULL, NULL }, + { 0x0feff, 0x0feff, 0, NULL, NULL }, + { 0x10a01, 0x10a03, 0, NULL, NULL }, + { 0x102e0, 0x102e0, 0, NULL, NULL }, + { 0x101fd, 0x101fd, 0, NULL, NULL }, + { 0x10376, 0x1037a, 0, NULL, NULL }, + { 0x10a0c, 0x10a0f, 0, NULL, NULL }, { 0x10a05, 0x10a06, 0, NULL, NULL }, + { 0x10a38, 0x10a3a, 0, NULL, NULL }, + { 0x11633, 0x1163a, 0, NULL, NULL }, + { 0x11236, 0x11237, 0, NULL, NULL }, + { 0x11100, 0x11102, 0, NULL, NULL }, + { 0x1107f, 0x11081, 0, NULL, NULL }, + { 0x11001, 0x11001, 0, NULL, NULL }, + { 0x10ae5, 0x10ae6, 0, NULL, NULL }, + { 0x11038, 0x11046, 0, NULL, NULL }, + { 0x110b9, 0x110ba, 0, NULL, NULL }, + { 0x110b3, 0x110b6, 0, NULL, NULL }, + { 0x110bd, 0x110bd, 0, NULL, NULL }, + { 0x11180, 0x11181, 0, NULL, NULL }, + { 0x1112d, 0x11134, 0, NULL, NULL }, + { 0x11127, 0x1112b, 0, NULL, NULL }, + { 0x11173, 0x11173, 0, NULL, NULL }, + { 0x1122f, 0x11231, 0, NULL, NULL }, + { 0x111b6, 0x111be, 0, NULL, NULL }, + { 0x11234, 0x11234, 0, NULL, NULL }, + { 0x11370, 0x11374, 0, NULL, NULL }, + { 0x11301, 0x11301, 0, NULL, NULL }, + { 0x112df, 0x112df, 0, NULL, NULL }, + { 0x112e3, 0x112ea, 0, NULL, NULL }, + { 0x11340, 0x11340, 0, NULL, NULL }, + { 0x1133c, 0x1133c, 0, NULL, NULL }, + { 0x11366, 0x1136c, 0, NULL, NULL }, + { 0x114c2, 0x114c3, 0, NULL, NULL }, + { 0x114ba, 0x114ba, 0, NULL, NULL }, + { 0x114b3, 0x114b8, 0, NULL, NULL }, + { 0x114bf, 0x114c0, 0, NULL, NULL }, + { 0x115bc, 0x115bd, 0, NULL, NULL }, + { 0x115b2, 0x115b5, 0, NULL, NULL }, + { 0x115bf, 0x115c0, 0, NULL, NULL }, + { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, + { 0x16b30, 0x16b36, 0, NULL, NULL }, + { 0x116ad, 0x116ad, 0, NULL, NULL }, + { 0x1163f, 0x11640, 0, NULL, NULL }, + { 0x1163d, 0x1163d, 0, NULL, NULL }, + { 0x116ab, 0x116ab, 0, NULL, NULL }, + { 0x116b7, 0x116b7, 0, NULL, NULL }, + { 0x116b0, 0x116b5, 0, NULL, NULL }, + { 0x16af0, 0x16af4, 0, NULL, NULL }, + { 0x1bca0, 0x1bca3, 0, NULL, NULL }, + { 0x1b000, 0x1b001, 2, NULL, NULL }, + { 0x16f8f, 0x16f92, 0, NULL, NULL }, + { 0x1bc9d, 0x1bc9e, 0, NULL, NULL }, + { 0x1d173, 0x1d182, 0, NULL, NULL }, + { 0x1d167, 0x1d169, 0, NULL, NULL }, + { 0x1d185, 0x1d18b, 0, NULL, NULL }, + { 0x2a700, 0x2b734, 2, NULL, NULL }, + { 0x1f210, 0x1f23a, 2, NULL, NULL }, + { 0x1e8d0, 0x1e8d6, 0, NULL, NULL }, + { 0x1d242, 0x1d244, 0, NULL, NULL }, + { 0x1f200, 0x1f202, 2, NULL, NULL }, + { 0x1f250, 0x1f251, 2, NULL, NULL }, + { 0x1f240, 0x1f248, 2, NULL, NULL }, + { 0x20000, 0x2a6d6, 2, NULL, NULL }, + { 0xe0020, 0xe007f, 0, NULL, NULL }, + { 0x2f800, 0x2fa1d, 2, NULL, NULL }, + { 0x2b740, 0x2b81d, 2, NULL, NULL }, + { 0xe0001, 0xe0001, 0, NULL, NULL }, + { 0xf0000, 0xffffd, 0, NULL, NULL }, + { 0xe0100, 0xe01ef, 0, NULL, NULL }, + { 0x100000, 0x10fffd, 0, NULL, NULL }, }; struct utf8_width_entry *utf8_width_root = NULL; From dcdccf83338654e41281b3b4a8f1c8d2e0621a13 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 5 Nov 2015 23:32:21 +0000 Subject: [PATCH 820/949] Same bug as last commit, but in the other copy of the loop in this file... --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index cb1f589e..46133923 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -108,7 +108,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c->peer, MSG_DETACH, s->name); + proc_send_s(c_loop->peer, MSG_DETACH, s->name); } } From 005e462e01b58015f3e1d841b185186e6b5d4e14 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 10 Nov 2015 22:29:33 +0000 Subject: [PATCH 821/949] Handle absolute paths properly, and don't use resolved path in realpath() fails. --- cmd-load-buffer.c | 12 ++++++++---- cmd-save-buffer.c | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index c55e6c11..d81288f3 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -77,11 +77,15 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else cwd = "."; - xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) - f = NULL; + if (*path == '/') + file = xstrdup(path); else - f = fopen(resolved, "rb"); + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) { + cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + return (CMD_RETURN_ERROR); + } + f = fopen(resolved, "rb"); free(file); if (f == NULL) { cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index dca2c8ef..8de99075 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -103,11 +103,15 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'a')) flags = "ab"; - xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) - f = NULL; + if (*path == '/') + file = xstrdup(path); else - f = fopen(resolved, flags); + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) { + cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + return (CMD_RETURN_ERROR); + } + f = fopen(resolved, flags); free(file); if (f == NULL) { cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); From 6f3475c6c713245f5ec760298af9a04b8465b844 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 10 Nov 2015 22:33:47 +0000 Subject: [PATCH 822/949] If realpath() fails just try the original path. --- cmd-load-buffer.c | 5 +++-- cmd-save-buffer.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index d81288f3..9352cbe5 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -81,8 +81,9 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) file = xstrdup(path); else xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) { - cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + if (realpath(file, resolved) == NULL && + strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { + cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); return (CMD_RETURN_ERROR); } f = fopen(resolved, "rb"); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 8de99075..9f3deeda 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -107,8 +107,9 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) file = xstrdup(path); else xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) { - cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + if (realpath(file, resolved) == NULL && + strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { + cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); return (CMD_RETURN_ERROR); } f = fopen(resolved, flags); From 00c34df1868ffde8ece952729ddf2e4df09667be Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Nov 2015 23:23:33 +0000 Subject: [PATCH 823/949] Drop mouse-utf8 option and always turn on UTF-8 mouse if the client says it supports UTF-8. --- options-table.c | 5 ----- server-client.c | 12 +++++------- tmux.1 | 4 ---- tmux.c | 1 - 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/options-table.c b/options-table.c index cfec138d..4e9a0a81 100644 --- a/options-table.c +++ b/options-table.c @@ -256,11 +256,6 @@ const struct options_table_entry session_options_table[] = { .default_num = 0 }, - { .name = "mouse-utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - { .name = "prefix", .type = OPTIONS_TABLE_KEY, .default_num = '\002', diff --git a/server-client.c b/server-client.c index f1e2deff..521f2737 100644 --- a/server-client.c +++ b/server-client.c @@ -801,14 +801,12 @@ server_client_reset_state(struct client *c) mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; /* - * Set UTF-8 mouse input if required. If the terminal is UTF-8, the - * user has set mouse-utf8 and any mouse mode is in effect, turn on - * UTF-8 mouse input. If the receiving terminal hasn't requested it - * (that is, it isn't in s->mode), then it'll be converted in - * input_mouse. + * Set UTF-8 mouse input if required. If the terminal is UTF-8 and any + * mouse mode is in effect, turn on UTF-8 mouse input. If the receiving + * terminal hasn't requested it (that is, it isn't in s->mode), then + * it'll be converted in input_mouse. */ - if ((c->tty.flags & TTY_UTF8) && - (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8")) + if ((c->tty.flags & TTY_UTF8) && (mode & ALL_MOUSE_MODES)) mode |= MODE_MOUSE_UTF8; else mode &= ~MODE_MOUSE_UTF8; diff --git a/tmux.1 b/tmux.1 index 1fb99252..4420ac3a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2655,10 +2655,6 @@ captures the mouse and allows mouse events to be bound as key bindings. See the .Sx MOUSE SUPPORT section for details. -.It Xo Ic mouse-utf8 -.Op Ic on | off -.Xc -If enabled, request mouse input as UTF-8 on UTF-8 terminals. .It Ic prefix Ar key Set the key accepted as a prefix key. .It Ic prefix2 Ar key diff --git a/tmux.c b/tmux.c index 758479b8..1e68d9bb 100644 --- a/tmux.c +++ b/tmux.c @@ -294,7 +294,6 @@ main(int argc, char **argv) /* Enable UTF-8 if the first client is on UTF-8 terminal. */ if (flags & CLIENT_UTF8) { options_set_number(global_s_options, "status-utf8", 1); - options_set_number(global_s_options, "mouse-utf8", 1); options_set_number(global_w_options, "utf8", 1); } From 7062b0e65dcbb94bb190f6c50f4089b2ea6278bb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 08:19:18 +0000 Subject: [PATCH 824/949] Default history-file should be "" not NULL, from Greg Onufe. --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 4e9a0a81..d216b6d8 100644 --- a/options-table.c +++ b/options-table.c @@ -85,7 +85,7 @@ const struct options_table_entry server_options_table[] = { { .name = "history-file", .type = OPTIONS_TABLE_STRING, - .default_str = NULL + .default_str = "" }, { .name = "message-limit", From 69e0b8326ad0a983759518b90ed8632146341acf Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:05:34 +0000 Subject: [PATCH 825/949] Support UTF-8 key bindings by expanding the key type from int to uint64_t and converting UTF-8 to Unicode on input and the reverse on output. (This allows key bindings, there are still omissions - the largest being that the various prompts do not accept UTF-8.) --- cmd-bind-key.c | 7 ++-- cmd-list-keys.c | 13 +++++--- cmd-send-keys.c | 3 +- cmd-set-option.c | 2 +- cmd-unbind-key.c | 9 +++--- input-keys.c | 38 ++++++++++++++-------- key-bindings.c | 10 ++++-- key-string.c | 63 ++++++++++++++++++++++++------------ mode-key.c | 16 ++++++--- server-client.c | 40 +++++++++++------------ status.c | 4 +-- tmux.h | 84 ++++++++++++++++++++++++++---------------------- tty-keys.c | 62 ++++++++++++++++++++++------------- utf8.c | 34 ++++++++++++++++++-- window-choose.c | 23 ++++++------- window-clock.c | 5 +-- window-copy.c | 12 +++---- window.c | 2 +- xterm-keys.c | 21 ++++++------ 19 files changed, 280 insertions(+), 168 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index fda39efc..1867e814 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -29,7 +29,8 @@ enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, + key_code); const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", @@ -45,7 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; char *cause; struct cmd_list *cmdlist; - int key; + key_code key; const char *tablename; if (args_has(args, 't')) { @@ -89,7 +90,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) { struct args *args = self->args; const char *tablename; diff --git a/cmd-list-keys.c b/cmd-list-keys.c index d26486bd..c76f9f47 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -57,6 +57,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) char tmp[BUFSIZ]; size_t used; int repeat, width, tablewidth, keywidth; + u_int i; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, cmdq)); @@ -83,8 +84,8 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) width = strlen(table->name); if (width > tablewidth) - tablewidth =width; - width = strlen(key); + tablewidth = width; + width = utf8_cstrwidth(key); if (width > keywidth) keywidth = width; } @@ -102,8 +103,12 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) r = "-r "; else r = " "; - used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ", r, - (int)tablewidth, table->name, (int)keywidth, key); + used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %s", r, + (int)tablewidth, table->name, key); + for (i = 0; i < keywidth - utf8_cstrwidth(key); i++) { + if (strlcat(tmp, " ", sizeof tmp) < sizeof tmp) + used++; + } if (used < sizeof tmp) { cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index dd796d60..73a308ae 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -53,7 +53,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; const u_char *str; - int i, key; + int i; + key_code key; if (args_has(args, 'M')) { wp = cmd_mouse_pane(m, &s, NULL); diff --git a/cmd-set-option.c b/cmd-set-option.c index 6762e6ad..c5a77b45 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -394,7 +394,7 @@ cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { - int key; + key_code key; if ((key = key_string_lookup_string(value)) == KEYC_NONE) { cmdq_error(cmdq, "bad key: %s", value); diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 493f6dde..cec538c0 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -26,8 +26,9 @@ * Unbind key from command. */ -enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, + key_code); const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", @@ -41,7 +42,7 @@ enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - int key; + key_code key; const char *tablename; if (!args_has(args, 'a')) { @@ -95,7 +96,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) { struct args *args = self->args; const char *tablename; diff --git a/input-keys.c b/input-keys.c index 579c0f39..a761a088 100644 --- a/input-keys.c +++ b/input-keys.c @@ -34,7 +34,7 @@ void input_key_mouse(struct window_pane *, struct mouse_event *); struct input_key_ent { - int key; + key_code key; const char *data; int flags; @@ -137,15 +137,16 @@ const struct input_key_ent input_keys[] = { /* Translate a key code into an output key sequence. */ void -input_key(struct window_pane *wp, int key, struct mouse_event *m) +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; - u_char ch; + const struct input_key_ent *ike; + u_int i; + size_t dlen; + char *out; + key_code justkey; + struct utf8_data utf8data; - log_debug("writing key 0x%x (%s) to %%%u", key, + log_debug("writing key 0x%llx (%s) to %%%u", key, key_string_lookup_key(key), wp->id); /* If this is a mouse key, pass off to mouse function. */ @@ -157,13 +158,22 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) /* * If this is a normal 7-bit key, just send it, with a leading escape - * if necessary. + * if necessary. If it is a UTF-8 key, split it and send it. */ - if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) { + justkey = (key & ~KEYC_ESCAPE); + if (key != KEYC_NONE && justkey < 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); - ch = key & ~KEYC_ESCAPE; - bufferevent_write(wp->event, &ch, 1); + utf8data.data[0] = justkey; + bufferevent_write(wp->event, &utf8data.data[0], 1); + return; + } + if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { + if (utf8_split(justkey, &utf8data) != 0) + return; + if (key & KEYC_ESCAPE) + bufferevent_write(wp->event, "\033", 1); + bufferevent_write(wp->event, utf8data.data, utf8data.size); return; } @@ -196,11 +206,11 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) break; } if (i == nitems(input_keys)) { - log_debug("key 0x%x missing", key); + log_debug("key 0x%llx missing", key); return; } dlen = strlen(ike->data); - log_debug("found key 0x%x: \"%s\"", key, ike->data); + log_debug("found key 0x%llx: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) diff --git a/key-bindings.c b/key-bindings.c index 83b94ac1..47a7d867 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -37,7 +37,11 @@ key_table_cmp(struct key_table *e1, struct key_table *e2) int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { - return (bd1->key - bd2->key); + if (bd1->key < bd2->key) + return (-1); + if (bd1->key > bd2->key) + return (1); + return (0); } struct key_table * @@ -80,7 +84,7 @@ key_bindings_unref_table(struct key_table *table) } void -key_bindings_add(const char *name, int key, int can_repeat, +key_bindings_add(const char *name, key_code key, int can_repeat, struct cmd_list *cmdlist) { struct key_table *table; @@ -105,7 +109,7 @@ key_bindings_add(const char *name, int key, int can_repeat, } void -key_bindings_remove(const char *name, int key) +key_bindings_remove(const char *name, key_code key) { struct key_table *table; struct key_binding bd_find, *bd; diff --git a/key-string.c b/key-string.c index 88cd2a32..4285e129 100644 --- a/key-string.c +++ b/key-string.c @@ -22,12 +22,12 @@ #include "tmux.h" -int key_string_search_table(const char *); -int key_string_get_modifiers(const char **); +key_code key_string_search_table(const char *); +key_code key_string_get_modifiers(const char **); const struct { const char *string; - int key; + key_code key; } key_string_table[] = { /* Function keys. */ { "F1", KEYC_F1 }, @@ -98,7 +98,7 @@ const struct { }; /* Find key string in table. */ -int +key_code key_string_search_table(const char *string) { u_int i; @@ -111,10 +111,10 @@ key_string_search_table(const char *string) } /* Find modifiers. */ -int +key_code key_string_get_modifiers(const char **string) { - int modifiers; + key_code modifiers; modifiers = 0; while (((*string)[0] != '\0') && (*string)[1] == '-') { @@ -138,13 +138,16 @@ key_string_get_modifiers(const char **string) } /* Lookup a string and convert to a key value. */ -int +key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; - int key, modifiers; + key_code key; u_short u; int size; + key_code modifiers; + struct utf8_data utf8data; + u_int i; /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { @@ -164,11 +167,21 @@ key_string_lookup_string(const char *string) return (KEYC_NONE); /* Is this a standard ASCII key? */ - if (string[1] == '\0') { - key = (u_char) string[0]; - if (key < 32 || key == 127 || key > 255) + if (string[1] == '\0' && (u_char)string[0] <= 127) { + key = (u_char)string[0]; + if (key < 32 || key == 127) return (KEYC_NONE); } else { + /* Try as a UTF-8 key. */ + if (utf8_open(&utf8data, (u_char)*string)) { + if (strlen(string) != utf8data.size) + return (KEYC_NONE); + for (i = 1; i < utf8data.size; i++) + utf8_append(&utf8data, (u_char)string[i]); + key = utf8_combine(&utf8data); + return (key | modifiers); + } + /* Otherwise look the key up in the table. */ key = key_string_search_table(string); if (key == KEYC_NONE) @@ -195,11 +208,12 @@ key_string_lookup_string(const char *string) /* Convert a key code into string format, with prefix if necessary. */ const char * -key_string_lookup_key(int key) +key_string_lookup_key(key_code key) { - static char out[24]; - char tmp[8]; - u_int i; + static char out[24]; + char tmp[8]; + u_int i; + struct utf8_data utf8data; *out = '\0'; @@ -237,23 +251,32 @@ key_string_lookup_key(int key) return (out); } + /* Is this a UTF-8 key? */ + if (key > 127 && key < KEYC_BASE) { + if (utf8_split(key, &utf8data) == 0) { + memcpy(out, utf8data.data, utf8data.size); + out[utf8data.size] = '\0'; + return (out); + } + } + /* Invalid keys are errors. */ if (key == 127 || key > 255) { - snprintf(out, sizeof out, "", key); + snprintf(out, sizeof out, "", key); return (out); } /* Check for standard or control key. */ - if (key >= 0 && key <= 32) { + if (key <= 32) { if (key == 0 || key > 26) - xsnprintf(tmp, sizeof tmp, "C-%c", 64 + key); + xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key)); else - xsnprintf(tmp, sizeof tmp, "C-%c", 96 + key); + xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key)); } else if (key >= 32 && key <= 126) { tmp[0] = key; tmp[1] = '\0'; } else if (key >= 128) - xsnprintf(tmp, sizeof tmp, "\\%o", key); + xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); return (out); diff --git a/mode-key.c b/mode-key.c index 5ed45bd8..a47cda0b 100644 --- a/mode-key.c +++ b/mode-key.c @@ -40,7 +40,7 @@ /* Entry in the default mode key tables. */ struct mode_key_entry { - int key; + key_code key; /* * Editing mode for vi: 0 is edit mode, keys not in the table are @@ -523,9 +523,15 @@ RB_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); int mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2) { - if (mbind1->mode != mbind2->mode) - return (mbind1->mode - mbind2->mode); - return (mbind1->key - mbind2->key); + if (mbind1->mode < mbind2->mode) + return (-1); + if (mbind1->mode > mbind2->mode) + return (1); + if (mbind1->key < mbind2->key) + return (-1); + if (mbind1->key > mbind2->key) + return (1); + return (0); } const char * @@ -588,7 +594,7 @@ mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree) } enum mode_key_cmd -mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) +mode_key_lookup(struct mode_key_data *mdata, key_code key, const char **arg) { struct mode_key_binding *mbind, mtmp; diff --git a/server-client.c b/server-client.c index 521f2737..fedc93bd 100644 --- a/server-client.c +++ b/server-client.c @@ -32,22 +32,22 @@ #include "tmux.h" -void server_client_key_table(struct client *, const char *); -void server_client_free(int, short, void *); -void server_client_check_focus(struct window_pane *); -void server_client_check_resize(struct window_pane *); -int server_client_check_mouse(struct client *); -void server_client_repeat_timer(int, short, void *); -void server_client_check_exit(struct client *); -void server_client_check_redraw(struct client *); -void server_client_set_title(struct client *); -void server_client_reset_state(struct client *); -int server_client_assume_paste(struct session *); +void server_client_key_table(struct client *, const char *); +void server_client_free(int, short, void *); +void server_client_check_focus(struct window_pane *); +void server_client_check_resize(struct window_pane *); +key_code server_client_check_mouse(struct client *); +void server_client_repeat_timer(int, short, void *); +void server_client_check_exit(struct client *); +void server_client_check_redraw(struct client *); +void server_client_set_title(struct client *); +void server_client_reset_state(struct client *); +int server_client_assume_paste(struct session *); -void server_client_dispatch(struct imsg *, void *); -void server_client_dispatch_command(struct client *, struct imsg *); -void server_client_dispatch_identify(struct client *, struct imsg *); -void server_client_dispatch_shell(struct client *); +void server_client_dispatch(struct imsg *, void *); +void server_client_dispatch_command(struct client *, struct imsg *); +void server_client_dispatch_identify(struct client *, struct imsg *); +void server_client_dispatch_shell(struct client *); /* Check if this client is inside this server. */ int @@ -257,7 +257,7 @@ server_client_free(unused int fd, unused short events, void *arg) } /* Check for mouse keys. */ -int +key_code server_client_check_mouse(struct client *c) { struct session *s = c->session; @@ -267,7 +267,7 @@ server_client_check_mouse(struct client *c) enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE; enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; u_int x, y, b; - int key; + key_code key; log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); @@ -501,7 +501,7 @@ server_client_assume_paste(struct session *s) /* Handle data key input from client. */ void -server_client_handle_key(struct client *c, int key) +server_client_handle_key(struct client *c, key_code key) { struct mouse_event *m = &c->tty.mouse; struct session *s = c->session; @@ -635,8 +635,8 @@ retry: * No match, but in the root table. Prefix switches to the prefix table * and everything else is passed through. */ - if (key == options_get_number(s->options, "prefix") || - key == options_get_number(s->options, "prefix2")) { + if (key == (key_code)options_get_number(s->options, "prefix") || + key == (key_code)options_get_number(s->options, "prefix2")) { server_client_key_table(c, "prefix"); server_status_client(c); return; diff --git a/status.c b/status.c index a2f46b95..df80d2b8 100644 --- a/status.c +++ b/status.c @@ -812,7 +812,7 @@ status_prompt_redraw(struct client *c) /* Handle keys in prompt. */ void -status_prompt_key(struct client *c, int key) +status_prompt_key(struct client *c, key_code key) { struct session *sess = c->session; struct options *oo = sess->options; @@ -1116,7 +1116,7 @@ status_prompt_key(struct client *c, int key) status_prompt_clear(c); break; case MODEKEY_OTHER: - if ((key & 0xff00) != 0 || key < 32 || key == 127) + if (key <= 0x1f || key >= 0x7f) break; c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2); diff --git a/tmux.h b/tmux.h index 25b51350..51ceefd8 100644 --- a/tmux.h +++ b/tmux.h @@ -95,13 +95,13 @@ struct tmuxproc; #define BELL_OTHER 3 /* Special key codes. */ -#define KEYC_NONE 0xfff -#define KEYC_BASE 0x1000 +#define KEYC_NONE 0xffff00000000ULL +#define KEYC_BASE 0x100000000000ULL /* Key modifier bits. */ -#define KEYC_ESCAPE 0x2000 -#define KEYC_CTRL 0x4000 -#define KEYC_SHIFT 0x8000 +#define KEYC_ESCAPE 0x200000000000ULL +#define KEYC_CTRL 0x400000000000ULL +#define KEYC_SHIFT 0x800000000000ULL /* Mask to obtain key w/o modifiers. */ #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT) @@ -121,8 +121,14 @@ struct tmuxproc; { #s "Status", KEYC_ ## name ## _STATUS }, \ { #s "Border", KEYC_ ## name ## _BORDER } +/* + * A single key. This can be ASCII or Unicode or one of the keys starting at + * KEYC_BASE. + */ +typedef uint64_t key_code; + /* Special key codes. */ -enum key_code { +enum { /* Focus events. */ KEYC_FOCUS_IN = KEYC_BASE, KEYC_FOCUS_OUT, @@ -570,7 +576,7 @@ struct mode_key_data { /* Binding between a key and a command. */ struct mode_key_binding { - int key; + key_code key; int mode; enum mode_key_cmd cmd; @@ -776,7 +782,7 @@ struct window_mode { void (*free)(struct window_pane *); void (*resize)(struct window_pane *, u_int, u_int); void (*key)(struct window_pane *, struct client *, struct session *, - int, struct mouse_event *); + key_code, struct mouse_event *); }; /* Structures for choose mode. */ @@ -1030,31 +1036,31 @@ RB_HEAD(sessions, session); /* Mouse input. */ struct mouse_event { - int valid; + int valid; - int key; - int statusat; + key_code key; + int statusat; - u_int x; - u_int y; - u_int b; + u_int x; + u_int y; + u_int b; - u_int lx; - u_int ly; - u_int lb; + u_int lx; + u_int ly; + u_int lb; - int s; - int w; - int wp; + int s; + int w; + int wp; - u_int sgr_type; - u_int sgr_b; + u_int sgr_type; + u_int sgr_b; }; /* TTY information. */ struct tty_key { char ch; - int key; + key_code key; struct tty_key *left; struct tty_key *right; @@ -1340,7 +1346,7 @@ struct cmd_entry { /* Key binding and key table. */ struct key_binding { - int key; + key_code key; struct cmd_list *cmdlist; int can_repeat; @@ -1488,7 +1494,8 @@ enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *, const struct mode_key_table *mode_key_findtable(const char *); void mode_key_init_trees(void); void mode_key_init(struct mode_key_data *, struct mode_key_tree *); -enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int, const char **); +enum mode_key_cmd mode_key_lookup(struct mode_key_data *, key_code, + const char **); /* notify.c */ void notify_enable(void); @@ -1632,9 +1639,9 @@ const char *tty_term_describe(struct tty_term *, enum tty_code_code); const char *tty_acs_get(struct tty *, u_char); /* tty-keys.c */ -void tty_keys_build(struct tty *); -void tty_keys_free(struct tty *); -int tty_keys_next(struct tty *); +void tty_keys_build(struct tty *); +void tty_keys_free(struct tty *); +key_code tty_keys_next(struct tty *); /* arguments.c */ int args_cmp(struct args_entry *, struct args_entry *); @@ -1720,16 +1727,16 @@ int key_table_cmp(struct key_table *, struct key_table *); int key_bindings_cmp(struct key_binding *, struct key_binding *); struct key_table *key_bindings_get_table(const char *, int); void key_bindings_unref_table(struct key_table *); -void key_bindings_add(const char *, int, int, struct cmd_list *); -void key_bindings_remove(const char *, int); +void key_bindings_add(const char *, key_code, int, struct cmd_list *); +void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *, struct mouse_event *); /* key-string.c */ -int key_string_lookup_string(const char *); -const char *key_string_lookup_key(int); +key_code key_string_lookup_string(const char *); +const char *key_string_lookup_key(key_code); /* alerts.c */ void alerts_reset_all(void); @@ -1753,7 +1760,7 @@ void server_add_accept(int); /* server-client.c */ int server_client_check_nested(struct client *); -void server_client_handle_key(struct client *, int); +void server_client_handle_key(struct client *, key_code); void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); @@ -1803,7 +1810,7 @@ void status_prompt_set(struct client *, const char *, const char *, int (*)(void *, const char *), void (*)(void *), void *, int); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); -void status_prompt_key(struct client *, int); +void status_prompt_key(struct client *, key_code); void status_prompt_update(struct client *, const char *, const char *); void status_prompt_load_history(void); void status_prompt_save_history(void); @@ -1819,11 +1826,11 @@ struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ -void input_key(struct window_pane *, int, struct mouse_event *); +void input_key(struct window_pane *, key_code, struct mouse_event *); /* xterm-keys.c */ -char *xterm_keys_lookup(int); -int xterm_keys_find(const char *, size_t, size_t *, int *); +char *xterm_keys_lookup(key_code); +int xterm_keys_find(const char *, size_t, size_t *, key_code *); /* colour.c */ int colour_find_rgb(u_char, u_char, u_char); @@ -2020,7 +2027,7 @@ int window_pane_set_mode( struct window_pane *, const struct window_mode *); void window_pane_reset_mode(struct window_pane *); void window_pane_key(struct window_pane *, struct client *, - struct session *, int, struct mouse_event *); + struct session *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); char *window_pane_search( struct window_pane *, const char *, u_int *); @@ -2179,6 +2186,7 @@ void utf8_set(struct utf8_data *, u_char); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); +int utf8_split(u_int, struct utf8_data *); u_int utf8_split2(u_int, u_char *); int utf8_strvis(char *, const char *, size_t, int); struct utf8_data *utf8_fromcstr(const char *); diff --git a/tty-keys.c b/tty-keys.c index 309e8c04..31adf0f0 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -33,11 +33,11 @@ * into a ternary tree. */ -void tty_keys_add1(struct tty_key **, const char *, int); -void tty_keys_add(struct tty *, const char *, int); +void tty_keys_add1(struct tty_key **, const char *, key_code); +void tty_keys_add(struct tty *, const char *, key_code); void tty_keys_free1(struct tty_key *); -struct tty_key *tty_keys_find1( - struct tty_key *, const char *, size_t, size_t *); +struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, + size_t *); struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); void tty_keys_callback(int, short, void *); int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); @@ -45,7 +45,7 @@ int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { const char *string; - int key; + key_code key; }; const struct tty_default_key_raw tty_default_raw_keys[] = { /* @@ -165,7 +165,7 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { /* Default terminfo(5) keys. */ struct tty_default_key_code { enum tty_code_code code; - int key; + key_code key; }; const struct tty_default_key_code tty_default_code_keys[] = { /* Function keys. */ @@ -317,7 +317,7 @@ const struct tty_default_key_code tty_default_code_keys[] = { /* Add key to tree. */ void -tty_keys_add(struct tty *tty, const char *s, int key) +tty_keys_add(struct tty *tty, const char *s, key_code key) { struct tty_key *tk; size_t size; @@ -325,17 +325,17 @@ tty_keys_add(struct tty *tty, const char *s, int key) keystr = key_string_lookup_key(key); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { - log_debug("new key %s: 0x%x (%s)", s, key, keystr); + log_debug("new key %s: 0x%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); } else { - log_debug("replacing key %s: 0x%x (%s)", s, key, keystr); + log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr); tk->key = key; } } /* Add next node to the tree. */ void -tty_keys_add1(struct tty_key **tkp, const char *s, int key) +tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) { struct tty_key *tk; @@ -464,15 +464,18 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) * Process at least one key in the buffer and invoke tty->key_callback. Return * 0 if there are no further keys, or 1 if there could be more in the buffer. */ -int +key_code tty_keys_next(struct tty *tty) { - struct tty_key *tk; - struct timeval tv; - const char *buf; - size_t len, size; - cc_t bspace; - int key, delay, expired = 0; + struct tty_key *tk; + struct timeval tv; + const char *buf; + size_t len, size; + cc_t bspace; + int delay, expired = 0; + key_code key; + struct utf8_data utf8data; + u_int i; /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); @@ -535,8 +538,23 @@ first_key: } } + /* Is this valid UTF-8? */ + if (utf8_open(&utf8data, (u_char)*buf)) { + size = utf8data.size; + if (len < size) { + if (expired) + goto discard_key; + goto partial_key; + } + for (i = 1; i < size; i++) + utf8_append(&utf8data, (u_char)buf[i]); + key = utf8_combine(&utf8data); + log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); + goto complete_key; + } + /* No key found, take first. */ - key = (u_char) *buf; + key = (u_char)*buf; size = 1; /* @@ -578,7 +596,7 @@ partial_key: return (0); complete_key: - log_debug("complete key %.*s %#x", (int) size, buf, key); + log_debug("complete key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); @@ -604,7 +622,7 @@ complete_key: return (1); discard_key: - log_debug("discard key %.*s %#x", (int) size, buf, key); + log_debug("discard key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); @@ -684,10 +702,10 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) utf8_append(&utf8data, buf[*size]); value = utf8_combine(&utf8data); } else - value = (u_char) buf[*size]; + value = (u_char)buf[*size]; (*size)++; } else { - value = (u_char) buf[*size]; + value = (u_char)buf[*size]; (*size)++; } diff --git a/utf8.c b/utf8.c index 9a06c186..e61bf996 100644 --- a/utf8.c +++ b/utf8.c @@ -394,6 +394,8 @@ utf8_open(struct utf8_data *utf8data, u_char ch) int utf8_append(struct utf8_data *utf8data, u_char ch) { + /* XXX this should do validity checks too! */ + if (utf8data->have >= utf8data->size) fatalx("UTF-8 character overflow"); if (utf8data->size > sizeof utf8data->data) @@ -467,18 +469,46 @@ utf8_combine(const struct utf8_data *utf8data) case 3: value = utf8data->data[2] & 0x3f; value |= (utf8data->data[1] & 0x3f) << 6; - value |= (utf8data->data[0] & 0x0f) << 12; + value |= (utf8data->data[0] & 0xf) << 12; break; case 4: value = utf8data->data[3] & 0x3f; value |= (utf8data->data[2] & 0x3f) << 6; value |= (utf8data->data[1] & 0x3f) << 12; - value |= (utf8data->data[0] & 0x07) << 18; + value |= (utf8data->data[0] & 0x7) << 18; break; } return (value); } +/* Split a UTF-8 character. */ +int +utf8_split(u_int uc, struct utf8_data *utf8data) +{ + if (uc < 0x7f) { + utf8data->size = 1; + utf8data->data[0] = uc; + } else if (uc < 0x7ff) { + utf8data->size = 2; + utf8data->data[0] = 0xc0 | ((uc >> 6) & 0x1f); + utf8data->data[1] = 0x80 | (uc & 0x3f); + } else if (uc < 0xffff) { + utf8data->size = 3; + utf8data->data[0] = 0xe0 | ((uc >> 12) & 0xf); + utf8data->data[1] = 0x80 | ((uc >> 6) & 0x3f); + utf8data->data[2] = 0x80 | (uc & 0x3f); + } else if (uc < 0x1fffff) { + utf8data->size = 4; + utf8data->data[0] = 0xf0 | ((uc >> 18) & 0x7); + utf8data->data[1] = 0x80 | ((uc >> 12) & 0x3f); + utf8data->data[2] = 0x80 | ((uc >> 6) & 0x3f); + utf8data->data[3] = 0x80 | (uc & 0x3f); + } else + return (-1); + utf8data->width = utf8_width(utf8data); + return (0); +} + /* Split a two-byte UTF-8 character. */ u_int utf8_split2(u_int uc, u_char *ptr) diff --git a/window-choose.c b/window-choose.c index f1c3f94a..fa0e95c8 100644 --- a/window-choose.c +++ b/window-choose.c @@ -29,11 +29,11 @@ struct screen *window_choose_init(struct window_pane *); void window_choose_free(struct window_pane *); void window_choose_resize(struct window_pane *, u_int, u_int); void window_choose_key(struct window_pane *, struct client *, - struct session *, int, struct mouse_event *); + struct session *, key_code, struct mouse_event *); void window_choose_default_callback(struct window_choose_data *); struct window_choose_mode_item *window_choose_get_item(struct window_pane *, - int, struct mouse_event *); + key_code, struct mouse_event *); void window_choose_fire_callback( struct window_pane *, struct window_choose_data *); @@ -86,9 +86,9 @@ struct window_choose_mode_data { void window_choose_free1(struct window_choose_mode_data *); int window_choose_key_index(struct window_choose_mode_data *, u_int); -int window_choose_index_key(struct window_choose_mode_data *, int); +int window_choose_index_key(struct window_choose_mode_data *, key_code); void window_choose_prompt_input(enum window_choose_input_type, - const char *, struct window_pane *, int); + const char *, struct window_pane *, key_code); void window_choose_reset_top(struct window_pane *, u_int); void @@ -314,7 +314,7 @@ window_choose_fire_callback( void window_choose_prompt_input(enum window_choose_input_type input_type, - const char *prompt, struct window_pane *wp, int key) + const char *prompt, struct window_pane *wp, key_code key) { struct window_choose_mode_data *data = wp->modedata; size_t input_len; @@ -490,7 +490,8 @@ window_choose_expand(struct window_pane *wp, struct session *s, u_int pos) } struct window_choose_mode_item * -window_choose_get_item(struct window_pane *wp, int key, struct mouse_event *m) +window_choose_get_item(struct window_pane *wp, key_code key, + struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; u_int x, y, idx; @@ -509,7 +510,7 @@ window_choose_get_item(struct window_pane *wp, int key, struct mouse_event *m) void window_choose_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, int key, struct mouse_event *m) + unused struct session *sess, key_code key, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -743,8 +744,8 @@ window_choose_key(struct window_pane *wp, unused struct client *c, } void -window_choose_write_line( - struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, + u_int py) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; @@ -821,7 +822,7 @@ window_choose_key_index(struct window_choose_mode_data *data, u_int idx) } int -window_choose_index_key(struct window_choose_mode_data *data, int key) +window_choose_index_key(struct window_choose_mode_data *data, key_code key) { static const char keys[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" @@ -834,7 +835,7 @@ window_choose_index_key(struct window_choose_mode_data *data, int key) mkey = mode_key_lookup(&data->mdata, *ptr, NULL); if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) continue; - if (key == *ptr) + if (key == (key_code)*ptr) return (idx); idx++; } diff --git a/window-clock.c b/window-clock.c index e366714b..51f97250 100644 --- a/window-clock.c +++ b/window-clock.c @@ -28,7 +28,7 @@ struct screen *window_clock_init(struct window_pane *); void window_clock_free(struct window_pane *); void window_clock_resize(struct window_pane *, u_int, u_int); void window_clock_key(struct window_pane *, struct client *, - struct session *, int, struct mouse_event *); + struct session *, key_code, struct mouse_event *); void window_clock_timer_callback(int, short, void *); void window_clock_draw_screen(struct window_pane *); @@ -186,7 +186,8 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) void window_clock_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, unused int key, unused struct mouse_event *m) + unused struct session *sess, unused key_code key, + unused struct mouse_event *m) { window_pane_reset_mode(wp); } diff --git a/window-copy.c b/window-copy.c index c5c053bf..d3e5b7d9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -28,9 +28,9 @@ struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_key(struct window_pane *, struct client *, struct session *, - int, struct mouse_event *); -int window_copy_key_input(struct window_pane *, int); -int window_copy_key_numeric_prefix(struct window_pane *, int); + key_code, struct mouse_event *); +int window_copy_key_input(struct window_pane *, key_code); +int window_copy_key_numeric_prefix(struct window_pane *, key_code); void window_copy_redraw_selection(struct window_pane *, u_int); void window_copy_redraw_lines(struct window_pane *, u_int, u_int); @@ -368,7 +368,7 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) void window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, - int key, struct mouse_event *m) + key_code key, struct mouse_event *m) { const char *word_separators; struct window_copy_mode_data *data = wp->modedata; @@ -800,7 +800,7 @@ input_off: } int -window_copy_key_input(struct window_pane *wp, int key) +window_copy_key_input(struct window_pane *wp, key_code key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -897,7 +897,7 @@ window_copy_key_input(struct window_pane *wp, int key) } int -window_copy_key_numeric_prefix(struct window_pane *wp, int key) +window_copy_key_numeric_prefix(struct window_pane *wp, key_code key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; diff --git a/window.c b/window.c index 49408297..c5f9f176 100644 --- a/window.c +++ b/window.c @@ -1102,7 +1102,7 @@ window_pane_reset_mode(struct window_pane *wp) void window_pane_key(struct window_pane *wp, struct client *c, struct session *s, - int key, struct mouse_event *m) + key_code key, struct mouse_event *m) { struct window_pane *wp2; diff --git a/xterm-keys.c b/xterm-keys.c index 10d51c78..f1490fcc 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -40,11 +40,12 @@ * We accept any but always output the latter (it comes first in the table). */ -int xterm_keys_match(const char *, const char *, size_t, size_t *, u_int *); -int xterm_keys_modifiers(const char *, size_t, size_t *, u_int *); +int xterm_keys_match(const char *, const char *, size_t, size_t *, + key_code *); +int xterm_keys_modifiers(const char *, size_t, size_t *, key_code *); struct xterm_keys_entry { - int key; + key_code key; const char *template; }; @@ -115,7 +116,7 @@ const struct xterm_keys_entry xterm_keys_table[] = { */ int xterm_keys_match(const char *template, const char *buf, size_t len, - size_t *size, u_int *modifiers) + size_t *size, key_code *modifiers) { size_t pos; int retval; @@ -148,7 +149,8 @@ xterm_keys_match(const char *template, const char *buf, size_t len, /* Find modifiers from buffer. */ int -xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) +xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, + key_code *modifiers) { u_int flags; @@ -179,11 +181,12 @@ xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) * key), -1 for not found, 1 for partial match. */ int -xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) +xterm_keys_find(const char *buf, size_t len, size_t *size, key_code *key) { const struct xterm_keys_entry *entry; - u_int i, modifiers; + u_int i; int matched; + key_code modifiers; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; @@ -201,11 +204,11 @@ xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) /* Lookup a key number from the table. */ char * -xterm_keys_lookup(int key) +xterm_keys_lookup(key_code key) { const struct xterm_keys_entry *entry; u_int i; - int modifiers; + key_code modifiers; char *out; modifiers = 1; From a0f3999ce7b6df32324b493fa3c8007de93d2c48 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:07:10 +0000 Subject: [PATCH 826/949] Remove the mouse_utf8_flag format as well. --- format.c | 2 -- tmux.1 | 1 - 2 files changed, 3 deletions(-) diff --git a/format.c b/format.c index 847ba08a..f799dfb5 100644 --- a/format.c +++ b/format.c @@ -1138,8 +1138,6 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", !!(wp->base.mode & MODE_MOUSE_BUTTON)); - format_add(ft, "mouse_utf8_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_UTF8)); format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } diff --git a/tmux.1 b/tmux.1 index 4420ac3a..a35c01f8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3394,7 +3394,6 @@ The following variables are available, where appropriate: .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" -.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" From 1b86f520ea1620628e569ea833c7b13306c18a4e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:09:11 +0000 Subject: [PATCH 827/949] Nuke the utf8 and status-utf8 options and make tmux only a UTF-8 terminal. We still support non-UTF-8 terminals outside tmux, but inside it is always UTF-8 (as when the utf8 and status-utf8 options were on). --- cmd-choose-buffer.c | 4 +-- cmd-list-buffers.c | 2 +- format.c | 5 ++-- input.c | 5 ---- options-table.c | 10 -------- paste.c | 7 ++--- screen-write.c | 35 +++++++++++++------------ status.c | 62 +++++++++++++++++++-------------------------- tmux.1 | 40 +++++------------------------ tmux.c | 6 ----- tmux.h | 32 +++++++++++------------ window-choose.c | 5 ++-- window-copy.c | 18 +++++-------- 13 files changed, 82 insertions(+), 149 deletions(-) diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index e790de6b..dbb20fc4 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -51,7 +51,6 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) char *action, *action_data; const char *template; u_int idx; - int utf8flag; if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { cmdq_error(cmdq, "no client available"); @@ -63,7 +62,6 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); - utf8flag = options_get_number(wl->window->options, "utf8"); if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); @@ -83,7 +81,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_defaults_paste_buffer(cdata->ft, pb, utf8flag); + format_defaults_paste_buffer(cdata->ft, pb); xasprintf(&action_data, "%s", paste_buffer_name(pb)); cdata->command = cmd_template_replace(action, action_data, 1); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 37571b80..3a8a790a 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -55,7 +55,7 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { ft = format_create(); - format_defaults_paste_buffer(ft, pb, 0); + format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); diff --git a/format.c b/format.c index f799dfb5..bcac7934 100644 --- a/format.c +++ b/format.c @@ -1144,8 +1144,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) /* Set default format keys for paste buffer. */ void -format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, - int utf8flag) +format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { size_t bufsize; char *s; @@ -1154,7 +1153,7 @@ format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, format_add(ft, "buffer_size", "%zu", bufsize); format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); - s = paste_make_sample(pb, utf8flag); + s = paste_make_sample(pb); format_add(ft, "buffer_sample", "%s", s); free(s); } diff --git a/input.c b/input.c index 3a02b0ce..41276d9a 100644 --- a/input.c +++ b/input.c @@ -1921,11 +1921,6 @@ input_exit_rename(struct input_ctx *ictx) int input_utf8_open(struct input_ctx *ictx) { - if (!options_get_number(ictx->wp->window->options, "utf8")) { - /* Print, and do not switch state. */ - input_print(ictx); - return (-1); - } log_debug("%s", __func__); utf8_open(&ictx->utf8data, ictx->ch); diff --git a/options-table.c b/options-table.c index d216b6d8..cf4e2d14 100644 --- a/options-table.c +++ b/options-table.c @@ -416,11 +416,6 @@ const struct options_table_entry session_options_table[] = { .default_str = "bg=green,fg=black" }, - { .name = "status-utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ - }, - { .name = "update-environment", .type = OPTIONS_TABLE_STRING, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " @@ -624,11 +619,6 @@ const struct options_table_entry window_options_table[] = { .default_num = 0 }, - { .name = "utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ - }, - { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, .default_str = "default" diff --git a/paste.c b/paste.c index ad3b56b5..5f60914f 100644 --- a/paste.c +++ b/paste.c @@ -275,7 +275,7 @@ paste_set(char *data, size_t size, const char *name, char **cause) /* Convert start of buffer into a nice string. */ char * -paste_make_sample(struct paste_buffer *pb, int utf8flag) +paste_make_sample(struct paste_buffer *pb) { char *buf; size_t len, used; @@ -287,10 +287,7 @@ paste_make_sample(struct paste_buffer *pb, int utf8flag) len = width; buf = xreallocarray(NULL, len, 4 + 4); - if (utf8flag) - used = utf8_strvis(buf, pb->data, len, flags); - else - used = strvisx(buf, pb->data, len, flags); + used = utf8_strvis(buf, pb->data, len, flags); if (pb->size > width || used > width) strlcpy(buf + width, "...", 4); return (buf); diff --git a/screen-write.c b/screen-write.c index f80048b6..6286d21f 100644 --- a/screen-write.c +++ b/screen-write.c @@ -73,7 +73,7 @@ screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, /* Calculate string length, with embedded formatting. */ size_t -screen_write_cstrlen(int utf8flag, const char *fmt, ...) +screen_write_cstrlen(const char *fmt, ...) { va_list ap; char *msg, *msg2, *ptr, *ptr2; @@ -98,7 +98,7 @@ screen_write_cstrlen(int utf8flag, const char *fmt, ...) } *ptr2 = '\0'; - size = screen_write_strlen(utf8flag, "%s", msg2); + size = screen_write_strlen("%s", msg2); free(msg); free(msg2); @@ -108,7 +108,7 @@ screen_write_cstrlen(int utf8flag, const char *fmt, ...) /* Calculate string length. */ size_t -screen_write_strlen(int utf8flag, const char *fmt, ...) +screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; @@ -122,7 +122,7 @@ screen_write_strlen(int utf8flag, const char *fmt, ...) ptr = msg; while (*ptr != '\0') { - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); @@ -134,7 +134,8 @@ screen_write_strlen(int utf8flag, const char *fmt, ...) size += utf8data.width; } else { - size++; + if (*ptr > 0x1f && *ptr < 0x7f) + size++; ptr++; } } @@ -151,25 +152,25 @@ screen_write_puts(struct screen_write_ctx *ctx, struct grid_cell *gc, va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, -1, gc, 0, fmt, ap); + screen_write_vnputs(ctx, -1, gc, fmt, ap); va_end(ap); } /* Write string with length limit (-1 for unlimited). */ void screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, int utf8flag, const char *fmt, ...) + struct grid_cell *gc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap); + screen_write_vnputs(ctx, maxlen, gc, fmt, ap); va_end(ap); } void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap) + struct grid_cell *gc, const char *fmt, va_list ap) { char *msg; struct utf8_data utf8data; @@ -180,7 +181,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr = msg; while (*ptr != '\0') { - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); @@ -208,7 +209,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, if (*ptr == '\001') gc->attr ^= GRID_ATTR_CHARSET; - else { + else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, gc, *ptr); } @@ -221,8 +222,8 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, /* Write string, similar to nputs, but with embedded formatting (#[]). */ void -screen_write_cnputs(struct screen_write_ctx *ctx, - ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) +screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, + struct grid_cell *gc, const char *fmt, ...) { struct grid_cell lgc; struct utf8_data utf8data; @@ -253,7 +254,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, continue; } - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); @@ -279,8 +280,10 @@ screen_write_cnputs(struct screen_write_ctx *ctx, if (maxlen > 0 && size + 1 > (size_t) maxlen) break; - size++; - screen_write_putc(ctx, &lgc, *ptr); + if (*ptr > 0x1f && *ptr < 0x7f) { + size++; + screen_write_putc(ctx, &lgc, *ptr); + } ptr++; } } diff --git a/status.c b/status.c index df80d2b8..a4bdf6fa 100644 --- a/status.c +++ b/status.c @@ -29,10 +29,10 @@ #include "tmux.h" -char *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *, +char *status_redraw_get_left(struct client *, time_t, struct grid_cell *, + size_t *); +char *status_redraw_get_right(struct client *, time_t, struct grid_cell *, size_t *); -char *status_redraw_get_right(struct client *, time_t, int, - struct grid_cell *, size_t *); char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); @@ -208,8 +208,8 @@ status_at_line(struct client *c) /* Retrieve options for left string. */ char * -status_redraw_get_left(struct client *c, time_t t, int utf8flag, - struct grid_cell *gc, size_t *size) +status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc, + size_t *size) { struct session *s = c->session; const char *template; @@ -222,7 +222,7 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag, left = status_replace(c, NULL, template, t); *size = options_get_number(s->options, "status-left-length"); - leftlen = screen_write_cstrlen(utf8flag, "%s", left); + leftlen = screen_write_cstrlen("%s", left); if (leftlen < *size) *size = leftlen; return (left); @@ -230,8 +230,8 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag, /* Retrieve options for right string. */ char * -status_redraw_get_right(struct client *c, time_t t, int utf8flag, - struct grid_cell *gc, size_t *size) +status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc, + size_t *size) { struct session *s = c->session; const char *template; @@ -244,7 +244,7 @@ status_redraw_get_right(struct client *c, time_t t, int utf8flag, right = status_replace(c, NULL, template, t); *size = options_get_number(s->options, "status-right-length"); - rightlen = screen_write_cstrlen(utf8flag, "%s", right); + rightlen = screen_write_cstrlen("%s", right); if (rightlen < *size) *size = rightlen; return (right); @@ -286,7 +286,7 @@ status_redraw(struct client *c) u_int offset, needed; u_int wlstart, wlwidth, wlavailable, wloffset, wlsize; size_t llen, rlen, seplen; - int larrow, rarrow, utf8flag; + int larrow, rarrow; /* No status line? */ if (c->tty.sy == 0 || !options_get_number(s->options, "status")) @@ -312,14 +312,11 @@ status_redraw(struct client *c) if (c->tty.sy <= 1) goto out; - /* Get UTF-8 flag. */ - utf8flag = options_get_number(s->options, "status-utf8"); - /* Work out left and right strings. */ memcpy(&lgc, &stdgc, sizeof lgc); - left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen); + left = status_redraw_get_left(c, t, &lgc, &llen); memcpy(&rgc, &stdgc, sizeof rgc); - right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen); + right = status_redraw_get_right(c, t, &rgc, &rlen); /* * Figure out how much space we have for the window list. If there @@ -340,15 +337,14 @@ status_redraw(struct client *c) free(wl->status_text); memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell); wl->status_text = status_print(c, wl, t, &wl->status_cell); - wl->status_width = - screen_write_cstrlen(utf8flag, "%s", wl->status_text); + wl->status_width = screen_write_cstrlen("%s", wl->status_text); if (wl == s->curw) wloffset = wlwidth; oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); - seplen = screen_write_strlen(utf8flag, "%s", sep); + seplen = screen_write_strlen("%s", sep); wlwidth += wl->status_width + seplen; } @@ -358,12 +354,12 @@ status_redraw(struct client *c) /* And draw the window list into it. */ screen_write_start(&ctx, NULL, &window_list); RB_FOREACH(wl, winlinks, &s->windows) { - screen_write_cnputs(&ctx, - -1, &wl->status_cell, utf8flag, "%s", wl->status_text); + screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s", + wl->status_text); oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); - screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep); + screen_write_nputs(&ctx, -1, &stdgc, "%s", sep); } screen_write_stop(&ctx); @@ -435,7 +431,7 @@ draw: /* Draw the left string and arrow. */ screen_write_cursormove(&ctx, 0, 0); if (llen != 0) - screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left); + screen_write_cnputs(&ctx, llen, &lgc, "%s", left); if (larrow != 0) { memcpy(&gc, &stdgc, sizeof gc); if (larrow == -1) @@ -453,7 +449,7 @@ draw: } else screen_write_cursormove(&ctx, c->tty.sx - rlen, 0); if (rlen != 0) - screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right); + screen_write_cnputs(&ctx, rlen, &rgc, "%s", right); /* Figure out the offset for the window list. */ if (llen != 0) @@ -624,16 +620,13 @@ status_message_redraw(struct client *c) struct screen old_status; size_t len; struct grid_cell gc; - int utf8flag; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(s->options, "status-utf8"); - - len = screen_write_strlen(utf8flag, "%s", c->message_string); + len = screen_write_strlen("%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; @@ -642,7 +635,7 @@ status_message_redraw(struct client *c) screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); - screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string); + screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); for (; len < c->tty.sx; len++) screen_write_putc(&ctx, &gc, ' '); @@ -754,16 +747,13 @@ status_prompt_redraw(struct client *c) struct screen old_status; size_t i, size, left, len, off; struct grid_cell gc, *gcp; - int utf8flag; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(s->options, "status-utf8"); - - len = screen_write_strlen(utf8flag, "%s", c->prompt_string); + len = screen_write_strlen("%s", c->prompt_string); if (len > c->tty.sx) len = c->tty.sx; off = 0; @@ -777,19 +767,19 @@ status_prompt_redraw(struct client *c) screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); - screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string); + screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string); left = c->tty.sx - len; if (left != 0) { - size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer); + size = screen_write_strlen("%s", c->prompt_buffer); if (c->prompt_index >= left) { off = c->prompt_index - left + 1; if (c->prompt_index == size) left--; size = left; } - screen_write_nputs( - &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off); + screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer + + off); for (i = len + size; i < c->tty.sx; i++) screen_write_putc(&ctx, &gc, ' '); diff --git a/tmux.1 b/tmux.1 index a35c01f8..d84265c6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -190,13 +190,11 @@ flag explicitly informs .Nm that UTF-8 is supported. .Pp -If the server is started from a client passed -.Fl u -or where UTF-8 is detected, the -.Ic utf8 -and -.Ic status-utf8 -options are enabled in the global window and session options respectively. +Note that +.Nm +itself always accepts UTF-8, this controls whether it will send UTF-8 +characters to the terminal it is running it (if not, they are replaced by +.Ql _ ) . .It Fl v Request verbose logging. This option may be specified multiple times for increasing verbosity. @@ -2770,12 +2768,6 @@ Examples are: #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp -By default, UTF-8 in -.Ar string -is not interpreted, to enable UTF-8, use the -.Ic status-utf8 -option. -.Pp The default is .Ql "[#S] " . .It Ic status-left-length Ar length @@ -2805,9 +2797,7 @@ As with .Ar string will be passed to .Xr strftime 3 , -character pairs are replaced, and UTF-8 is dependent on the -.Ic status-utf8 -option. +character pairs are replaced. .It Ic status-right-length Ar length Set the maximum .Ar length @@ -2827,17 +2817,6 @@ For how to specify see the .Ic message-command-style option. -.It Xo Ic status-utf8 -.Op Ic on | off -.Xc -Instruct -.Nm -to treat top-bit-set characters in the -.Ic status-left -and -.Ic status-right -strings as UTF-8; notably, this is important for wide characters. -This option defaults to off. .It Ic update-environment Ar variables Set a space-separated string containing a list of environment variables to be copied into the session environment when a new session is created or an @@ -3084,13 +3063,6 @@ command. Duplicate input to any pane to all other panes in the same window (only for panes that are not in any special mode). .Pp -.It Xo Ic utf8 -.Op Ic on | off -.Xc -Instructs -.Nm -to expect UTF-8 sequences to appear in this window. -.Pp .It Ic window-active-style Ar style Set the style for the window's active pane. For how to specify diff --git a/tmux.c b/tmux.c index 1e68d9bb..9f9f9a50 100644 --- a/tmux.c +++ b/tmux.c @@ -291,12 +291,6 @@ main(int argc, char **argv) global_w_options = options_create(NULL); options_table_populate_tree(window_options_table, global_w_options); - /* Enable UTF-8 if the first client is on UTF-8 terminal. */ - if (flags & CLIENT_UTF8) { - options_set_number(global_s_options, "status-utf8", 1); - options_set_number(global_w_options, "utf8", 1); - } - /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { if (strrchr(s, '/') != NULL) diff --git a/tmux.h b/tmux.h index 51ceefd8..618240b0 100644 --- a/tmux.h +++ b/tmux.h @@ -1456,7 +1456,7 @@ void paste_free(struct paste_buffer *); void paste_add(char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); -char *paste_make_sample(struct paste_buffer *, int); +char *paste_make_sample(struct paste_buffer *); /* format.c */ #define FORMAT_STATUS 0x1 @@ -1475,7 +1475,7 @@ void format_defaults_window(struct format_tree *, struct window *); void format_defaults_pane(struct format_tree *, struct window_pane *); void format_defaults_paste_buffer(struct format_tree *, - struct paste_buffer *, int); + struct paste_buffer *); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -1892,24 +1892,24 @@ void grid_view_delete_cells(struct grid *, u_int, u_int, u_int); char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ -void screen_write_start( - struct screen_write_ctx *, struct window_pane *, struct screen *); +void screen_write_start(struct screen_write_ctx *, struct window_pane *, + struct screen *); void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); -size_t printflike(2, 3) screen_write_cstrlen(int, const char *, ...); -void printflike(5, 6) screen_write_cnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, ...); -size_t printflike(2, 3) screen_write_strlen(int, const char *, ...); +size_t printflike(1, 2) screen_write_cstrlen(const char *, ...); +void printflike(4, 5) screen_write_cnputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, ...); +size_t printflike(1, 2) screen_write_strlen(const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, struct grid_cell *, const char *, ...); -void printflike(5, 6) screen_write_nputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, ...); -void screen_write_vnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, va_list); -void screen_write_putc( - struct screen_write_ctx *, struct grid_cell *, u_char); -void screen_write_copy(struct screen_write_ctx *, - struct screen *, u_int, u_int, u_int, u_int); +void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, ...); +void screen_write_vnputs(struct screen_write_ctx *, ssize_t, + struct grid_cell *, const char *, va_list); +void screen_write_putc(struct screen_write_ctx *, struct grid_cell *, + u_char); +void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, + u_int, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); void screen_write_mode_set(struct screen_write_ctx *, int); void screen_write_mode_clear(struct screen_write_ctx *, int); diff --git a/window-choose.c b/window-choose.c index fa0e95c8..5f406c78 100644 --- a/window-choose.c +++ b/window-choose.c @@ -754,13 +754,12 @@ window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, struct grid_cell gc; size_t last, xoff = 0; char hdr[32], label[32]; - int utf8flag, key; + int key; if (data->callbackfn == NULL) fatalx("called before callback assigned"); last = screen_size_y(s) - 1; - utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) style_apply(&gc, oo, "mode-style"); @@ -777,7 +776,7 @@ window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, xsnprintf(label, sizeof label, "(%c)", key); else xsnprintf(label, sizeof label, "(%d)", item->pos); - screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag, + screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, "%*s %s %s", data->width + 2, label, /* * Add indication to tree if necessary about whether it's diff --git a/window-copy.c b/window-copy.c index d3e5b7d9..1c1ea29c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -280,13 +280,11 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) struct screen *backing = data->backing; struct screen_write_ctx back_ctx, ctx; struct grid_cell gc; - int utf8flag; u_int old_hsize, old_cy; if (backing == &wp->base) return; - utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); @@ -301,7 +299,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) } else data->backing_written = 1; old_cy = backing->cy; - screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); + screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); screen_write_stop(&back_ctx); data->oy += screen_hsize(data->backing) - old_hsize; @@ -1021,19 +1019,18 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, last, fx, fy, px; - int utf8flag, n, wrapped, wrapflag, cis; + int n, wrapped, wrapflag, cis; const char *ptr; if (*searchstr == '\0') return; - utf8flag = options_get_number(wp->window->options, "utf8"); wrapflag = options_get_number(wp->window->options, "wrap-search"); - searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + searchlen = screen_write_strlen("%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); - screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_nputs(&ctx, -1, &gc, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; @@ -1088,19 +1085,18 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, first, fx, fy, px; - int utf8flag, n, wrapped, wrapflag, cis; + int n, wrapped, wrapflag, cis; const char *ptr; if (*searchstr == '\0') return; - utf8flag = options_get_number(wp->window->options, "utf8"); wrapflag = options_get_number(wp->window->options, "wrap-search"); - searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + searchlen = screen_write_strlen("%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); - screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_nputs(&ctx, -1, &gc, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; From c41673f3fae397e872214880bd005cce631cf11b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:10:50 +0000 Subject: [PATCH 828/949] If we know the terminal outside tmux is not UTF-8, replace UTF-8 in error messages and whatnot with underscores the same as we do when we draw UTF-8 characters as part of the screen. --- cmd-load-buffer.c | 7 ++++++- cmd-queue.c | 19 ++++++++++++++++--- tmux.h | 1 + utf8.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 9352cbe5..40bea9b7 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -133,7 +133,7 @@ void cmd_load_buffer_callback(struct client *c, int closed, void *data) { const char *bufname = data; - char *pdata, *cause; + char *pdata, *cause, *saved; size_t psize; if (!closed) @@ -154,6 +154,11 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) if (paste_set(pdata, psize, 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_push_stderr(c); free(pdata); diff --git a/cmd-queue.c b/cmd-queue.c index ff8c69cb..5015981c 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -69,14 +69,21 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) struct client *c = cmdq->client; struct window *w; va_list ap; + char *tmp, *msg; va_start(ap, fmt); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - + if (~c->flags & CLIENT_UTF8) { + vasprintf(&tmp, fmt, ap); + 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_push_stdout(c); } else { @@ -101,6 +108,7 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) va_list ap; char *msg; size_t msglen; + char *tmp; va_start(ap, fmt); msglen = xvasprintf(&msg, fmt, ap); @@ -109,9 +117,14 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) if (c == NULL) cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + if (~c->flags & CLIENT_UTF8) { + 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_push_stderr(c); c->retval = 1; } else { diff --git a/tmux.h b/tmux.h index 618240b0..de19159d 100644 --- a/tmux.h +++ b/tmux.h @@ -2189,6 +2189,7 @@ u_int utf8_combine(const struct utf8_data *); int utf8_split(u_int, struct utf8_data *); u_int utf8_split2(u_int, u_char *); int utf8_strvis(char *, const char *, size_t, int); +char *utf8_sanitize(const char *); struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); diff --git a/utf8.c b/utf8.c index e61bf996..cb20ea6d 100644 --- a/utf8.c +++ b/utf8.c @@ -585,6 +585,50 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) return (dst - start); } +/* + * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free + * the returned string. Anything not valid printable ASCII or UTF-8 is + * stripped. + */ +char * +utf8_sanitize(const char *src) +{ + char *dst; + size_t n; + int more; + struct utf8_data utf8data; + u_int i; + + dst = NULL; + + n = 0; + while (*src != '\0') { + dst = xreallocarray(dst, n + 1, sizeof *dst); + if (utf8_open(&utf8data, *src)) { + more = 1; + while (*++src != '\0' && more) + more = utf8_append(&utf8data, *src); + if (!more) { + dst = xreallocarray(dst, n + utf8data.width, + sizeof *dst); + for (i = 0; i < utf8data.width; i++) + dst[n++] = '_'; + continue; + } + src -= utf8data.have; + } + if (*src > 0x1f && *src < 0x7f) + dst[n] = *src; + src++; + + n++; + } + + dst = xreallocarray(dst, n + 1, sizeof *dst); + dst[n] = '\0'; + return (dst); +} + /* * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. * Caller frees. From 0cc812ae342d1a71c0337db8ffb4d7701668cb38 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:24:08 +0000 Subject: [PATCH 829/949] tmux is UTF-8, so if $TMUX is set (tmux running in tmux), the client is UTF-8. Also try to make the existing checks more readable. --- tmux.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tmux.c b/tmux.c index 9f9f9a50..5429a7cb 100644 --- a/tmux.c +++ b/tmux.c @@ -191,8 +191,9 @@ find_home(void) int main(int argc, char **argv) { - char *s, *path, *label, **var, tmp[PATH_MAX]; - int opt, flags, keys; + char *path, *label, **var, tmp[PATH_MAX]; + const char *s; + int opt, flags, keys; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -258,20 +259,25 @@ main(int argc, char **argv) "proc exec tty ps", NULL) != 0) err(1, "pledge"); - if (!(flags & CLIENT_UTF8)) { - /* - * If the user has set whichever of LC_ALL, LC_CTYPE or LANG - * exist (in that order) to contain UTF-8, it is a safe - * assumption that either they are using a UTF-8 terminal, or - * if not they know that output from UTF-8-capable programs may - * be wrong. - */ - if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { - if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') - s = getenv("LANG"); - } - if (s != NULL && (strcasestr(s, "UTF-8") != NULL || - strcasestr(s, "UTF8") != NULL)) + /* + * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. + * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain + * UTF-8, it is a safe assumption that either they are using a UTF-8 + * terminal, or if not they know that output from UTF-8-capable + * programs may be wrong. + */ + if (getenv("TMUX") != NULL) + flags |= CLIENT_UTF8; + else { + s = getenv("LC_ALL"); + if (s == NULL || *s == '\0') + s = getenv("LC_CTYPE"); + if (s == NULL || *s == '\0') + s = getenv("LANG"); + if (s == NULL || *s == '\0') + s = ""; + if (strcasestr(s, "UTF-8") != NULL || + strcasestr(s, "UTF8") != NULL) flags |= CLIENT_UTF8; } From d6daf37df4ccd7589d2d8f6911bf7270f12d1672 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 12:19:57 +0000 Subject: [PATCH 830/949] Tidy utf8.c a little: build table on first use, and make utf8_width take a u_int rather than splitting and then combining again in utf8_split. --- server.c | 1 - tmux.h | 2 +- utf8.c | 81 ++++++++++++++++++++------------------------------------ 3 files changed, 30 insertions(+), 54 deletions(-) diff --git a/server.c b/server.c index bc3fa51d..2808c0cc 100644 --- a/server.c +++ b/server.c @@ -186,7 +186,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) TAILQ_INIT(&session_groups); mode_key_init_trees(); key_bindings_init(); - utf8_build(); start_time = time(NULL); diff --git a/tmux.h b/tmux.h index de19159d..0d2d773d 100644 --- a/tmux.h +++ b/tmux.h @@ -2181,7 +2181,7 @@ void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ -void utf8_build(void); +u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); diff --git a/utf8.c b/utf8.c index cb20ea6d..0926f4bc 100644 --- a/utf8.c +++ b/utf8.c @@ -35,7 +35,7 @@ struct utf8_width_entry { }; /* Sorted, then repeatedly split in the middle to balance the tree. */ -struct utf8_width_entry utf8_width_table[] = { +static struct utf8_width_entry utf8_width_table[] = { { 0x00b41, 0x00b44, 0, NULL, NULL }, { 0x008e4, 0x00902, 0, NULL, NULL }, { 0x006d6, 0x006dd, 0, NULL, NULL }, @@ -344,12 +344,9 @@ struct utf8_width_entry utf8_width_table[] = { { 0xe0100, 0xe01ef, 0, NULL, NULL }, { 0x100000, 0x10fffd, 0, NULL, NULL }, }; +static struct utf8_width_entry *utf8_width_root = NULL; -struct utf8_width_entry *utf8_width_root = NULL; - -int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *); -u_int utf8_combine(const struct utf8_data *); -u_int utf8_width(const struct utf8_data *); +static void utf8_build(void); /* Set a single character. */ void @@ -405,40 +402,20 @@ utf8_append(struct utf8_data *utf8data, u_char ch) if (utf8data->have != utf8data->size) return (1); - utf8data->width = utf8_width(utf8data); - return (0); -} - -/* Check if two width tree entries overlap. */ -int -utf8_overlap(struct utf8_width_entry *item1, struct utf8_width_entry *item2) -{ - if (item1->first >= item2->first && item1->first <= item2->last) - return (1); - if (item1->last >= item2->first && item1->last <= item2->last) - return (1); - if (item2->first >= item1->first && item2->first <= item1->last) - return (1); - if (item2->last >= item1->first && item2->last <= item1->last) - return (1); + utf8data->width = utf8_width(utf8_combine(utf8data)); return (0); } /* Build UTF-8 width tree. */ -void +static void utf8_build(void) { struct utf8_width_entry **ptr, *item, *node; - u_int i, j; + u_int i; for (i = 0; i < nitems(utf8_width_table); i++) { item = &utf8_width_table[i]; - for (j = 0; j < nitems(utf8_width_table); j++) { - if (i != j && utf8_overlap(item, &utf8_width_table[j])) - log_fatalx("utf8 overlap: %u %u", i, j); - } - ptr = &utf8_width_root; while (*ptr != NULL) { node = *ptr; @@ -451,6 +428,27 @@ utf8_build(void) } } +/* Lookup width of UTF-8 data in tree. */ +u_int +utf8_width(u_int uc) +{ + struct utf8_width_entry *item; + + if (utf8_width_root == NULL) + utf8_build(); + + item = utf8_width_root; + while (item != NULL) { + if (uc < item->first) + item = item->left; + else if (uc > item->last) + item = item->right; + else + return (item->width); + } + return (1); +} + /* Combine UTF-8 into 32-bit Unicode. */ u_int utf8_combine(const struct utf8_data *utf8data) @@ -481,7 +479,7 @@ utf8_combine(const struct utf8_data *utf8data) return (value); } -/* Split a UTF-8 character. */ +/* Split 32-bit Unicode into UTF-8. */ int utf8_split(u_int uc, struct utf8_data *utf8data) { @@ -505,7 +503,7 @@ utf8_split(u_int uc, struct utf8_data *utf8data) utf8data->data[3] = 0x80 | (uc & 0x3f); } else return (-1); - utf8data->width = utf8_width(utf8data); + utf8data->width = utf8_width(uc); return (0); } @@ -522,27 +520,6 @@ utf8_split2(u_int uc, u_char *ptr) return (1); } -/* Lookup width of UTF-8 data in tree. */ -u_int -utf8_width(const struct utf8_data *utf8data) -{ - struct utf8_width_entry *item; - u_int value; - - value = utf8_combine(utf8data); - - item = utf8_width_root; - while (item != NULL) { - if (value < item->first) - item = item->left; - else if (value > item->last) - item = item->right; - else - return (item->width); - } - return (1); -} - /* * Encode len characters from src into dst, which is guaranteed to have four * bytes available for each character from src (for \abc or UTF-8) plus space From 1da7475d0e2cbfb8b301fcad5cbcfb3ee4c087bb Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 12 Nov 2015 12:36:34 +0000 Subject: [PATCH 831/949] tweak previous; ok nicm --- tmux.1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index d84265c6..e0646697 100644 --- a/tmux.1 +++ b/tmux.1 @@ -192,8 +192,8 @@ that UTF-8 is supported. .Pp Note that .Nm -itself always accepts UTF-8, this controls whether it will send UTF-8 -characters to the terminal it is running it (if not, they are replaced by +itself always accepts UTF-8; this controls whether it will send UTF-8 +characters to the terminal it is running (if not, they are replaced by .Ql _ ) . .It Fl v Request verbose logging. @@ -2796,8 +2796,8 @@ As with .Ic status-left , .Ar string will be passed to -.Xr strftime 3 , -character pairs are replaced. +.Xr strftime 3 +and character pairs are replaced. .It Ic status-right-length Ar length Set the maximum .Ar length From a209ea3953ba16742f6f6bb19b76ffdb1200960e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 12:43:36 +0000 Subject: [PATCH 832/949] Add utf8_padcstr and use it to align columns in list-keys. --- cmd-list-keys.c | 27 +++++++++++++++++---------- tmux.h | 1 + utf8.c | 21 +++++++++++++++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index c76f9f47..3b6afa3e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -18,6 +18,7 @@ #include +#include #include #include "tmux.h" @@ -54,10 +55,9 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct key_table *table; struct key_binding *bd; const char *key, *tablename, *r; - char tmp[BUFSIZ]; + char *cp, tmp[BUFSIZ]; size_t used; int repeat, width, tablewidth, keywidth; - u_int i; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, cmdq)); @@ -82,7 +82,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (bd->can_repeat) repeat = 1; - width = strlen(table->name); + width = utf8_cstrwidth(table->name); if (width > tablewidth) tablewidth = width; width = utf8_cstrwidth(key); @@ -103,13 +103,20 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) r = "-r "; else r = " "; - used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %s", r, - (int)tablewidth, table->name, key); - for (i = 0; i < keywidth - utf8_cstrwidth(key); i++) { - if (strlcat(tmp, " ", sizeof tmp) < sizeof tmp) - used++; - } - if (used < sizeof tmp) { + xsnprintf(tmp, sizeof tmp, "%s-T ", r); + + cp = utf8_padcstr(table->name, tablewidth); + strlcat(tmp, cp, sizeof tmp); + strlcat(tmp, " ", sizeof tmp); + free(cp); + + cp = utf8_padcstr(key, keywidth); + strlcat(tmp, cp, sizeof tmp); + strlcat(tmp, " ", sizeof tmp); + free(cp); + + used = strlen(tmp); + if (used < (sizeof tmp) - 1) { cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); } diff --git a/tmux.h b/tmux.h index 0d2d773d..cb7ed2c3 100644 --- a/tmux.h +++ b/tmux.h @@ -2194,6 +2194,7 @@ struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); char *utf8_trimcstr(const char *, u_int); +char *utf8_padcstr(const char *, u_int); /* procname.c */ char *get_proc_name(int, char *); diff --git a/utf8.c b/utf8.c index 0926f4bc..db738020 100644 --- a/utf8.c +++ b/utf8.c @@ -713,3 +713,24 @@ utf8_trimcstr(const char *s, u_int width) free(tmp); return (out); } + +/* Pad UTF-8 string to width. Caller frees. */ +char * +utf8_padcstr(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)); + memcpy(out, s, slen); + for (i = n; i < width; i++) + out[slen++] = ' '; + out[slen] = '\0'; + return (out); +} From f2d03f4fddddbdbc96566c55f92718c1f4319b33 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 14:50:57 +0000 Subject: [PATCH 833/949] grid_put_utf8 is unused, remove it. --- grid.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/grid.c b/grid.c index 99dafab2..b8c9cbb7 100644 --- a/grid.c +++ b/grid.c @@ -42,10 +42,6 @@ const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; memcpy(&gd->linedata[py].celldata[px], \ gc, sizeof gd->linedata[py].celldata[px]); \ } while (0) -#define grid_put_utf8(gd, px, py, gc) do { \ - memcpy(&gd->linedata[py].utf8data[px], \ - gc, sizeof gd->linedata[py].utf8data[px]); \ -} while (0) int grid_check_y(struct grid *, u_int); From e71a9154126c7ee853b9a657678de40d475279eb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 22:04:37 +0000 Subject: [PATCH 834/949] Rename overly-long utf8data to ud throughout. --- input-keys.c | 10 ++--- key-string.c | 20 ++++----- screen-write.c | 38 ++++++++--------- tty-keys.c | 20 ++++----- utf8.c | 114 ++++++++++++++++++++++++------------------------- 5 files changed, 101 insertions(+), 101 deletions(-) diff --git a/input-keys.c b/input-keys.c index a761a088..3938c185 100644 --- a/input-keys.c +++ b/input-keys.c @@ -144,7 +144,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) size_t dlen; char *out; key_code justkey; - struct utf8_data utf8data; + struct utf8_data ud; log_debug("writing key 0x%llx (%s) to %%%u", key, key_string_lookup_key(key), wp->id); @@ -164,16 +164,16 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if (key != KEYC_NONE && justkey < 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); - utf8data.data[0] = justkey; - bufferevent_write(wp->event, &utf8data.data[0], 1); + ud.data[0] = justkey; + bufferevent_write(wp->event, &ud.data[0], 1); return; } if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { - if (utf8_split(justkey, &utf8data) != 0) + if (utf8_split(justkey, &ud) != 0) return; if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); - bufferevent_write(wp->event, utf8data.data, utf8data.size); + bufferevent_write(wp->event, ud.data, ud.size); return; } diff --git a/key-string.c b/key-string.c index 4285e129..c2230218 100644 --- a/key-string.c +++ b/key-string.c @@ -146,7 +146,7 @@ key_string_lookup_string(const char *string) u_short u; int size; key_code modifiers; - struct utf8_data utf8data; + struct utf8_data ud; u_int i; /* Is this a hexadecimal value? */ @@ -173,12 +173,12 @@ key_string_lookup_string(const char *string) return (KEYC_NONE); } else { /* Try as a UTF-8 key. */ - if (utf8_open(&utf8data, (u_char)*string)) { - if (strlen(string) != utf8data.size) + if (utf8_open(&ud, (u_char)*string)) { + if (strlen(string) != ud.size) return (KEYC_NONE); - for (i = 1; i < utf8data.size; i++) - utf8_append(&utf8data, (u_char)string[i]); - key = utf8_combine(&utf8data); + for (i = 1; i < ud.size; i++) + utf8_append(&ud, (u_char)string[i]); + key = utf8_combine(&ud); return (key | modifiers); } @@ -213,7 +213,7 @@ key_string_lookup_key(key_code key) static char out[24]; char tmp[8]; u_int i; - struct utf8_data utf8data; + struct utf8_data ud; *out = '\0'; @@ -253,9 +253,9 @@ key_string_lookup_key(key_code key) /* Is this a UTF-8 key? */ if (key > 127 && key < KEYC_BASE) { - if (utf8_split(key, &utf8data) == 0) { - memcpy(out, utf8data.data, utf8data.size); - out[utf8data.size] = '\0'; + if (utf8_split(key, &ud) == 0) { + memcpy(out, ud.data, ud.size); + out[ud.size] = '\0'; return (out); } } diff --git a/screen-write.c b/screen-write.c index 6286d21f..4bf3ec72 100644 --- a/screen-write.c +++ b/screen-write.c @@ -112,7 +112,7 @@ screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; - struct utf8_data utf8data; + struct utf8_data ud; u_char *ptr; size_t left, size = 0; @@ -122,17 +122,17 @@ screen_write_strlen(const char *fmt, ...) ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while (utf8_append(&ud, *ptr)) ptr++; ptr++; - size += utf8data.width; + size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) size++; @@ -173,7 +173,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, va_list ap) { char *msg; - struct utf8_data utf8data; + struct utf8_data ud; u_char *ptr; size_t left, size = 0; @@ -181,27 +181,27 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while (utf8_append(&ud, *ptr)) ptr++; ptr++; if (maxlen > 0 && - size + utf8data.width > (size_t) maxlen) { + size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } - size += utf8data.width; + size += ud.width; - grid_cell_set(gc, &utf8data); + grid_cell_set(gc, &ud); screen_write_cell(ctx, gc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) @@ -226,7 +226,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, ...) { struct grid_cell lgc; - struct utf8_data utf8data; + struct utf8_data ud; va_list ap; char *msg; u_char *ptr, *last; @@ -254,27 +254,27 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, continue; } - if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while (utf8_append(&ud, *ptr)) ptr++; ptr++; if (maxlen > 0 && - size + utf8data.width > (size_t) maxlen) { + size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } - size += utf8data.width; + size += ud.width; - grid_cell_set(&lgc, &utf8data); + grid_cell_set(&lgc, &ud); screen_write_cell(ctx, &lgc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) diff --git a/tty-keys.c b/tty-keys.c index 31adf0f0..6a64ef15 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -474,7 +474,7 @@ tty_keys_next(struct tty *tty) cc_t bspace; int delay, expired = 0; key_code key; - struct utf8_data utf8data; + struct utf8_data ud; u_int i; /* Get key buffer. */ @@ -539,16 +539,16 @@ first_key: } /* Is this valid UTF-8? */ - if (utf8_open(&utf8data, (u_char)*buf)) { - size = utf8data.size; + if (utf8_open(&ud, (u_char)*buf)) { + size = ud.size; if (len < size) { if (expired) goto discard_key; goto partial_key; } for (i = 1; i < size; i++) - utf8_append(&utf8data, (u_char)buf[i]); - key = utf8_combine(&utf8data); + utf8_append(&ud, (u_char)buf[i]); + key = utf8_combine(&ud); log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); goto complete_key; } @@ -650,7 +650,7 @@ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; - struct utf8_data utf8data; + struct utf8_data ud; u_int i, value, x, y, b, sgr_b; u_char sgr_type, c; @@ -693,14 +693,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&utf8data, buf[*size])) { - if (utf8data.size != 2) + if (utf8_open(&ud, buf[*size])) { + if (ud.size != 2) return (-1); (*size)++; if (len <= *size) return (1); - utf8_append(&utf8data, buf[*size]); - value = utf8_combine(&utf8data); + utf8_append(&ud, buf[*size]); + value = utf8_combine(&ud); } else value = (u_char)buf[*size]; (*size)++; diff --git a/utf8.c b/utf8.c index db738020..82471c66 100644 --- a/utf8.c +++ b/utf8.c @@ -350,12 +350,12 @@ static void utf8_build(void); /* Set a single character. */ void -utf8_set(struct utf8_data *utf8data, u_char ch) +utf8_set(struct utf8_data *ud, u_char ch) { - *utf8data->data = ch; - utf8data->size = 1; + *ud->data = ch; + ud->size = 1; - utf8data->width = 1; + ud->width = 1; } /* @@ -368,18 +368,18 @@ utf8_set(struct utf8_data *utf8data, u_char ch) * Returns 1 if more UTF-8 to come, 0 if not UTF-8. */ int -utf8_open(struct utf8_data *utf8data, u_char ch) +utf8_open(struct utf8_data *ud, u_char ch) { - memset(utf8data, 0, sizeof *utf8data); + memset(ud, 0, sizeof *ud); if (ch >= 0xc2 && ch <= 0xdf) - utf8data->size = 2; + ud->size = 2; else if (ch >= 0xe0 && ch <= 0xef) - utf8data->size = 3; + ud->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) - utf8data->size = 4; + ud->size = 4; else return (0); - utf8_append(utf8data, ch); + utf8_append(ud, ch); return (1); } @@ -389,20 +389,20 @@ utf8_open(struct utf8_data *utf8data, u_char ch) * Returns 1 if more UTF-8 data to come, 0 if finished. */ int -utf8_append(struct utf8_data *utf8data, u_char ch) +utf8_append(struct utf8_data *ud, u_char ch) { /* XXX this should do validity checks too! */ - if (utf8data->have >= utf8data->size) + if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); - if (utf8data->size > sizeof utf8data->data) + if (ud->size > sizeof ud->data) fatalx("UTF-8 character size too large"); - utf8data->data[utf8data->have++] = ch; - if (utf8data->have != utf8data->size) + ud->data[ud->have++] = ch; + if (ud->have != ud->size) return (1); - utf8data->width = utf8_width(utf8_combine(utf8data)); + ud->width = utf8_width(utf8_combine(ud)); return (0); } @@ -451,29 +451,29 @@ utf8_width(u_int uc) /* Combine UTF-8 into 32-bit Unicode. */ u_int -utf8_combine(const struct utf8_data *utf8data) +utf8_combine(const struct utf8_data *ud) { u_int value; value = 0xff; - switch (utf8data->size) { + switch (ud->size) { case 1: - value = utf8data->data[0]; + value = ud->data[0]; break; case 2: - value = utf8data->data[1] & 0x3f; - value |= (utf8data->data[0] & 0x1f) << 6; + value = ud->data[1] & 0x3f; + value |= (ud->data[0] & 0x1f) << 6; break; case 3: - value = utf8data->data[2] & 0x3f; - value |= (utf8data->data[1] & 0x3f) << 6; - value |= (utf8data->data[0] & 0xf) << 12; + value = ud->data[2] & 0x3f; + value |= (ud->data[1] & 0x3f) << 6; + value |= (ud->data[0] & 0xf) << 12; break; case 4: - value = utf8data->data[3] & 0x3f; - value |= (utf8data->data[2] & 0x3f) << 6; - value |= (utf8data->data[1] & 0x3f) << 12; - value |= (utf8data->data[0] & 0x7) << 18; + value = ud->data[3] & 0x3f; + value |= (ud->data[2] & 0x3f) << 6; + value |= (ud->data[1] & 0x3f) << 12; + value |= (ud->data[0] & 0x7) << 18; break; } return (value); @@ -481,29 +481,29 @@ utf8_combine(const struct utf8_data *utf8data) /* Split 32-bit Unicode into UTF-8. */ int -utf8_split(u_int uc, struct utf8_data *utf8data) +utf8_split(u_int uc, struct utf8_data *ud) { if (uc < 0x7f) { - utf8data->size = 1; - utf8data->data[0] = uc; + ud->size = 1; + ud->data[0] = uc; } else if (uc < 0x7ff) { - utf8data->size = 2; - utf8data->data[0] = 0xc0 | ((uc >> 6) & 0x1f); - utf8data->data[1] = 0x80 | (uc & 0x3f); + ud->size = 2; + ud->data[0] = 0xc0 | ((uc >> 6) & 0x1f); + ud->data[1] = 0x80 | (uc & 0x3f); } else if (uc < 0xffff) { - utf8data->size = 3; - utf8data->data[0] = 0xe0 | ((uc >> 12) & 0xf); - utf8data->data[1] = 0x80 | ((uc >> 6) & 0x3f); - utf8data->data[2] = 0x80 | (uc & 0x3f); + ud->size = 3; + ud->data[0] = 0xe0 | ((uc >> 12) & 0xf); + ud->data[1] = 0x80 | ((uc >> 6) & 0x3f); + ud->data[2] = 0x80 | (uc & 0x3f); } else if (uc < 0x1fffff) { - utf8data->size = 4; - utf8data->data[0] = 0xf0 | ((uc >> 18) & 0x7); - utf8data->data[1] = 0x80 | ((uc >> 12) & 0x3f); - utf8data->data[2] = 0x80 | ((uc >> 6) & 0x3f); - utf8data->data[3] = 0x80 | (uc & 0x3f); + ud->size = 4; + ud->data[0] = 0xf0 | ((uc >> 18) & 0x7); + ud->data[1] = 0x80 | ((uc >> 12) & 0x3f); + ud->data[2] = 0x80 | ((uc >> 6) & 0x3f); + ud->data[3] = 0x80 | (uc & 0x3f); } else return (-1); - utf8data->width = utf8_width(uc); + ud->width = utf8_width(uc); return (0); } @@ -528,7 +528,7 @@ utf8_split2(u_int uc, u_char *ptr) int utf8_strvis(char *dst, const char *src, size_t len, int flag) { - struct utf8_data utf8data; + struct utf8_data ud; const char *start, *end; int more; size_t i; @@ -537,18 +537,18 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) end = src + len; while (src < end) { - if (utf8_open(&utf8data, *src)) { + if (utf8_open(&ud, *src)) { more = 1; while (++src < end && more) - more = utf8_append(&utf8data, *src); + more = utf8_append(&ud, *src); if (!more) { /* UTF-8 character finished. */ - for (i = 0; i < utf8data.size; i++) - *dst++ = utf8data.data[i]; + for (i = 0; i < ud.size; i++) + *dst++ = ud.data[i]; continue; - } else if (utf8data.have > 0) { + } else if (ud.have > 0) { /* Not a complete UTF-8 character. */ - src -= utf8data.have; + src -= ud.have; } } if (src < end - 1) @@ -573,7 +573,7 @@ utf8_sanitize(const char *src) char *dst; size_t n; int more; - struct utf8_data utf8data; + struct utf8_data ud; u_int i; dst = NULL; @@ -581,18 +581,18 @@ utf8_sanitize(const char *src) n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); - if (utf8_open(&utf8data, *src)) { + if (utf8_open(&ud, *src)) { more = 1; while (*++src != '\0' && more) - more = utf8_append(&utf8data, *src); + more = utf8_append(&ud, *src); if (!more) { - dst = xreallocarray(dst, n + utf8data.width, + dst = xreallocarray(dst, n + ud.width, sizeof *dst); - for (i = 0; i < utf8data.width; i++) + for (i = 0; i < ud.width; i++) dst[n++] = '_'; continue; } - src -= utf8data.have; + src -= ud.have; } if (*src > 0x1f && *src < 0x7f) dst[n] = *src; From c5689a5a4031a43769b8b721cafa6d1eab6abc44 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 08:09:28 +0000 Subject: [PATCH 835/949] Long overdue change to the way we store cells in the grid: now, instead of storing a full grid_cell with UTF-8 data and everything, store a new type grid_cell_entry. This can either be the cell itself (for ASCII cells), or an offset into an extended array (per line) for UTF-8 data. This avoid a large (8 byte) overhead on non-UTF-8 cells (by far the majority for most users) without the complexity of the shadow array we had before. Grid memory without any UTF-8 is about half. The disadvantage that cells can no longer be modified in place and need to be copied out of the grid and back but it turned out to be lot less complicated than I expected. --- Makefile | 1 - format.c | 1 + grid-cell.c | 55 ---------------- grid-view.c | 19 ++---- grid.c | 169 ++++++++++++++++++++++++++++++++++++------------- input.c | 4 +- screen-write.c | 92 ++++++++++++--------------- status.c | 7 +- tmux.h | 72 +++++++++++---------- tty.c | 37 ++++------- utf8.c | 17 +++++ window-copy.c | 105 ++++++++++++++---------------- 12 files changed, 297 insertions(+), 282 deletions(-) delete mode 100644 grid-cell.c diff --git a/Makefile b/Makefile index 89c8c5c8..10c6a783 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,6 @@ SRCS= alerts.c \ control-notify.c \ environ.c \ format.c \ - grid-cell.c \ grid-view.c \ grid.c \ input-keys.c \ diff --git a/format.c b/format.c index bcac7934..389026ac 100644 --- a/format.c +++ b/format.c @@ -415,6 +415,7 @@ format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) for (i = 0; i < gd->hsize; i++) { gl = &gd->linedata[i]; size += gl->cellsize * sizeof *gl->celldata; + size += gl->extdsize * sizeof *gl->extddata; } size += gd->hsize * sizeof *gd->linedata; diff --git a/grid-cell.c b/grid-cell.c deleted file mode 100644 index 09643a9c..00000000 --- a/grid-cell.c +++ /dev/null @@ -1,55 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2012 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include - -#include "tmux.h" - -/* Get cell width. */ -u_int -grid_cell_width(const struct grid_cell *gc) -{ - return (gc->xstate >> 4); -} - -/* Get cell data. */ -void -grid_cell_get(const struct grid_cell *gc, struct utf8_data *ud) -{ - ud->size = gc->xstate & 0xf; - ud->width = gc->xstate >> 4; - memcpy(ud->data, gc->xdata, ud->size); -} - -/* Set cell data. */ -void -grid_cell_set(struct grid_cell *gc, const struct utf8_data *ud) -{ - memcpy(gc->xdata, ud->data, ud->size); - gc->xstate = (ud->width << 4) | ud->size; -} - -/* Set a single character as cell data. */ -void -grid_cell_one(struct grid_cell *gc, u_char ch) -{ - *gc->xdata = ch; - gc->xstate = (1 << 4) | 1; -} diff --git a/grid-view.c b/grid-view.c index badabd56..5edcfd53 100644 --- a/grid-view.c +++ b/grid-view.c @@ -30,24 +30,17 @@ #define grid_view_x(gd, x) (x) #define grid_view_y(gd, y) ((gd)->hsize + (y)) -/* Get cell for reading. */ -const struct grid_cell * -grid_view_peek_cell(struct grid *gd, u_int px, u_int py) +/* Get cel. */ +void +grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { - return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); -} - -/* Get cell for writing. */ -struct grid_cell * -grid_view_get_cell(struct grid *gd, u_int px, u_int py) -{ - return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); + grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } /* Set cell. */ void -grid_view_set_cell( - struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +grid_view_set_cell(struct grid *gd, u_int px, u_int py, + const struct grid_cell *gc) { grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } diff --git a/grid.c b/grid.c index b8c9cbb7..36cde074 100644 --- a/grid.c +++ b/grid.c @@ -36,15 +36,17 @@ */ /* Default grid cell data. */ -const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; - -#define grid_put_cell(gd, px, py, gc) do { \ - memcpy(&gd->linedata[py].celldata[px], \ - gc, sizeof gd->linedata[py].celldata[px]); \ -} while (0) +const struct grid_cell grid_default_cell = { + 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } +}; +const struct grid_cell_entry grid_default_entry = { + 0, { .data = { 0, 8, 8, ' ' } } +}; int grid_check_y(struct grid *, u_int); +void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *l, + u_int, u_int); void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, u_int); @@ -54,6 +56,13 @@ size_t grid_string_cells_bg(const struct grid_cell *, int *); void grid_string_cells_code(const struct grid_cell *, const struct grid_cell *, char *, size_t, int); +/* Copy default into a cell. */ +static void +grid_clear_cell(struct grid *gd, u_int px, u_int py) +{ + gd->linedata[py].celldata[px] = grid_default_entry; +} + /* Check grid y position. */ int grid_check_y(struct grid *gd, u_int py) @@ -95,6 +104,7 @@ grid_destroy(struct grid *gd) for (yy = 0; yy < gd->hsize + gd->sy; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); + free(gl->extddata); } free(gd->linedata); @@ -107,7 +117,7 @@ int grid_compare(struct grid *ga, struct grid *gb) { struct grid_line *gla, *glb; - struct grid_cell *gca, *gcb; + struct grid_cell gca, gcb; u_int xx, yy; if (ga->sx != gb->sx || ga->sy != gb->sy) @@ -118,10 +128,10 @@ grid_compare(struct grid *ga, struct grid *gb) glb = &gb->linedata[yy]; if (gla->cellsize != glb->cellsize) return (1); - for (xx = 0; xx < ga->sx; xx++) { - gca = &gla->celldata[xx]; - gcb = &glb->celldata[xx]; - if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0) + for (xx = 0; xx < gla->cellsize; xx++) { + grid_get_cell(ga, xx, yy, &gca); + grid_get_cell(gb, xx, yy, &gcb); + if (memcmp(&gca, &gcb, sizeof (struct grid_cell)) != 0) return (1); } } @@ -224,7 +234,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx) gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); for (xx = gl->cellsize; xx < sx; xx++) - grid_put_cell(gd, xx, py, &grid_default_cell); + grid_clear_cell(gd, xx, py); gl->cellsize = sx; } @@ -238,37 +248,72 @@ grid_peek_line(struct grid *gd, u_int py) } /* Get cell for reading. */ -const struct grid_cell * -grid_peek_cell(struct grid *gd, u_int px, u_int py) +void +grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { - if (grid_check_y(gd, py) != 0) - return (&grid_default_cell); + struct grid_line *gl; + struct grid_cell_entry *gce; - if (px >= gd->linedata[py].cellsize) - return (&grid_default_cell); - return (&gd->linedata[py].celldata[px]); -} + if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) { + memcpy(gc, &grid_default_cell, sizeof *gc); + return; + } -/* Get cell at relative position (for writing). */ -struct grid_cell * -grid_get_cell(struct grid *gd, u_int px, u_int py) -{ - if (grid_check_y(gd, py) != 0) - return (NULL); + gl = &gd->linedata[py]; + gce = &gl->celldata[px]; - grid_expand_line(gd, py, px + 1); - return (&gd->linedata[py].celldata[px]); + if (gce->flags & GRID_FLAG_EXTENDED) { + if (gce->offset >= gl->extdsize) + memcpy(gc, &grid_default_cell, sizeof *gc); + else + memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); + return; + } + + gc->flags = gce->flags & ~GRID_FLAG_EXTENDED; + gc->attr = gce->data.attr; + gc->fg = gce->data.fg; + gc->bg = gce->data.bg; + utf8_set(&gc->data, gce->data.data); } /* Set cell at relative position. */ void grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { + struct grid_line *gl; + struct grid_cell_entry *gce; + struct grid_cell *gcp; + if (grid_check_y(gd, py) != 0) return; grid_expand_line(gd, py, px + 1); - grid_put_cell(gd, px, py, gc); + + gl = &gd->linedata[py]; + gce = &gl->celldata[px]; + + if ((gce->flags & GRID_FLAG_EXTENDED) || gc->data.size != 1 || + gc->data.width != 1) { + if (~gce->flags & GRID_FLAG_EXTENDED) { + gl->extddata = xreallocarray(gl->extddata, + gl->extdsize + 1, sizeof *gl->extddata); + gce->offset = gl->extdsize++; + gce->flags = gc->flags | GRID_FLAG_EXTENDED; + } + + if (gce->offset >= gl->extdsize) + fatalx("offset too big"); + gcp = &gl->extddata[gce->offset]; + memcpy(gcp, gc, sizeof *gcp); + return; + } + + gce->flags = gc->flags & ~GRID_FLAG_EXTENDED; + gce->data.attr = gc->attr; + gce->data.fg = gc->fg; + gce->data.bg = gc->bg; + gce->data.data = gc->data.data[0]; } /* Clear area. */ @@ -300,7 +345,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) for (xx = px; xx < px + nx; xx++) { if (xx >= gd->linedata[yy].cellsize) break; - grid_put_cell(gd, xx, yy, &grid_default_cell); + grid_clear_cell(gd, xx, yy); } } } @@ -324,6 +369,10 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) gl = &gd->linedata[yy]; free(gl->celldata); memset(gl, 0, sizeof *gl); + + free(gl->extddata); + gl->extddata = NULL; + gl->extdsize = 0; } } @@ -386,7 +435,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) for (xx = px; xx < px + nx; xx++) { if (xx >= dx && xx < dx + nx) continue; - grid_put_cell(gd, xx, py, &grid_default_cell); + grid_clear_cell(gd, xx, py); } } @@ -568,9 +617,8 @@ char * grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) { - const struct grid_cell *gc; + struct grid_cell gc; static struct grid_cell lastgc1; - struct utf8_data ud; const char *data; char *buf, code[128]; size_t len, off, size, codelen; @@ -590,21 +638,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, for (xx = px; xx < px + nx; xx++) { if (gl == NULL || xx >= gl->cellsize) break; - gc = &gl->celldata[xx]; - if (gc->flags & GRID_FLAG_PADDING) + grid_get_cell(gd, xx, py, &gc); + if (gc.flags & GRID_FLAG_PADDING) continue; - grid_cell_get(gc, &ud); if (with_codes) { - grid_string_cells_code(*lastgc, gc, code, sizeof code, + grid_string_cells_code(*lastgc, &gc, code, sizeof code, escape_c0); codelen = strlen(code); - memcpy(*lastgc, gc, sizeof *gc); + memcpy(*lastgc, &gc, sizeof **lastgc); } else codelen = 0; - data = ud.data; - size = ud.size; + data = gc.data.data; + size = gc.data.size; if (escape_c0 && size == 1 && *data == '\\') { data = "\\\\"; size = 2; @@ -663,11 +710,44 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, } else dstl->celldata = NULL; + if (srcl->extdsize != 0) { + dstl->extdsize = srcl->extdsize; + dstl->extddata = xreallocarray(NULL, dstl->extdsize, + sizeof *dstl->extddata); + memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * + sizeof *dstl->extddata); + } + sy++; dy++; } } +/* Copy a section of a line. */ +void +grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl, + u_int from, u_int to_copy) +{ + struct grid_cell_entry *gce; + u_int i, was; + + memcpy(&dst_gl->celldata[to], &src_gl->celldata[from], + to_copy * sizeof *dst_gl->celldata); + + for (i = to; i < to + to_copy; i++) { + gce = &dst_gl->celldata[i]; + if (~gce->flags & GRID_FLAG_EXTENDED) + continue; + was = gce->offset; + + dst_gl->extddata = xreallocarray(dst_gl->extddata, + dst_gl->extdsize + 1, sizeof *dst_gl->extddata); + gce->offset = dst_gl->extdsize++; + memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was], + sizeof *dst_gl->extddata); + } +} + /* Join line data. */ void grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, @@ -692,8 +772,7 @@ grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, dst_gl->cellsize = nx; /* Append as much as possible. */ - memcpy(&dst_gl->celldata[ox], &src_gl->celldata[0], - to_copy * sizeof src_gl->celldata[0]); + grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy); /* If there is any left in the source, split it. */ if (src_gl->cellsize > to_copy) { @@ -732,8 +811,7 @@ grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, dst_gl->flags |= GRID_LINE_WRAPPED; /* Copy the data. */ - memcpy(&dst_gl->celldata[0], &src_gl->celldata[offset], - to_copy * sizeof dst_gl->celldata[0]); + grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy); /* Move offset and reduce old line size. */ offset += to_copy; @@ -763,6 +841,7 @@ grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) /* Clear old line. */ src_gl->celldata = NULL; + src_gl->extddata = NULL; } /* @@ -792,7 +871,7 @@ grid_reflow(struct grid *dst, struct grid *src, u_int new_x) /* Previous was wrapped. Try to join. */ grid_reflow_join(dst, &py, src_gl, new_x); } - previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED; + previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED); } grid_destroy(src); diff --git a/input.c b/input.c index 41276d9a..c56cdc35 100644 --- a/input.c +++ b/input.c @@ -1006,7 +1006,7 @@ input_print(struct input_ctx *ictx) else ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; - grid_cell_one(&ictx->cell.cell, ictx->ch); + utf8_set(&ictx->cell.cell.data, ictx->ch); screen_write_cell(&ictx->ctx, &ictx->cell.cell); ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; @@ -1945,7 +1945,7 @@ input_utf8_close(struct input_ctx *ictx) utf8_append(&ictx->utf8data, ictx->ch); - grid_cell_set(&ictx->cell.cell, &ictx->utf8data); + utf8_copy(&ictx->cell.cell.data, &ictx->utf8data); screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); diff --git a/screen-write.c b/screen-write.c index 4bf3ec72..14b8a41a 100644 --- a/screen-write.c +++ b/screen-write.c @@ -67,7 +67,7 @@ void screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch) { - grid_cell_one(gc, ch); + utf8_set(&gc->data, ch); screen_write_cell(ctx, gc); } @@ -126,7 +126,7 @@ screen_write_strlen(const char *fmt, ...) ptr++; left = strlen(ptr); - if (left < ud.size - 1) + if (left < (size_t)ud.size - 1) break; while (utf8_append(&ud, *ptr)) ptr++; @@ -185,7 +185,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr++; left = strlen(ptr); - if (left < ud.size - 1) + if (left < (size_t)ud.size - 1) break; while (utf8_append(&ud, *ptr)) ptr++; @@ -201,7 +201,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, } size += ud.width; - grid_cell_set(gc, &ud); + utf8_copy(&gc->data, &ud); screen_write_cell(ctx, gc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) @@ -258,7 +258,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr++; left = strlen(ptr); - if (left < ud.size - 1) + if (left < (size_t)ud.size - 1) break; while (utf8_append(&ud, *ptr)) ptr++; @@ -274,7 +274,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, } size += ud.width; - grid_cell_set(&lgc, &ud); + utf8_copy(&lgc.data, &ud); screen_write_cell(ctx, &lgc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) @@ -299,8 +299,7 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *s = ctx->s; struct grid *gd = src->grid; struct grid_line *gl; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int xx, yy, cx, cy, ax, bx; cx = s->cx; @@ -324,12 +323,8 @@ screen_write_copy(struct screen_write_ctx *ctx, bx = px + nx; for (xx = ax; xx < bx; xx++) { - if (xx >= gl->cellsize) - gc = &grid_default_cell; - else - gc = &gl->celldata[xx]; - grid_cell_get(gc, &ud); - screen_write_cell(ctx, gc); + grid_get_cell(gd, xx, yy, &gc); + screen_write_cell(ctx, &gc); } if (px + nx == gd->sx && px + nx > gl->cellsize) screen_write_clearendofline(ctx); @@ -342,12 +337,12 @@ screen_write_copy(struct screen_write_ctx *ctx, /* Set up context for TTY command. */ void -screen_write_initctx( - struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last) +screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, + int save_last) { struct screen *s = ctx->s; struct grid *gd = s->grid; - const struct grid_cell *gc; + struct grid_cell gc; u_int xx; ttyctx->wp = ctx->wp; @@ -362,14 +357,14 @@ screen_write_initctx( return; /* Save the last cell on the screen. */ - gc = &grid_default_cell; + memcpy(&gc, &grid_default_cell, sizeof gc); for (xx = 1; xx <= screen_size_x(s); xx++) { - gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, screen_size_x(s) - xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; } ttyctx->last_width = xx; - memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell); + memcpy(&ttyctx->last_cell, &gc, sizeof ttyctx->last_cell); } /* Set a mode. */ @@ -507,7 +502,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) screen_write_initctx(ctx, &ttyctx, 0); memcpy(&gc, &grid_default_cell, sizeof gc); - grid_cell_one(&gc, 'E'); + utf8_set(&gc.data, 'E'); for (yy = 0; yy < screen_size_y(s); yy++) { for (xx = 0; xx < screen_size_x(s); xx++) @@ -904,14 +899,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int width, xx, last; - struct grid_cell tmp_gc, *tmp_gcp; - struct utf8_data ud; + struct grid_cell tmp_gc; int insert; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; - width = grid_cell_width(gc); + width = gc->data.width; /* * If this is a wide character and there is no room on the screen, for @@ -928,8 +922,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * there is space. */ if (width == 0) { - grid_cell_get(gc, &ud); - if (screen_write_combine(ctx, &ud) == 0) { + if (screen_write_combine(ctx, &gc->data) == 0) { screen_write_initctx(ctx, &ttyctx, 0); tty_write(tty_cmd_utf8character, &ttyctx); } @@ -964,11 +957,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ - for (xx = s->cx + 1; xx < s->cx + width; xx++) { - tmp_gcp = grid_view_get_cell(gd, xx, s->cy); - if (tmp_gcp != NULL) - tmp_gcp->flags |= GRID_FLAG_PADDING; - } + memcpy(&tmp_gc, &grid_default_cell, sizeof tmp_gc); + tmp_gc.flags |= GRID_FLAG_PADDING; + tmp_gc.data.width = 0; + for (xx = s->cx + 1; xx < s->cx + width; xx++) + grid_view_set_cell(gd, xx, s->cy, &tmp_gc); /* Set the cell. */ grid_view_set_cell(gd, s->cx, s->cy, gc); @@ -990,8 +983,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } if (screen_check_selection(s, s->cx - width, s->cy)) { memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); - grid_cell_get(gc, &ud); - grid_cell_set(&tmp_gc, &ud); + utf8_copy(&tmp_gc.data, &gc->data); tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); @@ -1011,8 +1003,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) { struct screen *s = ctx->s; struct grid *gd = s->grid; - struct grid_cell *gc; - struct utf8_data ud1; + struct grid_cell gc; /* Can't combine if at 0. */ if (s->cx == 0) @@ -1023,17 +1014,18 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) fatalx("UTF-8 data empty"); /* Retrieve the previous cell. */ - gc = grid_view_get_cell(gd, s->cx - 1, s->cy); - grid_cell_get(gc, &ud1); + grid_view_get_cell(gd, s->cx - 1, s->cy, &gc); /* Check there is enough space. */ - if (ud1.size + ud->size > sizeof ud1.data) + if (gc.data.size + ud->size > sizeof gc.data.data) return (-1); - /* Append the data and set the cell. */ - memcpy(ud1.data + ud1.size, ud->data, ud->size); - ud1.size += ud->size; - grid_cell_set(gc, &ud1); + /* Append the data. */ + memcpy(gc.data.data + gc.data.size, ud->data, ud->size); + gc.data.size += ud->size; + + /* Set the new cell. */ + grid_view_set_cell(gd, s->cx - 1, s->cy, &gc); return (0); } @@ -1052,11 +1044,11 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) { struct screen *s = ctx->s; struct grid *gd = s->grid; - const struct grid_cell *gc; + struct grid_cell gc; u_int xx; - gc = grid_view_peek_cell(gd, s->cx, s->cy); - if (gc->flags & GRID_FLAG_PADDING) { + grid_view_get_cell(gd, s->cx, s->cy, &gc); + if (gc.flags & GRID_FLAG_PADDING) { /* * A padding cell, so clear any following and leading padding * cells back to the character. Don't overwrite the current @@ -1064,8 +1056,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) */ xx = s->cx + 1; while (--xx > 0) { - gc = grid_view_peek_cell(gd, xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } @@ -1080,8 +1072,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) */ xx = s->cx + width - 1; while (++xx < screen_size_x(s)) { - gc = grid_view_peek_cell(gd, xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } diff --git a/status.c b/status.c index a4bdf6fa..326e5ad8 100644 --- a/status.c +++ b/status.c @@ -746,7 +746,7 @@ status_prompt_redraw(struct client *c) struct session *s = c->session; struct screen old_status; size_t i, size, left, len, off; - struct grid_cell gc, *gcp; + struct grid_cell gc; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -789,8 +789,9 @@ status_prompt_redraw(struct client *c) /* Apply fake cursor. */ off = len + c->prompt_index - off; - gcp = grid_view_get_cell(c->status.grid, off, 0); - gcp->attr ^= GRID_ATTR_REVERSE; + grid_view_get_cell(c->status.grid, off, 0, &gc); + gc.attr ^= GRID_ATTR_REVERSE; + grid_view_set_cell(c->status.grid, off, 0, &gc); if (grid_compare(c->status.grid, old_status.grid) == 0) { screen_free(&old_status); diff --git a/tmux.h b/tmux.h index cb7ed2c3..11f5fe58 100644 --- a/tmux.h +++ b/tmux.h @@ -621,11 +621,11 @@ struct mode_key_table { struct utf8_data { u_char data[UTF8_SIZE]; - size_t have; - size_t size; + u_char have; + u_char size; - u_int width; -}; + u_char width; +} __packed; /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 @@ -641,41 +641,56 @@ struct utf8_data { #define GRID_FLAG_FG256 0x1 #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 +#define GRID_FLAG_EXTENDED 0x8 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 /* Grid cell data. */ struct grid_cell { - u_char attr; - u_char flags; - u_char fg; - u_char bg; + u_char flags; + u_char attr; + u_char fg; + u_char bg; + struct utf8_data data; - u_char xstate; /* top 4 bits width, bottom 4 bits size */ - u_char xdata[UTF8_SIZE]; +}; +struct grid_cell_entry { + u_char flags; + union { + u_int offset; + struct { + u_char attr; + u_char fg; + u_char bg; + u_char data; + } data; + }; } __packed; /* Grid line. */ struct grid_line { - u_int cellsize; - struct grid_cell *celldata; + u_int cellsize; + struct grid_cell_entry *celldata; - int flags; + u_int extdsize; + struct grid_cell *extddata; + + int flags; } __packed; /* Entire grid of cells. */ struct grid { - int flags; -#define GRID_HISTORY 0x1 /* scroll lines into history */ + int flags; +#define GRID_HISTORY 0x1 /* scroll lines into history */ - u_int sx; - u_int sy; + u_int sx; + u_int sy; - u_int hsize; - u_int hlimit; + u_int hsize; + u_int hlimit; - struct grid_line *linedata; + struct grid_line *linedata; }; /* Option data structures. */ @@ -1854,9 +1869,8 @@ void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); void grid_clear_history(struct grid *); void grid_expand_line(struct grid *, u_int, u_int); -const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int); -struct grid_cell *grid_get_cell(struct grid *, u_int, u_int); +void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int); @@ -1868,17 +1882,10 @@ void grid_duplicate_lines( struct grid *, u_int, struct grid *, u_int, u_int); u_int grid_reflow(struct grid *, struct grid *, u_int); -/* grid-cell.c */ -u_int grid_cell_width(const struct grid_cell *); -void grid_cell_get(const struct grid_cell *, struct utf8_data *); -void grid_cell_set(struct grid_cell *, const struct utf8_data *); -void grid_cell_one(struct grid_cell *, u_char); - /* grid-view.c */ -const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int); -struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int); -void grid_view_set_cell( - struct grid *, u_int, u_int, const struct grid_cell *); +void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); +void grid_view_set_cell(struct grid *, u_int, u_int, + const struct grid_cell *); void grid_view_clear_history(struct grid *); void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_view_scroll_region_up(struct grid *, u_int, u_int); @@ -2183,6 +2190,7 @@ void session_renumber_windows(struct session *); /* utf8.c */ u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); +void utf8_copy(struct utf8_data *, const struct utf8_data *); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); diff --git a/tty.c b/tty.c index c0ae79bb..e67ebbb6 100644 --- a/tty.c +++ b/tty.c @@ -656,10 +656,8 @@ void tty_draw_line(struct tty *tty, const struct window_pane *wp, struct screen *s, u_int py, u_int ox, u_int oy) { - const struct grid_cell *gc; + struct grid_cell gc; struct grid_line *gl; - struct grid_cell tmpgc; - struct utf8_data ud; u_int i, sx; int flags; @@ -686,18 +684,13 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_cursor(tty, ox, oy + py); for (i = 0; i < sx; i++) { - gc = grid_view_peek_cell(s->grid, i, py); + grid_view_get_cell(s->grid, i, py, &gc); if (screen_check_selection(s, i, py)) { - memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc); - grid_cell_get(gc, &ud); - grid_cell_set(&tmpgc, &ud); - tmpgc.flags = gc->flags & - ~(GRID_FLAG_FG256|GRID_FLAG_BG256); - tmpgc.flags |= s->sel.cell.flags & + gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); - tty_cell(tty, &tmpgc, wp); - } else - tty_cell(tty, gc, wp); + } + tty_cell(tty, &gc, wp); } if (sx < tty->sx) { @@ -1078,7 +1071,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); /* Is the cursor in the very last position? */ - width = grid_cell_width(ctx->cell); + width = ctx->cell->data.width; if (ctx->ocx > wp->sx - width) { if (ctx->xoff != 0 || wp->sx != tty->sx) { /* @@ -1095,7 +1088,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) * move as far left as possible and redraw the last * cell to move into the last position. */ - cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell); + cx = screen_size_x(s) - ctx->last_cell.data.width; tty_cursor_pane(tty, ctx, cx, ctx->ocy); tty_cell(tty, &ctx->last_cell, wp); } @@ -1155,8 +1148,7 @@ void tty_cell(struct tty *tty, const struct grid_cell *gc, const struct window_pane *wp) { - struct utf8_data ud; - u_int i; + u_int i; /* Skip last character if terminal is stupid. */ if (tty->term->flags & TERM_EARLYWRAP && @@ -1171,23 +1163,22 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, tty_attributes(tty, gc, wp); /* Get the cell and if ASCII write with putc to do ACS translation. */ - grid_cell_get(gc, &ud); - if (ud.size == 1) { - if (*ud.data < 0x20 || *ud.data == 0x7f) + if (gc->data.size == 1) { + if (*gc->data.data < 0x20 || *gc->data.data == 0x7f) return; - tty_putc(tty, *ud.data); + tty_putc(tty, *gc->data.data); return; } /* If not UTF-8, write _. */ if (!(tty->flags & TTY_UTF8)) { - for (i = 0; i < ud.width; i++) + for (i = 0; i < gc->data.width; i++) tty_putc(tty, '_'); return; } /* Write the data. */ - tty_putn(tty, ud.data, ud.size, ud.width); + tty_putn(tty, gc->data.data, gc->data.size, gc->data.width); } void diff --git a/utf8.c b/utf8.c index 82471c66..ecc5e718 100644 --- a/utf8.c +++ b/utf8.c @@ -352,10 +352,27 @@ static void utf8_build(void); void utf8_set(struct utf8_data *ud, u_char ch) { + u_int i; + *ud->data = ch; ud->size = 1; ud->width = 1; + + for (i = ud->size; i < sizeof ud->data; i++) + ud->data[i] = '\0'; +} + +/* Copy UTF-8 character. */ +void +utf8_copy(struct utf8_data *to, const struct utf8_data *from) +{ + u_int i; + + memcpy(to, from, sizeof *to); + + for (i = to->size; i < sizeof to->data; i++) + to->data[i] = '\0'; } /* diff --git a/window-copy.c b/window-copy.c index 1c1ea29c..a28cdecc 100644 --- a/window-copy.c +++ b/window-copy.c @@ -942,21 +942,21 @@ int window_copy_search_compare(struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx, int cis) { - const struct grid_cell *gc, *sgc; - struct utf8_data ud, sud; + struct grid_cell gc, sgc; + const struct utf8_data *ud, *sud; - gc = grid_peek_cell(gd, px, py); - grid_cell_get(gc, &ud); - sgc = grid_peek_cell(sgd, spx, 0); - grid_cell_get(sgc, &sud); + grid_get_cell(gd, px, py, &gc); + ud = &gc.data; + grid_get_cell(sgd, spx, 0, &sgc); + sud = &sgc.data; - if (ud.size != sud.size || ud.width != sud.width) + if (ud->size != sud->size || ud->width != sud->width) return (0); - if (cis && ud.size == 1) - return (tolower(ud.data[0]) == sud.data[0]); + if (cis && ud->size == 1) + return (tolower(ud->data[0]) == sud->data[0]); - return (memcmp(ud.data, sud.data, ud.size) == 0); + return (memcmp(ud->data, sud->data, ud->size) == 0); } int @@ -1541,12 +1541,12 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) } void -window_copy_copy_line(struct window_pane *wp, - char **buf, size_t *off, u_int sy, u_int sx, u_int ex) +window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, + u_int sx, u_int ex) { struct window_copy_mode_data *data = wp->modedata; struct grid *gd = data->backing->grid; - const struct grid_cell *gc; + struct grid_cell gc; struct grid_line *gl; struct utf8_data ud; u_int i, xx, wrapped = 0; @@ -1575,11 +1575,11 @@ window_copy_copy_line(struct window_pane *wp, if (sx < ex) { for (i = sx; i < ex; i++) { - gc = grid_peek_cell(gd, i, sy); - if (gc->flags & GRID_FLAG_PADDING) + grid_get_cell(gd, i, sy, &gc); + if (gc.flags & GRID_FLAG_PADDING) continue; - grid_cell_get(gc, &ud); - if (ud.size == 1 && (gc->attr & GRID_ATTR_CHARSET)) { + utf8_copy(&ud, &gc.data); + if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { s = tty_acs_get(NULL, ud.data[0]); if (s != NULL && strlen(s) <= sizeof ud.data) { ud.size = strlen(s); @@ -1618,16 +1618,17 @@ int window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) { struct window_copy_mode_data *data = wp->modedata; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; + const struct utf8_data *ud; - gc = grid_peek_cell(data->backing->grid, px, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || gc->flags & GRID_FLAG_PADDING) + grid_get_cell(data->backing->grid, px, py, &gc); + + ud = &gc.data; + if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING)) return (0); - if (*ud.data == 0x00 || *ud.data == 0x7f) + if (*ud->data == 0x00 || *ud->data == 0x7f) return (0); - return (strchr(set, *ud.data) != NULL); + return (strchr(set, *ud->data) != NULL); } u_int @@ -1635,8 +1636,7 @@ window_copy_find_length(struct window_pane *wp, u_int py) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px; /* @@ -1649,9 +1649,8 @@ window_copy_find_length(struct window_pane *wp, u_int py) if (px > screen_size_x(s)) px = screen_size_x(s); while (px > 0) { - gc = grid_peek_cell(s->grid, px - 1, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || *ud.data != ' ') + grid_get_cell(s->grid, px - 1, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') break; px--; } @@ -1685,17 +1684,15 @@ window_copy_cursor_back_to_indentation(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py, xx; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; px = 0; py = screen_hsize(data->backing) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(data->backing->grid, px, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || *ud.data != ' ') + grid_get_cell(data->backing->grid, px, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') break; px++; } @@ -1909,8 +1906,7 @@ window_copy_cursor_jump(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py, xx; px = data->cx + 1; @@ -1918,10 +1914,9 @@ window_copy_cursor_jump(struct window_pane *wp) xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1936,8 +1931,7 @@ window_copy_cursor_jump_back(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py; px = data->cx; @@ -1947,10 +1941,9 @@ window_copy_cursor_jump_back(struct window_pane *wp) px--; for (;;) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1967,8 +1960,7 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py, xx; px = data->cx + 1 + jump_again; @@ -1976,10 +1968,9 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px - 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1994,8 +1985,7 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py; px = data->cx; @@ -2008,10 +1998,9 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) px--; for (;;) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px + 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); From 531869bd92f0daff3cc3c3cc0ab273846f411dc8 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 10:00:26 +0000 Subject: [PATCH 836/949] Add window_visible_layout which ignores zoomed panes and use it for control mode (which needs to know all panes), from George Nachman. --- control-notify.c | 11 +++++++++-- format.c | 15 +++++++++++++++ tmux.1 | 9 +++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/control-notify.c b/control-notify.c index 943d670c..16a98e3a 100644 --- a/control-notify.c +++ b/control-notify.c @@ -19,6 +19,8 @@ #include +#include + #include "tmux.h" #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ @@ -65,6 +67,10 @@ control_notify_window_layout_changed(struct window *w) struct format_tree *ft; struct winlink *wl; const char *template; + char *expanded; + + template = "%layout-change #{window_id} #{window_layout} " + "#{window_visible_layout} #{window_flags}"; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) @@ -81,13 +87,14 @@ control_notify_window_layout_changed(struct window *w) */ if (w->layout_root == NULL) continue; - template = "%layout-change #{window_id} #{window_layout}"; ft = format_create(); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_defaults(ft, c, NULL, wl, NULL); - control_write(c, "%s", format_expand(ft, template)); + expanded = format_expand(ft, template); + control_write(c, "%s", expanded); + free(expanded); } format_free(ft); } diff --git a/format.c b/format.c index 389026ac..9cfee277 100644 --- a/format.c +++ b/format.c @@ -48,6 +48,7 @@ void format_cb_host_short(struct format_tree *, struct format_entry *); void format_cb_pid(struct format_tree *, struct format_entry *); void format_cb_session_alerts(struct format_tree *, struct format_entry *); void format_cb_window_layout(struct format_tree *, struct format_entry *); +void format_cb_window_visible_layout(struct format_tree *, struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); @@ -362,6 +363,18 @@ format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) fe->value = layout_dump(w->layout_root); } +/* Callback for window_visible_layout. */ +void +format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->w; + + if (w == NULL) + return; + + fe->value = layout_dump(w->layout_root); +} + /* Callback for pane_start_command. */ void format_cb_start_command(struct format_tree *ft, struct format_entry *fe) @@ -1024,6 +1037,8 @@ format_defaults_window(struct format_tree *ft, struct window *w) format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); format_add_cb(ft, "window_layout", format_cb_window_layout); + format_add_cb(ft, "window_visible_layout", + format_cb_window_visible_layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); diff --git a/tmux.1 b/tmux.1 index e0646697..3a08c8cd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3412,11 +3412,12 @@ The following variables are available, where appropriate: .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" +.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_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" +.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes" .It Li "window_width" Ta "" Ta "Width of window" .It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" .It Li "wrap_flag" Ta "" Ta "Pane wrap flag" @@ -4007,12 +4008,16 @@ or an error occurred. If present, .Ar reason describes why the client exited. -.It Ic %layout-change Ar window-id Ar window-layout +.It Ic %layout-change Ar window-id Ar window-layout Ar window-visible-layout Ar window-flags The layout of a window with ID .Ar window-id changed. The new layout is .Ar window-layout . +The window's visible layout is +.Ar window-visible-layout +and the window flags are +.Ar window-flags . .It Ic %output Ar pane-id Ar value A window pane produced output. .Ar value From 88aa1c8dc3962380da8258b3be8fef63b2a2fff2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 12:18:52 +0000 Subject: [PATCH 837/949] Two spacing and spelling nits. --- format.c | 3 ++- grid-view.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 9cfee277..04e06fc6 100644 --- a/format.c +++ b/format.c @@ -48,7 +48,8 @@ void format_cb_host_short(struct format_tree *, struct format_entry *); void format_cb_pid(struct format_tree *, struct format_entry *); void format_cb_session_alerts(struct format_tree *, struct format_entry *); void format_cb_window_layout(struct format_tree *, struct format_entry *); -void format_cb_window_visible_layout(struct format_tree *, struct format_entry *); +void format_cb_window_visible_layout(struct format_tree *, + struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); diff --git a/grid-view.c b/grid-view.c index 5edcfd53..f6708c89 100644 --- a/grid-view.c +++ b/grid-view.c @@ -30,7 +30,7 @@ #define grid_view_x(gd, x) (x) #define grid_view_y(gd, y) ((gd)->hsize + (y)) -/* Get cel. */ +/* Get cell. */ void grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { From b7397bf413643768a44a79d9a32fb887b9a80cde Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 13 Nov 2015 16:05:58 +0000 Subject: [PATCH 838/949] utf8 option is gone. --- window.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/window.c b/window.c index 7f2b2279..831abcd3 100644 --- a/window.c +++ b/window.c @@ -863,8 +863,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); tio2.c_cc[VERASE] = '\177'; #ifdef IUTF8 - if (options_get_number(wp->window->options, "utf8")) - tio2.c_iflag |= IUTF8; + tio2.c_iflag |= IUTF8; #endif if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) fatal("tcgetattr failed"); From c4893d8efdba87843f7f0d8a85c084e1e38d1430 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 16:06:43 +0000 Subject: [PATCH 839/949] Log option names in fatal() for missing option. --- options.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/options.c b/options.c index 487918fd..8d2fb715 100644 --- a/options.c +++ b/options.c @@ -150,9 +150,9 @@ options_get_string(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + log_fatalx("missing option %s", name); if (o->type != OPTIONS_STRING) - fatalx("option not a string"); + log_fatalx("option %s not a string", name); return (o->str); } @@ -180,9 +180,9 @@ options_get_number(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + log_fatalx("missing option %s", name); if (o->type != OPTIONS_NUMBER) - fatalx("option not a number"); + log_fatalx("option %s not a number", name); return (o->num); } @@ -220,8 +220,8 @@ options_get_style(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + log_fatalx("missing option %s", name); if (o->type != OPTIONS_STYLE) - fatalx("option not a style"); + log_fatalx("option %s not a style", name); return (&o->style); } From 908e6bb68f127f2bdf0c15ac25dde9ccc06e9104 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 08:25:12 +0000 Subject: [PATCH 840/949] Log more of UTF-8 input. --- input.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/input.c b/input.c index c56cdc35..babdecdb 100644 --- a/input.c +++ b/input.c @@ -1921,9 +1921,12 @@ input_exit_rename(struct input_ctx *ictx) int input_utf8_open(struct input_ctx *ictx) { - log_debug("%s", __func__); + struct utf8_data *ud = &ictx->utf8data; + + utf8_open(ud, ictx->ch); + + log_debug("%s %hhu", __func__, ud->size); - utf8_open(&ictx->utf8data, ictx->ch); return (0); } @@ -1931,9 +1934,12 @@ input_utf8_open(struct input_ctx *ictx) int input_utf8_add(struct input_ctx *ictx) { + struct utf8_data *ud = &ictx->utf8data; + + utf8_append(ud, ictx->ch); + log_debug("%s", __func__); - utf8_append(&ictx->utf8data, ictx->ch); return (0); } @@ -1941,11 +1947,14 @@ input_utf8_add(struct input_ctx *ictx) int input_utf8_close(struct input_ctx *ictx) { - log_debug("%s", __func__); + struct utf8_data *ud = &ictx->utf8data; - utf8_append(&ictx->utf8data, ictx->ch); + utf8_append(ud, ictx->ch); - utf8_copy(&ictx->cell.cell.data, &ictx->utf8data); + log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, + (int)ud->size, ud->data, ud->width); + + utf8_copy(&ictx->cell.cell.data, ud); screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); From 31d880f751a65aa6bb5f0f12683cdb2c0cbbf5c0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 14 Nov 2015 09:04:13 +0000 Subject: [PATCH 841/949] Update the TODO file. --- TODO | 3 --- 1 file changed, 3 deletions(-) diff --git a/TODO b/TODO index 78d566a4..6094c023 100644 --- a/TODO +++ b/TODO @@ -18,7 +18,6 @@ * option to quote format (#{session_name:quoted}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces - * last window update time and format for it * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple attached sessions? @@ -51,10 +50,8 @@ * split-window -> split-pane? - better UTF-8 support: - * window names and titles * message display * prompt input - * multibyte key input * searching in copy mode - copy/paste improvements: From c56b81a2ce815f6d289232f20bb6e07cfd0e36ec Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 09:41:06 +0000 Subject: [PATCH 842/949] Push stdout and stderr to clients more aggressively, and add an event to continue if the send fails. --- client.c | 4 +-- cmd-capture-pane.c | 2 +- cmd-load-buffer.c | 2 +- cmd-queue.c | 6 ++-- cmd-save-buffer.c | 2 +- control.c | 4 +-- server-client.c | 90 ++++++++++++++++++++++++++++++++++++++++++++-- server-fn.c | 44 ----------------------- server.c | 2 +- tmux.c | 2 +- tmux.h | 4 +-- 11 files changed, 101 insertions(+), 61 deletions(-) diff --git a/client.c b/client.c index bc934d33..a6423630 100644 --- a/client.c +++ b/client.c @@ -289,7 +289,7 @@ 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 (0 && pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); /* Free stuff that is not used in the client. */ @@ -541,7 +541,7 @@ 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 (0 && pledge("stdio unix proc exec tty", NULL) != 0) fatal("pledge failed"); pledge_applied = 1; }; diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 8958a12d..6a7af47a 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -203,7 +203,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) free(buf); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } else { bufname = NULL; if (args_has(args, 'b')) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 40bea9b7..5ac2edc3 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -160,7 +160,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) free(saved); } evbuffer_add_printf(c->stderr_data, "%s", cause); - server_push_stderr(c); + server_client_push_stderr(c); free(pdata); free(cause); } diff --git a/cmd-queue.c b/cmd-queue.c index 5015981c..2d896212 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -85,7 +85,7 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) } else evbuffer_add_vprintf(c->stdout_data, fmt, ap); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } else { w = c->session->curw->window; if (w->active->mode != &window_copy_mode) { @@ -125,7 +125,7 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) } evbuffer_add(c->stderr_data, msg, msglen); evbuffer_add(c->stderr_data, "\n", 1); - server_push_stderr(c); + server_client_push_stderr(c); c->retval = 1; } else { *msg = toupper((u_char) *msg); @@ -146,7 +146,7 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long) cmdq->time, cmdq->number, flags); - server_push_stdout(c); + server_client_push_stdout(c); } /* Add command list to queue and begin processing if needed. */ diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 9f3deeda..daef88a8 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -130,7 +130,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) do_stdout: evbuffer_add(c->stdout_data, bufdata, bufsize); - server_push_stdout(c); + server_client_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: diff --git a/control.c b/control.c index 11fa2d80..f7264944 100644 --- a/control.c +++ b/control.c @@ -37,7 +37,7 @@ control_write(struct client *c, const char *fmt, ...) va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } /* Write a buffer, adding a terminal newline. Empties buffer. */ @@ -46,7 +46,7 @@ control_write_buffer(struct client *c, struct evbuffer *buffer) { evbuffer_add_buffer(c->stdout_data, buffer); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } /* Control input callback. Read lines and fire commands. */ diff --git a/server-client.c b/server-client.c index fedc93bd..645492d1 100644 --- a/server-client.c +++ b/server-client.c @@ -1035,9 +1035,6 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_client_dispatch_shell(c); break; } - - server_push_stdout(c); - server_push_stderr(c); } /* Handle command message. */ @@ -1214,3 +1211,90 @@ server_client_dispatch_shell(struct client *c) proc_kill_peer(c->peer); } + +/* Event callback to push more stdout data if any left. */ +static void +server_client_stdout_cb(unused int fd, unused short events, void *arg) +{ + struct client *c = arg; + + 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); + return; + } + + 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; + + if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0) + break; + evbuffer_drain(c->stderr_data, sent); + + 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); + } +} diff --git a/server-fn.c b/server-fn.c index e9a04cb5..bded7246 100644 --- a/server-fn.c +++ b/server-fn.c @@ -450,50 +450,6 @@ server_callback_identify(unused int fd, unused short events, void *data) server_clear_identify(c); } -/* Push stdout to client if possible. */ -void -server_push_stdout(struct client *c) -{ - struct msg_stdout_data data; - size_t size; - - size = EVBUFFER_LENGTH(c->stdout_data); - if (size == 0) - return; - if (size > sizeof data.data) - size = sizeof data.data; - - memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); - data.size = size; - - if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) == 0) - evbuffer_drain(c->stdout_data, size); -} - -/* Push stderr to client if possible. */ -void -server_push_stderr(struct client *c) -{ - struct msg_stderr_data data; - size_t size; - - if (c->stderr_data == c->stdout_data) { - server_push_stdout(c); - return; - } - size = EVBUFFER_LENGTH(c->stderr_data); - if (size == 0) - return; - if (size > sizeof data.data) - size = sizeof data.data; - - memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); - data.size = size; - - if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) == 0) - evbuffer_drain(c->stderr_data, size); -} - /* Set stdin callback. */ int server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, diff --git a/server.c b/server.c index 2808c0cc..160b8cac 100644 --- a/server.c +++ b/server.c @@ -175,7 +175,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) if (debug_level > 3) tty_create_log(); - if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " + if (0 && pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); diff --git a/tmux.c b/tmux.c index 5429a7cb..3f110156 100644 --- a/tmux.c +++ b/tmux.c @@ -255,7 +255,7 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " + if (0 && pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); diff --git a/tmux.h b/tmux.h index 11f5fe58..e69b74b1 100644 --- a/tmux.h +++ b/tmux.h @@ -1781,6 +1781,8 @@ int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_loop(void); +void server_client_push_stdout(struct client *); +void server_client_push_stderr(struct client *); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); @@ -1806,8 +1808,6 @@ void server_destroy_session(struct session *); void server_check_unattached(void); void server_set_identify(struct client *); void server_clear_identify(struct client *); -void server_push_stdout(struct client *); -void server_push_stderr(struct client *); int server_set_stdin_callback(struct client *, void (*)(struct client *, int, void *), void *, char **); void server_unzoom_window(struct window *); From 64333e3ef89047d1c09cdc5053af647dbd8344da Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 10:56:31 +0000 Subject: [PATCH 843/949] Be more strict about invalid UTF-8. --- input.c | 13 +++++++----- key-string.c | 6 ++++-- screen-write.c | 56 ++++++++++++++++++++++++++++---------------------- tmux.h | 2 +- tty-keys.c | 11 +++++++--- utf8.c | 42 ++++++++++++++++++++----------------- 6 files changed, 76 insertions(+), 54 deletions(-) diff --git a/input.c b/input.c index babdecdb..cb92e52f 100644 --- a/input.c +++ b/input.c @@ -446,11 +446,11 @@ const struct input_transition input_state_ground_table[] = { { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x7e, input_print, NULL }, { 0x7f, 0x7f, NULL, NULL }, - { 0x80, 0xc1, input_print, NULL }, + { 0x80, 0xc1, NULL, NULL }, { 0xc2, 0xdf, input_utf8_open, &input_state_utf8_one }, { 0xe0, 0xef, input_utf8_open, &input_state_utf8_two }, { 0xf0, 0xf4, input_utf8_open, &input_state_utf8_three }, - { 0xf5, 0xff, input_print, NULL }, + { 0xf5, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; @@ -1923,7 +1923,8 @@ input_utf8_open(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - utf8_open(ud, ictx->ch); + if (!utf8_open(ud, ictx->ch)) + log_fatalx("UTF-8 open invalid %#hhx", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1936,7 +1937,8 @@ input_utf8_add(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - utf8_append(ud, ictx->ch); + if (utf8_append(ud, ictx->ch) != 1) + log_fatalx("UTF-8 add invalid %#hhx", ictx->ch); log_debug("%s", __func__); @@ -1949,7 +1951,8 @@ input_utf8_close(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - utf8_append(ud, ictx->ch); + if (utf8_append(ud, ictx->ch) != 0) + log_fatalx("UTF-8 close invalid %#hhx", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); diff --git a/key-string.c b/key-string.c index c2230218..81d014ac 100644 --- a/key-string.c +++ b/key-string.c @@ -144,7 +144,7 @@ key_string_lookup_string(const char *string) static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; key_code key; u_short u; - int size; + int size, more; key_code modifiers; struct utf8_data ud; u_int i; @@ -177,7 +177,9 @@ key_string_lookup_string(const char *string) if (strlen(string) != ud.size) return (KEYC_NONE); for (i = 1; i < ud.size; i++) - utf8_append(&ud, (u_char)string[i]); + more = utf8_append(&ud, (u_char)string[i]); + if (more != 0) + return (KEYC_NONE); key = utf8_combine(&ud); return (key | modifiers); } diff --git a/screen-write.c b/screen-write.c index 14b8a41a..9e1ef822 100644 --- a/screen-write.c +++ b/screen-write.c @@ -115,6 +115,7 @@ screen_write_strlen(const char *fmt, ...) struct utf8_data ud; u_char *ptr; size_t left, size = 0; + int more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -128,11 +129,12 @@ screen_write_strlen(const char *fmt, ...) left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while (utf8_append(&ud, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == 1) ptr++; ptr++; - size += ud.width; + if (more == 0) + size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) size++; @@ -176,6 +178,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct utf8_data ud; u_char *ptr; size_t left, size = 0; + int more; xvasprintf(&msg, fmt, ap); @@ -187,22 +190,24 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while (utf8_append(&ud, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == 1) ptr++; ptr++; - if (maxlen > 0 && - size + ud.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; + if (more == 0) { + if (maxlen > 0 && + size + ud.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; } - break; - } - size += ud.width; + size += ud.width; - utf8_copy(&gc->data, &ud); - screen_write_cell(ctx, gc); + utf8_copy(&gc->data, &ud); + screen_write_cell(ctx, gc); + } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; @@ -231,6 +236,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, char *msg; u_char *ptr, *last; size_t left, size = 0; + int more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -260,22 +266,24 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while (utf8_append(&ud, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == 1) ptr++; ptr++; - if (maxlen > 0 && - size + ud.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; + if (more == 0) { + if (maxlen > 0 && + size + ud.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; } - break; - } - size += ud.width; + size += ud.width; - utf8_copy(&lgc.data, &ud); - screen_write_cell(ctx, &lgc); + utf8_copy(&lgc.data, &ud); + screen_write_cell(ctx, &lgc); + } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; diff --git a/tmux.h b/tmux.h index e69b74b1..2bc1f0a4 100644 --- a/tmux.h +++ b/tmux.h @@ -624,7 +624,7 @@ struct utf8_data { u_char have; u_char size; - u_char width; + u_char width; /* 0xff if invalid */ } __packed; /* Grid attributes. */ diff --git a/tty-keys.c b/tty-keys.c index 6a64ef15..4bdc061a 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -472,7 +472,7 @@ tty_keys_next(struct tty *tty) const char *buf; size_t len, size; cc_t bspace; - int delay, expired = 0; + int delay, expired = 0, more; key_code key; struct utf8_data ud; u_int i; @@ -547,7 +547,9 @@ first_key: goto partial_key; } for (i = 1; i < size; i++) - utf8_append(&ud, (u_char)buf[i]); + more = utf8_append(&ud, (u_char)buf[i]); + if (more != 0) + goto discard_key; key = utf8_combine(&ud); log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); goto complete_key; @@ -653,6 +655,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) struct utf8_data ud; u_int i, value, x, y, b, sgr_b; u_char sgr_type, c; + int more; /* * Standard mouse sequences are \033[M followed by three characters @@ -699,7 +702,9 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) (*size)++; if (len <= *size) return (1); - utf8_append(&ud, buf[*size]); + more = utf8_append(&ud, buf[*size]); + if (more != 0) + return (-1); value = utf8_combine(&ud); } else value = (u_char)buf[*size]; diff --git a/utf8.c b/utf8.c index ecc5e718..a36bccc3 100644 --- a/utf8.c +++ b/utf8.c @@ -403,22 +403,26 @@ utf8_open(struct utf8_data *ud, u_char ch) /* * Append character to UTF-8, closing if finished. * - * Returns 1 if more UTF-8 data to come, 0 if finished. + * Returns 1 if more UTF-8 data to come, 0 if finished and valid, -1 if + * finished and invalid. */ int utf8_append(struct utf8_data *ud, u_char ch) { - /* XXX this should do validity checks too! */ - if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); if (ud->size > sizeof ud->data) fatalx("UTF-8 character size too large"); + if (ud->have != 0 && (ch & 0xc0) != 0x80) + ud->width = 0xff; + ud->data[ud->have++] = ch; if (ud->have != ud->size) return (1); + if (ud->width == 0xff) + return (-1); ud->width = utf8_width(utf8_combine(ud)); return (0); } @@ -556,15 +560,15 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) while (src < end) { if (utf8_open(&ud, *src)) { more = 1; - while (++src < end && more) + while (++src < end && more == 1) more = utf8_append(&ud, *src); - if (!more) { + if (more == 0) { /* UTF-8 character finished. */ for (i = 0; i < ud.size; i++) *dst++ = ud.data[i]; continue; } else if (ud.have > 0) { - /* Not a complete UTF-8 character. */ + /* Not a complete, valid UTF-8 character. */ src -= ud.have; } } @@ -600,9 +604,9 @@ utf8_sanitize(const char *src) dst = xreallocarray(dst, n + 1, sizeof *dst); if (utf8_open(&ud, *src)) { more = 1; - while (*++src != '\0' && more) + while (*++src != '\0' && more == 1) more = utf8_append(&ud, *src); - if (!more) { + if (more != 1) { dst = xreallocarray(dst, n + ud.width, sizeof *dst); for (i = 0; i < ud.width; i++) @@ -612,10 +616,8 @@ utf8_sanitize(const char *src) src -= ud.have; } if (*src > 0x1f && *src < 0x7f) - dst[n] = *src; + dst[n++] = *src; src++; - - n++; } dst = xreallocarray(dst, n + 1, sizeof *dst); @@ -641,18 +643,19 @@ utf8_fromcstr(const char *src) dst = xreallocarray(dst, n + 1, sizeof *dst); if (utf8_open(&dst[n], *src)) { more = 1; - while (*++src != '\0' && more) + while (*++src != '\0' && more == 1) more = utf8_append(&dst[n], *src); - if (!more) { + if (more != 1) { n++; continue; } src -= dst[n].have; } - utf8_set(&dst[n], *src); + if (*src > 0x1f && *src < 0x7f) { + utf8_set(&dst[n], *src); + n++; + } src++; - - n++; } dst = xreallocarray(dst, n + 1, sizeof *dst); @@ -693,15 +696,16 @@ utf8_cstrwidth(const char *s) while (*s != '\0') { if (utf8_open(&tmp, *s)) { more = 1; - while (*++s != '\0' && more) + while (*++s != '\0' && more == 1) more = utf8_append(&tmp, *s); - if (!more) { + if (more != 1) { width += tmp.width; continue; } s -= tmp.have; } - width++; + if (*s > 0x1f && *s < 0x7f) + width++; s++; } return (width); From dab63b029e94dcabe335abf7f89c66c28486a542 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 10:57:59 +0000 Subject: [PATCH 844/949] Couple of assignments to remove compiler warnings. --- key-string.c | 1 + tty-keys.c | 1 + 2 files changed, 2 insertions(+) diff --git a/key-string.c b/key-string.c index 81d014ac..ad7cbf50 100644 --- a/key-string.c +++ b/key-string.c @@ -176,6 +176,7 @@ key_string_lookup_string(const char *string) if (utf8_open(&ud, (u_char)*string)) { if (strlen(string) != ud.size) return (KEYC_NONE); + more = 1; for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != 0) diff --git a/tty-keys.c b/tty-keys.c index 4bdc061a..cc6b934a 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -546,6 +546,7 @@ first_key: goto discard_key; goto partial_key; } + more = 1; for (i = 1; i < size; i++) more = utf8_append(&ud, (u_char)buf[i]); if (more != 0) From f401791a5689799ddf3cfa6ecee7da60318febf7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 11:13:44 +0000 Subject: [PATCH 845/949] Rename a variable in utf8_combine for consistency and use 0xfffd for unknown Unicode. --- utf8.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/utf8.c b/utf8.c index a36bccc3..b27d6671 100644 --- a/utf8.c +++ b/utf8.c @@ -474,30 +474,30 @@ utf8_width(u_int uc) u_int utf8_combine(const struct utf8_data *ud) { - u_int value; + u_int uc; - value = 0xff; + uc = 0xfffd; switch (ud->size) { case 1: - value = ud->data[0]; + uc = ud->data[0]; break; case 2: - value = ud->data[1] & 0x3f; - value |= (ud->data[0] & 0x1f) << 6; + uc = ud->data[1] & 0x3f; + uc |= (ud->data[0] & 0x1f) << 6; break; case 3: - value = ud->data[2] & 0x3f; - value |= (ud->data[1] & 0x3f) << 6; - value |= (ud->data[0] & 0xf) << 12; + uc = ud->data[2] & 0x3f; + uc |= (ud->data[1] & 0x3f) << 6; + uc |= (ud->data[0] & 0xf) << 12; break; case 4: - value = ud->data[3] & 0x3f; - value |= (ud->data[2] & 0x3f) << 6; - value |= (ud->data[1] & 0x3f) << 12; - value |= (ud->data[0] & 0x7) << 18; + uc = ud->data[3] & 0x3f; + uc |= (ud->data[2] & 0x3f) << 6; + uc |= (ud->data[1] & 0x3f) << 12; + uc |= (ud->data[0] & 0x7) << 18; break; } - return (value); + return (uc); } /* Split 32-bit Unicode into UTF-8. */ From 205d15e82d9e4aa90c7980b509d3489ad8eb6c2a Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 11:45:43 +0000 Subject: [PATCH 846/949] All these return values from utf8_* are confusing, use an enum. --- input-keys.c | 2 +- input.c | 6 ++-- key-string.c | 10 +++---- screen-write.c | 24 +++++++-------- tmux.h | 11 +++++-- tty-keys.c | 14 ++++----- utf8.c | 80 +++++++++++++++++++++----------------------------- 7 files changed, 70 insertions(+), 77 deletions(-) diff --git a/input-keys.c b/input-keys.c index 3938c185..156cbe6a 100644 --- a/input-keys.c +++ b/input-keys.c @@ -169,7 +169,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) return; } if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { - if (utf8_split(justkey, &ud) != 0) + if (utf8_split(justkey, &ud) != UTF8_DONE) return; if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); diff --git a/input.c b/input.c index cb92e52f..f7b4e460 100644 --- a/input.c +++ b/input.c @@ -1923,7 +1923,7 @@ input_utf8_open(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - if (!utf8_open(ud, ictx->ch)) + if (utf8_open(ud, ictx->ch) != UTF8_MORE) log_fatalx("UTF-8 open invalid %#hhx", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1937,7 +1937,7 @@ input_utf8_add(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - if (utf8_append(ud, ictx->ch) != 1) + if (utf8_append(ud, ictx->ch) != UTF8_MORE) log_fatalx("UTF-8 add invalid %#hhx", ictx->ch); log_debug("%s", __func__); @@ -1951,7 +1951,7 @@ input_utf8_close(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - if (utf8_append(ud, ictx->ch) != 0) + if (utf8_append(ud, ictx->ch) != UTF8_DONE) log_fatalx("UTF-8 close invalid %#hhx", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, diff --git a/key-string.c b/key-string.c index ad7cbf50..9a44892d 100644 --- a/key-string.c +++ b/key-string.c @@ -144,10 +144,11 @@ key_string_lookup_string(const char *string) static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; key_code key; u_short u; - int size, more; + int size; key_code modifiers; struct utf8_data ud; u_int i; + enum utf8_state more; /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { @@ -173,13 +174,12 @@ key_string_lookup_string(const char *string) return (KEYC_NONE); } else { /* Try as a UTF-8 key. */ - if (utf8_open(&ud, (u_char)*string)) { + if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) return (KEYC_NONE); - more = 1; for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); - if (more != 0) + if (more != UTF8_DONE) return (KEYC_NONE); key = utf8_combine(&ud); return (key | modifiers); @@ -256,7 +256,7 @@ key_string_lookup_key(key_code key) /* Is this a UTF-8 key? */ if (key > 127 && key < KEYC_BASE) { - if (utf8_split(key, &ud) == 0) { + if (utf8_split(key, &ud) == UTF8_DONE) { memcpy(out, ud.data, ud.size); out[ud.size] = '\0'; return (out); diff --git a/screen-write.c b/screen-write.c index 9e1ef822..a887b0f1 100644 --- a/screen-write.c +++ b/screen-write.c @@ -115,7 +115,7 @@ screen_write_strlen(const char *fmt, ...) struct utf8_data ud; u_char *ptr; size_t left, size = 0; - int more; + enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -123,17 +123,17 @@ screen_write_strlen(const char *fmt, ...) ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == 1) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == 0) + if (more == UTF8_DONE) size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) @@ -178,23 +178,23 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct utf8_data ud; u_char *ptr; size_t left, size = 0; - int more; + enum utf8_state more; xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == 1) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == 0) { + if (more == UTF8_DONE) { if (maxlen > 0 && size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { @@ -236,7 +236,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, char *msg; u_char *ptr, *last; size_t left, size = 0; - int more; + enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -260,17 +260,17 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, continue; } - if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == 1) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == 0) { + if (more == UTF8_DONE) { if (maxlen > 0 && size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { diff --git a/tmux.h b/tmux.h index 2bc1f0a4..30e54a64 100644 --- a/tmux.h +++ b/tmux.h @@ -626,6 +626,11 @@ struct utf8_data { u_char width; /* 0xff if invalid */ } __packed; +enum utf8_state { + UTF8_MORE, + UTF8_DONE, + UTF8_ERROR +}; /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 @@ -2191,10 +2196,10 @@ void session_renumber_windows(struct session *); u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); -int utf8_open(struct utf8_data *, u_char); -int utf8_append(struct utf8_data *, u_char); +enum utf8_state utf8_open(struct utf8_data *, u_char); +enum utf8_state utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); -int utf8_split(u_int, struct utf8_data *); +enum utf8_state utf8_split(u_int, struct utf8_data *); u_int utf8_split2(u_int, u_char *); int utf8_strvis(char *, const char *, size_t, int); char *utf8_sanitize(const char *); diff --git a/tty-keys.c b/tty-keys.c index cc6b934a..52dae4f7 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -472,9 +472,10 @@ tty_keys_next(struct tty *tty) const char *buf; size_t len, size; cc_t bspace; - int delay, expired = 0, more; + int delay, expired = 0; key_code key; struct utf8_data ud; + enum utf8_state more; u_int i; /* Get key buffer. */ @@ -539,17 +540,16 @@ first_key: } /* Is this valid UTF-8? */ - if (utf8_open(&ud, (u_char)*buf)) { + if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) { size = ud.size; if (len < size) { if (expired) goto discard_key; goto partial_key; } - more = 1; for (i = 1; i < size; i++) more = utf8_append(&ud, (u_char)buf[i]); - if (more != 0) + if (more != UTF8_DONE) goto discard_key; key = utf8_combine(&ud); log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); @@ -656,7 +656,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) struct utf8_data ud; u_int i, value, x, y, b, sgr_b; u_char sgr_type, c; - int more; + enum utf8_state more; /* * Standard mouse sequences are \033[M followed by three characters @@ -697,14 +697,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&ud, buf[*size])) { + if (utf8_open(&ud, buf[*size]) == UTF8_MORE) { if (ud.size != 2) return (-1); (*size)++; if (len <= *size) return (1); more = utf8_append(&ud, buf[*size]); - if (more != 0) + if (more != UTF8_DONE) return (-1); value = utf8_combine(&ud); } else diff --git a/utf8.c b/utf8.c index b27d6671..1bfea079 100644 --- a/utf8.c +++ b/utf8.c @@ -381,10 +381,8 @@ utf8_copy(struct utf8_data *to, const struct utf8_data *from) * 11000010-11011111 C2-DF start of 2-byte sequence * 11100000-11101111 E0-EF start of 3-byte sequence * 11110000-11110100 F0-F4 start of 4-byte sequence - * - * Returns 1 if more UTF-8 to come, 0 if not UTF-8. */ -int +enum utf8_state utf8_open(struct utf8_data *ud, u_char ch) { memset(ud, 0, sizeof *ud); @@ -395,18 +393,13 @@ utf8_open(struct utf8_data *ud, u_char ch) else if (ch >= 0xf0 && ch <= 0xf4) ud->size = 4; else - return (0); + return (UTF8_ERROR); utf8_append(ud, ch); - return (1); + return (UTF8_MORE); } -/* - * Append character to UTF-8, closing if finished. - * - * Returns 1 if more UTF-8 data to come, 0 if finished and valid, -1 if - * finished and invalid. - */ -int +/* Append character to UTF-8, closing if finished. */ +enum utf8_state utf8_append(struct utf8_data *ud, u_char ch) { if (ud->have >= ud->size) @@ -419,12 +412,12 @@ utf8_append(struct utf8_data *ud, u_char ch) ud->data[ud->have++] = ch; if (ud->have != ud->size) - return (1); + return (UTF8_MORE); if (ud->width == 0xff) - return (-1); + return (UTF8_ERROR); ud->width = utf8_width(utf8_combine(ud)); - return (0); + return (UTF8_DONE); } /* Build UTF-8 width tree. */ @@ -501,7 +494,7 @@ utf8_combine(const struct utf8_data *ud) } /* Split 32-bit Unicode into UTF-8. */ -int +enum utf8_state utf8_split(u_int uc, struct utf8_data *ud) { if (uc < 0x7f) { @@ -523,9 +516,9 @@ utf8_split(u_int uc, struct utf8_data *ud) ud->data[2] = 0x80 | ((uc >> 6) & 0x3f); ud->data[3] = 0x80 | (uc & 0x3f); } else - return (-1); + return (UTF8_ERROR); ud->width = utf8_width(uc); - return (0); + return (UTF8_DONE); } /* Split a two-byte UTF-8 character. */ @@ -551,26 +544,24 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) { struct utf8_data ud; const char *start, *end; - int more; + enum utf8_state more; size_t i; start = dst; end = src + len; while (src < end) { - if (utf8_open(&ud, *src)) { - more = 1; - while (++src < end && more == 1) + if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { + while (++src < end && more == UTF8_MORE) more = utf8_append(&ud, *src); - if (more == 0) { + if (more == UTF8_DONE) { /* UTF-8 character finished. */ for (i = 0; i < ud.size; i++) *dst++ = ud.data[i]; continue; - } else if (ud.have > 0) { - /* Not a complete, valid UTF-8 character. */ - src -= ud.have; } + /* Not a complete, valid UTF-8 character. */ + src -= ud.have; } if (src < end - 1) dst = vis(dst, src[0], flag, src[1]); @@ -593,7 +584,7 @@ utf8_sanitize(const char *src) { char *dst; size_t n; - int more; + enum utf8_state more; struct utf8_data ud; u_int i; @@ -602,11 +593,10 @@ utf8_sanitize(const char *src) n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); - if (utf8_open(&ud, *src)) { - more = 1; - while (*++src != '\0' && more == 1) + if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { + while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *src); - if (more != 1) { + if (more == UTF8_DONE) { dst = xreallocarray(dst, n + ud.width, sizeof *dst); for (i = 0; i < ud.width; i++) @@ -617,6 +607,8 @@ utf8_sanitize(const char *src) } if (*src > 0x1f && *src < 0x7f) dst[n++] = *src; + else + dst[n++] = '_'; src++; } @@ -634,27 +626,24 @@ utf8_fromcstr(const char *src) { struct utf8_data *dst; size_t n; - int more; + enum utf8_state more; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); - if (utf8_open(&dst[n], *src)) { - more = 1; - while (*++src != '\0' && more == 1) + if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { + while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&dst[n], *src); - if (more != 1) { + if (more == UTF8_DONE) { n++; continue; } src -= dst[n].have; } - if (*src > 0x1f && *src < 0x7f) { - utf8_set(&dst[n], *src); - n++; - } + utf8_set(&dst[n], *src); + n++; src++; } @@ -690,21 +679,20 @@ utf8_cstrwidth(const char *s) { struct utf8_data tmp; u_int width; - int more; + enum utf8_state more; width = 0; while (*s != '\0') { - if (utf8_open(&tmp, *s)) { - more = 1; - while (*++s != '\0' && more == 1) + if ((more = utf8_open(&tmp, *s)) == UTF8_MORE) { + while (*++s != '\0' && more == UTF8_MORE) more = utf8_append(&tmp, *s); - if (more != 1) { + if (more == UTF8_DONE) { width += tmp.width; continue; } s -= tmp.have; } - if (*s > 0x1f && *s < 0x7f) + if (*s > 0x1f && *s != 0x7f) width++; s++; } From 3db0d50df44d91a110f9320cb4fd327c97b96df2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 12:03:23 +0000 Subject: [PATCH 847/949] The private use area at U+E000 to U+F8FF is not very useful if it is width 0, make it width 1 instead. --- utf8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utf8.c b/utf8.c index 1bfea079..b391c5ec 100644 --- a/utf8.c +++ b/utf8.c @@ -264,7 +264,7 @@ static struct utf8_width_entry utf8_width_table[] = { { 0x0abe5, 0x0abe5, 0, NULL, NULL }, { 0x0abed, 0x0abed, 0, NULL, NULL }, { 0x0f900, 0x0fa6d, 2, NULL, NULL }, - { 0x0d800, 0x0f8ff, 0, NULL, NULL }, + { 0x0d800, 0x0dfff, 0, NULL, NULL }, { 0x0fa70, 0x0fad9, 2, NULL, NULL }, { 0x0fff9, 0x0fffb, 0, NULL, NULL }, { 0x0fe30, 0x0fe52, 2, NULL, NULL }, From 14d90e4901ef943e57e5dada7a0f53b2dc208571 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 12:20:19 +0000 Subject: [PATCH 848/949] The character is an int so use %x not %hhx. --- input.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index f7b4e460..6e1fec90 100644 --- a/input.c +++ b/input.c @@ -1924,7 +1924,7 @@ input_utf8_open(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_open(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 open invalid %#hhx", ictx->ch); + log_fatalx("UTF-8 open invalid %#x", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1938,7 +1938,7 @@ input_utf8_add(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 add invalid %#hhx", ictx->ch); + log_fatalx("UTF-8 add invalid %#x", ictx->ch); log_debug("%s", __func__); @@ -1952,7 +1952,7 @@ input_utf8_close(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_DONE) - log_fatalx("UTF-8 close invalid %#hhx", ictx->ch); + log_fatalx("UTF-8 close invalid %#x", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); From a582b622879714b0a100e8c7d3054f5a03476ca1 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 15 Nov 2015 14:32:48 +0000 Subject: [PATCH 849/949] Accidentally turned off pledge, turn it back on. --- client.c | 4 ++-- server.c | 2 +- tmux.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client.c b/client.c index a6423630..bc934d33 100644 --- a/client.c +++ b/client.c @@ -289,7 +289,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) * * "sendfd" is dropped later in client_dispatch_wait(). */ - if (0 && pledge("stdio unix sendfd proc exec tty", NULL) != 0) + if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); /* Free stuff that is not used in the client. */ @@ -541,7 +541,7 @@ client_dispatch_wait(struct imsg *imsg) * get the first message from the server. */ if (!pledge_applied) { - if (0 && pledge("stdio unix proc exec tty", NULL) != 0) + if (pledge("stdio unix proc exec tty", NULL) != 0) fatal("pledge failed"); pledge_applied = 1; }; diff --git a/server.c b/server.c index 160b8cac..2808c0cc 100644 --- a/server.c +++ b/server.c @@ -175,7 +175,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) if (debug_level > 3) tty_create_log(); - if (0 && pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " + if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); diff --git a/tmux.c b/tmux.c index 3f110156..5429a7cb 100644 --- a/tmux.c +++ b/tmux.c @@ -255,7 +255,7 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (0 && pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " + if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); From 661d0dfac9ad355ad32a9eaebd61d9e340fcfc3b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 15 Nov 2015 22:50:38 +0000 Subject: [PATCH 850/949] Make key_code unsigned long long not uint64_t which is more portable for printf formats, and move UTF8_SIZE define down to near the rest of the UTF-8 bits. --- tmux.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tmux.h b/tmux.h index 30e54a64..b81bbfcd 100644 --- a/tmux.h +++ b/tmux.h @@ -57,12 +57,6 @@ struct tmuxproc; /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 -/* - * UTF-8 data size. This must be big enough to hold combined characters as well - * as single. - */ -#define UTF8_SIZE 9 - /* * READ_SIZE is the maximum size of data to hold from a pty (the event high * watermark). READ_BACKOFF is the amount of data waiting to be output to a tty @@ -125,7 +119,7 @@ struct tmuxproc; * A single key. This can be ASCII or Unicode or one of the keys starting at * KEYC_BASE. */ -typedef uint64_t key_code; +typedef unsigned long long key_code; /* Special key codes. */ enum { @@ -617,7 +611,11 @@ struct mode_key_table { #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON) -/* A single UTF-8 character. */ +/* + * A single UTF-8 character. UTF8_SIZE must be big enough to hold at least one + * combining character as well. +*/ +#define UTF8_SIZE 9 struct utf8_data { u_char data[UTF8_SIZE]; From 775fb562bd0026ca4b2edc9bde99a85e4daf40ed Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Nov 2015 22:57:51 +0000 Subject: [PATCH 851/949] 0x7f is a valid key. --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index 156cbe6a..2915cb45 100644 --- a/input-keys.c +++ b/input-keys.c @@ -161,7 +161,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) * if necessary. If it is a UTF-8 key, split it and send it. */ justkey = (key & ~KEYC_ESCAPE); - if (key != KEYC_NONE && justkey < 0x7f) { + if (key != KEYC_NONE && justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; From d0505fd04256247a5c8d56888ff19a9b6da80cea Mon Sep 17 00:00:00 2001 From: tobias Date: Tue, 17 Nov 2015 18:25:03 +0000 Subject: [PATCH 852/949] Merge xmalloc.[ch] files across base, skipping OpenSSH for now. ok nicm --- xmalloc.c | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/xmalloc.c b/xmalloc.c index b1570a3a..3db67af6 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -25,16 +25,13 @@ #include "tmux.h" char * -xstrdup(const char *s) +xstrdup(const char *str) { - char *ptr; - size_t len; + char *cp; - len = strlen(s) + 1; - ptr = xmalloc(len); - - strlcpy(ptr, s, len); - return (ptr); + if ((cp = strdup(str)) == NULL) + fatal("xstrdup"); + return (cp); } void * @@ -43,11 +40,9 @@ xcalloc(size_t nmemb, size_t size) void *ptr; if (size == 0 || nmemb == 0) - fatalx("zero size"); - if (SIZE_MAX / nmemb < size) - fatalx("nmemb * size > SIZE_MAX"); + fatalx("xcalloc: zero size"); if ((ptr = calloc(nmemb, size)) == NULL) - fatal("xcalloc failed"); + log_fatal("xcalloc: allocating %zu bytes", size); return (ptr); } @@ -58,9 +53,9 @@ xmalloc(size_t size) void *ptr; if (size == 0) - fatalx("zero size"); + fatalx("xmalloc: zero size"); if ((ptr = malloc(size)) == NULL) - fatal("xmalloc failed"); + log_fatal("xmalloc: allocating %zu bytes", size); return (ptr); } @@ -71,9 +66,9 @@ xrealloc(void *oldptr, size_t newsize) void *newptr; if (newsize == 0) - fatalx("zero size"); + fatalx("xrealloc: zero size"); if ((newptr = realloc(oldptr, newsize)) == NULL) - fatal("xrealloc failed"); + log_fatal("xrealloc: allocating %zu bytes", newsize); return (newptr); } @@ -81,15 +76,13 @@ xrealloc(void *oldptr, size_t newsize) void * xreallocarray(void *oldptr, size_t nmemb, size_t size) { - size_t newsize = nmemb * size; void *newptr; - if (newsize == 0) - fatalx("zero size"); - if (SIZE_MAX / nmemb < size) - fatalx("nmemb * size > SIZE_MAX"); - if ((newptr = realloc(oldptr, newsize)) == NULL) - fatal("xreallocarray failed"); + if (nmemb == 0 || size == 0) + fatalx("xreallocarray: zero size"); + if ((newptr = reallocarray(oldptr, nmemb, size)) == NULL) + log_fatal("xreallocarray: allocating %zu * %zu bytes", + nmemb, size); return (newptr); } @@ -114,7 +107,7 @@ xvasprintf(char **ret, const char *fmt, va_list ap) i = vasprintf(ret, fmt, ap); if (i < 0 || *ret == NULL) - fatal("xvasprintf failed"); + fatal("xvasprintf"); return (i); } @@ -138,11 +131,11 @@ xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) int i; if (len > INT_MAX) - fatalx("len > INT_MAX"); + fatalx("xvsnprintf: len > INT_MAX"); i = vsnprintf(buf, len, fmt, ap); - if (i < 0) - fatal("vsnprintf failed"); + if (i < 0 || i >= (int)len) + fatalx("xvsnprintf: overflow"); return (i); } From ca5e6bf5f2f11796bf2bdbe136ba534e46b2e86e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 09:22:29 +0000 Subject: [PATCH 853/949] Don't update activity time twice for new sessions, and add some logging. --- session.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/session.c b/session.c index cbaadcee..deb29d18 100644 --- a/session.c +++ b/session.c @@ -131,10 +131,6 @@ session_create(const char *name, int argc, char **argv, const char *path, memcpy(s->tio, tio, sizeof *s->tio); } - if (gettimeofday(&s->creation_time, NULL) != 0) - fatal("gettimeofday failed"); - session_update_activity(s, &s->creation_time); - s->sx = sx; s->sy = sy; @@ -151,6 +147,8 @@ session_create(const char *name, int argc, char **argv, const char *path, } RB_INSERT(sessions, &sessions, s); + log_debug("new session %s $%u", s->name, s->id); + if (gettimeofday(&s->creation_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s, &s->creation_time); @@ -265,6 +263,10 @@ session_update_activity(struct session *s, struct timeval *from) else memcpy(&s->activity_time, from, sizeof s->activity_time); + log_debug("session %s activity %lld.%06d (last %lld.%06d)", s->name, + (long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec, + (long long)last->tv_sec, (int)last->tv_usec); + if (evtimer_initialized(&s->lock_timer)) evtimer_del(&s->lock_timer); else From a77960c540b6ee27ad5538a638ae3615cd025539 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 12:54:29 +0000 Subject: [PATCH 854/949] Add reallocarray to compat. --- Makefile.am | 3 +++ compat/reallocarray.c | 40 ++++++++++++++++++++++++++++++++++++++++ configure.ac | 7 +++++++ 3 files changed, 50 insertions(+) create mode 100644 compat/reallocarray.c diff --git a/Makefile.am b/Makefile.am index 02784c4a..5b11400c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -234,6 +234,9 @@ endif if NO_OPENAT nodist_tmux_SOURCES += compat/openat.c endif +if NO_REALLOCARRAY +nodist_tmux_SOURCES += compat/reallocarray.c +endif # Install tmux.1 in the right format. install-exec-hook: diff --git a/compat/reallocarray.c b/compat/reallocarray.c new file mode 100644 index 00000000..f4705fcd --- /dev/null +++ b/compat/reallocarray.c @@ -0,0 +1,40 @@ +/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "tmux.h" + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} diff --git a/configure.ac b/configure.ac index a2b9e054..6cd859f0 100644 --- a/configure.ac +++ b/configure.ac @@ -365,6 +365,13 @@ if test "x$found_openat" = xyes; then fi AM_CONDITIONAL(NO_OPENAT, [test "x$found_openat" = xno]) +# Look for reallocarray, compat/reallocarray.c used if missing. +AC_CHECK_FUNC(reallocarray, found_reallocarray=yes, found_reallocarray=no) +if test "x$found_reallocarray" = xyes; then + AC_DEFINE(HAVE_REALLOCARRAY) +fi +AM_CONDITIONAL(NO_REALLOCARRAY, [test "x$found_reallocarray" = xno]) + # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) From 64571368dc19219fc1ef9b6c20034ee143cbed0d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 13:06:54 +0000 Subject: [PATCH 855/949] Sync the entire xmalloc.[ch] with the other users, but with the addition of xrealloc, xvasprintf, xvsnprintf. --- input.c | 6 +-- log.c | 4 +- options.c | 12 ++--- tmux.h | 21 ++------ tty-term.c | 6 +-- xmalloc.c | 138 ++++++++++++++++++++++++++--------------------------- xmalloc.h | 40 ++++++++++++++++ 7 files changed, 125 insertions(+), 102 deletions(-) create mode 100644 xmalloc.h diff --git a/input.c b/input.c index 6e1fec90..d8e80afb 100644 --- a/input.c +++ b/input.c @@ -1924,7 +1924,7 @@ input_utf8_open(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_open(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 open invalid %#x", ictx->ch); + fatalx("UTF-8 open invalid %#x", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1938,7 +1938,7 @@ input_utf8_add(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 add invalid %#x", ictx->ch); + fatalx("UTF-8 add invalid %#x", ictx->ch); log_debug("%s", __func__); @@ -1952,7 +1952,7 @@ input_utf8_close(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_DONE) - log_fatalx("UTF-8 close invalid %#x", ictx->ch); + fatalx("UTF-8 close invalid %#x", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); diff --git a/log.c b/log.c index 711b9b8a..cf65c9e3 100644 --- a/log.c +++ b/log.c @@ -102,7 +102,7 @@ log_debug(const char *msg, ...) /* Log a critical error with error string and die. */ __dead void -log_fatal(const char *msg, ...) +fatal(const char *msg, ...) { char *fmt; va_list ap; @@ -116,7 +116,7 @@ log_fatal(const char *msg, ...) /* Log a critical error and die. */ __dead void -log_fatalx(const char *msg, ...) +fatalx(const char *msg, ...) { char *fmt; va_list ap; diff --git a/options.c b/options.c index 8d2fb715..c355f9ce 100644 --- a/options.c +++ b/options.c @@ -150,9 +150,9 @@ options_get_string(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - log_fatalx("missing option %s", name); + fatalx("missing option %s", name); if (o->type != OPTIONS_STRING) - log_fatalx("option %s not a string", name); + fatalx("option %s not a string", name); return (o->str); } @@ -180,9 +180,9 @@ options_get_number(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - log_fatalx("missing option %s", name); + fatalx("missing option %s", name); if (o->type != OPTIONS_NUMBER) - log_fatalx("option %s not a number", name); + fatalx("option %s not a number", name); return (o->num); } @@ -220,8 +220,8 @@ options_get_style(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - log_fatalx("missing option %s", name); + fatalx("missing option %s", name); if (o->type != OPTIONS_STYLE) - log_fatalx("option %s not a style", name); + fatalx("option %s not a style", name); return (&o->style); } diff --git a/tmux.h b/tmux.h index b81bbfcd..421f2369 100644 --- a/tmux.h +++ b/tmux.h @@ -45,6 +45,8 @@ struct session; struct tmuxpeer; struct tmuxproc; +#include "xmalloc.h" + /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" @@ -67,10 +69,6 @@ struct tmuxproc; #define READ_BACKOFF 512 #define READ_TIME 100 -/* Fatal errors. */ -#define fatal(msg) log_fatal("%s: %s", __func__, msg); -#define fatalx(msg) log_fatalx("%s: %s", __func__, msg); - /* Definition to shut gcc up about unused arguments. */ #define unused __attribute__ ((unused)) @@ -2214,19 +2212,8 @@ char *get_proc_name(int, char *); void log_open(const char *); void log_close(void); void printflike(1, 2) log_debug(const char *, ...); -__dead void printflike(1, 2) log_fatal(const char *, ...); -__dead void printflike(1, 2) log_fatalx(const char *, ...); - -/* xmalloc.c */ -char *xstrdup(const char *); -void *xcalloc(size_t, size_t); -void *xmalloc(size_t); -void *xrealloc(void *, size_t); -void *xreallocarray(void *, size_t, size_t); -int printflike(2, 3) xasprintf(char **, const char *, ...); -int xvasprintf(char **, const char *, va_list); -int printflike(3, 4) xsnprintf(char *, size_t, const char *, ...); -int xvsnprintf(char *, size_t, const char *, va_list); +__dead void printflike(1, 2) fatal(const char *, ...); +__dead void printflike(1, 2) fatalx(const char *, ...); /* style.c */ int style_parse(const struct grid_cell *, diff --git a/tty-term.c b/tty-term.c index c6147559..5f2e1a94 100644 --- a/tty-term.c +++ b/tty-term.c @@ -556,7 +556,7 @@ tty_term_string(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (""); if (term->codes[code].type != TTYCODE_STRING) - log_fatalx("not a string: %d", code); + fatalx("not a string: %d", code); return (term->codes[code].value.string); } @@ -591,7 +591,7 @@ tty_term_number(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_NUMBER) - log_fatalx("not a number: %d", code); + fatalx("not a number: %d", code); return (term->codes[code].value.number); } @@ -601,7 +601,7 @@ tty_term_flag(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_FLAG) - log_fatalx("not a flag: %d", code); + fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } diff --git a/xmalloc.c b/xmalloc.c index 3db67af6..afa8e585 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -1,141 +1,137 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2004 Nicholas Marriott + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". */ -#include - +#include +#include #include +#include #include #include #include "tmux.h" -char * -xstrdup(const char *str) +void * +xmalloc(size_t size) { - char *cp; + void *ptr; - if ((cp = strdup(str)) == NULL) - fatal("xstrdup"); - return (cp); + if (size == 0) + fatal("xmalloc: zero size"); + ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: allocating %zu bytes: %s", + size, strerror(errno)); + return ptr; } void * xcalloc(size_t nmemb, size_t size) { - void *ptr; + void *ptr; if (size == 0 || nmemb == 0) - fatalx("xcalloc: zero size"); - if ((ptr = calloc(nmemb, size)) == NULL) - log_fatal("xcalloc: allocating %zu bytes", size); - - return (ptr); + fatal("xcalloc: zero size"); + ptr = calloc(nmemb, size); + if (ptr == NULL) + fatal("xcalloc: allocating %zu * %zu bytes: %s", + nmemb, size, strerror(errno)); + return ptr; } void * -xmalloc(size_t size) +xrealloc(void *ptr, size_t size) { - void *ptr; - - if (size == 0) - fatalx("xmalloc: zero size"); - if ((ptr = malloc(size)) == NULL) - log_fatal("xmalloc: allocating %zu bytes", size); - - return (ptr); + return xreallocarray(ptr, 1, size); } void * -xrealloc(void *oldptr, size_t newsize) +xreallocarray(void *ptr, size_t nmemb, size_t size) { - void *newptr; - - if (newsize == 0) - fatalx("xrealloc: zero size"); - if ((newptr = realloc(oldptr, newsize)) == NULL) - log_fatal("xrealloc: allocating %zu bytes", newsize); - - return (newptr); -} - -void * -xreallocarray(void *oldptr, size_t nmemb, size_t size) -{ - void *newptr; + void *new_ptr; if (nmemb == 0 || size == 0) - fatalx("xreallocarray: zero size"); - if ((newptr = reallocarray(oldptr, nmemb, size)) == NULL) - log_fatal("xreallocarray: allocating %zu * %zu bytes", - nmemb, size); + fatal("xreallocarray: zero size"); + new_ptr = reallocarray(ptr, nmemb, size); + if (new_ptr == NULL) + fatal("xreallocarray: allocating %zu * %zu bytes: %s", + nmemb, size, strerror(errno)); + return new_ptr; +} - return (newptr); +char * +xstrdup(const char *str) +{ + char *cp; + + if ((cp = strdup(str)) == NULL) + fatal("xstrdup: %s", strerror(errno)); + return cp; } int xasprintf(char **ret, const char *fmt, ...) { va_list ap; - int i; + int i; va_start(ap, fmt); i = xvasprintf(ret, fmt, ap); va_end(ap); - return (i); + return i; } int xvasprintf(char **ret, const char *fmt, va_list ap) { - int i; + int i; i = vasprintf(ret, fmt, ap); - if (i < 0 || *ret == NULL) - fatal("xvasprintf"); - return (i); + if (i < 0 || *ret == NULL) + fatal("xasprintf: %s", strerror(errno)); + + return i; } int -xsnprintf(char *buf, size_t len, const char *fmt, ...) +xsnprintf(char *str, size_t len, const char *fmt, ...) { va_list ap; - int i; + int i; va_start(ap, fmt); - i = xvsnprintf(buf, len, fmt, ap); + i = xvsnprintf(str, len, fmt, ap); va_end(ap); - return (i); + return i; } int -xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) +xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) { - int i; + int i; if (len > INT_MAX) - fatalx("xvsnprintf: len > INT_MAX"); + fatal("xsnprintf: len > INT_MAX"); + + i = vsnprintf(str, len, fmt, ap); - i = vsnprintf(buf, len, fmt, ap); if (i < 0 || i >= (int)len) - fatalx("xvsnprintf: overflow"); + fatal("xsnprintf: overflow"); - return (i); + return i; } diff --git a/xmalloc.h b/xmalloc.h new file mode 100644 index 00000000..d331ce99 --- /dev/null +++ b/xmalloc.h @@ -0,0 +1,40 @@ +/* $OpenBSD$ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef XMALLOC_H +#define XMALLOC_H + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void *xrealloc(void *, size_t); +void *xreallocarray(void *, size_t, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +int xvasprintf(char **, const char *, va_list) + __attribute__((__nonnull__ (2))); +int xsnprintf(char *, size_t, const char *, ...) + __attribute__((__format__ (printf, 3, 4))) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); +int xvsnprintf(char *, size_t, const char *, va_list) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); + +#endif /* XMALLOC_H */ From 9bba26f8c53e8bf90daf5330bf959b3ce1f70426 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 14:09:17 +0000 Subject: [PATCH 856/949] Add reallocarray prototype. --- compat.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/compat.h b/compat.h index 8666a1d8..7d9b14fd 100644 --- a/compat.h +++ b/compat.h @@ -255,13 +255,18 @@ int unsetenv(const char *); #ifndef HAVE_CFMAKERAW /* cfmakeraw.c */ -void cfmakeraw(struct termios *); +void cfmakeraw(struct termios *); #endif #ifndef HAVE_OPENAT /* openat.c */ #define AT_FDCWD -100 -int openat(int, const char *, int, ...); +int openat(int, const char *, int, ...); +#endif + +#ifndef HAVE_REALLOCARRAY +/* reallocarray.c */ +void *reallocarray(void *, size_t, size_t size); #endif #ifdef HAVE_GETOPT From f8a1f8843c91ebb1262ec6a000fd495eeb27e179 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 14:10:48 +0000 Subject: [PATCH 857/949] Add -Wno-attributes. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 5b11400c..b561866a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,7 +30,7 @@ CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align -CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign +CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes CPPFLAGS += -DDEBUG endif if IS_COVERAGE From 5a5b950e8b86606edea8f61c40bf8af28ab4f08b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 14:13:55 +0000 Subject: [PATCH 858/949] Add s/foo/bar/: prefix for formats to substitute bar for foo. --- format.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++---------- tmux.1 | 7 +++++ 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/format.c b/format.c index 04e06fc6..e8d5dd09 100644 --- a/format.c +++ b/format.c @@ -99,6 +99,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_TIMESTRING 0x1 #define FORMAT_BASENAME 0x2 #define FORMAT_DIRNAME 0x4 +#define FORMAT_SUBSTITUTE 0x8 /* Entry in format tree. */ struct format_entry { @@ -682,8 +683,9 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr, *saved, *trimmed, *value; - size_t valuelen; + char *copy, *copy0, *endptr, *ptr, *found, *new, *value; + char *from = NULL, *to = NULL; + size_t valuelen, newlen, fromlen, tolen, used; u_long limit = 0; int modifiers = 0, brackets; @@ -721,6 +723,29 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, modifiers |= FORMAT_TIMESTRING; copy += 2; break; + case 's': + if (copy[1] != '/') + break; + from = copy + 2; + for (copy = from; *copy != '\0' && *copy != '/'; copy++) + /* nothing */; + if (copy[0] != '/' || copy == from) { + copy = copy0; + break; + } + copy[0] = '\0'; + to = copy + 1; + for (copy = to; *copy != '\0' && *copy != '/'; copy++) + /* nothing */; + if (copy[0] != '/' || copy[1] != ':') { + copy = copy0; + break; + } + copy[0] = '\0'; + + modifiers |= FORMAT_SUBSTITUTE; + copy += 2; + break; } /* @@ -734,7 +759,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, *ptr = '\0'; value = ptr + 1; - saved = format_find(ft, copy + 1, modifiers); + found = format_find(ft, copy + 1, modifiers); brackets = 0; for (ptr = ptr + 1; *ptr != '\0'; ptr++) { @@ -748,29 +773,56 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, if (*ptr == '\0') goto fail; - if (saved != NULL && *saved != '\0' && - (saved[0] != '0' || saved[1] != '\0')) { + if (found != NULL && *found != '\0' && + (found[0] != '0' || found[1] != '\0')) { *ptr = '\0'; } else value = ptr + 1; value = format_expand(ft, value); - free(saved); - saved = value; + free(found); } else { - saved = value = format_find(ft, copy, modifiers); + value = format_find(ft, copy, modifiers); if (value == NULL) - saved = value = xstrdup(""); + value = xstrdup(""); + } + + /* Perform substitution if any. */ + if (modifiers & FORMAT_SUBSTITUTE) { + fromlen = strlen(from); + tolen = strlen(to); + + newlen = strlen(value) + 1; + copy = new = xmalloc(newlen); + for (ptr = value; *ptr != '\0'; /* nothing */) { + if (strncmp(ptr, from, fromlen) != 0) { + *new++ = *ptr++; + continue; + } + used = new - copy; + + newlen += tolen; + copy = xrealloc(copy, newlen); + + new = copy + used; + memcpy(new, to, tolen); + + new += tolen; + ptr += fromlen; + } + *new = '\0'; + free(value); + value = copy; } /* Truncate the value if needed. */ if (limit != 0) { - value = trimmed = utf8_trimcstr(value, limit); - free(saved); - saved = trimmed; + new = utf8_trimcstr(value, limit); + free(value); + value = new; } - valuelen = strlen(value); /* Expand the buffer and copy in the value. */ + valuelen = strlen(value); while (*len - *off < valuelen + 1) { *buf = xreallocarray(*buf, 2, *len); *len *= 2; @@ -778,7 +830,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, memcpy(*buf + *off, value, valuelen); *off += valuelen; - free(saved); + free(value); free(copy0); return (0); diff --git a/tmux.1 b/tmux.1 index 3a08c8cd..a22bb3f3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3311,6 +3311,13 @@ prefixes are and .Xr dirname 3 of the variable respectively. +A prefix of the form +.Ql s/foo/bar/: +will substitute +.Ql foo +with +.Ql bar +throughout. .Pp In addition, the first line of a shell command's output may be inserted using .Ql #() . From 577c0e3e5a79e9f1f860487bc3f411d26758f026 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 14:27:44 +0000 Subject: [PATCH 859/949] Use __unused rather than rolling our own. --- alerts.c | 4 ++-- cfg.c | 2 +- client.c | 5 +++-- cmd-kill-server.c | 2 +- cmd-list-buffers.c | 2 +- cmd-list-keys.c | 6 +++--- cmd-lock-server.c | 2 +- cmd-pipe-pane.c | 4 ++-- cmd-set-option.c | 14 +++++++------- cmd-wait-for.c | 4 ++-- control-notify.c | 8 ++++---- control.c | 2 +- format.c | 8 ++++---- job.c | 5 +++-- log.c | 2 +- names.c | 2 +- proc.c | 4 ++-- screen-write.c | 2 +- server-client.c | 8 ++++---- server-fn.c | 2 +- server.c | 2 +- session.c | 4 ++-- status.c | 4 ++-- tmux.h | 3 --- tty-keys.c | 2 +- tty.c | 8 ++++---- window-choose.c | 4 ++-- window-clock.c | 8 ++++---- window-copy.c | 6 +++--- window.c | 8 ++++---- 30 files changed, 68 insertions(+), 69 deletions(-) diff --git a/alerts.c b/alerts.c index f1477030..32e0d496 100644 --- a/alerts.c +++ b/alerts.c @@ -35,7 +35,7 @@ int alerts_check_silence(struct session *, struct winlink *); void alerts_ring_bell(struct session *); void -alerts_timer(unused int fd, unused short events, void *arg) +alerts_timer(__unused int fd, __unused short events, void *arg) { struct window *w = arg; @@ -45,7 +45,7 @@ alerts_timer(unused int fd, unused short events, void *arg) } void -alerts_callback(unused int fd, unused short events, unused void *arg) +alerts_callback(__unused int fd, __unused short events, __unused void *arg) { struct window *w; struct session *s; diff --git a/cfg.c b/cfg.c index 2f457e6b..136c94d1 100644 --- a/cfg.c +++ b/cfg.c @@ -133,7 +133,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) } void -cfg_default_done(unused struct cmd_q *cmdq) +cfg_default_done(__unused struct cmd_q *cmdq) { if (--cfg_references != 0) return; diff --git a/client.c b/client.c index bc934d33..53a98a30 100644 --- a/client.c +++ b/client.c @@ -410,7 +410,8 @@ client_send_identify(const char *ttynam, const char *cwd) /* Callback for client stdin read events. */ void -client_stdin_callback(unused int fd, unused short events, unused void *arg) +client_stdin_callback(__unused int fd, __unused short events, + __unused void *arg) { struct msg_stdin_data data; @@ -512,7 +513,7 @@ client_signal(int sig) /* Callback for client read events. */ void -client_dispatch(struct imsg *imsg, unused void *arg) +client_dispatch(struct imsg *imsg, __unused void *arg) { if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 07d94302..4107e6b6 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -46,7 +46,7 @@ const struct cmd_entry cmd_start_server_entry = { }; enum cmd_retval -cmd_kill_server_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_kill_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { if (self->entry == &cmd_kill_server_entry) kill(getpid(), SIGTERM); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 3a8a790a..a0036032 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_list_buffers_entry = { }; enum cmd_retval -cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) +cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 3b6afa3e..4355f24e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -30,7 +30,7 @@ enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_list_keys_commands(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_list_keys_commands(struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { "list-keys", "lsk", @@ -60,7 +60,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) - return (cmd_list_keys_commands(self, cmdq)); + return (cmd_list_keys_commands(cmdq)); if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); @@ -178,7 +178,7 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_list_keys_commands(unused struct cmd *self, struct cmd_q *cmdq) +cmd_list_keys_commands(struct cmd_q *cmdq) { const struct cmd_entry **entryp; const struct cmd_entry *entry; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index de76475d..a0204129 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -51,7 +51,7 @@ const struct cmd_entry cmd_lock_client_entry = { }; enum cmd_retval -cmd_lock_server_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_lock_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index df706432..87c802b9 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -142,8 +142,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } void -cmd_pipe_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +cmd_pipe_pane_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { struct window_pane *wp = data; diff --git a/cmd-set-option.c b/cmd-set-option.c index c5a77b45..894f0c43 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -350,7 +350,7 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, /* Set a string option. */ struct options_entry * -cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, +cmd_set_option_string(struct cmd *self, __unused struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -372,7 +372,7 @@ cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, /* Set a number option. */ struct options_entry * -cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_number(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -390,7 +390,7 @@ cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, /* Set a key option. */ struct options_entry * -cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_key(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -406,7 +406,7 @@ cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, /* Set a colour option. */ struct options_entry * -cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_colour(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -422,7 +422,7 @@ cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, /* Set an attributes option. */ struct options_entry * -cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_attributes(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -438,7 +438,7 @@ cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, /* Set a flag option. */ struct options_entry * -cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_flag(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -466,7 +466,7 @@ cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, /* Set a choice option. */ struct options_entry * -cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_choice(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 79e0b55e..04316d5e 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -111,7 +111,7 @@ cmd_wait_for_remove(struct wait_channel *wc) } enum cmd_retval -cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *name = args->argv[0]; @@ -130,7 +130,7 @@ cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) } enum cmd_retval -cmd_wait_for_signal(unused struct cmd_q *cmdq, const char *name, +cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq, *wq1; diff --git a/control-notify.c b/control-notify.c index 16a98e3a..db3648a4 100644 --- a/control-notify.c +++ b/control-notify.c @@ -101,7 +101,7 @@ control_notify_window_layout_changed(struct window *w) } void -control_notify_window_unlinked(unused struct session *s, struct window *w) +control_notify_window_unlinked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; @@ -119,7 +119,7 @@ control_notify_window_unlinked(unused struct session *s, struct window *w) } void -control_notify_window_linked(unused struct session *s, struct window *w) +control_notify_window_linked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; @@ -183,7 +183,7 @@ control_notify_session_renamed(struct session *s) } void -control_notify_session_created(unused struct session *s) +control_notify_session_created(__unused struct session *s) { struct client *c; @@ -196,7 +196,7 @@ control_notify_session_created(unused struct session *s) } void -control_notify_session_close(unused struct session *s) +control_notify_session_close(__unused struct session *s) { struct client *c; diff --git a/control.c b/control.c index f7264944..e799a4cb 100644 --- a/control.c +++ b/control.c @@ -51,7 +51,7 @@ control_write_buffer(struct client *c, struct evbuffer *buffer) /* Control input callback. Read lines and fire commands. */ void -control_callback(struct client *c, int closed, unused void *data) +control_callback(struct client *c, int closed, __unused void *data) { char *line, *cause; struct cmd_list *cmdlist; diff --git a/format.c b/format.c index e8d5dd09..50fa7dea 100644 --- a/format.c +++ b/format.c @@ -259,7 +259,7 @@ format_job_get(struct format_tree *ft, const char *cmd) /* Remove old jobs. */ void -format_job_timer(unused int fd, unused short events, unused void *arg) +format_job_timer(__unused int fd, __unused short events, __unused void *arg) { struct format_job *fj, *fj1; time_t now; @@ -288,7 +288,7 @@ format_job_timer(unused int fd, unused short events, unused void *arg) /* Callback for host. */ void -format_cb_host(unused struct format_tree *ft, struct format_entry *fe) +format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1]; @@ -300,7 +300,7 @@ format_cb_host(unused struct format_tree *ft, struct format_entry *fe) /* Callback for host_short. */ void -format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) +format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1], *cp; @@ -315,7 +315,7 @@ format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) /* Callback for pid. */ void -format_cb_pid(unused struct format_tree *ft, struct format_entry *fe) +format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) { xasprintf(&fe->value, "%ld", (long)getpid()); } diff --git a/job.c b/job.c index 0aee7b05..e9799956 100644 --- a/job.c +++ b/job.c @@ -150,7 +150,7 @@ job_free(struct job *job) /* Called when output buffer falls below low watermark (default is 0). */ void -job_write_callback(unused struct bufferevent *bufev, void *data) +job_write_callback(__unused struct bufferevent *bufev, void *data) { struct job *job = data; size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); @@ -166,7 +166,8 @@ job_write_callback(unused struct bufferevent *bufev, void *data) /* Job buffer error callback. */ void -job_callback(unused struct bufferevent *bufev, unused short events, void *data) +job_callback(__unused struct bufferevent *bufev, __unused short events, + void *data) { struct job *job = data; diff --git a/log.c b/log.c index cf65c9e3..6c7be8b2 100644 --- a/log.c +++ b/log.c @@ -33,7 +33,7 @@ void log_vwrite(const char *, va_list); /* Log callback for libevent. */ void -log_event_cb(unused int severity, const char *msg) +log_event_cb(__unused int severity, const char *msg) { log_debug("%s", msg); } diff --git a/names.c b/names.c index 0e806ca0..7d956db3 100644 --- a/names.c +++ b/names.c @@ -29,7 +29,7 @@ void name_time_callback(int, short, void *); int name_time_expired(struct window *, struct timeval *); void -name_time_callback(unused int fd, unused short events, void *arg) +name_time_callback(__unused int fd, __unused short events, void *arg) { struct window *w = arg; diff --git a/proc.c b/proc.c index 6b84fc00..31fd136b 100644 --- a/proc.c +++ b/proc.c @@ -53,7 +53,7 @@ static int peer_check_version(struct tmuxpeer *, struct imsg *); static void proc_update_event(struct tmuxpeer *); static void -proc_event_cb(unused int fd, short events, void *arg) +proc_event_cb(__unused int fd, short events, void *arg) { struct tmuxpeer *peer = arg; ssize_t n; @@ -101,7 +101,7 @@ proc_event_cb(unused int fd, short events, void *arg) } static void -proc_signal_cb(int signo, unused short events, void *arg) +proc_signal_cb(int signo, __unused short events, void *arg) { struct tmuxproc *tp = arg; diff --git a/screen-write.c b/screen-write.c index a887b0f1..53067efe 100644 --- a/screen-write.c +++ b/screen-write.c @@ -42,7 +42,7 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, /* Finish writing. */ void -screen_write_stop(unused struct screen_write_ctx *ctx) +screen_write_stop(__unused struct screen_write_ctx *ctx) { } diff --git a/server-client.c b/server-client.c index 645492d1..04929dec 100644 --- a/server-client.c +++ b/server-client.c @@ -246,7 +246,7 @@ server_client_unref(struct client *c) /* Free dead client. */ void -server_client_free(unused int fd, unused short events, void *arg) +server_client_free(__unused int fd, __unused short events, void *arg) { struct client *c = arg; @@ -818,7 +818,7 @@ server_client_reset_state(struct client *c) /* Repeat time callback. */ void -server_client_repeat_timer(unused int fd, unused short events, void *data) +server_client_repeat_timer(__unused int fd, __unused short events, void *data) { struct client *c = data; @@ -1214,7 +1214,7 @@ server_client_dispatch_shell(struct client *c) /* Event callback to push more stdout data if any left. */ static void -server_client_stdout_cb(unused int fd, unused short events, void *arg) +server_client_stdout_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; @@ -1255,7 +1255,7 @@ server_client_push_stdout(struct client *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) +server_client_stderr_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; diff --git a/server-fn.c b/server-fn.c index bded7246..3e4b6116 100644 --- a/server-fn.c +++ b/server-fn.c @@ -443,7 +443,7 @@ server_clear_identify(struct client *c) } void -server_callback_identify(unused int fd, unused short events, void *data) +server_callback_identify(__unused int fd, __unused short events, void *data) { struct client *c = data; diff --git a/server.c b/server.c index 2808c0cc..55a53a40 100644 --- a/server.c +++ b/server.c @@ -298,7 +298,7 @@ server_update_socket(void) /* Callback for server socket. */ void -server_accept(int fd, short events, unused void *data) +server_accept(int fd, short events, __unused void *data) { struct sockaddr_storage sa; socklen_t slen = sizeof sa; diff --git a/session.c b/session.c index deb29d18..c5721c9a 100644 --- a/session.c +++ b/session.c @@ -181,7 +181,7 @@ session_unref(struct session *s) /* Free session. */ void -session_free(unused int fd, unused short events, void *arg) +session_free(__unused int fd, __unused short events, void *arg) { struct session *s = arg; @@ -236,7 +236,7 @@ session_check_name(const char *name) /* Lock session if it has timed out. */ void -session_lock_timer(unused int fd, unused short events, void *arg) +session_lock_timer(__unused int fd, __unused short events, void *arg) { struct session *s = arg; diff --git a/status.c b/status.c index 326e5ad8..e09ac592 100644 --- a/status.c +++ b/status.c @@ -145,7 +145,7 @@ status_prompt_save_history(void) /* Status timer callback. */ void -status_timer_callback(unused int fd, unused short events, void *arg) +status_timer_callback(__unused int fd, __unused short events, void *arg) { struct client *c = arg; struct session *s = c->session; @@ -604,7 +604,7 @@ status_message_clear(struct client *c) /* Clear status line message after timer expires. */ void -status_message_callback(unused int fd, unused short event, void *data) +status_message_callback(__unused int fd, __unused short event, void *data) { struct client *c = data; diff --git a/tmux.h b/tmux.h index 421f2369..b6cdcd80 100644 --- a/tmux.h +++ b/tmux.h @@ -69,9 +69,6 @@ struct tmuxproc; #define READ_BACKOFF 512 #define READ_TIME 100 -/* Definition to shut gcc up about unused arguments. */ -#define unused __attribute__ ((unused)) - /* Attribute to make gcc check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) diff --git a/tty-keys.c b/tty-keys.c index 52dae4f7..706dcc30 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -635,7 +635,7 @@ discard_key: /* Key timer callback. */ void -tty_keys_callback(unused int fd, unused short events, void *data) +tty_keys_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; diff --git a/tty.c b/tty.c index e67ebbb6..13ca7f8e 100644 --- a/tty.c +++ b/tty.c @@ -172,7 +172,7 @@ tty_open(struct tty *tty, char **cause) } void -tty_read_callback(unused struct bufferevent *bufev, void *data) +tty_read_callback(__unused struct bufferevent *bufev, void *data) { struct tty *tty = data; @@ -181,8 +181,8 @@ tty_read_callback(unused struct bufferevent *bufev, void *data) } void -tty_error_callback( - unused struct bufferevent *bufev, unused short what, unused void *data) +tty_error_callback(__unused struct bufferevent *bufev, __unused short what, + __unused void *data) { } @@ -591,7 +591,7 @@ tty_repeat_space(struct tty *tty, u_int n) * pane. */ int -tty_large_region(unused struct tty *tty, const struct tty_ctx *ctx) +tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; diff --git a/window-choose.c b/window-choose.c index 5f406c78..cab3e5d8 100644 --- a/window-choose.c +++ b/window-choose.c @@ -509,8 +509,8 @@ window_choose_get_item(struct window_pane *wp, key_code key, } void -window_choose_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, key_code key, struct mouse_event *m) +window_choose_key(struct window_pane *wp, __unused struct client *c, + __unused struct session *sess, key_code key, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; diff --git a/window-clock.c b/window-clock.c index 51f97250..e8451f22 100644 --- a/window-clock.c +++ b/window-clock.c @@ -120,7 +120,7 @@ const char window_clock_table[14][5][5] = { }; void -window_clock_timer_callback(unused int fd, unused short events, void *arg) +window_clock_timer_callback(__unused int fd, __unused short events, void *arg) { struct window_pane *wp = arg; struct window_clock_mode_data *data = wp->modedata; @@ -185,9 +185,9 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_clock_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, unused key_code key, - unused struct mouse_event *m) +window_clock_key(struct window_pane *wp, __unused struct client *c, + __unused struct session *sess, __unused key_code key, + __unused struct mouse_event *m) { window_pane_reset_mode(wp); } diff --git a/window-copy.c b/window-copy.c index a28cdecc..37025302 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2232,7 +2232,7 @@ window_copy_rectangle_toggle(struct window_pane *wp) } void -window_copy_start_drag(struct client *c, unused struct mouse_event *m) +window_copy_start_drag(struct client *c, struct mouse_event *m) { struct window_pane *wp; u_int x, y; @@ -2253,7 +2253,7 @@ window_copy_start_drag(struct client *c, unused struct mouse_event *m) } void -window_copy_drag_update(unused struct client *c, struct mouse_event *m) +window_copy_drag_update(__unused struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_copy_mode_data *data; @@ -2274,7 +2274,7 @@ window_copy_drag_update(unused struct client *c, struct mouse_event *m) } void -window_copy_drag_release(unused struct client *c, struct mouse_event *m) +window_copy_drag_release(__unused struct client *c, struct mouse_event *m) { struct window_pane *wp; diff --git a/window.c b/window.c index c5f9f176..8ee517a1 100644 --- a/window.c +++ b/window.c @@ -916,13 +916,13 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, } void -window_pane_timer_callback(unused int fd, unused short events, void *data) +window_pane_timer_callback(__unused int fd, __unused short events, void *data) { window_pane_read_callback(NULL, data); } void -window_pane_read_callback(unused struct bufferevent *bufev, void *data) +window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; struct evbuffer *evb = wp->event->input; @@ -968,8 +968,8 @@ start_timer: } void -window_pane_error_callback(unused struct bufferevent *bufev, unused short what, - void *data) +window_pane_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { struct window_pane *wp = data; From 8b4b3ff4fcc73578f66697b158b56fdc28895368 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 14:37:08 +0000 Subject: [PATCH 860/949] Add __unused, will be needed shortly. --- compat.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compat.h b/compat.h index 7d9b14fd..6812e9a5 100644 --- a/compat.h +++ b/compat.h @@ -21,6 +21,9 @@ #define __attribute__(a) #endif +#ifndef __unused +#define __unused __attribute__ ((__unused__)) +#endif #ifndef __dead #define __dead __attribute__ ((__noreturn__)) #endif From c15487318b08a88aebc9cf583cbdc591b1bc34c8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 16:45:44 +0000 Subject: [PATCH 861/949] unused -> __unused. --- osdep-aix.c | 2 +- osdep-cygwin.c | 2 +- osdep-darwin.c | 4 ++-- osdep-hpux.c | 4 ++-- osdep-linux.c | 2 +- osdep-unknown.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osdep-aix.c b/osdep-aix.c index 0a3d12e4..ef7d6c7e 100644 --- a/osdep-aix.c +++ b/osdep-aix.c @@ -26,7 +26,7 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, char *tty) +osdep_get_name(__unused int fd, char *tty) { struct psinfo p; char *path; diff --git a/osdep-cygwin.c b/osdep-cygwin.c index 91bc5fc9..9a3ea408 100644 --- a/osdep-cygwin.c +++ b/osdep-cygwin.c @@ -27,7 +27,7 @@ #include "tmux.h" char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { FILE *f; char *path, *buf; diff --git a/osdep-darwin.c b/osdep-darwin.c index 7be70375..40b18951 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -28,10 +28,10 @@ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); -#define unused __attribute__ ((unused)) +#define __unused __attribute__ ((__unused__)) char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { struct proc_bsdinfo bsdinfo; pid_t pgrp; diff --git a/osdep-hpux.c b/osdep-hpux.c index 4baa6d49..a6d75f94 100644 --- a/osdep-hpux.c +++ b/osdep-hpux.c @@ -23,13 +23,13 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } char * -osdep_get_cwd(unused int fd) +osdep_get_cwd(__unused int fd) { return (NULL); } diff --git a/osdep-linux.c b/osdep-linux.c index ad4c11cd..00501efb 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -28,7 +28,7 @@ #include "tmux.h" char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { FILE *f; char *path, *buf; diff --git a/osdep-unknown.c b/osdep-unknown.c index 4de1ee1e..9465db97 100644 --- a/osdep-unknown.c +++ b/osdep-unknown.c @@ -23,7 +23,7 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } From 82760a9960850ad33db050f3bec1ab84c55c2e30 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 16:49:13 +0000 Subject: [PATCH 862/949] Use format_expand_time for display-message. --- cmd-display-message.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index ee9eafe9..1f9f40fa 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -54,9 +54,6 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; char *msg; struct format_tree *ft; - char out[BUFSIZ]; - time_t t; - size_t len; if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); @@ -94,11 +91,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) ft = format_create(); format_defaults(ft, c, s, wl, wp); - t = time(NULL); - len = strftime(out, sizeof out, template, localtime(&t)); - out[len] = '\0'; - - msg = format_expand(ft, out); + msg = format_expand_time(ft, template, time(NULL)); if (args_has(self->args, 'p')) cmdq_print(cmdq, "%s", msg); else From 98967c5ec97feef99f8278df9c3b993bbff9c0d5 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Nov 2015 14:55:25 +0000 Subject: [PATCH 863/949] The activity flag could already be set, so queue the callback always (if not already queued) rather than only if the flag is being added. Fixes a problem reported by tim@ --- alerts.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/alerts.c b/alerts.c index 32e0d496..e08b183d 100644 --- a/alerts.c +++ b/alerts.c @@ -132,15 +132,15 @@ alerts_queue(struct window *w, int flags) if (!event_initialized(&w->alerts_timer)) evtimer_set(&w->alerts_timer, alerts_timer, w); - if (w->flags & flags) - return; - w->flags |= flags; - log_debug("@%u alerts flags added %#x", w->id, flags); + if (!alerts_fired) { + w->flags |= flags; + log_debug("@%u alerts flags added %#x", w->id, flags); - if (!alerts_fired && alerts_enabled(w, flags)) { - log_debug("alerts check queued (by @%u)", w->id); - event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); - alerts_fired = 1; + if (alerts_enabled(w, flags)) { + log_debug("alerts check queued (by @%u)", w->id); + event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); + alerts_fired = 1; + } } } From 374e273df5f66e90e06f60617495446facfbd4f3 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Nov 2015 22:46:46 +0000 Subject: [PATCH 864/949] Only assume pasting with at least two characters, reduces problems for people who can type ^B c very fast, or who are using tmux inside something else that buffers. --- server-client.c | 12 ++++++++++-- tmux.h | 7 ++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index 04929dec..64426a70 100644 --- a/server-client.c +++ b/server-client.c @@ -494,8 +494,16 @@ server_client_assume_paste(struct session *s) return (0); timersub(&s->activity_time, &s->last_activity_time, &tv); - if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) - return (1); + if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) { + log_debug("session %s pasting (flag %d)", s->name, + !!(s->flags & SESSION_PASTING)); + if (s->flags & SESSION_PASTING) + return (1); + s->flags |= SESSION_PASTING; + return (0); + } + log_debug("session %s not pasting", s->name); + s->flags &= ~SESSION_PASTING; return (0); } diff --git a/tmux.h b/tmux.h index b6cdcd80..d58ee8c9 100644 --- a/tmux.h +++ b/tmux.h @@ -33,6 +33,8 @@ #include #include +#include "xmalloc.h" + extern char *__progname; extern char **environ; @@ -45,8 +47,6 @@ struct session; struct tmuxpeer; struct tmuxproc; -#include "xmalloc.h" - /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" @@ -1014,6 +1014,7 @@ struct session { struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ +#define SESSION_PASTING 0x2 int flags; u_int attached; @@ -1147,7 +1148,7 @@ struct tty { struct tty_key *key_tree; }; -/* TTY command context and function pointer. */ +/* TTY command context. */ struct tty_ctx { struct window_pane *wp; From fce56c56efc57e80cc3ed755c192cf88822ebf0e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Nov 2015 12:01:19 +0000 Subject: [PATCH 865/949] Instead of separate tables for different types of options, give each option a scope type (server, session, window) in one table. --- cmd-set-option.c | 14 ++-- cmd-show-options.c | 45 ++++++------ options-table.c | 175 +++++++++++++++++++++++++++++++++++---------- status.c | 14 +--- tmux.c | 6 +- tmux.h | 32 +++++---- 6 files changed, 190 insertions(+), 96 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 894f0c43..7de91aa2 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -84,7 +84,7 @@ enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - const struct options_table_entry *table, *oe; + const struct options_table_entry *oe; struct session *s; struct winlink *wl; struct client *c; @@ -108,8 +108,8 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (cmd_set_option_user(self, cmdq, optstr, valstr)); /* Find the option entry, try each table. */ - table = oe = NULL; - if (options_table_find(optstr, &table, &oe) != 0) { + oe = NULL; + if (options_table_find(optstr, &oe) != 0) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "ambiguous option: %s", optstr); return (CMD_RETURN_ERROR); @@ -124,10 +124,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - /* Work out the tree from the table. */ - if (table == server_options_table) + /* Work out the tree from the scope of the option. */ + if (oe->scope == OPTIONS_TABLE_SERVER) oo = global_options; - else if (table == window_options_table) { + else if (oe->scope == OPTIONS_TABLE_WINDOW) { if (args_has(self->args, 'g')) oo = global_w_options; else { @@ -141,7 +141,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) } oo = wl->window->options; } - } else if (table == session_options_table) { + } else if (oe->scope == OPTIONS_TABLE_SESSION) { if (args_has(self->args, 'g')) oo = global_s_options; else { diff --git a/cmd-show-options.c b/cmd-show-options.c index e99d665f..fd96a0e2 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -32,7 +32,7 @@ enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_show_options_one(struct cmd *, struct cmd_q *, struct options *, int); enum cmd_retval cmd_show_options_all(struct cmd *, struct cmd_q *, - const struct options_table_entry *, struct options *); + struct options *, enum options_table_scope); const struct cmd_entry cmd_show_options_entry = { "show-options", "show", @@ -53,19 +53,19 @@ const struct cmd_entry cmd_show_window_options_entry = { enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct session *s; - struct winlink *wl; - const struct options_table_entry *table; - struct options *oo; - int quiet; + struct args *args = self->args; + struct session *s; + struct winlink *wl; + struct options *oo; + int quiet; + enum options_table_scope scope; if (args_has(self->args, 's')) { oo = global_options; - table = server_options_table; + scope = OPTIONS_TABLE_SERVER; } else if (args_has(self->args, 'w') || self->entry == &cmd_show_window_options_entry) { - table = window_options_table; + scope = OPTIONS_TABLE_WINDOW; if (args_has(self->args, 'g')) oo = global_w_options; else { @@ -75,7 +75,7 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) oo = wl->window->options; } } else { - table = session_options_table; + scope = OPTIONS_TABLE_SESSION; if (args_has(self->args, 'g')) oo = global_s_options; else { @@ -88,7 +88,7 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) quiet = args_has(self->args, 'q'); if (args->argc == 0) - return (cmd_show_options_all(self, cmdq, table, oo)); + return (cmd_show_options_all(self, cmdq, oo, scope)); else return (cmd_show_options_one(self, cmdq, oo, quiet)); } @@ -99,7 +99,7 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, { struct args *args = self->args; const char *name = args->argv[0]; - const struct options_table_entry *table, *oe; + const struct options_table_entry *oe; struct options_entry *o; const char *optval; @@ -118,14 +118,14 @@ retry: return (CMD_RETURN_NORMAL); } - table = oe = NULL; - if (options_table_find(name, &table, &oe) != 0) { + oe = NULL; + if (options_table_find(name, &oe) != 0) { cmdq_error(cmdq, "ambiguous option: %s", name); return (CMD_RETURN_ERROR); } if (oe == NULL) { if (quiet) - return (CMD_RETURN_NORMAL); + return (CMD_RETURN_NORMAL); cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } @@ -144,12 +144,13 @@ retry: } enum cmd_retval -cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *table, struct options *oo) +cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, struct options *oo, + enum options_table_scope scope) { const struct options_table_entry *oe; struct options_entry *o; const char *optval; + int vflag; o = options_first(oo); while (o != NULL) { @@ -162,14 +163,14 @@ cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, o = options_next(o); } - for (oe = table; oe->name != NULL; oe++) { - if (oe->style != NULL) + vflag = args_has(self->args, 'v'); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->style != NULL || oe->scope != scope) continue; if ((o = options_find1(oo, oe->name)) == NULL) continue; - optval = options_table_print_entry(oe, o, - args_has(self->args, 'v')); - if (args_has(self->args, 'v')) + optval = options_table_print_entry(oe, o, vflag); + if (vflag) cmdq_print(cmdq, "%s", optval); else cmdq_print(cmdq, "%s %s", oe->name, optval); diff --git a/options-table.c b/options-table.c index cf4e2d14..a0e65ff4 100644 --- a/options-table.c +++ b/options-table.c @@ -53,9 +53,10 @@ const char *options_table_bell_action_list[] = { }; /* Server options. */ -const struct options_table_entry server_options_table[] = { +const struct options_table_entry options_table[] = { { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = INT_MAX, .default_num = 20 @@ -63,11 +64,13 @@ const struct options_table_entry server_options_table[] = { { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "screen" }, { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 500 @@ -75,21 +78,25 @@ const struct options_table_entry server_options_table[] = { { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "history-file", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 100 @@ -97,28 +104,27 @@ const struct options_table_entry server_options_table[] = { { .name = "quiet", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "set-clipboard", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 1 }, { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" }, - { .name = NULL } -}; - -/* Session options. */ -const struct options_table_entry session_options_table[] = { { .name = "assume-paste-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 1, @@ -126,6 +132,7 @@ const struct options_table_entry session_options_table[] = { { .name = "base-index", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -133,47 +140,56 @@ const struct options_table_entry session_options_table[] = { { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, .default_num = BELL_ANY }, { .name = "bell-on-alert", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "" }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = _PATH_BSHELL }, { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 4 }, { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 1000 @@ -181,6 +197,7 @@ const struct options_table_entry session_options_table[] = { { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 750 @@ -188,6 +205,7 @@ const struct options_table_entry session_options_table[] = { { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 2000 @@ -195,6 +213,7 @@ const struct options_table_entry session_options_table[] = { { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -202,77 +221,91 @@ const struct options_table_entry session_options_table[] = { { .name = "lock-command", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "lock -np" }, { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 3, .style = "message-style" }, { .name = "message-command-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 3, .style = "message-command-style" }, { .name = "message-command-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=black,fg=yellow" }, { .name = "message-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black" }, { .name = "mouse", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "prefix", .type = OPTIONS_TABLE_KEY, + .scope = OPTIONS_TABLE_SESSION, .default_num = '\002', }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, + .scope = OPTIONS_TABLE_SESSION, .default_num = KEYC_NONE, }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "repeat-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 500 @@ -280,44 +313,52 @@ const struct options_table_entry session_options_table[] = { { .name = "set-remain-on-exit", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "status", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-style" }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 2, .style = "status-style" }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-style" }, { .name = "status-interval", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 15 @@ -325,41 +366,48 @@ const struct options_table_entry session_options_table[] = { { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_justify_list, .default_num = 0 }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_keys_list, .default_num = MODEKEY_EMACS }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "[#S] " }, { .name = "status-left-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-left-style" }, { .name = "status-left-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-left-style" }, { .name = "status-left-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-left-style" }, { .name = "status-left-length", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 10 @@ -367,40 +415,47 @@ const struct options_table_entry session_options_table[] = { { .name = "status-left-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_position_list, .default_num = 1 }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-right-style" }, { .name = "status-right-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-right-style" }, { .name = "status-right-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-right-style" }, { .name = "status-right-length", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 40 @@ -408,16 +463,19 @@ const struct options_table_entry session_options_table[] = { { .name = "status-right-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=green,fg=black" }, { .name = "update-environment", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " "SSH_CONNECTION WINDOWID XAUTHORITY" @@ -425,68 +483,75 @@ const struct options_table_entry session_options_table[] = { { .name = "visual-activity", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-bell", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-silence", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = " -_@" }, - { .name = NULL } -}; - -/* Window options. */ -const struct options_table_entry window_options_table[] = { { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "automatic-rename", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "automatic-rename-format", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" "#{?pane_dead,[dead],}" }, { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 4 }, { .name = "clock-mode-style", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_clock_mode_style_list, .default_num = 1 }, { .name = "force-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -494,6 +559,7 @@ const struct options_table_entry window_options_table[] = { { .name = "force-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -501,6 +567,7 @@ const struct options_table_entry window_options_table[] = { { .name = "main-pane-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 24 @@ -508,6 +575,7 @@ const struct options_table_entry window_options_table[] = { { .name = "main-pane-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 80 @@ -515,40 +583,47 @@ const struct options_table_entry window_options_table[] = { { .name = "mode-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "mode-style" }, { .name = "mode-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 3, .style = "mode-style" }, { .name = "mode-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "mode-style" }, { .name = "mode-keys", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_mode_keys_list, .default_num = MODEKEY_EMACS }, { .name = "mode-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=yellow,fg=black" }, { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "monitor-silence", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -556,6 +631,7 @@ const struct options_table_entry window_options_table[] = { { .name = "other-pane-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -563,6 +639,7 @@ const struct options_table_entry window_options_table[] = { { .name = "other-pane-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -570,23 +647,27 @@ const struct options_table_entry window_options_table[] = { { .name = "pane-active-border-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-active-border-style" }, { .name = "pane-active-border-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 2, .style = "pane-active-border-style" }, { .name = "pane-active-border-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "fg=green" }, { .name = "pane-base-index", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = USHRT_MAX, .default_num = 0 @@ -594,178 +675,210 @@ const struct options_table_entry window_options_table[] = { { .name = "pane-border-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-border-style" }, { .name = "pane-border-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-border-style" }, { .name = "pane-border-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-activity-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = GRID_ATTR_REVERSE, .style = "window-status-activity-style" }, { .name = "window-status-activity-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-activity-style" }, { .name = "window-status-activity-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-activity-style" }, { .name = "window-status-activity-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-style" }, { .name = "window-status-bell-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = GRID_ATTR_REVERSE, .style = "window-status-bell-style" }, { .name = "window-status-bell-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-bell-style" }, { .name = "window-status-bell-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-bell-style" }, { .name = "window-status-bell-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-style" }, { .name = "window-status-current-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-current-style" }, { .name = "window-status-current-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-current-style" }, { .name = "window-status-current-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-current-style" }, { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-current-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-style" }, { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-last-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-last-style" }, { .name = "window-status-last-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-last-style" }, { .name = "window-status-last-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-last-style" }, { .name = "window-status-last-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = " " }, { .name = "window-status-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "xterm-keys", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, @@ -774,12 +887,13 @@ const struct options_table_entry window_options_table[] = { /* Populate an options tree from a table. */ void -options_table_populate_tree( - const struct options_table_entry *table, struct options *oo) +options_table_populate_tree(enum options_table_scope scope, struct options *oo) { const struct options_table_entry *oe; - for (oe = table; oe->name != NULL; oe++) { + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope != scope) + continue; switch (oe->type) { case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); @@ -845,33 +959,22 @@ options_table_print_entry(const struct options_table_entry *oe, /* Find an option. */ int -options_table_find( - const char *optstr, const struct options_table_entry **table, - const struct options_table_entry **oe) +options_table_find(const char *optstr, const struct options_table_entry **oe) { - static const struct options_table_entry *tables[] = { - server_options_table, - window_options_table, - session_options_table - }; const struct options_table_entry *oe_loop; - u_int i; - for (i = 0; i < nitems(tables); i++) { - for (oe_loop = tables[i]; oe_loop->name != NULL; oe_loop++) { - if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) - continue; + for (oe_loop = options_table; oe_loop->name != NULL; oe_loop++) { + if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) + continue; - /* If already found, ambiguous. */ - if (*oe != NULL) - return (-1); - *oe = oe_loop; - *table = tables[i]; + /* If already found, ambiguous. */ + if (*oe != NULL) + return (-1); + *oe = oe_loop; - /* Bail now if an exact match. */ - if (strcmp((*oe)->name, optstr) == 0) - break; - } + /* Bail now if an exact match. */ + if (strcmp(oe_loop->name, optstr) == 0) + break; } return (0); } diff --git a/status.c b/status.c index e09ac592..cbba2ead 100644 --- a/status.c +++ b/status.c @@ -1205,19 +1205,7 @@ status_prompt_complete_list(u_int *size, const char *s) list[(*size)++] = (*cmdent)->name; } } - for (oe = server_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = oe->name; - } - } - for (oe = session_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = oe->name; - } - } - for (oe = window_options_table; oe->name != NULL; oe++) { + for (oe = options_table; oe->name != NULL; oe++) { if (strncmp(oe->name, s, strlen(s)) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = oe->name; diff --git a/tmux.c b/tmux.c index 5429a7cb..9baa8183 100644 --- a/tmux.c +++ b/tmux.c @@ -288,14 +288,14 @@ main(int argc, char **argv) environ_set(global_environ, "PWD", tmp); global_options = options_create(NULL); - options_table_populate_tree(server_options_table, global_options); + options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); global_s_options = options_create(NULL); - options_table_populate_tree(session_options_table, global_s_options); + options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); options_set_string(global_s_options, "default-shell", "%s", getshell()); global_w_options = options_create(NULL); - options_table_populate_tree(window_options_table, global_w_options); + options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { diff --git a/tmux.h b/tmux.h index d58ee8c9..a112e7df 100644 --- a/tmux.h +++ b/tmux.h @@ -1393,19 +1393,25 @@ enum options_table_type { OPTIONS_TABLE_CHOICE, OPTIONS_TABLE_STYLE }; +enum options_table_scope { + OPTIONS_TABLE_SERVER, + OPTIONS_TABLE_SESSION, + OPTIONS_TABLE_WINDOW, +}; struct options_table_entry { - const char *name; - enum options_table_type type; + const char *name; + enum options_table_type type; + enum options_table_scope scope; - u_int minimum; - u_int maximum; - const char **choices; + u_int minimum; + u_int maximum; + const char **choices; - const char *default_str; - long long default_num; + const char *default_str; + long long default_num; - const char *style; + const char *style; }; /* Common command usages. */ @@ -1543,15 +1549,11 @@ struct options_entry *options_set_style(struct options *, const char *, struct grid_cell *options_get_style(struct options *, const char *); /* options-table.c */ -extern const struct options_table_entry server_options_table[]; -extern const struct options_table_entry session_options_table[]; -extern const struct options_table_entry window_options_table[]; -void options_table_populate_tree(const struct options_table_entry *, - struct options *); +extern const struct options_table_entry options_table[]; +void options_table_populate_tree(enum options_table_scope, struct options *); const char *options_table_print_entry(const struct options_table_entry *, struct options_entry *, int); -int options_table_find(const char *, const struct options_table_entry **, - const struct options_table_entry **); +int options_table_find(const char *, const struct options_table_entry **); /* job.c */ extern struct joblist all_jobs; From 40fefe2cbc92012d4ec5261ce71a57a112c5ec12 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Nov 2015 16:33:46 +0000 Subject: [PATCH 866/949] The alerts callback should be fired for bells even if bell-action is none because it also affects the status line bell indicator (and bell-action does not). Fixes a problem reported by tim@. --- alerts.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/alerts.c b/alerts.c index e08b183d..55e7044d 100644 --- a/alerts.c +++ b/alerts.c @@ -77,8 +77,8 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) int alerts_enabled(struct window *w, int flags) { - struct session *s; - + if (flags & WINDOW_BELL) + return (1); if (flags & WINDOW_ACTIVITY) { if (options_get_number(w->options, "monitor-activity")) return (1); @@ -87,14 +87,6 @@ alerts_enabled(struct window *w, int flags) if (options_get_number(w->options, "monitor-silence") != 0) return (1); } - if (~flags & WINDOW_BELL) - return (0); - RB_FOREACH(s, sessions, &sessions) { - if (!session_has(s, w)) - continue; - if (options_get_number(s->options, "bell-action") != BELL_NONE) - return (1); - } return (0); } From 933929cd622478bb43afe590670613da2e9ff359 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Nov 2015 22:02:54 +0000 Subject: [PATCH 867/949] Memory leaks and an uninitialized part of utf8_data, from Patrick Palka. --- cmd-if-shell.c | 1 + cmd-new-window.c | 2 +- utf8.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a9c84261..47f259e7 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -97,6 +97,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[1]; else if (args->argc == 3) cmd = args->argv[2]; + free(shellcmd); if (cmd == NULL) return (CMD_RETURN_NORMAL); if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { diff --git a/cmd-new-window.c b/cmd-new-window.c index fb89e24f..24204746 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -97,7 +97,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cwd = format_expand(ft, args_get(args, 'c')); + cwd = to_free = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; diff --git a/utf8.c b/utf8.c index b391c5ec..2210675a 100644 --- a/utf8.c +++ b/utf8.c @@ -355,6 +355,7 @@ utf8_set(struct utf8_data *ud, u_char ch) u_int i; *ud->data = ch; + ud->have = 1; ud->size = 1; ud->width = 1; From 2c482939fdd92f1bd187cdcc661890dfa4e45512 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 21 Nov 2015 08:03:18 +0000 Subject: [PATCH 868/949] Move tmux.h below system includes. --- compat/closefrom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compat/closefrom.c b/compat/closefrom.c index 591769dd..8c650aa5 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -14,8 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "tmux.h" - #ifndef HAVE_CLOSEFROM #include @@ -47,6 +45,8 @@ # endif #endif +#include "tmux.h" + #ifndef OPEN_MAX # define OPEN_MAX 256 #endif From 4fcc02ee9dde0f11f6a6db77b2ba6271421cea78 Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 22 Nov 2015 18:28:01 +0000 Subject: [PATCH 869/949] If display-time is set to 0, show status messages until a key is pressed; OK nicm@ --- options-table.c | 2 +- status.c | 14 ++++++++------ tmux.1 | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/options-table.c b/options-table.c index a0e65ff4..dc4922ba 100644 --- a/options-table.c +++ b/options-table.c @@ -198,7 +198,7 @@ const struct options_table_entry options_table[] = { { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, - .minimum = 1, + .minimum = 0, .maximum = INT_MAX, .default_num = 750 }, diff --git a/status.c b/status.c index cbba2ead..d05376c0 100644 --- a/status.c +++ b/status.c @@ -574,13 +574,15 @@ status_message_set(struct client *c, const char *fmt, ...) } delay = options_get_number(c->session->options, "display-time"); - tv.tv_sec = delay / 1000; - tv.tv_usec = (delay % 1000) * 1000L; + if (delay > 0) { + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; - if (event_initialized(&c->message_timer)) - evtimer_del(&c->message_timer); - evtimer_set(&c->message_timer, status_message_callback, c); - evtimer_add(&c->message_timer, &tv); + if (event_initialized(&c->message_timer)) + evtimer_del(&c->message_timer); + evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); + } c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_STATUS; diff --git a/tmux.1 b/tmux.1 index a22bb3f3..3fd383d6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2557,6 +2557,7 @@ command appear. .It Ic display-time Ar time Set the amount of time for which status line messages and other on-screen indicators are displayed. +If set to 0, messages and indicators are displayed until a key is pressed. .Ar time is in milliseconds. .It Ic history-limit Ar lines From 01a2ddf3f8d22c58bd8423be69ab0e7843d71652 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 22 Nov 2015 19:41:19 +0000 Subject: [PATCH 870/949] Add getpw to pledge, makes tmux work in YP environments, discovered by matthieu, ok deraadt --- server.c | 4 ++-- tmux.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server.c b/server.c index 55a53a40..e17b9356 100644 --- a/server.c +++ b/server.c @@ -175,8 +175,8 @@ server_start(struct event_base *base, int lockfd, char *lockfile) if (debug_level > 3) tty_create_log(); - if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " - "ps", NULL) != 0) + if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " + "tty ps", NULL) != 0) fatal("pledge failed"); RB_INIT(&windows); diff --git a/tmux.c b/tmux.c index 9baa8183..aa827178 100644 --- a/tmux.c +++ b/tmux.c @@ -255,8 +255,8 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " - "proc exec tty ps", NULL) != 0) + if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " + "recvfd proc exec tty ps", NULL) != 0) err(1, "pledge"); /* From 28e72ae34d43dda28ca0e6dc652eaa1179c351c7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 22 Nov 2015 19:42:57 +0000 Subject: [PATCH 871/949] Don't leak extddata, memset after freeing it, not before. From Patrick Palka. --- grid.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/grid.c b/grid.c index 36cde074..579eb966 100644 --- a/grid.c +++ b/grid.c @@ -368,11 +368,8 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); - memset(gl, 0, sizeof *gl); - free(gl->extddata); - gl->extddata = NULL; - gl->extdsize = 0; + memset(gl, 0, sizeof *gl); } } From 32e510bd70eedbeec8590b9bf786b11430ddaac3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Nov 2015 20:53:09 +0000 Subject: [PATCH 872/949] Remove support for the UTF-8 mouse extension. This was a briefly used, poor idea that was fairly quickly replaced by SGR mouse input (which is now widespread). It is impossible to tell the difference between UTF-8 and non-UTF-8 mouse input; since the mouse-utf8 option was removed tmux has not handled it correctly in any case; and it is ridiculous to have three different forms of mouse input. --- input-keys.c | 6 ------ input.c | 6 ------ screen-write.c | 2 +- server-client.c | 11 ----------- tmux.h | 2 +- tty-keys.c | 36 ++++++++---------------------------- tty.c | 11 +---------- 7 files changed, 11 insertions(+), 63 deletions(-) diff --git a/input-keys.c b/input-keys.c index 2915cb45..4fee2f58 100644 --- a/input-keys.c +++ b/input-keys.c @@ -243,17 +243,11 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) * is because an old style mouse release event cannot be converted into * the new SGR format, since the released button is unknown). Otherwise * pretend that tmux doesn't speak this extension, and fall back to the - * UTF-8 (1005) extension if the application requested, or to the * legacy format. */ if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) { len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); - } else if (wp->screen->mode & MODE_MOUSE_UTF8) { - len = xsnprintf(buf, sizeof buf, "\033[M"); - len += utf8_split2(m->b + 32, &buf[len]); - len += utf8_split2(x + 33, &buf[len]); - len += utf8_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; diff --git a/input.c b/input.c index d8e80afb..b4663d10 100644 --- a/input.c +++ b/input.c @@ -1461,9 +1461,6 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 1004: screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); break; - case 1005: - screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); - break; case 1006: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); break; @@ -1544,9 +1541,6 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); wp->flags |= PANE_FOCUSPUSH; /* force update */ break; - case 1005: - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); - break; case 1006: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); break; diff --git a/screen-write.c b/screen-write.c index 53067efe..0c27945b 100644 --- a/screen-write.c +++ b/screen-write.c @@ -56,7 +56,7 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); - s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); + s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); screen_write_cursormove(ctx, 0, 0); diff --git a/server-client.c b/server-client.c index 64426a70..c5e93438 100644 --- a/server-client.c +++ b/server-client.c @@ -808,17 +808,6 @@ server_client_reset_state(struct client *c) if (options_get_number(oo, "mouse")) mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; - /* - * Set UTF-8 mouse input if required. If the terminal is UTF-8 and any - * mouse mode is in effect, turn on UTF-8 mouse input. If the receiving - * terminal hasn't requested it (that is, it isn't in s->mode), then - * it'll be converted in input_mouse. - */ - if ((c->tty.flags & TTY_UTF8) && (mode & ALL_MOUSE_MODES)) - mode |= MODE_MOUSE_UTF8; - else - mode &= ~MODE_MOUSE_UTF8; - /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode, s); tty_reset(&c->tty); diff --git a/tmux.h b/tmux.h index a112e7df..e697b9f8 100644 --- a/tmux.h +++ b/tmux.h @@ -599,7 +599,7 @@ struct mode_key_table { #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_BLINKING 0x80 -#define MODE_MOUSE_UTF8 0x100 +/* 0x100 unused */ #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 diff --git a/tty-keys.c b/tty-keys.c index 706dcc30..b5e36f8e 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -481,6 +481,7 @@ tty_keys_next(struct tty *tty) /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); len = EVBUFFER_LENGTH(tty->event->input); + if (len == 0) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); @@ -653,10 +654,8 @@ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; - struct utf8_data ud; - u_int i, value, x, y, b, sgr_b; + u_int i, x, y, b, sgr_b; u_char sgr_type, c; - enum utf8_state more; /* * Standard mouse sequences are \033[M followed by three characters @@ -686,8 +685,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (1); /* - * Third byte is M in old standard and UTF-8 extension, < in SGR - * extension. + * Third byte is M in old standard (and UTF-8 extension which we do not + * support), < in SGR extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ @@ -695,32 +694,13 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) for (i = 0; i < 3; i++) { if (len <= *size) return (1); - - if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&ud, buf[*size]) == UTF8_MORE) { - if (ud.size != 2) - return (-1); - (*size)++; - if (len <= *size) - return (1); - more = utf8_append(&ud, buf[*size]); - if (more != UTF8_DONE) - return (-1); - value = utf8_combine(&ud); - } else - value = (u_char)buf[*size]; - (*size)++; - } else { - value = (u_char)buf[*size]; - (*size)++; - } - + c = (u_char)buf[(*size)++]; if (i == 0) - b = value; + b = c; else if (i == 1) - x = value; + x = c; else - y = value; + y = c; } log_debug("mouse input: %.*s", (int)*size, buf); diff --git a/tty.c b/tty.c index 13ca7f8e..42be6d9f 100644 --- a/tty.c +++ b/tty.c @@ -521,21 +521,15 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } tty->cstyle = s->cstyle; } - if (changed & (ALL_MOUSE_MODES|MODE_MOUSE_UTF8)) { + if (changed & ALL_MOUSE_MODES) { if (mode & ALL_MOUSE_MODES) { /* - * Enable the UTF-8 (1005) extension if configured to. * Enable the SGR (1006) extension unconditionally, as * this is safe from misinterpretation. Do it in this * order, because in some terminals it's the last one * that takes effect and SGR is the preferred one. */ - if (mode & MODE_MOUSE_UTF8) - tty_puts(tty, "\033[?1005h"); - else - tty_puts(tty, "\033[?1005l"); tty_puts(tty, "\033[?1006h"); - if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) @@ -545,10 +539,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); - tty_puts(tty, "\033[?1006l"); - if (tty->mode & MODE_MOUSE_UTF8) - tty_puts(tty, "\033[?1005l"); } } if (changed & MODE_KKEYPAD) { From 2adf3f42ee451b30ef492229dac322808f93d8dc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Nov 2015 23:47:57 +0000 Subject: [PATCH 873/949] Partly revert previous, it is harmless to keep support for UTF-8 mouse mode inside tmux, just no longer support it for tmux itself. --- input-keys.c | 6 ++++++ input.c | 6 ++++++ screen-write.c | 2 +- tmux.h | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index 4fee2f58..2915cb45 100644 --- a/input-keys.c +++ b/input-keys.c @@ -243,11 +243,17 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) * is because an old style mouse release event cannot be converted into * the new SGR format, since the released button is unknown). Otherwise * pretend that tmux doesn't speak this extension, and fall back to the + * UTF-8 (1005) extension if the application requested, or to the * legacy format. */ if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) { len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); + } else if (wp->screen->mode & MODE_MOUSE_UTF8) { + len = xsnprintf(buf, sizeof buf, "\033[M"); + len += utf8_split2(m->b + 32, &buf[len]); + len += utf8_split2(x + 33, &buf[len]); + len += utf8_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; diff --git a/input.c b/input.c index b4663d10..d8e80afb 100644 --- a/input.c +++ b/input.c @@ -1461,6 +1461,9 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 1004: screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); break; + case 1005: + screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); + break; case 1006: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); break; @@ -1541,6 +1544,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); wp->flags |= PANE_FOCUSPUSH; /* force update */ break; + case 1005: + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); + break; case 1006: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); break; diff --git a/screen-write.c b/screen-write.c index 0c27945b..53067efe 100644 --- a/screen-write.c +++ b/screen-write.c @@ -56,7 +56,7 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); - s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_SGR); + s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); screen_write_cursormove(ctx, 0, 0); diff --git a/tmux.h b/tmux.h index e697b9f8..a112e7df 100644 --- a/tmux.h +++ b/tmux.h @@ -599,7 +599,7 @@ struct mode_key_table { #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_BLINKING 0x80 -/* 0x100 unused */ +#define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 From b32ce34cf2b5e48eab4f626f378d50f365b594a3 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 09:34:55 +0000 Subject: [PATCH 874/949] Don't allow options in table without scope set. --- options-table.c | 2 ++ tmux.h | 1 + 2 files changed, 3 insertions(+) diff --git a/options-table.c b/options-table.c index dc4922ba..0dab0c0e 100644 --- a/options-table.c +++ b/options-table.c @@ -892,6 +892,8 @@ options_table_populate_tree(enum options_table_scope scope, struct options *oo) const struct options_table_entry *oe; for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope == OPTIONS_TABLE_NONE) + fatalx("no scope for %s", oe->name); if (oe->scope != scope) continue; switch (oe->type) { diff --git a/tmux.h b/tmux.h index a112e7df..591fe54e 100644 --- a/tmux.h +++ b/tmux.h @@ -1394,6 +1394,7 @@ enum options_table_type { OPTIONS_TABLE_STYLE }; enum options_table_scope { + OPTIONS_TABLE_NONE, OPTIONS_TABLE_SERVER, OPTIONS_TABLE_SESSION, OPTIONS_TABLE_WINDOW, From 7b085136a7291cbcdfcc53182fbd13aaca70306e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 24 Nov 2015 18:46:50 +0000 Subject: [PATCH 875/949] -sys/queue.h in proc.c, and nuke the unnecessary C++ header guards stuff and sys/cdefs.h in vis.h (it causes problems on some platforms). Reported by someone on GitHub, issue 212. --- compat/vis.h | 5 ----- proc.c | 1 - 2 files changed, 6 deletions(-) diff --git a/compat/vis.h b/compat/vis.h index 6795139c..9f12d236 100644 --- a/compat/vis.h +++ b/compat/vis.h @@ -73,9 +73,6 @@ */ #define UNVIS_END 1 /* no more characters */ -#include - -__BEGIN_DECLS char *vis(char *, int, int, int); int strvis(char *, const char *, int); int stravis(char **, const char *, int); @@ -85,6 +82,4 @@ int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t); -__END_DECLS - #endif /* !_VIS_H_ */ diff --git a/proc.c b/proc.c index 7aa59cff..3e1e81dd 100644 --- a/proc.c +++ b/proc.c @@ -17,7 +17,6 @@ */ #include -#include #include #include From 4ec61bef461aba6d5849bce971a241850b8d7ef6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 20:40:51 +0000 Subject: [PATCH 876/949] Fix usage of detach-client. --- cmd-detach-client.c | 2 +- tmux.1 | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 813ac032..f7369df0 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -31,7 +31,7 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { "detach-client", "detach", "as:t:P", 0, 0, - "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + "[-aP] [-s target-session] " CMD_TARGET_CLIENT_USAGE, CMD_READONLY, cmd_detach_client_exec }; diff --git a/tmux.1 b/tmux.1 index 3fd383d6..455de33b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -714,8 +714,7 @@ is used, .Ic update-environment option will not be applied. .It Xo Ic detach-client -.Op Fl P -.Op Fl a +.Op Fl aP .Op Fl s Ar target-session .Op Fl t Ar target-client .Xc From 9cccb8c1159b0a4747b5152e2df08e42207b574d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:19:46 +0000 Subject: [PATCH 877/949] Make the log stuff a bit tidier with some helper functions. --- cmd-show-messages.c | 1 - log.c | 35 +++++++++++++++++++++++++++++------ proc.c | 2 +- server.c | 2 +- tmux.c | 15 +-------------- tmux.h | 8 ++++---- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 92ac5cc2..3d2edf9a 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -61,7 +61,6 @@ cmd_show_messages_server(struct cmd_q *cmdq) cmdq_print(cmdq, "started %s", tim); cmdq_print(cmdq, "socket path %s", socket_path); - cmdq_print(cmdq, "debug level %d", debug_level); cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); return (1); diff --git a/log.c b/log.c index 6c7be8b2..46f1673c 100644 --- a/log.c +++ b/log.c @@ -22,30 +22,53 @@ #include #include #include +#include #include #include "tmux.h" -FILE *log_file; +static FILE *log_file; +static int log_level; -void log_event_cb(int, const char *); -void log_vwrite(const char *, va_list); +static void log_event_cb(int, const char *); +static void log_vwrite(const char *, va_list); /* Log callback for libevent. */ -void +static void log_event_cb(__unused int severity, const char *msg) { log_debug("%s", msg); } +/* Increment log level. */ +void +log_add_level(void) +{ + log_level++; +} + +/* Get log level. */ +int +log_get_level(void) +{ + return (log_level); +} + /* Open logging to file. */ void -log_open(const char *path) +log_open(const char *name) { + char *path; + + if (log_level == 0) + return; + if (log_file != NULL) fclose(log_file); + xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid()); log_file = fopen(path, "w"); + free(path); if (log_file == NULL) return; @@ -65,7 +88,7 @@ log_close(void) } /* Write a log message. */ -void +static void log_vwrite(const char *msg, va_list ap) { char *fmt, *out; diff --git a/proc.c b/proc.c index 31fd136b..06340cd9 100644 --- a/proc.c +++ b/proc.c @@ -188,7 +188,7 @@ proc_start(const char *name, struct event_base *base, int forkflag, fatalx("event_reinit failed"); } - logfile(name); + log_open(name); setproctitle("%s (%s)", name, socket_path); log_debug("%s started (%ld): socket %s, protocol %d", name, diff --git a/server.c b/server.c index e17b9356..e9159422 100644 --- a/server.c +++ b/server.c @@ -173,7 +173,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); - if (debug_level > 3) + if (log_get_level() > 3) tty_create_log(); if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " "tty ps", NULL) != 0) diff --git a/tmux.c b/tmux.c index aa827178..f5383fcf 100644 --- a/tmux.c +++ b/tmux.c @@ -44,7 +44,6 @@ struct options *global_w_options; /* window options */ struct environ *global_environ; char *shell_cmd; -int debug_level; time_t start_time; char socket_path[PATH_MAX]; @@ -61,18 +60,6 @@ usage(void) exit(1); } -void -logfile(const char *name) -{ - char *path; - - if (debug_level > 0) { - xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); - log_open(path); - free(path); - } -} - const char * getshell(void) { @@ -243,7 +230,7 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; break; case 'v': - debug_level++; + log_add_level(); break; default: usage(); diff --git a/tmux.h b/tmux.h index 591fe54e..5f469f92 100644 --- a/tmux.h +++ b/tmux.h @@ -1432,10 +1432,8 @@ extern struct options *global_s_options; extern struct options *global_w_options; extern struct environ *global_environ; extern char *shell_cmd; -extern int debug_level; extern time_t start_time; extern char socket_path[PATH_MAX]; -void logfile(const char *); const char *getshell(void); int checkshell(const char *); int areshell(const char *); @@ -2210,8 +2208,10 @@ char *utf8_padcstr(const char *, u_int); char *get_proc_name(int, char *); /* log.c */ -void log_open(const char *); -void log_close(void); +void log_add_level(void); +int log_get_level(void); +void log_open(const char *); +void log_close(void); void printflike(1, 2) log_debug(const char *, ...); __dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatalx(const char *, ...); From bdbbd9711c05507161bc70ccdde91bdb719d943b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:23:44 +0000 Subject: [PATCH 878/949] Show libevent version in showmsgs -I. --- cmd-show-messages.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 3d2edf9a..8cf71f41 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -62,6 +62,8 @@ cmd_show_messages_server(struct cmd_q *cmdq) cmdq_print(cmdq, "started %s", tim); cmdq_print(cmdq, "socket path %s", socket_path); cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); + cmdq_print(cmdq, "libevent %s (%s)", event_get_version(), + event_get_method()); return (1); } From 4e3015a8925f72b35393afec250cc46e4f480641 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:32:36 +0000 Subject: [PATCH 879/949] Log some system and libevent information at startup. --- proc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/proc.c b/proc.c index 06340cd9..593c1b8b 100644 --- a/proc.c +++ b/proc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -170,6 +171,7 @@ proc_start(const char *name, struct event_base *base, int forkflag, void (*signalcb)(int)) { struct tmuxproc *tp; + struct utsname u; if (forkflag) { switch (fork()) { @@ -191,8 +193,13 @@ proc_start(const char *name, struct event_base *base, int forkflag, log_open(name); setproctitle("%s (%s)", name, socket_path); + if (uname(&u) < 0) + memset(&u, 0, sizeof u); + log_debug("%s started (%ld): socket %s, protocol %d", name, (long)getpid(), 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()); tp = xcalloc(1, sizeof *tp); tp->name = xstrdup(name); From 9fd3318dd818c45432581beff5b1a5f6cb55f2ff Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:50:06 +0000 Subject: [PATCH 880/949] All kill-session -C to clear alerts in all windows, suggested by Aaron U'Ren. --- cmd-kill-session.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 74843eb6..3f39c241 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { "kill-session", NULL, - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, + "aCt:", 0, 0, + "[-aC] " CMD_TARGET_SESSION_USAGE, 0, cmd_kill_session_exec }; @@ -42,11 +42,18 @@ cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s, *sloop, *stmp; + struct winlink *wl; if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (args_has(args, 'a')) { + if (args_has(args, 'C')) { + RB_FOREACH(wl, winlinks, &s->windows) { + wl->window->flags &= ~WINDOW_ALERTFLAGS; + wl->flags &= ~WINLINK_ALERTFLAGS; + } + server_redraw_session(s); + } else if (args_has(args, 'a')) { RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { if (sloop != s) { server_destroy_session(sloop); From 1e2df2d46496fc025330c25fa08e83d14e62d11b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:52:06 +0000 Subject: [PATCH 881/949] Remove the -I part of show-messages which isn't really that useful; the server start time can now be accessed with a new start_time format (use: tmux display -p '#{t:start_time}') --- cmd-show-messages.c | 26 ++------------------------ format.c | 2 ++ server.c | 2 +- tmux.1 | 12 ++++++++---- tmux.c | 2 +- tmux.h | 6 +++--- 6 files changed, 17 insertions(+), 33 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 8cf71f41..c98c2c91 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -33,8 +33,8 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { "show-messages", "showmsgs", - "IJTt:", 0, 0, - "[-IJT] " CMD_TARGET_CLIENT_USAGE, + "JTt:", 0, 0, + "[-JT] " CMD_TARGET_CLIENT_USAGE, 0, cmd_show_messages_exec }; @@ -47,27 +47,9 @@ const struct cmd_entry cmd_server_info_entry = { cmd_show_messages_exec }; -int cmd_show_messages_server(struct cmd_q *); int cmd_show_messages_terminals(struct cmd_q *, int); int cmd_show_messages_jobs(struct cmd_q *, int); -int -cmd_show_messages_server(struct cmd_q *cmdq) -{ - char *tim; - - tim = ctime(&start_time); - *strchr(tim, '\n') = '\0'; - - cmdq_print(cmdq, "started %s", tim); - cmdq_print(cmdq, "socket path %s", socket_path); - cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); - cmdq_print(cmdq, "libevent %s (%s)", event_get_version(), - event_get_method()); - - return (1); -} - int cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { @@ -118,10 +100,6 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) int done, blank; done = blank = 0; - if (args_has(args, 'I') || self->entry == &cmd_server_info_entry) { - blank = cmd_show_messages_server(cmdq); - done = 1; - } if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { blank = cmd_show_messages_terminals(cmdq, blank); done = 1; diff --git a/format.c b/format.c index 50fa7dea..bafd8ce6 100644 --- a/format.c +++ b/format.c @@ -488,6 +488,8 @@ format_create_flags(int flags) 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); + format_add(ft, "socket_path", "%s", socket_path); + format_add_tv(ft, "start_time", &start_time); return (ft); } diff --git a/server.c b/server.c index e9159422..d950ed77 100644 --- a/server.c +++ b/server.c @@ -187,7 +187,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) mode_key_init_trees(); key_bindings_init(); - start_time = time(NULL); + gettimeofday(&start_time, NULL); server_fd = server_create_socket(); if (server_fd == -1) diff --git a/tmux.1 b/tmux.1 index 455de33b..cba38264 100644 --- a/tmux.1 +++ b/tmux.1 @@ -740,7 +740,7 @@ Kill the .Nm server and clients and destroy all sessions. .It Xo Ic kill-session -.Op Fl a +.Op Fl aC .Op Fl t Ar target-session .Xc Destroy the given session, closing any windows linked to it and no other @@ -748,6 +748,10 @@ sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. +The +.Fl C +clears alerts (bell, activity, or silence) in all windows linked to the +session. .It Xo Ic list-clients .Op Fl F Ar format .Op Fl t Ar target-session @@ -887,7 +891,7 @@ is specified, only update the client's status bar. Rename the session to .Ar new-name . .It Xo Ic show-messages -.Op Fl IJT +.Op Fl JT .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) @@ -900,11 +904,10 @@ With .Fl t , display the log for .Ar target-client . -.Fl I , .Fl J and .Fl T -show debugging information about the running server, jobs and terminals. +show debugging information about jobs and terminals. .It Ic source-file Ar path .D1 (alias: Ic source ) Execute commands from @@ -3410,6 +3413,7 @@ The following variables are available, where appropriate: .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "start_time" Ta "" Ta "Server start time" .It Li "window_activity" Ta "" Ta "Integer time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" diff --git a/tmux.c b/tmux.c index f5383fcf..7e89d526 100644 --- a/tmux.c +++ b/tmux.c @@ -44,7 +44,7 @@ struct options *global_w_options; /* window options */ struct environ *global_environ; char *shell_cmd; -time_t start_time; +struct timeval start_time; char socket_path[PATH_MAX]; __dead void usage(void); diff --git a/tmux.h b/tmux.h index 5f469f92..97908d98 100644 --- a/tmux.h +++ b/tmux.h @@ -1431,9 +1431,9 @@ extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; extern struct environ *global_environ; -extern char *shell_cmd; -extern time_t start_time; -extern char socket_path[PATH_MAX]; +extern char *shell_cmd; +extern struct timeval start_time; +extern char socket_path[PATH_MAX]; const char *getshell(void); int checkshell(const char *); int areshell(const char *); From bef217b241ec50d6aed87dbb3604acd1bdb74121 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:04:36 +0000 Subject: [PATCH 882/949] Switch a fprintf to a fatal, and wrap some long lines. --- client.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/client.c b/client.c index 53a98a30..fcb704fb 100644 --- a/client.c +++ b/client.c @@ -303,11 +303,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); if (client_flags & CLIENT_CONTROLCONTROL) { - if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { - fprintf(stderr, "tcgetattr failed: %s\n", - strerror(errno)); - return (1); - } + if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) + fatal("tcgetattr failed"); cfmakeraw(&tio); tio.c_iflag = ICRNL|IXANY; tio.c_oflag = OPOST|ONLCR; @@ -389,7 +386,8 @@ client_send_identify(const char *ttynam, const char *cwd) s = ""; proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); + proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, + strlen(ttynam) + 1); proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) @@ -401,8 +399,9 @@ client_send_identify(const char *ttynam, const char *cwd) for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; - if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) - proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) + continue; + proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); From 8976dac9e0c2e55b240ad55f4f7fa0a3b887c0e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:09:53 +0000 Subject: [PATCH 883/949] Remove malloc_options DEBUG bit. --- tmux.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tmux.c b/tmux.c index 7e89d526..c515ebb2 100644 --- a/tmux.c +++ b/tmux.c @@ -34,10 +34,6 @@ #include "tmux.h" -#ifdef DEBUG -extern char *malloc_options; -#endif - struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ @@ -182,10 +178,6 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; -#ifdef DEBUG - malloc_options = (char *) "AFGJPX"; -#endif - setlocale(LC_TIME, ""); tzset(); From c913fb99b6f0f7d08b77d8d021df7d7fa27f82d0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:27:22 +0000 Subject: [PATCH 884/949] Tidy the code that works out the socket path, and just use the full path in the global socket_path rather than copying it. --- client.c | 4 +-- tmux.c | 86 +++++++++++++++++++++++++------------------------------- tmux.h | 2 +- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/client.c b/client.c index fcb704fb..595aff9b 100644 --- a/client.c +++ b/client.c @@ -55,7 +55,7 @@ int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); -int client_connect(struct event_base *, char *, int); +int client_connect(struct event_base *, const char *, int); void client_send_identify(const char *, const char *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); @@ -96,7 +96,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ int -client_connect(struct event_base *base, char *path, int start_server) +client_connect(struct event_base *base, const char *path, int start_server) { struct sockaddr_un sa; size_t size; diff --git a/tmux.c b/tmux.c index c515ebb2..f2143755 100644 --- a/tmux.c +++ b/tmux.c @@ -41,10 +41,10 @@ struct environ *global_environ; char *shell_cmd; struct timeval start_time; -char socket_path[PATH_MAX]; +const char *socket_path; __dead void usage(void); -char *makesocketpath(const char *); +static char *make_label(const char *); __dead void usage(void) @@ -102,38 +102,48 @@ areshell(const char *shell) return (0); } -char * -makesocketpath(const char *label) +static char * +make_label(const char *label) { - char base[PATH_MAX], realbase[PATH_MAX], *path, *s; - struct stat sb; - u_int uid; + char *base, resolved[PATH_MAX], *path, *s; + struct stat sb; + u_int uid; + int saved_errno; + + if (label == NULL) + label = "default"; uid = getuid(); + if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); + xasprintf(&base, "%s/tmux-%u", s, uid); else - xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); + xasprintf(&base, "%s/tmux-%u", _PATH_TMP, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) - return (NULL); + goto fail; if (lstat(base, &sb) != 0) - return (NULL); + goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; - return (NULL); + goto fail; } if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { errno = EACCES; - return (NULL); + goto fail; } - if (realpath(base, realbase) == NULL) - strlcpy(realbase, base, sizeof realbase); - - xasprintf(&path, "%s/%s", realbase, label); + if (realpath(base, resolved) == NULL) + strlcpy(resolved, base, sizeof resolved); + xasprintf(&path, "%s/%s", resolved, label); return (path); + +fail: + saved_errno = errno; + free(base); + errno = saved_errno; + return (NULL); } void @@ -289,41 +299,23 @@ main(int argc, char **argv) } /* - * Figure out the socket path. If specified on the command-line with -S - * or -L, use it, otherwise try $TMUX or assume -L default. + * If socket is specified on the command-line with -S or -L, it is + * used. Otherwise, $TMUX is checked and if that fails "default" is + * used. */ - if (path == NULL) { - /* If no -L, use the environment. */ - if (label == NULL) { - s = getenv("TMUX"); - if (s != NULL) { - path = xstrdup(s); - path[strcspn (path, ",")] = '\0'; - if (*path == '\0') { - free(path); - label = xstrdup("default"); - } - } else - label = xstrdup("default"); - } - - /* -L or default set. */ - if (label != NULL) { - if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket: %s\n", - strerror(errno)); - exit(1); - } + if (path == NULL && label == NULL) { + s = getenv("TMUX"); + if (s != NULL && *s != '\0' && *s != ',') { + path = xstrdup(s); + path[strcspn (path, ",")] = '\0'; } } - free(label); - - if (strlcpy(socket_path, path, sizeof socket_path) >= - sizeof socket_path) { - fprintf(stderr, "socket path too long: %s\n", path); + if (path == NULL && (path = make_label(label)) == NULL) { + fprintf(stderr, "can't create socket: %s\n", strerror(errno)); exit(1); } - free(path); + socket_path = path; + free(label); /* Pass control to the client. */ exit(client_main(event_init(), argc, argv, flags)); diff --git a/tmux.h b/tmux.h index 97908d98..50d7fb03 100644 --- a/tmux.h +++ b/tmux.h @@ -1433,7 +1433,7 @@ extern struct options *global_w_options; extern struct environ *global_environ; extern char *shell_cmd; extern struct timeval start_time; -extern char socket_path[PATH_MAX]; +extern const char *socket_path; const char *getshell(void); int checkshell(const char *); int areshell(const char *); From c18fbefe93dfbc7525b27e0129f8a0e8e9b70117 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:27:59 +0000 Subject: [PATCH 885/949] Document socket_path format. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index cba38264..8fa6ad55 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3413,6 +3413,7 @@ The following variables are available, where appropriate: .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "socket_path" Ta "" "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" .It Li "window_activity" Ta "" Ta "Integer time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" From 73e30cbda8ade3a1c7ba3b771a911545826f76b7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:45:44 +0000 Subject: [PATCH 886/949] Actually show something (even if it not that helpful) if the server fails to start (for example if it can't create the socket), rather than hanging or showing nothing. --- client.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/client.c b/client.c index 595aff9b..5fdfd029 100644 --- a/client.c +++ b/client.c @@ -76,8 +76,12 @@ client_get_lock(char *lockfile) { int lockfd; - if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) - fatal("open failed"); + if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { + lockfd = open("/dev/null", O_WRONLY); + if (lockfd == -1) + fatal("open failed"); + return (lockfd); + } log_debug("lock file is %s", lockfile); if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { @@ -114,10 +118,10 @@ client_connect(struct event_base *base, const char *path, int start_server) retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); + return (-1); log_debug("trying connect"); - if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { + if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; @@ -255,6 +259,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) cmd_list_free(cmdlist); } + /* Create client process structure (starts logging). */ + client_proc = proc_start("client", base, 0, client_signal); + /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { @@ -267,9 +274,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } - - /* Build process state. */ - client_proc = proc_start("client", base, 0, client_signal); client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); /* Save these before pledge(). */ @@ -365,7 +369,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) printf("%%exit\n"); printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); - } + } else + fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); return (client_exitval); } @@ -517,7 +522,11 @@ client_dispatch(struct imsg *imsg, __unused void *arg) if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; client_exitval = 1; - } else if (client_attached) + proc_exit(client_proc); + return; + } + + if (client_attached) client_dispatch_attached(imsg); else client_dispatch_wait(imsg); From dca93c56e05ce631dd2f80da759f40c4d4b20ab5 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 23:01:51 +0000 Subject: [PATCH 887/949] Do lock failures slightly better, return a special value so we don't unlink the wrong thing. --- client.c | 34 ++++++++++++++++++---------------- server.c | 8 +++++--- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/client.c b/client.c index 5fdfd029..8144dd1c 100644 --- a/client.c +++ b/client.c @@ -67,23 +67,21 @@ const char *client_exit_message(void); /* * Get server create lock. If already held then server start is happening in - * another client, so block until the lock is released and return -1 to - * retry. Ignore other errors - just continue and start the server without the - * lock. + * another client, so block until the lock is released and return -2 to + * retry. Return -1 on failure to continue and start the server anyway. */ int client_get_lock(char *lockfile) { int lockfd; - if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { - lockfd = open("/dev/null", O_WRONLY); - if (lockfd == -1) - fatal("open failed"); - return (lockfd); - } log_debug("lock file is %s", lockfile); + if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { + log_debug("open failed: %s", strerror(errno)); + return (-1); + } + if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { log_debug("flock failed: %s", strerror(errno)); if (errno != EAGAIN) @@ -91,7 +89,7 @@ client_get_lock(char *lockfile) while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); - return (-1); + return (-2); } log_debug("flock succeeded"); @@ -131,12 +129,16 @@ retry: if (!locked) { xasprintf(&lockfile, "%s.lock", path); - if ((lockfd = client_get_lock(lockfile)) == -1) { - log_debug("didn't get lock"); + if ((lockfd = client_get_lock(lockfile)) < 0) { + log_debug("didn't get lock (%d)", lockfd); + free(lockfile); - goto retry; + lockfile = NULL; + + if (lockfd == -2) + goto retry; } - log_debug("got lock"); + log_debug("got lock (%d)", lockfd); /* * Always retry at least once, even if we got the lock, @@ -148,7 +150,7 @@ retry: goto retry; } - if (unlink(path) != 0 && errno != ENOENT) { + if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { free(lockfile); close(lockfd); return (-1); @@ -156,7 +158,7 @@ retry: fd = server_start(base, lockfd, lockfile); } - if (locked) { + if (locked && lockfd >= 0) { free(lockfile); close(lockfd); } diff --git a/server.c b/server.c index d950ed77..c45e8a2c 100644 --- a/server.c +++ b/server.c @@ -195,9 +195,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) server_update_socket(); server_client_create(pair[1]); - unlink(lockfile); - free(lockfile); - close(lockfd); + if (lockfd >= 0) { + unlink(lockfile); + free(lockfile); + close(lockfd); + } start_cfg(); From 3ff46b2e43d48376f74c0fd74b5616925d4063f0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 23:22:51 +0000 Subject: [PATCH 888/949] Shell command from -c doesn't have to be global, pass it as an argument. --- client.c | 26 ++++++++++++++------------ tmux.c | 11 +++++------ tmux.h | 3 +-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/client.c b/client.c index 8144dd1c..5987efa5 100644 --- a/client.c +++ b/client.c @@ -53,7 +53,7 @@ enum msgtype client_exittype; const char *client_exitsession; int client_attached; -__dead void client_exec(const char *); +__dead void client_exec(const char *,const char *); int client_get_lock(char *); int client_connect(struct event_base *, const char *, int); void client_send_identify(const char *, const char *); @@ -62,7 +62,7 @@ void client_write(int, const char *, size_t); void client_signal(int); void client_dispatch(struct imsg *, void *); void client_dispatch_attached(struct imsg *); -void client_dispatch_wait(struct imsg *); +void client_dispatch_wait(struct imsg *, const char *); const char *client_exit_message(void); /* @@ -213,7 +213,8 @@ client_exit_message(void) /* Client main loop. */ int -client_main(struct event_base *base, int argc, char **argv, int flags) +client_main(struct event_base *base, int argc, char **argv, int flags, + const char *shellcmd) { struct cmd *cmd; struct cmd_list *cmdlist; @@ -234,7 +235,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Set up the initial command. */ cmdflags = 0; - if (shell_cmd != NULL) { + if (shellcmd != NULL) { msg = MSG_SHELL; cmdflags = CMD_STARTSERVER; } else if (argc == 0) { @@ -276,7 +277,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } - client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); + client_peer = proc_add_peer(client_proc, fd, client_dispatch, + (void *)shellcmd); /* Save these before pledge(). */ if ((cwd = getcwd(path, sizeof path)) == NULL) { @@ -450,12 +452,12 @@ client_write(int fd, const char *data, size_t size) /* Run command in shell; used for -c. */ __dead void -client_exec(const char *shell) +client_exec(const char *shell, const char *shellcmd) { const char *name, *ptr; char *argv0; - log_debug("shell %s, command %s", shell, shell_cmd); + log_debug("shell %s, command %s", shell, shellcmd); ptr = strrchr(shell, '/'); if (ptr != NULL && *(ptr + 1) != '\0') @@ -473,7 +475,7 @@ client_exec(const char *shell) setblocking(STDERR_FILENO, 1); closefrom(STDERR_FILENO + 1); - execl(shell, argv0, "-c", shell_cmd, (char *) NULL); + execl(shell, argv0, "-c", shellcmd, (char *) NULL); fatal("execl failed"); } @@ -519,7 +521,7 @@ client_signal(int sig) /* Callback for client read events. */ void -client_dispatch(struct imsg *imsg, __unused void *arg) +client_dispatch(struct imsg *imsg, void *arg) { if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; @@ -531,12 +533,12 @@ client_dispatch(struct imsg *imsg, __unused void *arg) if (client_attached) client_dispatch_attached(imsg); else - client_dispatch_wait(imsg); + client_dispatch_wait(imsg, arg); } /* Dispatch imsgs when in wait state (before MSG_READY). */ void -client_dispatch_wait(struct imsg *imsg) +client_dispatch_wait(struct imsg *imsg, const char *shellcmd) { char *data; ssize_t datalen; @@ -616,7 +618,7 @@ client_dispatch_wait(struct imsg *imsg) fatalx("bad MSG_SHELL string"); clear_signals(0); - client_exec(data); + client_exec(data, shellcmd); /* NOTREACHED */ case MSG_DETACH: case MSG_DETACHKILL: diff --git a/tmux.c b/tmux.c index f2143755..7d49dd30 100644 --- a/tmux.c +++ b/tmux.c @@ -39,7 +39,6 @@ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ struct environ *global_environ; -char *shell_cmd; struct timeval start_time; const char *socket_path; @@ -184,7 +183,7 @@ find_home(void) int main(int argc, char **argv) { - char *path, *label, **var, tmp[PATH_MAX]; + char *path, *label, **var, tmp[PATH_MAX], *shellcmd = NULL; const char *s; int opt, flags, keys; @@ -203,8 +202,8 @@ main(int argc, char **argv) flags |= CLIENT_256COLOURS; break; case 'c': - free(shell_cmd); - shell_cmd = xstrdup(optarg); + free(shellcmd); + shellcmd = xstrdup(optarg); break; case 'C': if (flags & CLIENT_CONTROL) @@ -241,7 +240,7 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (shell_cmd != NULL && argc != 0) + if (shellcmd != NULL && argc != 0) usage(); if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " @@ -318,5 +317,5 @@ main(int argc, char **argv) free(label); /* Pass control to the client. */ - exit(client_main(event_init(), argc, argv, flags)); + exit(client_main(event_init(), argc, argv, flags, shellcmd)); } diff --git a/tmux.h b/tmux.h index 50d7fb03..f4638bc8 100644 --- a/tmux.h +++ b/tmux.h @@ -1431,7 +1431,6 @@ extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; extern struct environ *global_environ; -extern char *shell_cmd; extern struct timeval start_time; extern const char *socket_path; const char *getshell(void); @@ -1732,7 +1731,7 @@ int cmd_string_parse(const char *, struct cmd_list **, const char *, void cmd_wait_for_flush(void); /* client.c */ -int client_main(struct event_base *, int, char **, int); +int client_main(struct event_base *, int, char **, int, const char *); /* key-bindings.c */ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); From 62d3af17f9e6aec244ab1d91c1c46fdbbf6f8a86 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 23:46:15 +0000 Subject: [PATCH 889/949] Make environ_set va_args and use it to tidy up some calls. Also add a missing word in manpage (from jmc). --- cmd-set-environment.c | 4 ++-- environ.c | 46 ++++++++++++++++++++++++++++++------------- server-fn.c | 11 +++++------ tmux.1 | 2 +- tmux.c | 2 +- tmux.h | 6 ++++-- window.c | 7 +++---- 7 files changed, 48 insertions(+), 30 deletions(-) diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 864e1d9b..2c0010af 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -79,13 +79,13 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "can't specify a value with -r"); return (CMD_RETURN_ERROR); } - environ_set(env, name, NULL); + environ_clear(env, name); } else { if (value == NULL) { cmdq_error(cmdq, "no value specified"); return (CMD_RETURN_ERROR); } - environ_set(env, name, value); + environ_set(env, name, "%s", value); } return (CMD_RETURN_NORMAL); diff --git a/environ.c b/environ.c index 43a9ce01..de560896 100644 --- a/environ.c +++ b/environ.c @@ -83,8 +83,12 @@ environ_copy(struct environ *srcenv, struct environ *dstenv) { struct environ_entry *envent; - RB_FOREACH(envent, environ, srcenv) - environ_set(dstenv, envent->name, envent->value); + RB_FOREACH(envent, environ, srcenv) { + if (envent->value == NULL) + environ_clear(dstenv, envent->name); + else + environ_set(dstenv, envent->name, "%s", envent->value); + } } /* Find an environment variable. */ @@ -99,23 +103,37 @@ environ_find(struct environ *env, const char *name) /* Set an environment variable. */ void -environ_set(struct environ *env, const char *name, const char *value) +environ_set(struct environ *env, const char *name, const char *fmt, ...) +{ + struct environ_entry *envent; + va_list ap; + + va_start(ap, fmt); + if ((envent = environ_find(env, name)) != NULL) { + free(envent->value); + xvasprintf(&envent->value, fmt, ap); + } else { + envent = xmalloc(sizeof *envent); + envent->name = xstrdup(name); + xvasprintf(&envent->value, fmt, ap); + RB_INSERT(environ, env, envent); + } + va_end(ap); +} + +/* Clear an environment variable. */ +void +environ_clear(struct environ *env, const char *name) { struct environ_entry *envent; if ((envent = environ_find(env, name)) != NULL) { free(envent->value); - if (value != NULL) - envent->value = xstrdup(value); - else - envent->value = NULL; + envent->value = NULL; } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); - if (value != NULL) - envent->value = xstrdup(value); - else - envent->value = NULL; + envent->value = NULL; RB_INSERT(environ, env, envent); } } @@ -134,7 +152,7 @@ environ_put(struct environ *env, const char *var) name = xstrdup(var); name[strcspn(name, "=")] = '\0'; - environ_set(env, name, value); + environ_set(env, name, "%s", value); free(name); } @@ -166,9 +184,9 @@ environ_update(const char *vars, struct environ *srcenv, copyvars = next = xstrdup(vars); while ((var = strsep(&next, " ")) != NULL) { if ((envent = environ_find(srcenv, var)) == NULL) - environ_set(dstenv, var, NULL); + environ_clear(dstenv, var); else - environ_set(dstenv, envent->name, envent->value); + environ_set(dstenv, envent->name, "%s", envent->value); } free(copyvars); } diff --git a/server-fn.c b/server-fn.c index 3e4b6116..a22c964d 100644 --- a/server-fn.c +++ b/server-fn.c @@ -34,20 +34,19 @@ void server_callback_identify(int, short, void *); void server_fill_environ(struct session *s, struct environ *env) { - char var[PATH_MAX], *term; - u_int idx; - long pid; + char *term; + u_int idx; + long pid; if (s != NULL) { term = options_get_string(global_options, "default-terminal"); - environ_set(env, "TERM", term); + environ_set(env, "TERM", "%s", term); idx = s->id; } else idx = (u_int)-1; pid = getpid(); - xsnprintf(var, sizeof var, "%s,%ld,%u", socket_path, pid, idx); - environ_set(env, "TMUX", var); + environ_set(env, "TMUX", "%s,%ld,%u", socket_path, pid, idx); } void diff --git a/tmux.1 b/tmux.1 index 8fa6ad55..454af1d2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -750,7 +750,7 @@ If is given, all sessions but the specified one is killed. The .Fl C -clears alerts (bell, activity, or silence) in all windows linked to the +flag clears alerts (bell, activity, or silence) in all windows linked to the session. .It Xo Ic list-clients .Op Fl F Ar format diff --git a/tmux.c b/tmux.c index 7d49dd30..68f0092d 100644 --- a/tmux.c +++ b/tmux.c @@ -273,7 +273,7 @@ main(int argc, char **argv) for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); if (getcwd(tmp, sizeof tmp) != NULL) - environ_set(global_environ, "PWD", tmp); + environ_set(global_environ, "PWD", "%s", tmp); global_options = options_create(NULL); options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); diff --git a/tmux.h b/tmux.h index f4638bc8..2acfc75f 100644 --- a/tmux.h +++ b/tmux.h @@ -1567,7 +1567,9 @@ struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); -void environ_set(struct environ *, const char *, const char *); +void printflike(3, 4) environ_set(struct environ *, const char *, const char *, + ...); +void environ_clear(struct environ *, const char *); void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); void environ_update(const char *, struct environ *, struct environ *); @@ -1739,7 +1741,7 @@ RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); extern struct key_tables key_tables; int key_table_cmp(struct key_table *, struct key_table *); int key_bindings_cmp(struct key_binding *, struct key_binding *); -struct key_table *key_bindings_get_table(const char *, int); +struct key_table *key_bindings_get_table(const char *, int); void key_bindings_unref_table(struct key_table *); void key_bindings_add(const char *, key_code, int, struct cmd_list *); void key_bindings_remove(const char *, key_code); diff --git a/window.c b/window.c index 8ee517a1..deda2895 100644 --- a/window.c +++ b/window.c @@ -808,7 +808,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, struct termios *tio, char **cause) { struct winsize ws; - char *argv0, *cmd, **argvp, paneid[16]; + char *argv0, *cmd, **argvp; const char *ptr, *first, *home; struct termios tio2; int i; @@ -863,9 +863,8 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, closefrom(STDERR_FILENO + 1); if (path != NULL) - environ_set(env, "PATH", path); - xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); - environ_set(env, "TMUX_PANE", paneid); + environ_set(env, "PATH", "%s", path); + environ_set(env, "TMUX_PANE", "%%%u", wp->id); environ_push(env); clear_signals(1); From ac8678aefe157d7e40c5bcedd12333eaedf0df92 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Nov 2015 07:58:55 +0000 Subject: [PATCH 890/949] Don't print error if none to print. --- client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.c b/client.c index 5987efa5..516ed3bf 100644 --- a/client.c +++ b/client.c @@ -373,7 +373,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags, printf("%%exit\n"); printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); - } else + } else if (client_exitreason != CLIENT_EXIT_NONE) fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); return (client_exitval); From 260de2cb5e384a0cf193b5f9f996fa8fb2c5aaf8 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Wed, 25 Nov 2015 16:48:47 +0000 Subject: [PATCH 891/949] Remove logfile() --- proc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/proc.c b/proc.c index 6a68d74b..e4c69484 100644 --- a/proc.c +++ b/proc.c @@ -188,8 +188,6 @@ proc_start(const char *name, struct event_base *base, int forkflag, fatalx("event_reinit failed"); } - logfile(name); - #ifdef HAVE_SETPROCTITLE log_open(name); setproctitle("%s (%s)", name, socket_path); From a1bc339340abebfd9938a8e98f7cb26186c7e7c6 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Wed, 25 Nov 2015 16:51:17 +0000 Subject: [PATCH 892/949] log_open() isn't conditional on proctitle --- proc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proc.c b/proc.c index e4c69484..f84ecf92 100644 --- a/proc.c +++ b/proc.c @@ -188,8 +188,9 @@ proc_start(const char *name, struct event_base *base, int forkflag, fatalx("event_reinit failed"); } -#ifdef HAVE_SETPROCTITLE log_open(name); + +#ifdef HAVE_SETPROCTITLE setproctitle("%s (%s)", name, socket_path); #endif From 3b83bda29c51c7b2c3aeef01dde1d3ba6a441e89 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 25 Nov 2015 23:35:24 +0000 Subject: [PATCH 893/949] Add to TODO. --- TODO | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TODO b/TODO index 6094c023..c52d42b6 100644 --- a/TODO +++ b/TODO @@ -121,3 +121,13 @@ * automatic pane logging * BCE? We are halfway there (output side is done for pane backgrounds), just need to change how screen/grid handles erase + * copy mode key bindings should just be a standard key table, using + something like "copy-mode start-selection"; it could use + command-prompt for search, goto, etc: + + bind -Temacs command-prompt -p'Search Up: ' 'copy-mode search-up %%' + + it'd need a separate lookup, because modes are per-pane, perhaps a + table() cb to give the table name ("vi" or "emacs"). anything in the + table fires the command, anything not in the table is injected as a + key From 6a2ca34216530c687027cf9e767d2b46c85976e6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 27 Nov 2015 15:06:43 +0000 Subject: [PATCH 894/949] Do not set a limit on the length of commands when printing them. --- arguments.c | 86 +++++++++++++++++++++++-------------------------- cmd-list-keys.c | 9 ++---- cmd-list.c | 35 +++++++++++--------- cmd-queue.c | 7 ++-- cmd.c | 24 +++++++------- tmux.h | 6 ++-- 6 files changed, 81 insertions(+), 86 deletions(-) diff --git a/arguments.c b/arguments.c index 54753de3..0a42cc38 100644 --- a/arguments.c +++ b/arguments.c @@ -128,77 +128,73 @@ args_free(struct args *args) free(args); } -/* Print a set of arguments. */ -size_t -args_print(struct args *args, char *buf, size_t len) +/* Add to string. */ +static void printflike(3, 4) +args_print_add(char **buf, size_t *len, const char *fmt, ...) { - size_t off, used; + va_list ap; + char *s; + size_t slen; + + va_start(ap, fmt); + slen = xvasprintf(&s, fmt, ap); + va_end(ap); + + *len += slen; + *buf = xrealloc(*buf, *len); + + strlcat(*buf, s, *len); + free(s); +} + +/* Print a set of arguments. */ +char * +args_print(struct args *args) +{ + size_t len; + char *buf; int i; - const char *quotes; struct args_entry *entry; - /* There must be at least one byte at the start. */ - if (len == 0) - return (0); - off = 0; + len = 1; + buf = xcalloc(1, len); /* Process the flags first. */ - buf[off++] = '-'; RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value != NULL) continue; - if (off == len - 1) { - buf[off] = '\0'; - return (len); - } - buf[off++] = entry->flag; - buf[off] = '\0'; + if (*buf == '\0') + args_print_add(&buf, &len, "-"); + args_print_add(&buf, &len, "%c", entry->flag); } - if (off == 1) - buf[--off] = '\0'; /* Then the flags with arguments. */ RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value == NULL) continue; - if (off >= len) { - /* snprintf will have zero terminated. */ - return (len); - } - - if (strchr(entry->value, ' ') != NULL) - quotes = "\""; + if (*buf != '\0') + args_print_add(&buf, &len, " -%c ", entry->flag); else - quotes = ""; - used = xsnprintf(buf + off, len - off, "%s-%c %s%s%s", - off != 0 ? " " : "", entry->flag, quotes, entry->value, - quotes); - if (used > len - off) - used = len - off; - off += used; + args_print_add(&buf, &len, "-%c ", entry->flag); + if (strchr(entry->value, ' ') != NULL) + args_print_add(&buf, &len, "\"%s\"", entry->value); + else + args_print_add(&buf, &len, "%s", entry->value); } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) { - if (off >= len) { - /* snprintf will have zero terminated. */ - return (len); - } - + if (*buf != '\0') + args_print_add(&buf, &len, " "); if (strchr(args->argv[i], ' ') != NULL) - quotes = "\""; + args_print_add(&buf, &len, "\"%s\"", args->argv[i]); else - quotes = ""; - used = xsnprintf(buf + off, len - off, "%s%s%s%s", - off != 0 ? " " : "", quotes, args->argv[i], quotes); - if (used > len - off) - used = len - off; - off += used; + args_print_add(&buf, &len, "%s", args->argv[i]); } - return (off); + return (buf); } /* Return if an argument is present. */ diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 4355f24e..f0a59c0b 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -56,7 +56,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct key_binding *bd; const char *key, *tablename, *r; char *cp, tmp[BUFSIZ]; - size_t used; int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) @@ -115,11 +114,9 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) strlcat(tmp, " ", sizeof tmp); free(cp); - used = strlen(tmp); - if (used < (sizeof tmp) - 1) { - cmd_list_print(bd->cmdlist, tmp + used, - (sizeof tmp) - used); - } + cp = cmd_list_print(bd->cmdlist); + strlcat(tmp, cp, sizeof tmp); + free(cp); cmdq_print(cmdq, "bind-key %s", tmp); } diff --git a/cmd-list.c b/cmd-list.c index 0c75ed49..59fc7796 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -99,25 +99,28 @@ cmd_list_free(struct cmd_list *cmdlist) free(cmdlist); } -size_t -cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) +char * +cmd_list_print(struct cmd_list *cmdlist) { struct cmd *cmd; - size_t off, used; + char *buf, *this; + size_t len; + + len = 1; + buf = xcalloc(1, len); - off = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if (off >= len) - break; - off += cmd_print(cmd, buf + off, len - off); - if (off >= len) - break; - if (TAILQ_NEXT(cmd, qentry) != NULL) { - used = xsnprintf(buf + off, len - off, " ; "); - if (used > len - off) - used = len - off; - off += used; - } + this = cmd_print(cmd); + + len += strlen(this) + 3; + buf = xrealloc(buf, len); + + strlcat(buf, this, len); + if (TAILQ_NEXT(cmd, qentry) != NULL) + strlcat(buf, " ; ", len); + + free(this); } - return (off); + + return (buf); } diff --git a/cmd-queue.c b/cmd-queue.c index 2d896212..c85fb048 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -184,11 +184,12 @@ cmdq_continue_one(struct cmd_q *cmdq) { struct cmd *cmd = cmdq->cmd; enum cmd_retval retval; - char tmp[1024]; + char *s; int flags = !!(cmd->flags & CMD_CONTROL); - cmd_print(cmd, tmp, sizeof tmp); - log_debug("cmdq %p: %s", cmdq, tmp); + s = cmd_print(cmd); + log_debug("cmdq %p: %s", cmdq, s); + free(s); cmdq->time = time(NULL); cmdq->number++; diff --git a/cmd.c b/cmd.c index 0c20d656..824d9caf 100644 --- a/cmd.c +++ b/cmd.c @@ -384,21 +384,19 @@ usage: return (NULL); } -size_t -cmd_print(struct cmd *cmd, char *buf, size_t len) +char * +cmd_print(struct cmd *cmd) { - size_t off, used; + char *out, *s; - off = xsnprintf(buf, len, "%s ", cmd->entry->name); - if (off + 1 < len) { - used = args_print(cmd->args, buf + off, len - off - 1); - if (used == 0) - off--; - else - off += used; - buf[off] = '\0'; - } - return (off); + s = args_print(cmd->args); + if (*s != '\0') + xasprintf(&out, "%s %s", cmd->entry->name, s); + else + out = xstrdup(cmd->entry->name); + free(s); + + return (out); } /* Adjust current mouse position for a pane. */ diff --git a/tmux.h b/tmux.h index 2acfc75f..0808a30c 100644 --- a/tmux.h +++ b/tmux.h @@ -1665,7 +1665,7 @@ RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); struct args *args_create(int, ...); struct args *args_parse(const char *, int, char **); void args_free(struct args *); -size_t args_print(struct args *, char *, size_t); +char *args_print(struct args *); int args_has(struct args *, u_char); void args_set(struct args *, u_char, const char *); const char *args_get(struct args *, u_char); @@ -1694,7 +1694,7 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); -size_t cmd_print(struct cmd *, char *, size_t); +char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); @@ -1710,7 +1710,7 @@ enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); void cmd_list_free(struct cmd_list *); -size_t cmd_list_print(struct cmd_list *, char *, size_t); +char *cmd_list_print(struct cmd_list *); /* cmd-queue.c */ struct cmd_q *cmdq_new(struct client *); From 1d331c7e6263826e92bb22824d97d781106d0e51 Mon Sep 17 00:00:00 2001 From: guenther Date: Sun, 29 Nov 2015 17:06:59 +0000 Subject: [PATCH 895/949] Delete a duplicated line ok jmc@ --- tmux.1 | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 454af1d2..7d45688c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -871,7 +871,6 @@ If is used, .Ic update-environment option will not be applied. -.Ic update-environment . .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client From a785a7f7005761a3ab52b8c336e1ff5881eb7d83 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Dec 2015 09:41:03 +0000 Subject: [PATCH 896/949] Do not deref wp if window_get_active_at returns NULL which can happen on very large terminals, from Michael Graczyk. --- server-client.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server-client.c b/server-client.c index c5e93438..d2abd356 100644 --- a/server-client.c +++ b/server-client.c @@ -332,10 +332,11 @@ server_client_check_mouse(struct client *c) where = BORDER; else { wp = window_get_active_at(s->curw->window, x, y); - if (wp != NULL) + if (wp != NULL) { where = PANE; - log_debug("mouse at %u,%u is on pane %%%u", x, y, - wp->id); + log_debug("mouse at %u,%u is on pane %%%u", + x, y, wp->id); + } } if (where == NOWHERE) return (KEYC_NONE); From 7236838dead7885b90c6a57736433bea63c26599 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Dec 2015 23:09:22 +0000 Subject: [PATCH 897/949] Mark new active pane changed after pane lost in window, and after break-pane. Reported by tim@. --- cmd-break-pane.c | 1 + window.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 39179cc7..707cd09b 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -82,6 +82,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) window_set_name(w, name); free(name); layout_init(w, wp); + wp->flags |= PANE_CHANGED; if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); diff --git a/window.c b/window.c index deda2895..75b99672 100644 --- a/window.c +++ b/window.c @@ -579,6 +579,8 @@ window_lost_pane(struct window *w, struct window_pane *wp) if (w->active == NULL) w->active = TAILQ_NEXT(wp, entry); } + if (w->active != NULL) + w->active->flags |= PANE_CHANGED; } else if (wp == w->last) w->last = NULL; } From 3cdb2f0bb793b641ec07ef45b3fd485c2089d9e4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 3 Dec 2015 14:43:24 +0000 Subject: [PATCH 898/949] Add to TODO. --- TODO | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index c52d42b6..b4b8231b 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,8 @@ * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions + * list-keys should quote output so that bindings can just be used in + config file as-is - make command sequences more usable * don't require space after ; @@ -15,7 +17,7 @@ * way to set socket path from config file - format improvements: - * option to quote format (#{session_name:quoted}) + * option to quote format (#{q:session_name}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces * formats to show if a window is linked into multiple sessions, into From 0417f1f2bef1e8899dad4564db797250ae2c8c49 Mon Sep 17 00:00:00 2001 From: claudio Date: Sat, 5 Dec 2015 13:18:24 +0000 Subject: [PATCH 899/949] EAGAIN handling for imsg_read. OK henning@ benno@ --- proc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proc.c b/proc.c index 593c1b8b..5f51b0ac 100644 --- a/proc.c +++ b/proc.c @@ -61,7 +61,8 @@ proc_event_cb(__unused int fd, short events, void *arg) struct imsg imsg; if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { - if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { + if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) || + n == 0) { peer->dispatchcb(NULL, peer->arg); return; } From b9563340b7e9f516f77e674791e7635e948ca7fa Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Dec 2015 09:47:41 +0000 Subject: [PATCH 900/949] Fix bell indicators across detach, reported by Torbjorn Lonnemark, diff from Thomas Adam. --- alerts.c | 44 +++++++++++++++++++++++++++++++++----------- cmd-attach-session.c | 1 + tmux.h | 1 + 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/alerts.c b/alerts.c index 55e7044d..536ea750 100644 --- a/alerts.c +++ b/alerts.c @@ -29,6 +29,7 @@ int alerts_enabled(struct window *, int); void alerts_callback(int, short, void *); void alerts_reset(struct window *); +int alerts_check_all(struct session *, struct winlink *); int alerts_check_bell(struct session *, struct winlink *); int alerts_check_activity(struct session *, struct winlink *); int alerts_check_silence(struct session *, struct winlink *); @@ -54,16 +55,14 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) RB_FOREACH(w, windows, &windows) { RB_FOREACH(s, sessions, &sessions) { + if (s->flags & SESSION_UNATTACHED) + continue; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window != w) continue; flags = w->flags; - alerts = alerts_check_bell(s, wl); - alerts |= alerts_check_activity(s, wl); - alerts |= alerts_check_silence(s, wl); - if (alerts != 0) - server_status_session(s); + alerts = alerts_check_all(s, wl); log_debug("%s:%d @%u alerts check, alerts %#x, " "flags %#x", s->name, wl->idx, w->id, @@ -74,6 +73,29 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) alerts_fired = 0; } +int +alerts_check_all(struct session *s, struct winlink *wl) +{ + int alerts; + + alerts = alerts_check_bell(s, wl); + alerts |= alerts_check_activity(s, wl); + alerts |= alerts_check_silence(s, wl); + if (alerts != 0) + server_status_session(s); + + return (alerts); +} + +void +alerts_check_session(struct session *s) +{ + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) + alerts_check_all(s, wl); +} + int alerts_enabled(struct window *w, int flags) { @@ -143,12 +165,12 @@ alerts_check_bell(struct session *s, struct winlink *wl) struct window *w = wl->window; int action, visual; - if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) + if (!(w->flags & WINDOW_BELL)) return (0); - if (s->curw != wl || s->flags & SESSION_UNATTACHED) + if (s->curw != wl) { wl->flags |= WINLINK_BELL; - if (s->flags & SESSION_UNATTACHED) - return (0); + w->flags &= ~WINDOW_BELL; + } if (s->curw->window == w) w->flags &= ~WINDOW_BELL; @@ -190,7 +212,7 @@ alerts_check_activity(struct session *s, struct winlink *wl) if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) + if (s->curw == wl) return (0); if (!options_get_number(w->options, "monitor-activity")) @@ -222,7 +244,7 @@ alerts_check_silence(struct session *s, struct winlink *wl) if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) + if (s->curw == wl) return (0); if (options_get_number(w->options, "monitor-silence") == 0) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 46133923..5bde0d80 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -162,6 +162,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, cmdq->client_exit = 0; } recalculate_sizes(); + alerts_check_session(s); server_update_socket(); return (CMD_RETURN_NORMAL); diff --git a/tmux.h b/tmux.h index 0808a30c..a1e16955 100644 --- a/tmux.h +++ b/tmux.h @@ -1757,6 +1757,7 @@ const char *key_string_lookup_key(key_code); /* alerts.c */ void alerts_reset_all(void); void alerts_queue(struct window *, int); +void alerts_check_session(struct session *); /* server.c */ extern struct tmuxproc *server_proc; From d20a3ef57c1571898bb351f1f9287114be0aa419 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Dec 2015 12:51:06 +0000 Subject: [PATCH 901/949] Update .mailmap file. --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index a9b07251..847494ba 100644 --- a/.mailmap +++ b/.mailmap @@ -1,4 +1,5 @@ Bob Beck beck +Claudio Jeker claudio Igor Sobrado sobrado Ingo Schwarze schwarze Jacek Masiulaniec jacekm @@ -29,6 +30,7 @@ Theo de Raadt Theo Deraadt Thomas Adam Thomas Thomas Adam n6tadam Thomas Adam Thomas Adam +Tim van der Molen tim Tobias Stoeckmann tobias Todd C Miller millert William Yodlowsky william From 5411033f66ced27168bbd407b8b9c4589072a39a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Dec 2015 12:54:34 +0000 Subject: [PATCH 902/949] Update tmux.vim from Teubel Gyorgy. --- examples/tmux.vim | 119 ++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 6f85db5b..d9b60408 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -31,15 +31,16 @@ syn keyword tmuxAction any current none syn keyword tmuxBoolean off on syn keyword tmuxCmds - \ attach[-session] - \ bind[-key] + \ attach + \ attach-session + \ bind + \ bind-key \ break-pane \ breakp \ capture-pane \ capturep \ choose-buffer \ choose-client - \ choose-list \ choose-session \ choose-tree \ choose-window @@ -47,124 +48,147 @@ syn keyword tmuxCmds \ clearhist \ clock-mode \ command-prompt - \ confirm[-before] + \ confirm + \ confirm-before \ copy-mode \ delete-buffer \ deleteb - \ detach[-client] - \ display[-message] + \ detach + \ detach-client + \ display + \ display-message \ display-panes \ displayp \ find-window \ findw - \ has[-session] - \ if[-shell] + \ has + \ has-session + \ if + \ if-shell + \ info \ join-pane \ joinp \ kill-pane - \ killp \ kill-server \ kill-session \ kill-window + \ killp \ killw + \ last \ last-pane + \ last-window \ lastp - \ last[-window] \ link-window \ linkw \ list-buffers - \ lsb \ list-clients - \ lsc \ list-commands - \ lscm \ list-keys - \ lsk \ list-panes - \ lsp \ list-sessions - \ ls \ list-windows - \ lsw \ load-buffer \ loadb + \ lock \ lock-client - \ lockc - \ lock[-server] + \ lock-server \ lock-session + \ lockc \ locks + \ ls + \ lsb + \ lsc + \ lscm + \ lsk + \ lsp + \ lsw \ move-pane - \ movep \ move-window + \ movep \ movew - \ new[-session] + \ new + \ new-session + \ new-window + \ neww + \ next \ next-layout + \ next-window \ nextl - \ next[-window] \ paste-buffer \ pasteb + \ path \ pipe-pane \ pipep + \ prev \ previous-layout + \ previous-window \ prevl - \ prev[ious-window] - \ refresh[-client] - \ rename[-session] + \ refresh + \ refresh-client + \ rename + \ rename-session \ rename-window \ renamew \ resize-pane \ resizep \ respawn-pane - \ respawnp \ respawn-window + \ respawnp \ respawnw \ rotate-window \ rotatew - \ run[-shell] + \ run + \ run-shell \ save-buffer \ saveb \ select-layout - \ selectl \ select-pane - \ selectp \ select-window + \ selectl + \ selectp \ selectw - \ send[-keys] + \ send + \ send-keys \ send-prefix \ server-info - \ info + \ set \ set-buffer - \ setb \ set-environment - \ setenv - \ set[-option] + \ set-option \ set-window-option + \ setb + \ setenv \ setw + \ show \ show-buffer - \ showb \ show-environment - \ showenv \ show-messages - \ showmsgs - \ show[-options] + \ show-options \ show-window-options + \ showb + \ showenv + \ showmsgs \ showw - \ source[-file] + \ source + \ source-file \ split-window \ splitw - \ start[-server] + \ start + \ start-server \ suspend-client \ suspendc \ swap-pane - \ swapp \ swap-window + \ swapp \ swapw \ switch-client \ switchc - \ unbind[-key] + \ unbind + \ unbind-key \ unlink-window \ unlinkw - \ wait[-for] + \ wait + \ wait-for syn keyword tmuxOptsSet \ assume-paste-time @@ -188,14 +212,11 @@ syn keyword tmuxOptsSet \ history-limit \ lock-after-time \ lock-command - \ lock-server \ message-command-style \ message-limit \ message-style \ mouse \ mouse-utf8 - \ pane-active-border-style - \ pane-border-style \ prefix \ prefix2 \ quiet @@ -215,8 +236,9 @@ syn keyword tmuxOptsSet \ status-position \ status-right \ status-right-length + \ status-right-style + \ status-style \ status-utf8 - \ staus-right-style \ terminal-overrides \ update-environment \ visual-activity @@ -229,6 +251,7 @@ syn keyword tmuxOptsSetw \ allow-rename \ alternate-screen \ automatic-rename + \ automatic-rename-format \ clock-mode-colour \ clock-mode-style \ force-height @@ -241,7 +264,9 @@ syn keyword tmuxOptsSetw \ monitor-silence \ other-pane-height \ other-pane-width + \ pane-active-border-style \ pane-base-index + \ pane-border-style \ remain-on-exit \ synchronize-panes \ utf8 From ff16836d1de31b81bc093c42e4c336fb7e12306f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 00:47:27 +0000 Subject: [PATCH 903/949] pty is in section 4 --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 87c1ac8f..5d78b2ea 100644 --- a/tmux.1 +++ b/tmux.1 @@ -61,7 +61,7 @@ A window occupies the entire screen and may be split into rectangular panes, each of which is a separate pseudo terminal (the -.Xr pty 7 +.Xr pty 4 manual page documents the technical details of pseudo terminals). Any number of .Nm From b580a551914ae398162b0eec9bcbfa5901fabcdd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 00:48:22 +0000 Subject: [PATCH 904/949] pty(7) -> pty(4) --- tmux.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 5d78b2ea..dcf0dc57 100644 --- a/tmux.1 +++ b/tmux.1 @@ -370,7 +370,7 @@ These specify the client, session, window or pane which a command should affect. .Pp .Ar target-client is the name of the -.Xr pty 7 +.Xr pty 4 file to which the client is connected, for example either of .Pa /dev/ttyp1 or @@ -1319,7 +1319,7 @@ interactively from a list. After a client is chosen, .Ql %% is replaced by the client -.Xr pty 7 +.Xr pty 4 path in .Ar template and the result executed as a command. @@ -4165,6 +4165,6 @@ bind-key / command-prompt "split-window 'exec man %%'" bind-key S command-prompt "new-window -n %1 'ssh %1'" .Ed .Sh SEE ALSO -.Xr pty 7 +.Xr pty 4 .Sh AUTHORS .An Nicholas Marriott Aq Mt nicm@users.sourceforge.net From 98994a8bb1ca7602f16a5de0d4482efd299f5d7b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 00:49:10 +0000 Subject: [PATCH 905/949] termios(4) --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index dcf0dc57..931fe327 100644 --- a/tmux.1 +++ b/tmux.1 @@ -824,7 +824,7 @@ and specify the size of the initial window (80 by 24 if not given). .Pp If run from a terminal, any -.Xr termios 3 +.Xr termios 4 special characters are saved and used for new windows in the new session. .Pp The From dbfce2a4d8ea0bd4773eacf154fd3e3406d1be5e Mon Sep 17 00:00:00 2001 From: mmcc Date: Tue, 8 Dec 2015 00:51:17 +0000 Subject: [PATCH 906/949] Use ^= instead of a verbose alternative. ok nicm@ --- cmd-switch-client.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index dbdc5b63..4c847584 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -52,12 +52,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (args_has(args, 'r')) { - if (c->flags & CLIENT_READONLY) - c->flags &= ~CLIENT_READONLY; - else - c->flags |= CLIENT_READONLY; - } + if (args_has(args, 'r')) + c->flags ^= CLIENT_READONLY; tablename = args_get(args, 'T'); if (tablename != NULL) { From d2fb0efcd197bf0d581a0f7b1e27223d095cb339 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Dec 2015 01:10:31 +0000 Subject: [PATCH 907/949] Add hooks infrastructure, basic commands (set-hook, show-hooks) and a couple of not very useful client hooks. This will eventually let commands be run at various points and on notifications. Joint work with Thomas Adam. --- Makefile | 2 + cmd-attach-session.c | 5 +- cmd-detach-client.c | 12 ++-- cmd-set-hook.c | 116 ++++++++++++++++++++++++++++++++++++ cmd.c | 4 ++ hooks.c | 139 +++++++++++++++++++++++++++++++++++++++++++ server-client.c | 15 +++++ session.c | 4 ++ tmux.1 | 46 ++++++++++++++ tmux.c | 3 + tmux.h | 31 ++++++++-- 11 files changed, 364 insertions(+), 13 deletions(-) create mode 100644 cmd-set-hook.c create mode 100644 hooks.c diff --git a/Makefile b/Makefile index 10c6a783..a60f1f19 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ SRCS= alerts.c \ cmd-send-keys.c \ cmd-set-buffer.c \ cmd-set-environment.c \ + cmd-set-hook.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ @@ -78,6 +79,7 @@ SRCS= alerts.c \ format.c \ grid-view.c \ grid.c \ + hooks.c \ input-keys.c \ input.c \ job.c \ diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 5bde0d80..45b05b09 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -108,7 +108,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c_loop->peer, MSG_DETACH, s->name); + server_client_detach(c, MSG_DETACH); } } @@ -139,7 +139,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c_loop->peer, MSG_DETACH, s->name); + server_client_detach(c_loop, MSG_DETACH); } } @@ -159,6 +159,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); + hooks_run(c->session->hooks, "client-attached", c); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index f7369df0..d8128eae 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -72,9 +72,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session != s) - continue; - proc_send_s(cloop->peer, msgtype, cloop->session->name); + if (cloop->session == s) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_STOP); } @@ -85,13 +84,12 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session == NULL || cloop == c) - continue; - proc_send_s(cloop->peer, msgtype, cloop->session->name); + if (cloop->session != NULL && cloop != c) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_NORMAL); } - proc_send_s(c->peer, msgtype, c->session->name); + server_client_detach(c, msgtype); return (CMD_RETURN_STOP); } diff --git a/cmd-set-hook.c b/cmd-set-hook.c new file mode 100644 index 00000000..f35e7a0a --- /dev/null +++ b/cmd-set-hook.c @@ -0,0 +1,116 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Thomas Adam + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Set or show global or session hooks. + */ + +enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmd_q *); + +const struct cmd_entry cmd_set_hook_entry = { + "set-hook", NULL, + "gt:u", 1, 2, + "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", + 0, + cmd_set_hook_exec +}; + +const struct cmd_entry cmd_show_hooks_entry = { + "show-hooks", NULL, + "gt:", 0, 1, + "[-g] " CMD_TARGET_SESSION_USAGE, + 0, + cmd_set_hook_exec +}; + +enum cmd_retval +cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + struct session *s; + struct cmd_list *cmdlist; + struct hooks *hooks; + struct hook *hook; + char *cause, *tmp; + const char *name, *cmd; + + if (args_has(args, 'g')) + hooks = global_hooks; + else { + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) + return (CMD_RETURN_ERROR); + hooks = s->hooks; + } + + if (self->entry == &cmd_show_hooks_entry) { + hook = hooks_first(hooks); + while (hook != NULL) { + tmp = cmd_list_print(hook->cmdlist); + cmdq_print(cmdq, "%s -> %s", hook->name, tmp); + free(tmp); + + hook = hooks_next(hook); + } + return (CMD_RETURN_NORMAL); + } + + name = args->argv[0]; + if (*name == '\0') { + cmdq_error(cmdq, "invalid hook name"); + return (CMD_RETURN_ERROR); + } + if (args->argc < 2) + cmd = NULL; + else + cmd = args->argv[1]; + + if (args_has(args, 'u')) { + if (cmd != NULL) { + cmdq_error(cmdq, "command passed to unset hook: %s", + name); + return (CMD_RETURN_ERROR); + } + if ((hook = hooks_find(hooks, name)) != NULL) + hooks_remove(hooks, hook); + return (CMD_RETURN_NORMAL); + } + + if (cmd == NULL) { + cmdq_error(cmdq, "no command to set hook: %s", name); + return (CMD_RETURN_ERROR); + } + if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { + if (cause != NULL) { + cmdq_error(cmdq, "%s", cause); + free(cause); + } + return (CMD_RETURN_ERROR); + } + hooks_add(hooks, name, cmdlist); + cmd_list_free(cmdlist); + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd.c b/cmd.c index 824d9caf..a950a49a 100644 --- a/cmd.c +++ b/cmd.c @@ -96,10 +96,12 @@ extern const struct cmd_entry cmd_send_prefix_entry; extern const struct cmd_entry cmd_server_info_entry; extern const struct cmd_entry cmd_set_buffer_entry; extern const struct cmd_entry cmd_set_environment_entry; +extern const struct cmd_entry cmd_set_hook_entry; extern const struct cmd_entry cmd_set_option_entry; extern const struct cmd_entry cmd_set_window_option_entry; extern const struct cmd_entry cmd_show_buffer_entry; extern const struct cmd_entry cmd_show_environment_entry; +extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_window_options_entry; @@ -183,10 +185,12 @@ const struct cmd_entry *cmd_table[] = { &cmd_server_info_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, + &cmd_set_hook_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, &cmd_show_buffer_entry, &cmd_show_environment_entry, + &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, &cmd_show_window_options_entry, diff --git a/hooks.c b/hooks.c new file mode 100644 index 00000000..95d6b9d8 --- /dev/null +++ b/hooks.c @@ -0,0 +1,139 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Thomas Adam + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +struct hooks { + RB_HEAD(hooks_tree, hook) tree; + struct hooks *parent; +}; + +static int hooks_cmp(struct hook *, struct hook *); +RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); +RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); + +struct hook *hooks_find1(struct hooks *, const char *); + +static int +hooks_cmp(struct hook *hook1, struct hook *hook2) +{ + return (strcmp(hook1->name, hook2->name)); +} + +struct hooks * +hooks_create(struct hooks *parent) +{ + struct hooks *hooks; + + hooks = xcalloc(1, sizeof *hooks); + RB_INIT(&hooks->tree); + hooks->parent = parent; + return (hooks); +} + +void +hooks_free(struct hooks *hooks) +{ + struct hook *hook, *hook1; + + RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) + hooks_remove(hooks, hook); + free(hooks); +} + +struct hook * +hooks_first(struct hooks *hooks) +{ + return (RB_MIN(hooks_tree, &hooks->tree)); +} + +struct hook * +hooks_next(struct hook *hook) +{ + return (RB_NEXT(hooks_tree, &hooks->tree, hook)); +} + +void +hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) +{ + struct hook *hook; + + if ((hook = hooks_find1(hooks, name)) != NULL) + hooks_remove(hooks, hook); + + hook = xcalloc(1, sizeof *hook); + hook->name = xstrdup(name); + hook->cmdlist = cmdlist; + hook->cmdlist->references++; + RB_INSERT(hooks_tree, &hooks->tree, hook); +} + +void +hooks_remove(struct hooks *hooks, struct hook *hook) +{ + RB_REMOVE(hooks_tree, &hooks->tree, hook); + cmd_list_free(hook->cmdlist); + free((char *) hook->name); + free(hook); +} + +struct hook * +hooks_find1(struct hooks *hooks, const char *name) +{ + struct hook hook; + + hook.name = name; + return (RB_FIND(hooks_tree, &hooks->tree, &hook)); +} + +struct hook * +hooks_find(struct hooks *hooks, const char *name) +{ + struct hook hook0, *hook; + + hook0.name = name; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + while (hook == NULL) { + hooks = hooks->parent; + if (hooks == NULL) + break; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + } + return (hook); +} + +void +hooks_run(struct hooks *hooks, const char *name, struct client *c) +{ + struct hook *hook; + struct cmd_q *cmdq; + + hook = hooks_find(hooks, name); + if (hook == NULL) + return; + log_debug("running hook %s", name); + + cmdq = cmdq_new(c); + cmdq_run(cmdq, hook->cmdlist, NULL); + cmdq_free(cmdq); +} diff --git a/server-client.c b/server-client.c index d2abd356..c948e980 100644 --- a/server-client.c +++ b/server-client.c @@ -256,6 +256,19 @@ server_client_free(__unused int fd, __unused short events, void *arg) free(c); } +/* Detach a client. */ +void +server_client_detach(struct client *c, enum msgtype msgtype) +{ + struct session *s = c->session; + + if (s == NULL) + return; + + hooks_run(c->session->hooks, "client-detached", c); + proc_send_s(c->peer, msgtype, s->name); +} + /* Check for mouse keys. */ key_code server_client_check_mouse(struct client *c) @@ -995,6 +1008,8 @@ server_client_dispatch(struct imsg *imsg, void *arg) recalculate_sizes(); server_redraw_client(c); } + if (c->session != NULL) + hooks_run(c->session->hooks, "client-resized", c); break; case MSG_EXITING: if (datalen != 0) diff --git a/session.c b/session.c index c5721c9a..b3ae2b42 100644 --- a/session.c +++ b/session.c @@ -123,7 +123,9 @@ session_create(const char *name, int argc, char **argv, const char *path, s->environ = environ_create(); if (env != NULL) environ_copy(env, s->environ); + s->options = options_create(global_s_options); + s->hooks = hooks_create(global_hooks); s->tio = NULL; if (tio != NULL) { @@ -189,7 +191,9 @@ session_free(__unused int fd, __unused short events, void *arg) if (s->references == 0) { environ_free(s->environ); + options_free(s->options); + hooks_free(s->hooks); free(s->name); free(s); diff --git a/tmux.1 b/tmux.1 index 7d45688c..6971100e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3193,6 +3193,52 @@ is used. .Fl v shows only the option value, not the name. .El +.Sh HOOKS +.Nm +allows commands to run on various triggers, called +.Em hooks . +Each hook has a +.Em name . +The following hooks are available: +.Bl -tag -width "XXXXXXXXXXXXXXXX" +.It client-attached +Run when a client is attached. +.It client-detached +Run when a client is detached +.It client-resized +Run when a client is resized. +.El +.Pp +Hooks are managed with these commands: +.Bl -tag -width Ds +.It Xo Ic set-hook +.Op Fl g +.Op Fl t Ar target-session +.Ar hook-name +.Ar command +.Xc +Sets hook +.Ar hook-name +to +.Ar command . +If +.Fl g +is given, +.Em hook-name +is added to the global list of hooks, otherwise it is added to the session +hooks (for +.Ar target-session +with +.Fl t ) . +Like options, session hooks inherit from the global ones. +.It Xo Ic show-hooks +.Op Fl g +.Op Fl t Ar target-session +.Xc +Shows the global list of hooks with +.Fl g , +otherwise the session hooks. +.Ed .Sh MOUSE SUPPORT If the .Ic mouse diff --git a/tmux.c b/tmux.c index 68f0092d..fe5e54a5 100644 --- a/tmux.c +++ b/tmux.c @@ -38,6 +38,7 @@ struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ struct environ *global_environ; +struct hooks *global_hooks; struct timeval start_time; const char *socket_path; @@ -269,6 +270,8 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; } + global_hooks = hooks_create(NULL); + global_environ = environ_create(); for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); diff --git a/tmux.h b/tmux.h index a1e16955..fa589f57 100644 --- a/tmux.h +++ b/tmux.h @@ -691,6 +691,14 @@ struct grid { struct grid_line *linedata; }; +/* Hook data structures. */ +struct hook { + const char *name; + struct cmd_q *cmdq; + struct cmd_list *cmdlist; + RB_ENTRY(hook) entry; +}; + /* Option data structures. */ struct options_entry { char *name; @@ -1011,6 +1019,7 @@ struct session { struct winlink_stack lastw; struct winlinks windows; + struct hooks *hooks; struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ @@ -1427,10 +1436,11 @@ struct options_table_entry { #define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ -extern struct options *global_options; -extern struct options *global_s_options; -extern struct options *global_w_options; -extern struct environ *global_environ; +extern struct hooks *global_hooks; +extern struct options *global_options; +extern struct options *global_s_options; +extern struct options *global_w_options; +extern struct environ *global_environ; extern struct timeval start_time; extern const char *socket_path; const char *getshell(void); @@ -1495,6 +1505,18 @@ void format_defaults_pane(struct format_tree *, void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); +/* hooks.c */ +struct hook; +struct hooks *hooks_create(struct hooks *); +void hooks_free(struct hooks *); +struct hook *hooks_first(struct hooks *); +struct hook *hooks_next(struct hook *); +void hooks_add(struct hooks *, const char *, struct cmd_list *); +void hooks_copy(struct hooks *, struct hooks *); +void hooks_remove(struct hooks *, struct hook *); +struct hook *hooks_find(struct hooks *, const char *); +void hooks_run(struct hooks *, const char *, struct client *); + /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; extern struct mode_key_tree mode_key_tree_vi_edit; @@ -1782,6 +1804,7 @@ void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); +void server_client_detach(struct client *, enum msgtype); void server_client_loop(void); void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); From 1f94274b92b9b367887196755bdd3a848b3b847a Mon Sep 17 00:00:00 2001 From: jmc Date: Tue, 8 Dec 2015 06:42:07 +0000 Subject: [PATCH 908/949] Ed was meant to be El; --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 6971100e..cbd01d9e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3238,7 +3238,7 @@ Like options, session hooks inherit from the global ones. Shows the global list of hooks with .Fl g , otherwise the session hooks. -.Ed +.El .Sh MOUSE SUPPORT If the .Ic mouse From 8f671d3eefa0af67b9e753eeae032e3ded6ffd00 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Dec 2015 08:14:04 +0000 Subject: [PATCH 909/949] Spacing nits. --- options.c | 2 +- tmux.h | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/options.c b/options.c index c355f9ce..5f958d23 100644 --- a/options.c +++ b/options.c @@ -47,7 +47,7 @@ options_cmp(struct options_entry *o1, struct options_entry *o2) struct options * options_create(struct options *parent) { - struct options *oo; + struct options *oo; oo = xcalloc(1, sizeof *oo); RB_INIT(&oo->tree); diff --git a/tmux.h b/tmux.h index fa589f57..7757f12f 100644 --- a/tmux.h +++ b/tmux.h @@ -693,10 +693,12 @@ struct grid { /* Hook data structures. */ struct hook { - const char *name; - struct cmd_q *cmdq; - struct cmd_list *cmdlist; - RB_ENTRY(hook) entry; + const char *name; + + struct cmd_q *cmdq; + struct cmd_list *cmdlist; + + RB_ENTRY(hook) entry; }; /* Option data structures. */ From e0f26dcda36dc35741da6047a11efb853b3137d9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Dec 2015 08:34:18 +0000 Subject: [PATCH 910/949] Remove format_create_flags and just pass flags to format_create. --- cmd-attach-session.c | 2 +- cmd-break-pane.c | 2 +- cmd-display-message.c | 2 +- cmd-if-shell.c | 2 +- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 2 +- cmd-list-panes.c | 2 +- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 2 +- cmd-new-session.c | 4 ++-- cmd-new-window.c | 4 ++-- cmd-pipe-pane.c | 2 +- cmd-run-shell.c | 2 +- cmd-split-window.c | 4 ++-- control-notify.c | 2 +- format.c | 9 +-------- names.c | 2 +- server-client.c | 2 +- status.c | 8 ++++---- tmux.h | 3 +-- window-choose.c | 2 +- window-copy.c | 2 +- 22 files changed, 28 insertions(+), 36 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 45b05b09..dd953ebd 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -93,7 +93,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (cflag != NULL) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = format_expand(ft, cflag); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 707cd09b..75ae9044 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -101,7 +101,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, wp); diff --git a/cmd-display-message.c b/cmd-display-message.c index 1f9f40fa..2fb76caa 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -88,7 +88,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, s, wl, wp); msg = format_expand_time(ft, template, time(NULL)); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 47f259e7..9907a764 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -86,7 +86,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(); + ft = format_create(0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index a0036032..a95cc336 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -54,7 +54,7 @@ cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { - ft = format_create(); + ft = format_create(0); format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 372b5283..2bde6b32 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -69,7 +69,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session == NULL || (s != NULL && s != c->session)) continue; - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 0af391c5..14ee7064 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -125,7 +125,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 49ef9467..099b4f2e 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -61,7 +61,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmd_q *cmdq) n = 0; RB_FOREACH(s, sessions, &sessions) { - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 3f6b2a4c..958380e2 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -105,7 +105,7 @@ cmd_list_windows_session( n = 0; RB_FOREACH(wl, winlinks, &s->windows) { - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 65dc6cf5..52e24ae6 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -119,7 +119,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Get the new session working directory. */ to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -283,7 +283,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); diff --git a/cmd-new-window.c b/cmd-new-window.c index 24204746..0865a9bc 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -94,7 +94,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = to_free = format_expand(ft, args_get(args, 'c')); @@ -143,7 +143,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, NULL); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 87c802b9..1577c252 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -89,7 +89,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } /* Expand the command. */ - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, s, wl, wp); cmd = format_expand_time(ft, args->argv[0], time(NULL)); format_free(ft); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index def3ef01..a381fb91 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -100,7 +100,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(); + ft = format_create(0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-split-window.c b/cmd-split-window.c index fb766d3b..859e1559 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -88,7 +88,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -165,7 +165,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, new_wp); diff --git a/control-notify.c b/control-notify.c index db3648a4..7036f6b5 100644 --- a/control-notify.c +++ b/control-notify.c @@ -88,7 +88,7 @@ control_notify_window_layout_changed(struct window *w) if (w->layout_root == NULL) continue; - ft = format_create(); + ft = format_create(0); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_defaults(ft, c, NULL, wl, NULL); diff --git a/format.c b/format.c index bafd8ce6..61a911ed 100644 --- a/format.c +++ b/format.c @@ -465,14 +465,7 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) /* Create a new tree. */ struct format_tree * -format_create(void) -{ - return (format_create_flags(0)); -} - -/* Create a new tree for the status line. */ -struct format_tree * -format_create_flags(int flags) +format_create(int flags) { struct format_tree *ft; diff --git a/names.c b/names.c index 7d956db3..268403e7 100644 --- a/names.c +++ b/names.c @@ -118,7 +118,7 @@ format_window_name(struct window *w) struct format_tree *ft; char *fmt, *name; - ft = format_create(); + ft = format_create(0); format_defaults_window(ft, w); format_defaults_pane(ft, w->active); diff --git a/server-client.c b/server-client.c index c948e980..92ce024d 100644 --- a/server-client.c +++ b/server-client.c @@ -933,7 +933,7 @@ server_client_set_title(struct client *c) template = options_get_string(s->options, "set-titles-string"); - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, NULL, NULL, NULL); title = format_expand_time(ft, template, time(NULL)); diff --git a/status.c b/status.c index d05376c0..5539fa31 100644 --- a/status.c +++ b/status.c @@ -500,9 +500,9 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) return (xstrdup("")); if (c->flags & CLIENT_STATUSFORCE) - ft = format_create_flags(FORMAT_STATUS|FORMAT_FORCE); + ft = format_create(FORMAT_STATUS|FORMAT_FORCE); else - ft = format_create_flags(FORMAT_STATUS); + ft = format_create(FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); @@ -661,7 +661,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, int keys; time_t t; - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); @@ -722,7 +722,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) struct format_tree *ft; time_t t; - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); diff --git a/tmux.h b/tmux.h index 7757f12f..5df72052 100644 --- a/tmux.h +++ b/tmux.h @@ -1492,8 +1492,7 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 struct format_tree; -struct format_tree *format_create(void); -struct format_tree *format_create_flags(int); +struct format_tree *format_create(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); diff --git a/window-choose.c b/window-choose.c index cab3e5d8..862531fb 100644 --- a/window-choose.c +++ b/window-choose.c @@ -186,7 +186,7 @@ window_choose_data_create(int type, struct client *c, struct session *s) wcd = xmalloc(sizeof *wcd); wcd->type = type; - wcd->ft = format_create(); + wcd->ft = format_create(0); wcd->ft_template = NULL; wcd->command = NULL; diff --git a/window-copy.c b/window-copy.c index 37025302..eb17460d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1480,7 +1480,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, if (buf == NULL) return; - ft = format_create(); + ft = format_create(0); format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); From a988c36ccb7d411d995edfa9dbdb7682295d62a0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 08:46:06 +0000 Subject: [PATCH 911/949] Add to .mailmap, and sort. --- .mailmap | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.mailmap b/.mailmap index 847494ba..b4956775 100644 --- a/.mailmap +++ b/.mailmap @@ -1,10 +1,9 @@ Bob Beck beck -Claudio Jeker claudio +Claudio Jeker claudio Igor Sobrado sobrado Ingo Schwarze schwarze Jacek Masiulaniec jacekm Jason McIntyre jmc -Jason McIntyre jcm Joel Sing jsing Jonathan Gray jsg Kenneth R Westerback krw @@ -12,6 +11,7 @@ Marc Espie espie Matthew Dempsky matthew Matthias Kilian kili Matthieu Herrb matthieu +Michael McConville mmcc Miod Vallat miod Nicholas Marriott Nicholas Marriott Nicholas Marriott nicm @@ -25,12 +25,12 @@ Sebastian Benoit benno Stefan Sperling stsp Stuart Henderson sthen Ted Unangst tedu -Theo de Raadt deraadt Theo de Raadt Theo Deraadt +Theo de Raadt deraadt Thomas Adam Thomas -Thomas Adam n6tadam Thomas Adam Thomas Adam -Tim van der Molen tim +Thomas Adam n6tadam +Tim van der Molen tim Tobias Stoeckmann tobias Todd C Miller millert William Yodlowsky william From 01831da5f5dd2a1b7622ad421a757cacedbdfc59 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 12:27:36 +0000 Subject: [PATCH 912/949] Add cmdq as an argument to format_create and add a format for the command name (will also be used for more later). --- cmd-attach-session.c | 2 +- cmd-break-pane.c | 2 +- cmd-display-message.c | 2 +- cmd-if-shell.c | 2 +- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 2 +- cmd-list-panes.c | 2 +- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 2 +- cmd-new-session.c | 4 ++-- cmd-new-window.c | 4 ++-- cmd-pipe-pane.c | 2 +- cmd-run-shell.c | 2 +- cmd-split-window.c | 4 ++-- control-notify.c | 2 +- format.c | 5 ++++- names.c | 2 +- server-client.c | 2 +- status.c | 8 ++++---- tmux.1 | 1 + tmux.h | 2 +- window-choose.c | 2 +- window-copy.c | 2 +- 23 files changed, 32 insertions(+), 28 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index dd953ebd..73c82248 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -93,7 +93,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (cflag != NULL) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = format_expand(ft, cflag); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 75ae9044..98d6ad3d 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -101,7 +101,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, wp); diff --git a/cmd-display-message.c b/cmd-display-message.c index 2fb76caa..7ca8e9c4 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -88,7 +88,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, c, s, wl, wp); msg = format_expand_time(ft, template, time(NULL)); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 9907a764..3345e8ce 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -86,7 +86,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index a95cc336..218eb6ff 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -54,7 +54,7 @@ cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 2bde6b32..2c13d398 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -69,7 +69,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session == NULL || (s != NULL && s != c->session)) continue; - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 14ee7064..23e530e2 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -125,7 +125,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 099b4f2e..fed0c2ee 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -61,7 +61,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmd_q *cmdq) n = 0; RB_FOREACH(s, sessions, &sessions) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 958380e2..81793e10 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -105,7 +105,7 @@ cmd_list_windows_session( n = 0; RB_FOREACH(wl, winlinks, &s->windows) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 52e24ae6..aee69e12 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -119,7 +119,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Get the new session working directory. */ to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -283,7 +283,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); diff --git a/cmd-new-window.c b/cmd-new-window.c index 0865a9bc..ddfd6a20 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -94,7 +94,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = to_free = format_expand(ft, args_get(args, 'c')); @@ -143,7 +143,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, NULL); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 1577c252..79ef7818 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -89,7 +89,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } /* Expand the command. */ - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, c, s, wl, wp); cmd = format_expand_time(ft, args->argv[0], time(NULL)); format_free(ft); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index a381fb91..fd5d84d4 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -100,7 +100,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-split-window.c b/cmd-split-window.c index 859e1559..80d9e2b1 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -88,7 +88,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -165,7 +165,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, new_wp); diff --git a/control-notify.c b/control-notify.c index 7036f6b5..a40f8d7c 100644 --- a/control-notify.c +++ b/control-notify.c @@ -88,7 +88,7 @@ control_notify_window_layout_changed(struct window *w) if (w->layout_root == NULL) continue; - ft = format_create(0); + ft = format_create(NULL, 0); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_defaults(ft, c, NULL, wl, NULL); diff --git a/format.c b/format.c index 61a911ed..3e666330 100644 --- a/format.c +++ b/format.c @@ -465,7 +465,7 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) /* Create a new tree. */ struct format_tree * -format_create(int flags) +format_create(struct cmd_q *cmdq, int flags) { struct format_tree *ft; @@ -484,6 +484,9 @@ format_create(int flags) format_add(ft, "socket_path", "%s", socket_path); format_add_tv(ft, "start_time", &start_time); + if (cmdq != NULL && cmdq->cmd != NULL) + format_add(ft, "command_name", "%s", cmdq->cmd->entry->name); + return (ft); } diff --git a/names.c b/names.c index 268403e7..3c25e215 100644 --- a/names.c +++ b/names.c @@ -118,7 +118,7 @@ format_window_name(struct window *w) struct format_tree *ft; char *fmt, *name; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults_window(ft, w); format_defaults_pane(ft, w->active); diff --git a/server-client.c b/server-client.c index 92ce024d..8b6be5d9 100644 --- a/server-client.c +++ b/server-client.c @@ -933,7 +933,7 @@ server_client_set_title(struct client *c) template = options_get_string(s->options, "set-titles-string"); - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); title = format_expand_time(ft, template, time(NULL)); diff --git a/status.c b/status.c index 5539fa31..b555a9de 100644 --- a/status.c +++ b/status.c @@ -500,9 +500,9 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) return (xstrdup("")); if (c->flags & CLIENT_STATUSFORCE) - ft = format_create(FORMAT_STATUS|FORMAT_FORCE); + ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE); else - ft = format_create(FORMAT_STATUS); + ft = format_create(NULL, FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); @@ -661,7 +661,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, int keys; time_t t; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); @@ -722,7 +722,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) struct format_tree *ft; time_t t; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); diff --git a/tmux.1 b/tmux.1 index cbd01d9e..a0dbd486 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3406,6 +3406,7 @@ The following variables are available, where appropriate: .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports utf8" .It Li "client_width" Ta "" Ta "Width of client" +.It Li "command_name" Ta "" Ta "Name of command in use, if any" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" .It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" diff --git a/tmux.h b/tmux.h index 5df72052..082c9068 100644 --- a/tmux.h +++ b/tmux.h @@ -1492,7 +1492,7 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 struct format_tree; -struct format_tree *format_create(int); +struct format_tree *format_create(struct cmd_q *, int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); diff --git a/window-choose.c b/window-choose.c index 862531fb..35170c65 100644 --- a/window-choose.c +++ b/window-choose.c @@ -186,7 +186,7 @@ window_choose_data_create(int type, struct client *c, struct session *s) wcd = xmalloc(sizeof *wcd); wcd->type = type; - wcd->ft = format_create(0); + wcd->ft = format_create(NULL, 0); wcd->ft_template = NULL; wcd->command = NULL; diff --git a/window-copy.c b/window-copy.c index eb17460d..e09a504c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1480,7 +1480,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, if (buf == NULL) return; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); From d7e11d0af78bacc7722998509ac93be7fcffc7b4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 12:39:47 +0000 Subject: [PATCH 913/949] Check alerts when session changes, from Patrick Palka. --- cmd-switch-client.c | 1 + server-fn.c | 1 + 2 files changed, 2 insertions(+) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 4c847584..edc0fd69 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -131,6 +131,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) server_check_unattached(); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + alerts_check_session(s); return (CMD_RETURN_NORMAL); } diff --git a/server-fn.c b/server-fn.c index a22c964d..07ade08c 100644 --- a/server-fn.c +++ b/server-fn.c @@ -389,6 +389,7 @@ server_destroy_session(struct session *s) session_update_activity(s_new, NULL); gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); + alerts_check_session(s_new); } } recalculate_sizes(); From bd5918760ecd1f40a574ccc8a302af869f68c27f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 15:46:57 +0000 Subject: [PATCH 914/949] We cannot do hooks_find and then hooks_remove because it might have come from the parent (global) tree, instead make it remove by name like options. While here, also tidy up a few bits of options and hooks handling (use RB_FOREACH_SAFE, and a helper function for the free). --- cmd-set-hook.c | 3 +-- hooks.c | 28 +++++++++++++++++++--------- options.c | 42 +++++++++++++++++++++--------------------- tmux.h | 4 ++-- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/cmd-set-hook.c b/cmd-set-hook.c index f35e7a0a..ec58418e 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -93,8 +93,7 @@ cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) name); return (CMD_RETURN_ERROR); } - if ((hook = hooks_find(hooks, name)) != NULL) - hooks_remove(hooks, hook); + hooks_remove(hooks, name); return (CMD_RETURN_NORMAL); } diff --git a/hooks.c b/hooks.c index 95d6b9d8..6f548d23 100644 --- a/hooks.c +++ b/hooks.c @@ -32,7 +32,8 @@ static int hooks_cmp(struct hook *, struct hook *); RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); -struct hook *hooks_find1(struct hooks *, const char *); +static struct hook *hooks_find1(struct hooks *, const char *); +static void hooks_free1(struct hooks *, struct hook *); static int hooks_cmp(struct hook *hook1, struct hook *hook2) @@ -51,13 +52,22 @@ hooks_create(struct hooks *parent) return (hooks); } +static void +hooks_free1(struct hooks *hooks, struct hook *hook) +{ + RB_REMOVE(hooks_tree, &hooks->tree, hook); + cmd_list_free(hook->cmdlist); + free((char *)hook->name); + free(hook); +} + void hooks_free(struct hooks *hooks) { struct hook *hook, *hook1; RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) - hooks_remove(hooks, hook); + hooks_free1(hooks, hook); free(hooks); } @@ -79,7 +89,7 @@ hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) struct hook *hook; if ((hook = hooks_find1(hooks, name)) != NULL) - hooks_remove(hooks, hook); + hooks_free1(hooks, hook); hook = xcalloc(1, sizeof *hook); hook->name = xstrdup(name); @@ -89,15 +99,15 @@ hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) } void -hooks_remove(struct hooks *hooks, struct hook *hook) +hooks_remove(struct hooks *hooks, const char *name) { - RB_REMOVE(hooks_tree, &hooks->tree, hook); - cmd_list_free(hook->cmdlist); - free((char *) hook->name); - free(hook); + struct hook *hook; + + if ((hook = hooks_find1(hooks, name)) != NULL) + hooks_free1(hooks, hook); } -struct hook * +static struct hook * hooks_find1(struct hooks *hooks, const char *name) { struct hook hook; diff --git a/options.c b/options.c index 5f958d23..02f0f957 100644 --- a/options.c +++ b/options.c @@ -34,11 +34,13 @@ struct options { struct options *parent; }; -int options_cmp(struct options_entry *, struct options_entry *); +static int options_cmp(struct options_entry *, struct options_entry *); RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); RB_GENERATE(options_tree, options_entry, entry, options_cmp); -int +static void options_free1(struct options *, struct options_entry *); + +static int options_cmp(struct options_entry *o1, struct options_entry *o2) { return (strcmp(o1->name, o2->name)); @@ -55,19 +57,23 @@ options_create(struct options *parent) return (oo); } +static void +options_free1(struct options *oo, struct options_entry *o) +{ + RB_REMOVE(options_tree, &oo->tree, o); + free((char *)o->name); + if (o->type == OPTIONS_STRING) + free(o->str); + free(o); +} + void options_free(struct options *oo) { - struct options_entry *o; + struct options_entry *o, *o1; - while (!RB_EMPTY(&oo->tree)) { - o = RB_ROOT(&oo->tree); - RB_REMOVE(options_tree, &oo->tree, o); - free(o->name); - if (o->type == OPTIONS_STRING) - free(o->str); - free(o); - } + RB_FOREACH_SAFE (o, options_tree, &oo->tree, o1) + options_free1(oo, o); free(oo); } @@ -88,7 +94,7 @@ options_find1(struct options *oo, const char *name) { struct options_entry p; - p.name = (char *) name; + p.name = (char *)name; return (RB_FIND(options_tree, &oo->tree, &p)); } @@ -97,7 +103,7 @@ options_find(struct options *oo, const char *name) { struct options_entry *o, p; - p.name = (char *) name; + p.name = (char *)name; o = RB_FIND(options_tree, &oo->tree, &p); while (o == NULL) { oo = oo->parent; @@ -113,14 +119,8 @@ options_remove(struct options *oo, const char *name) { struct options_entry *o; - if ((o = options_find1(oo, name)) == NULL) - return; - - RB_REMOVE(options_tree, &oo->tree, o); - free(o->name); - if (o->type == OPTIONS_STRING) - free(o->str); - free(o); + if ((o = options_find1(oo, name)) != NULL) + options_free1(oo, o); } struct options_entry * diff --git a/tmux.h b/tmux.h index 082c9068..a20b96c5 100644 --- a/tmux.h +++ b/tmux.h @@ -703,7 +703,7 @@ struct hook { /* Option data structures. */ struct options_entry { - char *name; + const char *name; enum { OPTIONS_STRING, @@ -1514,7 +1514,7 @@ struct hook *hooks_first(struct hooks *); struct hook *hooks_next(struct hook *); void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); -void hooks_remove(struct hooks *, struct hook *); +void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); void hooks_run(struct hooks *, const char *, struct client *); From f2be3ad46fc37e5689d836f994fa79968d047133 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 16:27:01 +0000 Subject: [PATCH 915/949] Mention {src,dst}-{window,pane} where we define target-{window,pane}. --- tmux.1 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index a0dbd486..115050a6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -416,6 +416,10 @@ If a session is omitted, the current session is used if available; if no current session is available, the most recently used is chosen. .Pp .Ar target-window +(or +.Ar src-window +or +.Ar dst-window ) specifies a window in the form .Em session Ns \&: Ns Em window . .Em session @@ -471,8 +475,11 @@ Each has a single-character alternative form. .El .Pp .Ar target-pane -may be a -pane ID or takes a similar form to +(or +.Ar src-pane +or +.Ar dst-pane ) +may be a pane ID or takes a similar form to .Ar target-window but with the optional addition of a period followed by a pane index or pane ID, for example: From 88bc8f3528b973adb17f541e21aa415923a10f1e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 16:37:21 +0000 Subject: [PATCH 916/949] Style nits and line wrapping of function declarations. --- cmd-list-windows.c | 4 ++-- layout.c | 22 +++++++++++----------- screen-write.c | 12 ++++++------ status.c | 4 ++-- tmux.h | 38 +++++++++++++++++++------------------- tty-term.c | 8 ++++---- tty.c | 12 ++++++------ window-choose.c | 32 ++++++++++++++++---------------- 8 files changed, 66 insertions(+), 66 deletions(-) diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 81793e10..1eaee2d7 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -81,8 +81,8 @@ cmd_list_windows_server(struct cmd *self, struct cmd_q *cmdq) } void -cmd_list_windows_session( - struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) +cmd_list_windows_session(struct cmd *self, struct session *s, + struct cmd_q *cmdq, int type) { struct args *args = self->args; struct winlink *wl; diff --git a/layout.c b/layout.c index 266d1f39..c448814a 100644 --- a/layout.c +++ b/layout.c @@ -85,9 +85,9 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) { struct layout_cell *lcchild; - log_debug( - "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc, - lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy); + log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, + " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, + lc->sy); switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: @@ -100,8 +100,8 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) } void -layout_set_size( - struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff) +layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, + u_int yoff) { lc->sx = sx; lc->sy = sy; @@ -521,8 +521,8 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) /* Helper function to grow pane. */ int -layout_resize_pane_grow( - struct layout_cell *lc, enum layout_type type, int needed) +layout_resize_pane_grow(struct layout_cell *lc, enum layout_type type, + int needed) { struct layout_cell *lcadd, *lcremove; u_int size; @@ -562,8 +562,8 @@ layout_resize_pane_grow( /* Helper function to shrink pane. */ int -layout_resize_pane_shrink( - struct layout_cell *lc, enum layout_type type, int needed) +layout_resize_pane_shrink(struct layout_cell *lc, enum layout_type type, + int needed) { struct layout_cell *lcadd, *lcremove; u_int size; @@ -605,8 +605,8 @@ layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) * split. This must be followed by layout_assign_pane before much else happens! **/ struct layout_cell * -layout_split_pane( - struct window_pane *wp, enum layout_type type, int size, int insert_before) +layout_split_pane(struct window_pane *wp, enum layout_type type, int size, + int insert_before) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; u_int sx, sy, xoff, yoff, size1, size2; diff --git a/screen-write.c b/screen-write.c index 53067efe..e53d3799 100644 --- a/screen-write.c +++ b/screen-write.c @@ -767,8 +767,8 @@ screen_write_reverseindex(struct screen_write_ctx *ctx) /* Set scroll region. */ void -screen_write_scrollregion( - struct screen_write_ctx *ctx, u_int rupper, u_int rlower) +screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, + u_int rlower) { struct screen *s = ctx->s; @@ -874,16 +874,16 @@ screen_write_clearscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; + u_int sx = screen_size_x(s); + u_int sy = screen_size_y(s); screen_write_initctx(ctx, &ttyctx, 0); /* Scroll into history if it is enabled. */ if (s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid); - else { - grid_view_clear( - s->grid, 0, 0, screen_size_x(s), screen_size_y(s)); - } + else + grid_view_clear(s->grid, 0, 0, sx, sy); tty_write(tty_cmd_clearscreen, &ttyctx); } diff --git a/status.c b/status.c index b555a9de..ca78bd53 100644 --- a/status.c +++ b/status.c @@ -1124,8 +1124,8 @@ status_prompt_key(struct client *c, key_code key) } if (c->prompt_flags & PROMPT_SINGLE) { - if (c->prompt_callbackfn( - c->prompt_data, c->prompt_buffer) == 0) + if (c->prompt_callbackfn(c->prompt_data, + c->prompt_buffer) == 0) status_prompt_clear(c); } diff --git a/tmux.h b/tmux.h index a20b96c5..60d16fe3 100644 --- a/tmux.h +++ b/tmux.h @@ -1664,10 +1664,10 @@ void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); -const char *tty_term_string2( - struct tty_term *, enum tty_code_code, int, int); -const char *tty_term_ptr1( - struct tty_term *, enum tty_code_code, const void *); +const char *tty_term_string2(struct tty_term *, enum tty_code_code, int, + int); +const char *tty_term_ptr1(struct tty_term *, enum tty_code_code, + const void *); const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const void *, const void *); int tty_term_number(struct tty_term *, enum tty_code_code); @@ -1904,8 +1904,8 @@ void grid_move_lines(struct grid *, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int, struct grid_cell **, int, int, int); -void grid_duplicate_lines( - struct grid *, u_int, struct grid *, u_int, u_int); +void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, + u_int); u_int grid_reflow(struct grid *, struct grid *, u_int); /* grid-view.c */ @@ -2056,14 +2056,14 @@ void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); void window_pane_alternate_off(struct window_pane *, struct grid_cell *, int); -int window_pane_set_mode( - struct window_pane *, const struct window_mode *); +int window_pane_set_mode(struct window_pane *, + const struct window_mode *); void window_pane_reset_mode(struct window_pane *); void window_pane_key(struct window_pane *, struct client *, struct session *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); -char *window_pane_search( - struct window_pane *, const char *, u_int *); +char *window_pane_search(struct window_pane *, const char *, + u_int *); char *window_printable_flags(struct session *, struct winlink *); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); @@ -2079,17 +2079,17 @@ u_int layout_count_cells(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *); void layout_free_cell(struct layout_cell *); void layout_print_cell(struct layout_cell *, const char *, u_int); -void layout_destroy_cell(struct layout_cell *, struct layout_cell **); -void layout_set_size( - struct layout_cell *, u_int, u_int, u_int, u_int); -void layout_make_leaf( - struct layout_cell *, struct window_pane *); +void layout_destroy_cell(struct layout_cell *, + struct layout_cell **); +void layout_set_size(struct layout_cell *, u_int, u_int, u_int, + u_int); +void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_offsets(struct layout_cell *); void layout_fix_panes(struct window *, u_int, u_int); u_int layout_resize_check(struct layout_cell *, enum layout_type); -void layout_resize_adjust( - struct layout_cell *, enum layout_type, int); +void layout_resize_adjust(struct layout_cell *, enum layout_type, + int); void layout_init(struct window *, struct window_pane *); void layout_free(struct window *); void layout_resize(struct window *, u_int, u_int); @@ -2098,8 +2098,8 @@ void layout_resize_pane(struct window_pane *, enum layout_type, void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); void layout_assign_pane(struct layout_cell *, struct window_pane *); -struct layout_cell *layout_split_pane( - struct window_pane *, enum layout_type, int, int); +struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, + int, int); void layout_close_pane(struct window_pane *); /* layout-custom.c */ diff --git a/tty-term.c b/tty-term.c index 5f2e1a94..27c904a4 100644 --- a/tty-term.c +++ b/tty-term.c @@ -406,12 +406,12 @@ tty_term_find(char *name, int fd, char **cause) if (setupterm(name, fd, &error) != OK) { switch (error) { case 1: - xasprintf( - cause, "can't use hardcopy terminal: %s", name); + xasprintf(cause, "can't use hardcopy terminal: %s", + name); break; case 0: - xasprintf( - cause, "missing or unsuitable terminal: %s", name); + xasprintf(cause, "missing or unsuitable terminal: %s", + name); break; case -1: xasprintf(cause, "can't find terminfo database"); diff --git a/tty.c b/tty.c index 42be6d9f..da5eac4d 100644 --- a/tty.c +++ b/tty.c @@ -48,8 +48,8 @@ void tty_colours_bg(struct tty *, const struct grid_cell *); int tty_large_region(struct tty *, const struct tty_ctx *); int tty_fake_bce(const struct tty *, const struct window_pane *); void tty_redraw_region(struct tty *, const struct tty_ctx *); -void tty_emulate_repeat( - struct tty *, enum tty_code_code, enum tty_code_code, u_int); +void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, + u_int); void tty_repeat_space(struct tty *, u_int); void tty_cell(struct tty *, const struct grid_cell *, const struct window_pane *); @@ -161,8 +161,8 @@ tty_open(struct tty *tty, char **cause) tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_TIMER); - tty->event = bufferevent_new( - tty->fd, tty_read_callback, NULL, tty_error_callback, tty); + tty->event = bufferevent_new(tty->fd, tty_read_callback, NULL, + tty_error_callback, tty); tty_start_tty(tty); @@ -1188,8 +1188,8 @@ tty_reset(struct tty *tty) /* Set region inside pane. */ void -tty_region_pane( - struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) +tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, + u_int rlower) { tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower); } diff --git a/window-choose.c b/window-choose.c index 35170c65..fdfc47a0 100644 --- a/window-choose.c +++ b/window-choose.c @@ -35,11 +35,11 @@ void window_choose_default_callback(struct window_choose_data *); struct window_choose_mode_item *window_choose_get_item(struct window_pane *, key_code, struct mouse_event *); -void window_choose_fire_callback( - struct window_pane *, struct window_choose_data *); +void window_choose_fire_callback(struct window_pane *, + struct window_choose_data *); void window_choose_redraw_screen(struct window_pane *); -void window_choose_write_line( - struct window_pane *, struct screen_write_ctx *, u_int); +void window_choose_write_line(struct window_pane *, + struct screen_write_ctx *, u_int); void window_choose_scroll_up(struct window_pane *); void window_choose_scroll_down(struct window_pane *); @@ -299,8 +299,8 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_choose_fire_callback( - struct window_pane *wp, struct window_choose_data *wcd) +window_choose_fire_callback(struct window_pane *wp, + struct window_choose_data *wcd) { struct window_choose_mode_data *data = wp->modedata; @@ -614,10 +614,10 @@ window_choose_key(struct window_pane *wp, __unused struct client *c, window_choose_scroll_up(wp); else { screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, data->selected - data->top); - window_choose_write_line( - wp, &ctx, data->selected + 1 - data->top); + window_choose_write_line(wp, &ctx, + data->selected - data->top); + window_choose_write_line(wp, &ctx, + data->selected + 1 - data->top); screen_write_stop(&ctx); } break; @@ -634,10 +634,10 @@ window_choose_key(struct window_pane *wp, __unused struct client *c, if (data->selected < data->top + screen_size_y(s)) { screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, data->selected - data->top); - window_choose_write_line( - wp, &ctx, data->selected - 1 - data->top); + window_choose_write_line(wp, &ctx, + data->selected - data->top); + window_choose_write_line(wp, &ctx, + data->selected - 1 - data->top); screen_write_stop(&ctx); } else window_choose_scroll_down(wp); @@ -649,8 +649,8 @@ window_choose_key(struct window_pane *wp, __unused struct client *c, data->selected--; window_choose_scroll_up(wp); screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, screen_size_y(s) - 1); + window_choose_write_line(wp, &ctx, + screen_size_y(s) - 1); screen_write_stop(&ctx); } else window_choose_scroll_up(wp); From 38cc1a1843b370eaeff749802d1d8803b73c4b93 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 11 Dec 2015 19:58:09 +0000 Subject: [PATCH 917/949] Look for ncurses with PKG_CONFIG, and remove libtinfo because it just causes confusion. --- configure.ac | 24 +++++++++++++++++------- tty-term.c | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 6cd859f0..824b4ab2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,5 @@ # configure.ac -# Miscellaneous bits. AC_INIT(tmux, 2.2) AC_CONFIG_AUX_DIR(etc) @@ -143,12 +142,23 @@ if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi -# Look for curses. -AC_SEARCH_LIBS( - setupterm, - [terminfo curses ncurses tinfo], - found_curses=yes, - found_curses=no +# Look for ncurses +PKG_CHECK_MODULES( + LIBNCURSES, + ncurses, + [ + CPPFLAGS="$LIBNCURSES_CFLAGS $CPPFLAGS" + LIBS="$LIBNCURSES_LIBS $LIBS" + found_curses=yes + ], + [ + AC_SEARCH_LIBS( + setupterm, + [ncurses curses terminfo], + found_curses=yes, + found_curses=no + ) + ] ) if test "x$found_curses" = xno; then AC_MSG_ERROR("curses not found") diff --git a/tty-term.c b/tty-term.c index f536859e..21756e51 100644 --- a/tty-term.c +++ b/tty-term.c @@ -18,9 +18,9 @@ #include -#ifdef HAVE_CURSES_H +#if defined(HAVE_CURSES_H) #include -#else +#elif defined(HAVE_NCURSES_H) #include #endif #include From 39cf9c9d31954198ad73e2b6721a92fe782ee56c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 12 Dec 2015 18:19:00 +0000 Subject: [PATCH 918/949] Allow prefix and prefix2 to be set to None to disable (useful if you would rather bind the prefix in the root table). --- cmd-bind-key.c | 2 +- cmd-send-keys.c | 23 +++++++++++++---------- cmd-set-option.c | 3 ++- cmd-unbind-key.c | 8 ++++---- input-keys.c | 4 ++-- key-string.c | 40 ++++++++++++++++++++++++---------------- server-client.c | 14 +++++++------- tmux.1 | 11 +++++++++++ tmux.h | 1 + tty-keys.c | 8 ++++---- 10 files changed, 69 insertions(+), 45 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 1867e814..b13409cb 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -62,7 +62,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 73a308ae..1461baa9 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -52,8 +52,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct mouse_event *m = &cmdq->item->mouse; struct window_pane *wp; struct session *s; - const u_char *str; - int i; + int i, literal; + const u_char *keystr; key_code key; if (args_has(args, 'M')) { @@ -82,14 +82,17 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) input_reset(wp); for (i = 0; i < args->argc; i++) { - str = args->argv[i]; - - if (!args_has(args, 'l') && - (key = key_string_lookup_string(str)) != KEYC_NONE) { - window_pane_key(wp, NULL, s, key, NULL); - } else { - for (; *str != '\0'; str++) - window_pane_key(wp, NULL, s, *str, NULL); + literal = args_has(args, 'l'); + if (!literal) { + key = key_string_lookup_string(args->argv[i]); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) + window_pane_key(wp, NULL, s, key, NULL); + else + literal = 1; + } + if (literal) { + for (keystr = args->argv[i]; *keystr != '\0'; keystr++) + window_pane_key(wp, NULL, s, *keystr, NULL); } } diff --git a/cmd-set-option.c b/cmd-set-option.c index 7de91aa2..e2a4768c 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -396,7 +396,8 @@ cmd_set_option_key(__unused struct cmd *self, struct cmd_q *cmdq, { key_code key; - if ((key = key_string_lookup_string(value)) == KEYC_NONE) { + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { cmdq_error(cmdq, "bad key: %s", value); return (NULL); } diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index cec538c0..66a4525e 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -51,7 +51,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } @@ -60,13 +60,13 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "key given with -a"); return (CMD_RETURN_ERROR); } - key = KEYC_NONE; + key = KEYC_UNKNOWN; } if (args_has(args, 't')) return (cmd_unbind_key_mode_table(self, cmdq, key)); - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { key_bindings_remove_table("root"); @@ -109,7 +109,7 @@ cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) return (CMD_RETURN_ERROR); } - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { while (!RB_EMPTY(mtab->tree)) { mbind = RB_ROOT(mtab->tree); RB_REMOVE(mode_key_tree, mtab->tree, mbind); diff --git a/input-keys.c b/input-keys.c index 2915cb45..3bc1f812 100644 --- a/input-keys.c +++ b/input-keys.c @@ -161,14 +161,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) * if necessary. If it is a UTF-8 key, split it and send it. */ justkey = (key & ~KEYC_ESCAPE); - if (key != KEYC_NONE && justkey <= 0x7f) { + if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; bufferevent_write(wp->event, &ud.data[0], 1); return; } - if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { + if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return; if (key & KEYC_ESCAPE) diff --git a/key-string.c b/key-string.c index 9a44892d..1ff3ca30 100644 --- a/key-string.c +++ b/key-string.c @@ -22,8 +22,8 @@ #include "tmux.h" -key_code key_string_search_table(const char *); -key_code key_string_get_modifiers(const char **); +static key_code key_string_search_table(const char *); +static key_code key_string_get_modifiers(const char **); const struct { const char *string; @@ -98,7 +98,7 @@ const struct { }; /* Find key string in table. */ -key_code +static key_code key_string_search_table(const char *string) { u_int i; @@ -107,11 +107,11 @@ key_string_search_table(const char *string) if (strcasecmp(string, key_string_table[i].string) == 0) return (key_string_table[i].key); } - return (KEYC_NONE); + return (KEYC_UNKNOWN); } /* Find modifiers. */ -key_code +static key_code key_string_get_modifiers(const char **string) { key_code modifiers; @@ -150,10 +150,14 @@ key_string_lookup_string(const char *string) u_int i; enum utf8_state more; + /* Is this no key? */ + if (strcasecmp(string, "None") == 0) + return (KEYC_NONE); + /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%hx%n", &u, &size) != 1 || size > 4) - return (KEYC_NONE); + return (KEYC_UNKNOWN); return (u); } @@ -165,30 +169,30 @@ key_string_lookup_string(const char *string) } modifiers |= key_string_get_modifiers(&string); if (string[0] == '\0') - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Is this a standard ASCII key? */ if (string[1] == '\0' && (u_char)string[0] <= 127) { key = (u_char)string[0]; if (key < 32 || key == 127) - return (KEYC_NONE); + return (KEYC_UNKNOWN); } else { /* Try as a UTF-8 key. */ if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) - return (KEYC_NONE); + return (KEYC_UNKNOWN); for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); key = utf8_combine(&ud); return (key | modifiers); } /* Otherwise look the key up in the table. */ key = key_string_search_table(string); - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); } /* Convert the standard control keys. */ @@ -202,7 +206,7 @@ key_string_lookup_string(const char *string) else if (key == 63) key = KEYC_BSPACE; else - return (KEYC_NONE); + return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; } @@ -222,9 +226,13 @@ key_string_lookup_key(key_code key) /* Handle no key. */ if (key == KEYC_NONE) - return (""); + return ("None"); + + /* Handle special keys. */ + if (key == KEYC_UNKNOWN) + return ("Unknown"); if (key == KEYC_MOUSE) - return (""); + return ("Mouse"); /* * Special case: display C-@ as C-Space. Could do this below in @@ -265,7 +273,7 @@ key_string_lookup_key(key_code key) /* Invalid keys are errors. */ if (key == 127 || key > 255) { - snprintf(out, sizeof out, "", key); + snprintf(out, sizeof out, "Invalid#%llx", key); return (out); } diff --git a/server-client.c b/server-client.c index 8b6be5d9..ed0eefdd 100644 --- a/server-client.c +++ b/server-client.c @@ -309,7 +309,7 @@ server_client_check_mouse(struct client *c) log_debug("down at %u,%u", x, y); } if (type == NOTYPE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Always save the session. */ m->s = s->id; @@ -319,7 +319,7 @@ server_client_check_mouse(struct client *c) if (m->statusat != -1 && y == (u_int)m->statusat) { w = status_get_window_at(c, x); if (w == NULL) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->w = w->id; where = STATUS; } else @@ -352,7 +352,7 @@ server_client_check_mouse(struct client *c) } } if (where == NOWHERE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->wp = wp->id; m->w = wp->window->id; } else @@ -371,7 +371,7 @@ server_client_check_mouse(struct client *c) } /* Convert to a key binding. */ - key = KEYC_NONE; + key = KEYC_UNKNOWN; switch (type) { case NOTYPE: break; @@ -483,8 +483,8 @@ server_client_check_mouse(struct client *c) } break; } - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) @@ -572,7 +572,7 @@ server_client_handle_key(struct client *c, key_code key) if (c->flags & CLIENT_READONLY) return; key = server_client_check_mouse(c); - if (key == KEYC_NONE) + if (key == KEYC_UNKNOWN) return; m->valid = 1; diff --git a/tmux.1 b/tmux.1 index 115050a6..b02237f1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2664,8 +2664,19 @@ See the section for details. .It Ic prefix Ar key Set the key accepted as a prefix key. +In addition to the standard keys described under +.Sx KEY BINDINGS , +.Ic prefix +can be set to the special key +.Ql None +to set no prefix. .It Ic prefix2 Ar key Set a secondary key accepted as a prefix key. +Like +.Ic prefix , +.Ic prefix2 +can be set to +.Ql None . .It Xo Ic renumber-windows .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index 60d16fe3..d5e5d822 100644 --- a/tmux.h +++ b/tmux.h @@ -85,6 +85,7 @@ struct tmuxproc; /* Special key codes. */ #define KEYC_NONE 0xffff00000000ULL +#define KEYC_UNKNOWN 0xfffe00000000ULL #define KEYC_BASE 0x100000000000ULL /* Key modifier bits. */ diff --git a/tty-keys.c b/tty-keys.c index b5e36f8e..86839a17 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -344,7 +344,7 @@ tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) if (tk == NULL) { tk = *tkp = xcalloc(1, sizeof *tk); tk->ch = *s; - tk->key = KEYC_NONE; + tk->key = KEYC_UNKNOWN; } /* Find the next entry. */ @@ -444,7 +444,7 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) (*size)++; /* At the end of the string, return the current node. */ - if (len == 0 || (tk->next == NULL && tk->key != KEYC_NONE)) + if (len == 0 || (tk->next == NULL && tk->key != KEYC_UNKNOWN)) return (tk); /* Move into the next tree for the following character. */ @@ -534,7 +534,7 @@ first_key: if (tk->next != NULL) goto partial_key; key = tk->key; - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) key |= KEYC_ESCAPE; goto complete_key; } @@ -620,7 +620,7 @@ complete_key: } /* Fire the key. */ - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) server_client_handle_key(tty->client, key); return (1); From 6a50cf89b40d472cd1f8b439913ca4caddbfd001 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 12 Dec 2015 18:28:47 +0000 Subject: [PATCH 919/949] Return after changing key table. --- cmd-switch-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index edc0fd69..85cbce78 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -65,6 +65,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) table->references++; key_bindings_unref_table(c->keytable); c->keytable = table; + return (CMD_RETURN_NORMAL); } tflag = args_get(args, 't'); From 5ed17e84faed0a7655ec1eb3de291b60839dcb12 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 12 Dec 2015 18:32:24 +0000 Subject: [PATCH 920/949] Add key-table option to set the default key table for a session, allows different key bindings for different sessions and a few other things. --- cmd-attach-session.c | 2 ++ cmd-new-session.c | 1 + cmd-set-option.c | 4 ++++ cmd-switch-client.c | 1 + format.c | 4 +++- options-table.c | 6 ++++++ server-client.c | 36 +++++++++++++++++++++++++++--------- server-fn.c | 1 + tmux.1 | 5 +++++ tmux.h | 2 ++ 10 files changed, 52 insertions(+), 10 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 73c82248..00ced9df 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -119,6 +119,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); @@ -150,6 +151,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index aee69e12..341399be 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -262,6 +262,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } else if (c->session != NULL) c->last_session = c->session; c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); diff --git a/cmd-set-option.c b/cmd-set-option.c index e2a4768c..daee62ac 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -183,6 +183,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) w->active->flags |= PANE_CHANGED; } } + if (strcmp(oe->name, "key-table") == 0) { + TAILQ_FOREACH(c, &clients, entry) + server_client_set_key_table(c, NULL); + } if (strcmp(oe->name, "status") == 0 || strcmp(oe->name, "status-interval") == 0) status_timer_start_all(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 85cbce78..4746b15a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -124,6 +124,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); diff --git a/format.c b/format.c index 3e666330..79445936 100644 --- a/format.c +++ b/format.c @@ -1035,6 +1035,7 @@ void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; + const char *name; if (ft->s == NULL) ft->s = c->session; @@ -1052,7 +1053,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); - if (strcmp(c->keytable->name, "root") == 0) + name = server_client_get_key_table(c); + if (strcmp(c->keytable->name, name) == 0) format_add(ft, "client_prefix", "%d", 0); else format_add(ft, "client_prefix", "%d", 1); diff --git a/options-table.c b/options-table.c index 0dab0c0e..63e7ab61 100644 --- a/options-table.c +++ b/options-table.c @@ -211,6 +211,12 @@ const struct options_table_entry options_table[] = { .default_num = 2000 }, + { .name = "key-table", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "root" + }, + { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, diff --git a/server-client.c b/server-client.c index ed0eefdd..c2b43f38 100644 --- a/server-client.c +++ b/server-client.c @@ -32,7 +32,6 @@ #include "tmux.h" -void server_client_key_table(struct client *, const char *); void server_client_free(int, short, void *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); @@ -72,13 +71,32 @@ server_client_check_nested(struct client *c) /* Set client key table. */ void -server_client_key_table(struct client *c, const char *name) +server_client_set_key_table(struct client *c, const char *name) { + if (name == NULL) + name = server_client_get_key_table(c); + key_bindings_unref_table(c->keytable); c->keytable = key_bindings_get_table(name, 1); c->keytable->references++; } +/* Get default key table. */ +const char * +server_client_get_key_table(struct client *c) +{ + struct session *s = c->session; + const char *name; + + if (s == NULL) + return ("root"); + + name = options_get_string(s->options, "key-table"); + if (*name == '\0') + return ("root"); + return (name); +} + /* Create a new client. */ void server_client_create(int fd) @@ -598,7 +616,7 @@ retry: * again in the root table. */ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; @@ -625,7 +643,7 @@ retry: evtimer_add(&c->repeat_timer, &tv); } else { c->flags &= ~CLIENT_REPEAT; - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); } server_status_client(c); @@ -640,15 +658,15 @@ retry: * root table and try again. */ if (c->flags & CLIENT_REPEAT) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; } /* If no match and we're not in the root table, that's it. */ - if (strcmp(c->keytable->name, "root") != 0) { - server_client_key_table(c, "root"); + if (strcmp(c->keytable->name, server_client_get_key_table(c)) != 0) { + server_client_set_key_table(c, NULL); server_status_client(c); return; } @@ -659,7 +677,7 @@ retry: */ if (key == (key_code)options_get_number(s->options, "prefix") || key == (key_code)options_get_number(s->options, "prefix2")) { - server_client_key_table(c, "prefix"); + server_client_set_key_table(c, "prefix"); server_status_client(c); return; } @@ -834,7 +852,7 @@ server_client_repeat_timer(__unused int fd, __unused short events, void *data) struct client *c = data; if (c->flags & CLIENT_REPEAT) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); } diff --git a/server-fn.c b/server-fn.c index 07ade08c..a4c2dfea 100644 --- a/server-fn.c +++ b/server-fn.c @@ -384,6 +384,7 @@ server_destroy_session(struct session *s) } else { c->last_session = NULL; c->session = s_new; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new, NULL); diff --git a/tmux.1 b/tmux.1 index b02237f1..39e9bd4b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2572,6 +2572,11 @@ is in milliseconds. Set the maximum number of lines held in window history. This setting applies only to new windows - existing window histories are not resized and retain the limit at the point they were created. +.It Ic key-table Ar key-table +Set the default key table to +.Ar key-table +instead of +.Em root . .It Ic lock-after-time Ar number Lock the session (like the .Ic lock-session diff --git a/tmux.h b/tmux.h index d5e5d822..e10fbcdf 100644 --- a/tmux.h +++ b/tmux.h @@ -1800,6 +1800,8 @@ void server_update_socket(void); void server_add_accept(int); /* server-client.c */ +void server_client_set_key_table(struct client *, const char *); +const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, key_code); void server_client_create(int); From 92f187d1c2d84322860dc595da604260999a52f0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 12 Dec 2015 22:04:25 +0000 Subject: [PATCH 921/949] Need to use pkg-config --static when doing a static build. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 824b4ab2..82e8a452 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_EGREP AC_PROG_INSTALL +PKG_PROG_PKG_CONFIG # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc @@ -47,6 +48,7 @@ AC_ARG_ENABLE( ) if test "x$found_static" = xyes; then LDFLAGS="$LDFLAGS -static" + PKG_CONFIG="pkg-config --static" fi # Is this gcc? From 4a4daf13031673870341c68b990e20c314140118 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 14:32:38 +0000 Subject: [PATCH 922/949] Instead of every command resolving the target (-t or -s) itself, prepare the state (client, session, winlink, pane) for it it before entering the command. Each command provides some flags that tell the prepare step what it is expecting. This is a requirement for having hooks on commands (for example, if you hook "select-window -t1:2", the hook command should to operate on window 1:2 not whatever it thinks is the current window), and should allow some other target improvements. The old cmd_find_* functions remain for the moment but that layer will be dropped later. Joint work with Thomas Adam. --- cmd-attach-session.c | 43 ++----- cmd-break-pane.c | 23 ++-- cmd-capture-pane.c | 7 +- cmd-choose-buffer.c | 11 +- cmd-choose-client.c | 11 +- cmd-choose-tree.c | 17 ++- cmd-clear-history.c | 11 +- cmd-command-prompt.c | 7 +- cmd-confirm-before.c | 7 +- cmd-copy-mode.c | 9 +- cmd-detach-client.c | 19 +--- cmd-display-message.c | 32 +----- cmd-display-panes.c | 12 +- cmd-find-window.c | 14 +-- cmd-if-shell.c | 33 ++---- cmd-join-pane.c | 15 ++- cmd-kill-pane.c | 9 +- cmd-kill-session.c | 5 +- cmd-kill-window.c | 16 +-- cmd-list-clients.c | 10 +- cmd-list-panes.c | 17 +-- cmd-list-windows.c | 11 +- cmd-load-buffer.c | 2 +- cmd-lock-server.c | 24 ++-- cmd-move-window.c | 30 ++--- cmd-new-session.c | 45 ++++---- cmd-new-window.c | 28 +++-- cmd-paste-buffer.c | 8 +- cmd-pipe-pane.c | 14 +-- cmd-queue.c | 25 ++-- cmd-refresh-client.c | 7 +- cmd-rename-session.c | 7 +- cmd-rename-window.c | 8 +- cmd-resize-pane.c | 16 +-- cmd-respawn-pane.c | 14 +-- cmd-respawn-window.c | 16 +-- cmd-rotate-window.c | 11 +- cmd-run-shell.c | 33 ++---- cmd-save-buffer.c | 2 +- cmd-select-layout.c | 11 +- cmd-select-pane.c | 24 ++-- cmd-select-window.c | 21 +--- cmd-send-keys.c | 13 +-- cmd-set-environment.c | 10 +- cmd-set-hook.c | 13 +-- cmd-set-option.c | 34 ++---- cmd-show-environment.c | 11 +- cmd-show-messages.c | 7 +- cmd-show-options.c | 28 ++--- cmd-source-file.c | 3 +- cmd-split-window.c | 19 ++-- cmd-swap-pane.c | 24 ++-- cmd-swap-window.c | 21 ++-- cmd-switch-client.c | 44 ++------ cmd.c | 251 +++++++++++++++++++++++++++++++++++++++++ tmux.h | 42 ++++++- 56 files changed, 606 insertions(+), 599 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 00ced9df..c29e9d1a 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -36,48 +36,27 @@ const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", "c:dErt:", 0, 0, "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - CMD_STARTSERVER, + CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, cmd_attach_session_exec }; enum cmd_retval -cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, - const char *cflag, int Eflag) +cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, + int Eflag) { - struct session *s; + struct session *s = cmdq->state.tflag.s; struct client *c = cmdq->client, *c_loop; - struct winlink *wl = NULL; - struct window *w = NULL; - struct window_pane *wp = NULL; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; const char *update; - char *cause; + char *cause, *cwd; struct format_tree *ft; - char *cwd; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); return (CMD_RETURN_ERROR); } - if (tflag == NULL) { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - } else if (tflag[strcspn(tflag, ":.")] != '\0') { - if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - } else { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - w = window_find_by_id_str(tflag); - if (w == NULL) { - wp = window_pane_find_by_id_str(tflag); - if (wp != NULL) - w = wp->window; - } - if (w != NULL) - wl = winlink_find_by_window(&s->windows, w); - } - if (c == NULL) return (CMD_RETURN_NORMAL); if (server_client_check_nested(c)) { @@ -94,8 +73,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (cflag != NULL) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, - NULL, NULL); + format_defaults(ft, c, s, wl, wp); cwd = format_expand(ft, cflag); format_free(ft); @@ -176,7 +154,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - return (cmd_attach_session(cmdq, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), - args_has(args, 'E'))); + return (cmd_attach_session(cmdq, args_has(args, 'd'), + args_has(args, 'r'), args_get(args, 'c'), args_has(args, 'E'))); } diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 98d6ad3d..eb07fb87 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_break_pane_entry = { "break-pane", "breakp", "dPF:s:t:", 0, 0, "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_S|CMD_INDEX_T, cmd_break_pane_exec }; @@ -42,28 +42,22 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct session *src_s; - struct session *dst_s; - struct window_pane *wp; - struct window *w; + struct winlink *wl = cmdq->state.sflag.wl; + struct session *src_s = cmdq->state.sflag.s; + struct session *dst_s = cmdq->state.tflag.s; + struct window_pane *wp = cmdq->state.sflag.wp; + struct window *w = wl->window; char *name; char *cause; - int idx; + int idx = cmdq->state.tflag.idx; struct format_tree *ft; const char *template; char *cp; - wl = cmd_find_pane(cmdq, args_get(args, 's'), &src_s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst_s)) == -2) - return (CMD_RETURN_ERROR); if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { cmdq_error(cmdq, "index %d already in use", idx); return (CMD_RETURN_ERROR); } - w = wl->window; if (window_count_panes(w) == 1) { cmdq_error(cmdq, "can't break with only one pane"); @@ -102,8 +96,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) template = BREAK_PANE_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, - wp); + format_defaults(ft, cmdq->state.c, dst_s, wl, wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 6a7af47a..9d22a0f2 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_capture_pane_entry = { "ab:CeE:JpPqS:t:", 0, 0, "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_capture_pane_exec }; @@ -175,14 +175,11 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; char *buf, *cause; const char *bufname; size_t len; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - len = 0; if (args_has(args, 'P')) buf = cmd_capture_pane_pending(args, wp, &len); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index dbb20fc4..4418d415 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_choose_buffer_entry = { "choose-buffer", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_buffer_exec }; @@ -44,15 +44,15 @@ enum cmd_retval cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; + struct winlink *wl = cmdq->state.tflag.wl; struct window_choose_data *cdata; - struct winlink *wl; struct paste_buffer *pb; char *action, *action_data; const char *template; u_int idx; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } @@ -60,9 +60,6 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = CHOOSE_BUFFER_TEMPLATE; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 93a141ee..c58bc826 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_choose_client_entry = { "choose-client", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_client_exec }; @@ -53,22 +53,19 @@ enum cmd_retval cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct client *c1; struct window_choose_data *cdata; - struct winlink *wl; + struct winlink *wl = cmdq->state.tflag.wl; const char *template; char *action; u_int idx, cur; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 0b0fea6a..766978fe 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -48,7 +48,7 @@ const struct cmd_entry cmd_choose_tree_entry = { "S:W:swub:c:t:", 0, 1, "[-suw] [-b session-template] [-c window template] [-S format] " \ "[-W format] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_choose_tree_exec }; @@ -56,7 +56,7 @@ const struct cmd_entry cmd_choose_session_entry = { "choose-session", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_tree_exec }; @@ -64,7 +64,7 @@ const struct cmd_entry cmd_choose_window_entry = { "choose-window", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE "[-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_tree_exec }; @@ -72,9 +72,9 @@ enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl, *wm; - struct session *s, *s2; - struct client *c; + struct client *c = cmdq->state.c; + struct winlink *wl = cmdq->state.tflag.wl, *wm; + struct session *s = cmdq->state.tflag.s, *s2; struct window_choose_data *wcd = NULL; const char *ses_template, *win_template; char *final_win_action, *cur_win_template; @@ -87,14 +87,11 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) ses_template = win_template = NULL; ses_action = win_action = NULL; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 63e9d548..a76cab80 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -30,20 +30,17 @@ const struct cmd_entry cmd_clear_history_entry = { "clear-history", "clearhist", "t:", 0, 0, CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_clear_history_exec }; enum cmd_retval -cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_clear_history_exec(__unused struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; struct grid *gd; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - gd = wp->base.grid; + gd = cmdq->state.tflag.wp->base.grid; if (wp->mode == &window_copy_mode) window_pane_reset_mode(wp); diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 1622e0b7..900fceb9 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_command_prompt_entry = { "command-prompt", NULL, "I:p:t:", 0, 1, "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", - 0, + CMD_CLIENT_T, cmd_command_prompt_exec }; @@ -58,13 +58,10 @@ cmd_command_prompt_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; - struct client *c; + struct client *c = cmdq->state.c; char *prompt, *ptr, *input = NULL; size_t n; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (c->prompt_string != NULL) return (CMD_RETURN_NORMAL); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 248515cd..17a575a2 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_confirm_before_entry = { "confirm-before", "confirm", "p:t:", 1, 1, "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - 0, + CMD_CLIENT_T, cmd_confirm_before_exec }; @@ -51,13 +51,10 @@ cmd_confirm_before_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_confirm_before_data *cdata; - struct client *c; + struct client *c = cmdq->state.c; char *cmd, *copy, *new_prompt, *ptr; const char *prompt; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index e04b561b..79d06cee 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -29,8 +29,8 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { "copy-mode", NULL, "Met:u", 0, 0, - "[-Meu] " CMD_TARGET_PANE_USAGE, - 0, + "[-Mu] " CMD_TARGET_PANE_USAGE, + CMD_PANE_T, cmd_copy_mode_exec }; @@ -48,15 +48,14 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct client *c = cmdq->client; struct session *s; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; if (args_has(args, 'M')) { if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); - } else if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); + } if (self->entry == &cmd_clock_mode_entry) { window_pane_set_mode(wp, &window_clock_mode); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index d8128eae..86f8063c 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { "detach-client", "detach", "as:t:P", 0, 0, - "[-aP] [-s target-session] " CMD_TARGET_CLIENT_USAGE, - CMD_READONLY, + "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, cmd_detach_client_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_suspend_client_entry = { "suspend-client", "suspendc", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_detach_client_exec }; @@ -48,13 +48,11 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c, *cloop; + struct client *c = cmdq->state.c, *cloop; struct session *s; enum msgtype msgtype; if (self->entry == &cmd_suspend_client_entry) { - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); @@ -67,10 +65,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) msgtype = MSG_DETACH; if (args_has(args, 's')) { - s = cmd_find_session(cmdq, args_get(args, 's'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - + s = cmdq->state.sflag.s; TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session == s) server_client_detach(cloop, msgtype); @@ -78,10 +73,6 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_STOP); } - c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session != NULL && cloop != c) diff --git a/cmd-display-message.c b/cmd-display-message.c index 7ca8e9c4..201f8b75 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_display_message_entry = { "c:pt:F:", 0, 1, "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - 0, + CMD_CLIENT_C|CMD_PANE_T, cmd_display_message_exec }; @@ -47,41 +47,19 @@ enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct client *c = cmdq->state.c; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; const char *template; char *msg; struct format_tree *ft; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - } else { - wl = cmd_find_pane(cmdq, NULL, &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - } - if (args_has(args, 'F') && args->argc != 0) { cmdq_error(cmdq, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } - if (args_has(args, 'c')) { - c = cmd_find_client(cmdq, args_get(args, 'c'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c == NULL && !args_has(self->args, 'p')) { - cmdq_error(cmdq, "no client available"); - return (CMD_RETURN_ERROR); - } - } - template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 9ce89712..714ee19e 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -30,20 +30,14 @@ const struct cmd_entry cmd_display_panes_entry = { "display-panes", "displayp", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_display_panes_exec }; enum cmd_retval -cmd_display_panes_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_display_panes_exec(__unused struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - server_set_identify(c); + server_set_identify(cmdq->state.c); return (CMD_RETURN_NORMAL); } diff --git a/cmd-find-window.c b/cmd-find-window.c index e92ae60f..1733b717 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -51,7 +51,7 @@ const struct cmd_entry cmd_find_window_entry = { "find-window", "findw", "F:CNt:T", 1, 4, "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", - 0, + CMD_WINDOW_T, cmd_find_window_exec }; @@ -137,10 +137,10 @@ enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct window_choose_data *cdata; - struct session *s; - struct winlink *wl, *wm; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl, *wm; struct cmd_find_window_list find_list; struct cmd_find_window_data *find_data; struct cmd_find_window_data *find_data1; @@ -148,14 +148,10 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; u_int i, match_flags; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - s = c->session; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); if ((template = args_get(args, 'F')) == NULL) template = FIND_WINDOW_TEMPLATE; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 3345e8ce..404f4671 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_if_shell_entry = { "if-shell", "if", "bFt:", 2, 3, "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command [command]", - 0, + CMD_PANE_T|CMD_CANFAIL, cmd_if_shell_exec }; @@ -61,31 +61,20 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_if_shell_data *cdata; char *shellcmd, *cmd, *cause; struct cmd_list *cmdlist; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window_pane *wp = NULL; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; const char *cwd; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - cwd = wp->cwd; - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - if (cmdq->client != NULL && cmdq->client->session == NULL) - cwd = cmdq->client->cwd; - else if (s != NULL) - cwd = s->cwd; - else - cwd = NULL; - } + cwd = wp->cwd; + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = NULL; ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index cc6432a4..6fc5b977 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_join_pane_entry = { "join-pane", "joinp", "bdhvp:l:s:t:", 0, 0, "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_MARKED_S|CMD_PANE_T, cmd_join_pane_exec }; @@ -45,7 +45,7 @@ const struct cmd_entry cmd_move_pane_entry = { "move-pane", "movep", "bdhvp:l:s:t:", 0, 0, "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_S|CMD_PANE_T, cmd_join_pane_exec }; @@ -68,16 +68,15 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) enum layout_type type; struct layout_cell *lc; - dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp); - if (dst_wl == NULL) - return (CMD_RETURN_ERROR); + dst_s = cmdq->state.tflag.s; + dst_wl = cmdq->state.tflag.wl; + dst_wp = cmdq->state.tflag.wp; dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); + src_wl = cmdq->state.sflag.wl; + src_wp = cmdq->state.sflag.wp; src_w = src_wl->window; server_unzoom_window(src_w); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index f4735fd2..d5f69ea9 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -32,19 +32,16 @@ const struct cmd_entry cmd_kill_pane_entry = { "kill-pane", "killp", "at:", 0, 0, "[-a] " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_kill_pane_exec }; enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct window_pane *loopwp, *tmpwp, *wp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *loopwp, *tmpwp, *wp = cmdq->state.tflag.wp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); server_unzoom_window(wl->window); if (window_count_panes(wl->window) == 1) { diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 3f39c241..a4b0d5d2 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_kill_session_entry = { "kill-session", NULL, "aCt:", 0, 0, "[-aC] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_kill_session_exec }; @@ -44,8 +44,7 @@ cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) struct session *s, *sloop, *stmp; struct winlink *wl; - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); + s = cmdq->state.tflag.s; if (args_has(args, 'C')) { RB_FOREACH(wl, winlinks, &s->windows) { diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 4d346a71..4ab17472 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_kill_window_entry = { "kill-window", "killw", "at:", 0, 0, "[-a] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_kill_window_exec }; @@ -38,21 +38,17 @@ const struct cmd_entry cmd_unlink_window_entry = { "unlink-window", "unlinkw", "kt:", 0, 0, "[-k] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_kill_window_exec }; enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl, *wl2, *wl3; - struct window *w; - struct session *s; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; + struct args *args = self->args; + struct winlink *wl = cmdq->state.tflag.wl, *wl2, *wl3; + struct window *w = wl->window; + struct session *s = cmdq->state.tflag.s; if (self->entry == &cmd_unlink_window_entry) { if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 2c13d398..effd8275 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_list_clients_entry = { "list-clients", "lsc", "F:t:", 0, 0, "[-F format] " CMD_TARGET_SESSION_USAGE, - CMD_READONLY, + CMD_READONLY|CMD_SESSION_T, cmd_list_clients_exec }; @@ -54,11 +54,9 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) u_int idx; char *line; - if (args_has(args, 't')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - } else + if (args_has(args, 't')) + s = cmdq->state.tflag.s; + else s = NULL; if ((template = args_get(args, 'F')) == NULL) diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 23e530e2..2c9fa623 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_list_panes_entry = { "list-panes", "lsp", "asF:t:", 0, 0, "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_list_panes_exec }; @@ -46,22 +46,15 @@ enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; if (args_has(args, 'a')) cmd_list_panes_server(self, cmdq); - else if (args_has(args, 's')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + else if (args_has(args, 's')) cmd_list_panes_session(self, s, cmdq, 1); - } else { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); + else cmd_list_panes_window(self, s, wl, cmdq, 0); - } return (CMD_RETURN_NORMAL); } diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 1eaee2d7..992ba0de 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -49,7 +49,7 @@ const struct cmd_entry cmd_list_windows_entry = { "list-windows", "lsw", "F:at:", 0, 0, "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_list_windows_exec }; @@ -57,16 +57,11 @@ enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; if (args_has(args, 'a')) cmd_list_windows_server(self, cmdq); - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - cmd_list_windows_session(self, s, cmdq, 0); - } + else + cmd_list_windows_session(self, cmdq->state.tflag.s, cmdq, 0); return (CMD_RETURN_NORMAL); } diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 5ac2edc3..929e3bf2 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -72,7 +72,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((s = cmd_find_current(cmdq)) != NULL) + else if ((s = c->session) != NULL) cwd = s->cwd; else cwd = "."; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index a0204129..5d20ebd4 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_lock_session_entry = { "lock-session", "locks", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_lock_server_exec }; @@ -46,30 +46,20 @@ const struct cmd_entry cmd_lock_client_entry = { "lock-client", "lockc", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_lock_server_exec }; enum cmd_retval cmd_lock_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - struct session *s; - if (self->entry == &cmd_lock_server_entry) server_lock(); - else if (self->entry == &cmd_lock_session_entry) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - server_lock_session(s); - } else { - c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - server_lock_client(c); - } + else if (self->entry == &cmd_lock_session_entry) + server_lock_session(cmdq->state.tflag.s); + else + server_lock_client(cmdq->state.c); + recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-move-window.c b/cmd-move-window.c index 1bd46ab2..9e3a1c09 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_move_window_entry = { "move-window", "movew", "adkrs:t:", 0, 0, "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - 0, + CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, cmd_move_window_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_link_window_entry = { "link-window", "linkw", "adks:t:", 0, 0, "[-dk] " CMD_SRCDST_WINDOW_USAGE, - 0, + CMD_WINDOW_S|CMD_INDEX_T, cmd_move_window_exec }; @@ -48,36 +48,28 @@ enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *src, *dst, *s; - struct winlink *wl; + struct session *src = cmdq->state.sflag.s; + struct session *dst = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.sflag.wl; char *cause; - int idx, kflag, dflag, sflag; + int idx = cmdq->state.tflag.idx, kflag, dflag, sflag; + + kflag = args_has(self->args, 'k'); + dflag = args_has(self->args, 'd'); if (args_has(args, 'r')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - - session_renumber_windows(s); + session_renumber_windows(dst); recalculate_sizes(); return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) - return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) - return (CMD_RETURN_ERROR); - kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); sflag = args_has(self->args, 's'); if (args_has(self->args, 'a')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - if ((idx = winlink_shuffle_up(s, s->curw)) == -1) + if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) return (CMD_RETURN_ERROR); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 341399be..e589a361 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_new_session_entry = { "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", - CMD_STARTSERVER, + CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, cmd_new_session_exec }; @@ -49,7 +49,7 @@ const struct cmd_entry cmd_has_session_entry = { "has-session", "has", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_new_session_exec }; @@ -57,8 +57,9 @@ enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c = cmdq->client, *c0; - struct session *s, *groupwith; + struct client *c = cmdq->client; + struct session *s, *attach_sess; + struct session *groupwith = cmdq->state.tflag.s; struct window *w; struct environ *env; struct termios tio, *tiop; @@ -71,8 +72,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ_entry *envent; if (self->entry == &cmd_has_session_entry) { - if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL) - return (CMD_RETURN_ERROR); + /* + * cmd_prepare() will fail if the session cannot be found, + * hence always return success here. + */ return (CMD_RETURN_NORMAL); } @@ -87,9 +90,16 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } - if (session_find(newname) != NULL) { + if ((attach_sess = session_find(newname)) != NULL) { if (args_has(args, 'A')) { - return (cmd_attach_session(cmdq, newname, + /* + * This cmdq is now destined for + * attach-session. Because attach-session + * will have already been prepared, copy this + * session into its tflag so it can be used. + */ + cmdq->state.tflag.s = attach_sess; + return (cmd_attach_session(cmdq, args_has(args, 'D'), 0, NULL, args_has(args, 'E'))); } @@ -98,12 +108,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } } - target = args_get(args, 't'); - if (target != NULL) { - groupwith = cmd_find_session(cmdq, target, 0); - if (groupwith == NULL) - return (CMD_RETURN_ERROR); - } else + if ((target = args_get(args, 't')) == NULL) groupwith = NULL; /* Set -d if no client. */ @@ -120,14 +125,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, - NULL); + format_defaults(ft, c, NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) - cwd = c0->session->cwd; else cwd = "."; @@ -193,7 +195,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Figure out the command for the new window. */ argc = -1; argv = NULL; - if (target == NULL && args->argc != 0) { + if (!args_has(args, 't') && args->argc != 0) { argc = args->argc; argv = args->argv; } else if (target == NULL) { @@ -245,7 +247,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * If a target session is given, this is to be part of a session group, * so add it to the group and synchronize. */ - if (groupwith != NULL) { + if (args_has(args, 't')) { session_group_add(groupwith, s); session_group_synchronize_to(s); session_select(s, RB_MIN(winlinks, &s->windows)->idx); @@ -285,8 +287,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) template = NEW_SESSION_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); + format_defaults(ft, c, s, NULL, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-new-window.c b/cmd-new-window.c index ddfd6a20..8154bdfb 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -39,7 +39,12 @@ const struct cmd_entry cmd_new_window_entry = { "ac:dF:kn:Pt:", 0, -1, "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", - 0, + /* + * Using PREP_CANFAIL here ensures that the wl is filled in + * regardless; making PREP_INDEX the thing we want -t to be used for + * in the specific case. + */ + CMD_INDEX_T|CMD_CANFAIL, cmd_new_window_exec }; @@ -47,26 +52,21 @@ enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct client *c = cmdq->state.c; + int idx = cmdq->state.tflag.idx; const char *cmd, *path, *template, *cwd, *to_free; char **argv, *cause, *cp; - int argc, idx, detached; + int argc, detached; struct format_tree *ft; struct environ_entry *envent; if (args_has(args, 'a')) { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } - } else { - idx = cmd_find_index(cmdq, args_get(args, 't'), &s); - if (idx == -2) - return (CMD_RETURN_ERROR); } detached = args_has(args, 'd'); @@ -95,8 +95,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); + format_defaults(ft, c, s, NULL, NULL); cwd = to_free = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -144,8 +143,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) template = NEW_WINDOW_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, - NULL); + format_defaults(ft, c, s, wl, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 87f09ee6..92a31c53 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_paste_buffer_entry = { "paste-buffer", "pasteb", "db:prs:t:", 0, 0, "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_paste_buffer_exec }; @@ -45,16 +45,12 @@ enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct window_pane *wp; - struct session *s; + struct window_pane *wp = cmdq->state.tflag.wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; size_t seplen, bufsize; int bracket = args_has(args, 'p'); - if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) - return (CMD_RETURN_ERROR); - bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 79ef7818..ad4b02e0 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_pipe_pane_entry = { "pipe-pane", "pipep", "ot:", 0, 1, "[-o] " CMD_TARGET_PANE_USAGE " [command]", - 0, + CMD_PANE_T, cmd_pipe_pane_exec }; @@ -49,18 +49,14 @@ enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct client *c = cmdq->state.c; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; char *cmd; int old_fd, pipe_fd[2], null_fd; struct format_tree *ft; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - c = cmd_find_client(cmdq, NULL, 1); - /* Destroy the old pipe. */ old_fd = wp->pipe_fd; if (wp->pipe_fd != -1) { diff --git a/cmd-queue.c b/cmd-queue.c index c85fb048..7b2675fa 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,7 +25,7 @@ #include "tmux.h" -enum cmd_retval cmdq_continue_one(struct cmd_q *); +static enum cmd_retval cmdq_continue_one(struct cmd_q *); /* Create new command queue. */ struct cmd_q * @@ -179,37 +179,40 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) } /* Process one command. */ -enum cmd_retval +static enum cmd_retval cmdq_continue_one(struct cmd_q *cmdq) { struct cmd *cmd = cmdq->cmd; enum cmd_retval retval; - char *s; + char *tmp; int flags = !!(cmd->flags & CMD_CONTROL); - s = cmd_print(cmd); - log_debug("cmdq %p: %s", cmdq, s); - free(s); + tmp = cmd_print(cmd); + log_debug("cmdq %p: %s", cmdq, tmp); + free(tmp); cmdq->time = time(NULL); cmdq->number++; cmdq_guard(cmdq, "begin", flags); + if (cmd_prepare_state(cmd, cmdq) != 0) + goto error; retval = cmd->entry->exec(cmd, cmdq); - if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error", flags); - else - cmdq_guard(cmdq, "end", flags); + cmdq_guard(cmdq, "end", flags); return (retval); + +error: + cmdq_guard(cmdq, "error", flags); + return (CMD_RETURN_ERROR); } /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { - struct client *c = cmdq->client; + struct client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; int empty; diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 5a45ec25..9d1d0fce 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_refresh_client_entry = { "refresh-client", "refresh", "C:St:", 0, 0, "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_refresh_client_exec }; @@ -38,13 +38,10 @@ enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; const char *size; u_int w, h; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (args_has(args, 'C')) { if ((size = args_get(args, 'C')) == NULL) { cmdq_error(cmdq, "missing size"); diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 481154ce..0c1a7e8c 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_rename_session_entry = { "rename-session", "rename", "t:", 1, 1, CMD_TARGET_SESSION_USAGE " new-name", - 0, + CMD_SESSION_T, cmd_rename_session_exec }; @@ -40,7 +40,7 @@ enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; + struct session *s = cmdq->state.tflag.s; const char *newname; newname = args->argv[0]; @@ -53,9 +53,6 @@ cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - RB_REMOVE(sessions, &sessions, s); free(s->name); s->name = xstrdup(newname); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index bc85d96b..6609ebab 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_rename_window_entry = { "rename-window", "renamew", "t:", 1, 1, CMD_TARGET_WINDOW_USAGE " new-name", - 0, + CMD_WINDOW_T, cmd_rename_window_exec }; @@ -40,11 +40,7 @@ enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); + struct winlink *wl = cmdq->state.tflag.wl; window_set_name(wl->window, args->argv[0]); options_set_number(wl->window->options, "automatic-rename", 0); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index b342307d..a4de32df 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -33,9 +33,8 @@ void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); const struct cmd_entry cmd_resize_pane_entry = { "resize-pane", "resizep", "DLMRt:Ux:y:Z", 0, 1, - "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE - " [adjustment]", - 0, + "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", + CMD_PANE_T, cmd_resize_pane_exec }; @@ -43,13 +42,13 @@ enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct window_pane *wp = cmdq->state.tflag.wp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct client *c = cmdq->client; - struct session *s; - struct winlink *wl; - struct window *w; + struct session *s = cmdq->state.tflag.s; const char *errstr; char *cause; - struct window_pane *wp; u_int adjust; int x, y; @@ -63,10 +62,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); w = wl->window; - if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index a50b9d06..45098d80 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { "respawn-pane", "respawnp", "kt:", 0, -1, "[-k] " CMD_TARGET_PANE_USAGE " [command]", - 0, + CMD_PANE_T, cmd_respawn_pane_exec }; @@ -42,20 +42,16 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - struct session *s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; struct environ *env; const char *path; char *cause; u_int idx; struct environ_entry *envent; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (!args_has(self->args, 'k') && wp->fd != -1) { if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 93af1b68..f6550dee 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_respawn_window_entry = { "respawn-window", "respawnw", "kt:", 0, -1, "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", - 0, + CMD_WINDOW_T, cmd_respawn_window_exec }; @@ -41,25 +41,21 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct window_pane *wp; - struct session *s; struct environ *env; const char *path; char *cause; struct environ_entry *envent; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (!args_has(self->args, 'k')) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd == -1) continue; - cmdq_error(cmdq, - "window still active: %s:%d", s->name, wl->idx); + cmdq_error(cmdq, "window still active: %s:%d", s->name, + wl->idx); return (CMD_RETURN_ERROR); } } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 859ff04a..4122886a 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -30,24 +30,19 @@ const struct cmd_entry cmd_rotate_window_entry = { "rotate-window", "rotatew", "Dt:U", 0, 0, "[-DU] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_rotate_window_exec }; enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct window *w; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct window_pane *wp, *wp2; struct layout_cell *lc; u_int sx, sy, xoff, yoff; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (args_has(self->args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); TAILQ_REMOVE(&w->panes, wp, entry); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index fd5d84d4..0dae39ac 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_run_shell_entry = { "run-shell", "run", "bt:", 1, 1, "[-b] " CMD_TARGET_PANE_USAGE " shell-command", - 0, + CMD_PANE_T|CMD_CANFAIL, cmd_run_shell_exec }; @@ -75,31 +75,18 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct cmd_run_shell_data *cdata; char *shellcmd; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window_pane *wp = NULL; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; const char *cwd; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - cwd = wp->cwd; - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - if (cmdq->client != NULL && cmdq->client->session == NULL) - cwd = cmdq->client->cwd; - else if (s != NULL) - cwd = s->cwd; - else - cwd = NULL; - } - + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = NULL; ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index daef88a8..fc8d7bee 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -94,7 +94,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((s = cmd_find_current(cmdq)) != NULL) + else if ((s = c->session) != NULL) cwd = s->cwd; else cwd = "."; diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 14737dc8..aac60083 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_select_layout_entry = { "select-layout", "selectl", "nopt:", 0, 1, "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - 0, + CMD_WINDOW_T, cmd_select_layout_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_next_layout_entry = { "next-layout", "nextl", "t:", 0, 0, CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_layout_exec }; @@ -48,7 +48,7 @@ const struct cmd_entry cmd_previous_layout_entry = { "previous-layout", "prevl", "t:", 0, 0, CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_layout_exec }; @@ -56,16 +56,13 @@ enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; + struct winlink *wl = cmdq->state.tflag.wl; struct window *w; const char *layoutname; char *oldlayout; int next, previous, layout; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); w = wl->window; - server_unzoom_window(w); next = self->entry == &cmd_next_layout_entry; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 7986e98c..e83dc609 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", "DdegLlMmP:Rt:U", 0, 0, "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_select_pane_exec }; @@ -38,7 +38,7 @@ const struct cmd_entry cmd_last_pane_entry = { "last-pane", "lastp", "det:", 0, 0, "[-de] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_pane_exec }; @@ -46,19 +46,15 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; - struct session *s; - struct window_pane *wp, *lastwp, *markedwp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct session *s = cmdq->state.tflag.s; + struct window_pane *wp = cmdq->state.tflag.wp, *lastwp, *markedwp; const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (w->last == NULL) { + if (wl->window->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } @@ -79,9 +75,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) + server_unzoom_window(wp->window); + if (!window_pane_visible(wp)) { + cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); - w = wl->window; + } if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) diff --git a/cmd-select-window.c b/cmd-select-window.c index f530f1fe..ede60dae 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_select_window_entry = { "select-window", "selectw", "lnpTt:", 0, 0, "[-lnpT] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_window_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_next_window_entry = { "next-window", "next", "at:", 0, 0, "[-a] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_select_window_exec }; @@ -48,7 +48,7 @@ const struct cmd_entry cmd_previous_window_entry = { "previous-window", "prev", "at:", 0, 0, "[-a] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_select_window_exec }; @@ -56,16 +56,15 @@ const struct cmd_entry cmd_last_window_entry = { "last-window", "last", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_select_window_exec }; enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct session *s; + struct winlink *wl = cmdq->state.tflag.wl; + struct session *s = cmdq->state.tflag.s; int next, previous, last, activity; next = self->entry == &cmd_next_window_entry; @@ -79,10 +78,6 @@ cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) last = 1; if (next || previous || last) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - activity = args_has(self->args, 'a'); if (next) { if (session_next(s, activity) != 0) { @@ -103,10 +98,6 @@ cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) server_redraw_session(s); } else { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); - /* * If -T and select-window is invoked on same window as * current, switch to previous window. diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 1461baa9..8f9b4ddf 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_send_keys_entry = { "send-keys", "send", "lRMt:", 0, -1, "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", - 0, + CMD_PANE_T, cmd_send_keys_exec }; @@ -41,7 +41,7 @@ const struct cmd_entry cmd_send_prefix_entry = { "send-prefix", NULL, "2t:", 0, 0, "[-2] " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_send_keys_exec }; @@ -49,11 +49,11 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; struct mouse_event *m = &cmdq->item->mouse; - struct window_pane *wp; - struct session *s; - int i, literal; const u_char *keystr; + int i, literal; key_code key; if (args_has(args, 'M')) { @@ -66,9 +66,6 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) - return (CMD_RETURN_ERROR); - if (self->entry == &cmd_send_prefix_entry) { if (args_has(args, '2')) key = options_get_number(s->options, "prefix2"); diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 2c0010af..4be967b7 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_set_environment_entry = { "set-environment", "setenv", "grt:u", 1, 2, "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - 0, + CMD_SESSION_T, cmd_set_environment_exec }; @@ -41,7 +41,6 @@ enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct environ *env; const char *name, *value; @@ -62,11 +61,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) env = global_environ; - else { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - env = s->environ; - } + else + env = cmdq->state.tflag.s->environ; if (args_has(self->args, 'u')) { if (value != NULL) { diff --git a/cmd-set-hook.c b/cmd-set-hook.c index ec58418e..d75b0ba1 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_set_hook_entry = { "set-hook", NULL, "gt:u", 1, 2, "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", - 0, + CMD_SESSION_T, cmd_set_hook_exec }; @@ -41,7 +41,7 @@ const struct cmd_entry cmd_show_hooks_entry = { "show-hooks", NULL, "gt:", 0, 1, "[-g] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_set_hook_exec }; @@ -49,7 +49,6 @@ enum cmd_retval cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct cmd_list *cmdlist; struct hooks *hooks; struct hook *hook; @@ -58,12 +57,8 @@ cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'g')) hooks = global_hooks; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - hooks = s->hooks; - } + else + hooks = cmdq->state.tflag.s->hooks; if (self->entry == &cmd_show_hooks_entry) { hook = hooks_first(hooks); diff --git a/cmd-set-option.c b/cmd-set-option.c index daee62ac..86856fbb 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -67,8 +67,8 @@ struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, const struct cmd_entry cmd_set_option_entry = { "set-option", "set", "agoqst:uw", 1, 2, - "[-agosquw] [-t target-session|target-window] option [value]", - 0, + "[-agosquw] [-t target-window] option [value]", + CMD_WINDOW_T|CMD_CANFAIL, cmd_set_option_exec }; @@ -76,7 +76,7 @@ const struct cmd_entry cmd_set_window_option_entry = { "set-window-option", "setw", "agoqt:u", 1, 2, "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", - 0, + CMD_WINDOW_T|CMD_CANFAIL, cmd_set_option_exec }; @@ -84,12 +84,12 @@ enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - const struct options_table_entry *oe; - struct session *s; - struct winlink *wl; - struct client *c; - struct options *oo; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; struct window *w; + struct client *c; + const struct options_table_entry *oe; + struct options *oo; const char *optstr, *valstr; /* Get the option name and value. */ @@ -131,7 +131,6 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) oo = global_w_options; else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) { cmdq_error(cmdq, "couldn't set '%s'%s", optstr, @@ -145,7 +144,6 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) oo = global_s_options; else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) { cmdq_error(cmdq, "couldn't set '%s'%s", optstr, @@ -209,8 +207,8 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, const char *valstr) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; struct options *oo; if (args_has(args, 's')) @@ -219,21 +217,13 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, self->entry == &cmd_set_window_option_entry) { if (args_has(self->args, 'g')) oo = global_w_options; - else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); + else oo = wl->window->options; - } } else { if (args_has(self->args, 'g')) oo = global_s_options; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + else oo = s->options; - } } if (args_has(args, 'u')) { diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 96cfa289..8feb2e22 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", "gst:", 0, 1, "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - 0, + CMD_SESSION_T, cmd_show_environment_exec }; @@ -86,18 +86,13 @@ enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct environ *env; struct environ_entry *envent; if (args_has(self->args, 'g')) env = global_environ; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - env = s->environ; - } + else + env = cmdq->state.tflag.s->environ; if (args->argc != 0) { envent = environ_find(env, args->argv[0]); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index c98c2c91..b80b9fe7 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -35,7 +35,7 @@ const struct cmd_entry cmd_show_messages_entry = { "show-messages", "showmsgs", "JTt:", 0, 0, "[-JT] " CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_show_messages_exec }; @@ -94,7 +94,7 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct message_entry *msg; char *tim; int done, blank; @@ -111,9 +111,6 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) if (done) return (CMD_RETURN_NORMAL); - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - TAILQ_FOREACH(msg, &c->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; diff --git a/cmd-show-options.c b/cmd-show-options.c index fd96a0e2..5437ec73 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_show_options_entry = { "show-options", "show", "gqst:vw", 0, 1, "[-gqsvw] [-t target-session|target-window] [option]", - 0, + CMD_WINDOW_T, cmd_show_options_exec }; @@ -46,19 +46,19 @@ const struct cmd_entry cmd_show_window_options_entry = { "show-window-options", "showw", "gvt:", 0, 1, "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - 0, + CMD_WINDOW_T, cmd_show_options_exec }; enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct session *s; - struct winlink *wl; - struct options *oo; - int quiet; - enum options_table_scope scope; + struct args *args = self->args; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct options *oo; + enum options_table_scope scope; + int quiet; if (args_has(self->args, 's')) { oo = global_options; @@ -68,22 +68,14 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) scope = OPTIONS_TABLE_WINDOW; if (args_has(self->args, 'g')) oo = global_w_options; - else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); + else oo = wl->window->options; - } } else { scope = OPTIONS_TABLE_SESSION; if (args_has(self->args, 'g')) oo = global_s_options; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + else oo = s->options; - } } quiet = args_has(self->args, 'q'); diff --git a/cmd-source-file.c b/cmd-source-file.c index e5710a0c..e776712c 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -45,8 +45,7 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_q *cmdq1; char *cause; - cmdq1 = cmdq_new(NULL); - cmdq1->client = cmdq->client; + cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_source_file_done; cmdq1->data = cmdq; diff --git a/cmd-split-window.c b/cmd-split-window.c index 80d9e2b1..59efae35 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_split_window_entry = { "bc:dF:l:hp:Pt:v", 0, -1, "[-bdhvP] [-c start-directory] [-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", - 0, + CMD_PANE_T, cmd_split_window_exec }; @@ -48,10 +48,10 @@ enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp, *new_wp = NULL; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct window_pane *wp = cmdq->state.tflag.wp, *new_wp = NULL; struct environ *env; const char *cmd, *path, *shell, *template, *cwd, *to_free; char **argv, *cause, *new_cause, *cp; @@ -62,9 +62,6 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct format_tree *ft; struct environ_entry *envent; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; server_unzoom_window(w); env = environ_create(); @@ -89,8 +86,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); + format_defaults(ft, cmdq->state.c, s, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -166,8 +162,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) template = SPLIT_WINDOW_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, - new_wp); + format_defaults(ft, cmdq->state.c, s, wl, new_wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index dc2b7246..62d6bad3 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_swap_pane_entry = { "swap-pane", "swapp", "dDs:t:U", 0, 0, "[-dDU] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_MARKED_S|CMD_PANE_T, cmd_swap_pane_exec }; @@ -40,16 +40,18 @@ enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *src_wl, *dst_wl; + struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &dst_wp); - if (dst_wl == NULL) - return (CMD_RETURN_ERROR); + dst_wl = cmdq->state.tflag.wl; dst_w = dst_wl->window; + dst_wp = cmdq->state.tflag.wp; + src_wp = cmdq->state.sflag.wp; + src_wl = cmdq->state.sflag.wl; + src_w = src_wl->window; server_unzoom_window(dst_w); if (!args_has(args, 's')) { @@ -62,19 +64,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - } else { - src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, - &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); - src_w = src_wl->window; } - } else { - src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, - &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); - src_w = src_wl->window; } server_unzoom_window(src_w); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 51682e37..0ed3ca81 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -32,31 +32,28 @@ const struct cmd_entry cmd_swap_window_entry = { "swap-window", "swapw", "ds:t:", 0, 0, "[-d] " CMD_SRCDST_WINDOW_USAGE, - 0, + CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, cmd_swap_window_exec }; enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - const char *target_src, *target_dst; struct session *src, *dst; struct session_group *sg_src, *sg_dst; struct winlink *wl_src, *wl_dst; struct window *w; - target_src = args_get(args, 's'); - if ((wl_src = cmd_find_window_marked(cmdq, target_src, &src)) == NULL) - return (CMD_RETURN_ERROR); - target_dst = args_get(args, 't'); - if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) - return (CMD_RETURN_ERROR); - + wl_src = cmdq->state.sflag.wl; + src = cmdq->state.sflag.s; sg_src = session_group_find(src); + + wl_dst = cmdq->state.tflag.wl; + dst = cmdq->state.tflag.s; sg_dst = session_group_find(dst); - if (src != dst && - sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) { + + if (src != dst && sg_src != NULL && sg_dst != NULL && + sg_src == sg_dst) { cmdq_error(cmdq, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 4746b15a..919d0057 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", "lc:Enpt:rT:", 0, 0, "[-Elnpr] [-c target-client] [-t target-session] [-T key-table]", - CMD_READONLY, + CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T|CMD_PREFERUNATTACHED, cmd_switch_client_exec }; @@ -41,16 +41,13 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window *w = NULL; - struct window_pane *wp = NULL; - const char *tflag, *tablename, *update; + struct cmd_state *state = &cmdq->state; + struct client *c = state->c; + struct session *s = cmdq->state.tflag.s; + struct window_pane *wp; + const char *tablename, *update; struct key_table *table; - if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); if (args_has(args, 'r')) c->flags ^= CLIENT_READONLY; @@ -68,7 +65,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - tflag = args_get(args, 't'); if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(cmdq, "can't find next session"); @@ -82,37 +78,21 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } else if (args_has(args, 'l')) { if (c->last_session != NULL && session_alive(c->last_session)) s = c->last_session; + else + s = NULL; if (s == NULL) { cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } - } else { - if (tflag == NULL) { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - } else if (tflag[strcspn(tflag, ":.")] != '\0') { - if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - } else { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - w = window_find_by_id_str(tflag); - if (w == NULL) { - wp = window_pane_find_by_id_str(tflag); - if (wp != NULL) - w = wp->window; - } - if (w != NULL) - wl = winlink_find_by_window(&s->windows, w); - } - if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); - if (wl != NULL) { + s = state->tflag.s; + if (state->tflag.wl != NULL) { + wp = state->tflag.wp; if (wp != NULL) window_set_active_pane(wp->window, wp); - session_set_current(s, wl); + session_set_current(s, state->tflag.wl); } } diff --git a/cmd.c b/cmd.c index a950a49a..eac3c199 100644 --- a/cmd.c +++ b/cmd.c @@ -207,6 +207,10 @@ const struct cmd_entry *cmd_table[] = { NULL }; +static void cmd_clear_state(struct cmd_state *); +static struct client *cmd_get_state_client(struct cmd_q *, int); +static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); + int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { @@ -388,6 +392,253 @@ usage: return (NULL); } +static void +cmd_clear_state(struct cmd_state *state) +{ + state->c = NULL; + + state->tflag.s = NULL; + state->tflag.wl = NULL; + state->tflag.wp = NULL; + state->tflag.idx = -1; + + state->sflag.s = NULL; + state->sflag.wl = NULL; + state->sflag.wp = NULL; + state->sflag.idx = -1; +} + +static struct client * +cmd_get_state_client(struct cmd_q *cmdq, int quiet) +{ + struct cmd *cmd = cmdq->cmd; + struct args *args = cmd->args; + + switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { + case 0: + return (cmd_find_client(cmdq, NULL, 1)); + case CMD_CLIENT_C: + return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); + case CMD_CLIENT_T: + return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); + default: + fatalx("both -t and -c for %s", cmd->entry->name); + } +} + +static int +cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) +{ + struct cmd_state *state = &cmdq->state; + struct cmd_state_flag *statef = NULL; + const char *flag; + int flags = cmd->entry->flags, everything = 0; + int allflags = 0; + int prefer = !!(flags & CMD_PREFERUNATTACHED); + struct session *s; + struct window *w; + struct winlink *wl; + struct window_pane *wp; + + if (c == 't') { + statef = &cmdq->state.tflag; + allflags = CMD_ALL_T; + } else if (c == 's') { + statef = &cmdq->state.sflag; + allflags = CMD_ALL_S; + } + + /* + * If the command wants something and no argument is present, use the + * base command instead. + */ + flag = args_get(cmd->args, c); + if (flag == NULL) { + if ((flags & allflags) == 0) + return (0); /* doesn't care about flag */ + cmd = cmdq->cmd; + everything = 1; + flag = args_get(cmd->args, c); + } + + /* + * If no flag and the current command is allowed to fail, just skip to + * fill in as much we can, otherwise continue and fail later if needed. + */ + if (flag == NULL && (flags & CMD_CANFAIL)) + goto complete_everything; + + /* Fill in state using command (current or base) flags. */ + switch (cmd->entry->flags & allflags) { + case 0: + break; + case CMD_SESSION_T|CMD_PANE_T: + case CMD_SESSION_S|CMD_PANE_S: + if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { + statef->wl = cmd_find_pane(cmdq, flag, &statef->s, + &statef->wp); + if (statef->wl == NULL) + return (-1); + } else { + statef->s = cmd_find_session(cmdq, flag, prefer); + if (statef->s == NULL) + return (-1); + + s = statef->s; + if (flag == NULL) { + statef->wl = s->curw; + statef->wp = s->curw->window->active; + } else { + if ((w = window_find_by_id_str(flag)) != NULL) + wp = w->active; + else { + wp = window_pane_find_by_id_str(flag); + if (wp != NULL) + w = wp->window; + } + wl = winlink_find_by_window(&s->windows, w); + if (wl != NULL) { + statef->wl = wl; + statef->wp = wp; + } + } + } + break; + case CMD_MOVEW_R|CMD_INDEX_T: + case CMD_MOVEW_R|CMD_INDEX_S: + statef->s = cmd_find_session(cmdq, flag, prefer); + if (statef->s == NULL) { + statef->idx = cmd_find_index(cmdq, flag, &statef->s); + if (statef->idx == -2) + return (-1); + } + break; + case CMD_SESSION_T: + case CMD_SESSION_S: + statef->s = cmd_find_session(cmdq, flag, prefer); + if (statef->s == NULL) + return (-1); + break; + case CMD_WINDOW_T: + case CMD_WINDOW_S: + statef->wl = cmd_find_window(cmdq, flag, &statef->s); + if (statef->wl == NULL) + return (-1); + break; + case CMD_WINDOW_MARKED_T: + case CMD_WINDOW_MARKED_S: + statef->wl = cmd_find_window_marked(cmdq, flag, &statef->s); + if (statef->wl == NULL) + return (-1); + break; + case CMD_PANE_T: + case CMD_PANE_S: + statef->wl = cmd_find_pane(cmdq, flag, &statef->s, + &statef->wp); + if (statef->wl == NULL) + return (-1); + break; + case CMD_PANE_MARKED_S: + case CMD_PANE_MARKED_T: + statef->wl = cmd_find_pane_marked(cmdq, flag, &statef->s, + &statef->wp); + if (statef->wl == NULL) + return (-1); + break; + case CMD_INDEX_T: + case CMD_INDEX_S: + statef->idx = cmd_find_index(cmdq, flag, &statef->s); + if (statef->idx == -2) + return (-1); + break; + default: + fatalx("too many -%c for %s", c, cmd->entry->name); + } + + /* + * If this is still the current command, it wants what it asked for and + * nothing more. If it's the base command, fill in as much as possible + * because the current command may have different flags. + */ + if (!everything) + return (0); + +complete_everything: + if (statef->s == NULL) { + if (state->c != NULL) + statef->s = state->c->session; + if (statef->s == NULL) + statef->s = cmd_find_current(cmdq); + if (statef->s == NULL) { + if (flags & CMD_CANFAIL) + return (0); + + cmdq_error(cmdq, "no current session"); + return (-1); + } + } + if (statef->wl == NULL) + statef->wl = cmd_find_window(cmdq, flag, &statef->s); + if (statef->wp == NULL) + statef->wl = cmd_find_pane(cmdq, flag, &statef->s, &statef->wp); + + return (0); +} + +int +cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) +{ + struct cmd_state *state = &cmdq->state; + struct args *args = cmd->args; + const char *cflag, *tflag; + char *tmp; + int error; + + tmp = cmd_print(cmd); + log_debug("preparing state for: %s (client %d)", tmp, + cmdq->client != NULL ? cmdq->client->fd : -1); + free(tmp); + + /* Start with an empty state. */ + cmd_clear_state(state); + + /* + * If the command wants a client and provides -c or -t, use it. If not, + * try the base command instead via cmd_get_state_client. No client is + * allowed if no flags, otherwise it must be available. + */ + switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { + case 0: + state->c = cmd_get_state_client(cmdq, 1); + break; + case CMD_CLIENT_C: + cflag = args_get(args, 'c'); + if (cflag == NULL) + state->c = cmd_get_state_client(cmdq, 0); + else + state->c = cmd_find_client(cmdq, cflag, 0); + if (state->c == NULL) + return (-1); + break; + case CMD_CLIENT_T: + tflag = args_get(args, 't'); + if (tflag == NULL) + state->c = cmd_get_state_client(cmdq, 0); + else + state->c = cmd_find_client(cmdq, tflag, 0); + if (state->c == NULL) + return (-1); + break; + default: + fatalx("both -c and -t for %s", cmd->entry->name); + } + + error = cmd_set_state_flag(cmd, cmdq, 't'); + if (error == 0) + error = cmd_set_state_flag(cmd, cmdq, 's'); + return (error); +} + char * cmd_print(struct cmd *cmd) { diff --git a/tmux.h b/tmux.h index e10fbcdf..5924470e 100644 --- a/tmux.h +++ b/tmux.h @@ -1293,6 +1293,20 @@ struct args { char **argv; }; +/* Context for a command about to be executed. */ +struct cmd_state_flag { + struct session *s; + struct winlink *wl; + struct window_pane *wp; + int idx; + +}; +struct cmd_state { + struct client *c; + struct cmd_state_flag tflag; + struct cmd_state_flag sflag; +}; + /* Command and list of commands. */ struct cmd { const struct cmd_entry *entry; @@ -1343,6 +1357,8 @@ struct cmd_q { struct cmd_q_item *item; struct cmd *cmd; + struct cmd_state state; + time_t time; u_int number; @@ -1365,10 +1381,31 @@ struct cmd_entry { #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 +#define CMD_SESSION_T 0x4 +#define CMD_SESSION_S 0x8 +#define CMD_WINDOW_T 0x10 +#define CMD_WINDOW_S 0x20 +#define CMD_PANE_T 0x40 +#define CMD_PANE_S 0x80 +#define CMD_CLIENT_T 0x100 +#define CMD_CLIENT_C 0x200 +#define CMD_INDEX_T 0x400 +#define CMD_INDEX_S 0x800 +#define CMD_CANFAIL 0x1000 +#define CMD_PREFERUNATTACHED 0x2000 +#define CMD_MOVEW_R 0x4000 /* for movew -r only */ +#define CMD_PANE_MARKED_S 0x8000 +#define CMD_PANE_MARKED_T 0x10000 +#define CMD_WINDOW_MARKED_T 0x20000 +#define CMD_WINDOW_MARKED_S 0x40000 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; +#define CMD_ALL_T (CMD_SESSION_T|CMD_WINDOW_T|CMD_PANE_T|CMD_INDEX_T| \ + CMD_MOVEW_R|CMD_PANE_MARKED_T|CMD_WINDOW_MARKED_T) +#define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ + CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) /* Key binding and key table. */ struct key_binding { @@ -1718,6 +1755,7 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); +int cmd_prepare_state(struct cmd *, struct cmd_q *); char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); @@ -1728,8 +1766,8 @@ char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ -enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, - const char *, int); +enum cmd_retval cmd_attach_session(struct cmd_q *, int, int, const char *, + int); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); From 9d191a6093673646c6c42a753ea3a337686b2b8d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 15:00:37 +0000 Subject: [PATCH 923/949] Move logging into cmd_find_target rather than each function. --- cmd-find.c | 45 ++++++++++++++++++++++----------------------- cmd.c | 5 ++--- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 609297aa..f85f0707 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -76,7 +76,7 @@ int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); -void cmd_find_log_state(const char *, const char *, struct cmd_find_state *); +void cmd_find_log_state(const char *, struct cmd_find_state *); struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, enum cmd_find_type, int); @@ -827,6 +827,13 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, char *colon, *period, *copy = NULL; const char *session, *window, *pane; + /* Log the arguments. */ + if (target == NULL) + log_debug("%s: target none, type %d", __func__, type); + else + log_debug("%s: target %s, type %d", __func__, target, type); + log_debug("%s: cmdq %p, flags %#x", __func__, cmdq, flags); + /* Find current state. */ cmd_find_clear_state(¤t, cmdq, flags); if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { @@ -873,7 +880,7 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, cmdq_error(cmdq, "no mouse target"); goto error; } - return (&fs); + goto found; } /* Marked target is a plain ~ or {marked}. */ @@ -888,7 +895,7 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, fs.idx = fs.wl->idx; fs.w = fs.wl->window; fs.wp = marked_window_pane; - return (&fs); + goto found; } /* Find separators if they exist. */ @@ -1053,13 +1060,16 @@ current: free(copy); if (flags & CMD_FIND_WINDOW_INDEX) current.idx = -1; + cmd_find_log_state(__func__, ¤t); return (¤t); error: free(copy); + log_debug(" error"); return (NULL); found: + cmd_find_log_state(__func__, &fs); free(copy); return (&fs); @@ -1081,29 +1091,25 @@ no_pane: /* Log the result. */ void -cmd_find_log_state(const char *f, const char *target, struct cmd_find_state *fs) +cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) { - log_debug("%s: target %s%s", f, target == NULL ? "none" : target, - fs != NULL ? "" : " (failed)"); - if (fs == NULL) - return; if (fs->s != NULL) - log_debug("\ts=$%u", fs->s->id); + log_debug("%s: s=$%u", prefix, fs->s->id); else - log_debug("\ts=none"); + log_debug("%s: s=none", prefix); if (fs->wl != NULL) { - log_debug("\twl=%u %d w=@%u %s", fs->wl->idx, + log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, fs->wl->window == fs->w, fs->w->id, fs->w->name); } else - log_debug("\twl=none"); + log_debug("%s: wl=none", prefix); if (fs->wp != NULL) - log_debug("\twp=%%%u", fs->wp->id); + log_debug("%s: wp=%%%u", prefix, fs->wp->id); else - log_debug("\twp=none"); + log_debug("%s: wp=none", prefix); if (fs->idx != -1) - log_debug("\tidx=%d", fs->idx); + log_debug("%s: idx=%d", prefix, fs->idx); else - log_debug("\tidx=none"); + log_debug("%s: idx=none", prefix); } /* Find the current session. */ @@ -1114,7 +1120,6 @@ cmd_find_current(struct cmd_q *cmdq) int flags = CMD_FIND_QUIET; fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, flags); - cmd_find_log_state(__func__, NULL, fs); if (fs == NULL) return (NULL); @@ -1132,7 +1137,6 @@ cmd_find_session(struct cmd_q *cmdq, const char *target, int prefer_unattached) flags |= CMD_FIND_PREFER_UNATTACHED; fs = cmd_find_target(cmdq, target, CMD_FIND_SESSION, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1146,7 +1150,6 @@ cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) struct cmd_find_state *fs; fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, 0); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1164,7 +1167,6 @@ cmd_find_window_marked(struct cmd_q *cmdq, const char *target, int flags = CMD_FIND_DEFAULT_MARKED; fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1181,7 +1183,6 @@ cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, struct cmd_find_state *fs; fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, 0); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1201,7 +1202,6 @@ cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, int flags = CMD_FIND_DEFAULT_MARKED; fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1275,7 +1275,6 @@ cmd_find_index(struct cmd_q *cmdq, const char *target, struct session **sp) int flags = CMD_FIND_WINDOW_INDEX; fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (-2); diff --git a/cmd.c b/cmd.c index eac3c199..fc483740 100644 --- a/cmd.c +++ b/cmd.c @@ -538,8 +538,8 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) if (statef->wl == NULL) return (-1); break; - case CMD_PANE_MARKED_S: case CMD_PANE_MARKED_T: + case CMD_PANE_MARKED_S: statef->wl = cmd_find_pane_marked(cmdq, flag, &statef->s, &statef->wp); if (statef->wl == NULL) @@ -595,8 +595,7 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) int error; tmp = cmd_print(cmd); - log_debug("preparing state for: %s (client %d)", tmp, - cmdq->client != NULL ? cmdq->client->fd : -1); + log_debug("preparing state for: %s (client %p)", tmp, cmdq->client); free(tmp); /* Start with an empty state. */ From ff599f4004aaa6aae325ece5cbc996e2dc6f0b4f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 15:32:12 +0000 Subject: [PATCH 924/949] Remove the cmd_find_{session,window,pane,index} functions (which are just wrappers around cmd_find_target) and just use cmd_find_target directly. --- cmd-find.c | 149 ----------------------------------------------------- cmd.c | 119 ++++++++++++++++++++++++++---------------- tmux.h | 40 +++++++++----- 3 files changed, 103 insertions(+), 205 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index f85f0707..8059957b 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -27,31 +27,6 @@ #include "tmux.h" -#define CMD_FIND_PREFER_UNATTACHED 0x1 -#define CMD_FIND_QUIET 0x2 -#define CMD_FIND_WINDOW_INDEX 0x4 -#define CMD_FIND_DEFAULT_MARKED 0x8 -#define CMD_FIND_EXACT_SESSION 0x10 -#define CMD_FIND_EXACT_WINDOW 0x20 - -enum cmd_find_type { - CMD_FIND_PANE, - CMD_FIND_WINDOW, - CMD_FIND_SESSION, -}; - -struct cmd_find_state { - struct cmd_q *cmdq; - int flags; - struct cmd_find_state *current; - - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - int idx; -}; - struct session *cmd_find_try_TMUX(struct client *, struct window *); int cmd_find_client_better(struct client *, struct client *); struct client *cmd_find_best_client(struct client **, u_int); @@ -78,9 +53,6 @@ int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); void cmd_find_log_state(const char *, struct cmd_find_state *); -struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, - enum cmd_find_type, int); - const char *cmd_find_session_table[][2] = { { NULL, NULL } }; @@ -1112,106 +1084,6 @@ cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) log_debug("%s: idx=none", prefix); } -/* Find the current session. */ -struct session * -cmd_find_current(struct cmd_q *cmdq) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_QUIET; - - fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, flags); - if (fs == NULL) - return (NULL); - - return (fs->s); -} - -/* Find the target session or report an error and return NULL. */ -struct session * -cmd_find_session(struct cmd_q *cmdq, const char *target, int prefer_unattached) -{ - struct cmd_find_state *fs; - int flags = 0; - - if (prefer_unattached) - flags |= CMD_FIND_PREFER_UNATTACHED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_SESSION, flags); - if (fs == NULL) - return (NULL); - - return (fs->s); -} - -/* Find the target window or report an error and return NULL. */ -struct winlink * -cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) -{ - struct cmd_find_state *fs; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, 0); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - return (fs->wl); -} - -/* Find the target window, defaulting to marked rather than current. */ -struct winlink * -cmd_find_window_marked(struct cmd_q *cmdq, const char *target, - struct session **sp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_DEFAULT_MARKED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - return (fs->wl); -} - -/* Find the target pane and report an error and return NULL. */ -struct winlink * -cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, - struct window_pane **wpp) -{ - struct cmd_find_state *fs; - - fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, 0); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - if (wpp != NULL) - *wpp = fs->wp; - return (fs->wl); -} - -/* Find the target pane, defaulting to marked rather than current. */ -struct winlink * -cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, - struct session **sp, struct window_pane **wpp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_DEFAULT_MARKED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - if (wpp != NULL) - *wpp = fs->wp; - return (fs->wl); -} - /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) @@ -1261,24 +1133,3 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) log_debug("%s: target %s, return %p", __func__, target, c); return (c); } - -/* - * Find the target session and window index, whether or not it exists in the - * session. Return -2 on error or -1 if no window index is specified. This is - * used when parsing an argument for a window target that may not exist (for - * example if it is going to be created). - */ -int -cmd_find_index(struct cmd_q *cmdq, const char *target, struct session **sp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_WINDOW_INDEX; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - if (fs == NULL) - return (-2); - - if (sp != NULL) - *sp = fs->s; - return (fs->idx); -} diff --git a/cmd.c b/cmd.c index fc483740..0bb804ef 100644 --- a/cmd.c +++ b/cmd.c @@ -433,13 +433,14 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) struct cmd_state_flag *statef = NULL; const char *flag; int flags = cmd->entry->flags, everything = 0; - int allflags = 0; - int prefer = !!(flags & CMD_PREFERUNATTACHED); + int allflags = 0, targetflags; struct session *s; struct window *w; struct winlink *wl; struct window_pane *wp; + struct cmd_find_state *fs; + /* Set up state for either -t or -s. */ if (c == 't') { statef = &cmdq->state.tflag; allflags = CMD_ALL_T; @@ -469,26 +470,35 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) goto complete_everything; /* Fill in state using command (current or base) flags. */ + if (flags & CMD_PREFERUNATTACHED) + targetflags = CMD_FIND_PREFER_UNATTACHED; + else + targetflags = 0; switch (cmd->entry->flags & allflags) { case 0: break; case CMD_SESSION_T|CMD_PANE_T: case CMD_SESSION_S|CMD_PANE_S: if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - statef->wl = cmd_find_pane(cmdq, flag, &statef->s, - &statef->wp); - if (statef->wl == NULL) + fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, + targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->wl = fs->wl; + statef->wp = fs->wp; } else { - statef->s = cmd_find_session(cmdq, flag, prefer); - if (statef->s == NULL) + fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, + targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; - s = statef->s; if (flag == NULL) { - statef->wl = s->curw; - statef->wp = s->curw->window->active; + statef->wl = statef->s->curw; + statef->wp = statef->s->curw->window->active; } else { + s = statef->s; if ((w = window_find_by_id_str(flag)) != NULL) wp = w->active; else { @@ -506,50 +516,58 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) break; case CMD_MOVEW_R|CMD_INDEX_T: case CMD_MOVEW_R|CMD_INDEX_S: - statef->s = cmd_find_session(cmdq, flag, prefer); - if (statef->s == NULL) { - statef->idx = cmd_find_index(cmdq, flag, &statef->s); - if (statef->idx == -2) + fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); + if (fs != NULL) + statef->s = fs->s; + else { + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, + CMD_FIND_WINDOW_INDEX); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->idx = fs->idx; } break; case CMD_SESSION_T: case CMD_SESSION_S: - statef->s = cmd_find_session(cmdq, flag, prefer); - if (statef->s == NULL) - return (-1); - break; - case CMD_WINDOW_T: - case CMD_WINDOW_S: - statef->wl = cmd_find_window(cmdq, flag, &statef->s); - if (statef->wl == NULL) + fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; break; case CMD_WINDOW_MARKED_T: case CMD_WINDOW_MARKED_S: - statef->wl = cmd_find_window_marked(cmdq, flag, &statef->s); - if (statef->wl == NULL) - return (-1); - break; - case CMD_PANE_T: - case CMD_PANE_S: - statef->wl = cmd_find_pane(cmdq, flag, &statef->s, - &statef->wp); - if (statef->wl == NULL) + targetflags |= CMD_FIND_DEFAULT_MARKED; + /* FALLTHROUGH */ + case CMD_WINDOW_T: + case CMD_WINDOW_S: + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->wl = fs->wl; break; case CMD_PANE_MARKED_T: case CMD_PANE_MARKED_S: - statef->wl = cmd_find_pane_marked(cmdq, flag, &statef->s, - &statef->wp); - if (statef->wl == NULL) + targetflags |= CMD_FIND_DEFAULT_MARKED; + /* FALLTHROUGH */ + case CMD_PANE_T: + case CMD_PANE_S: + fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->wl = fs->wl; + statef->wp = fs->wp; break; case CMD_INDEX_T: case CMD_INDEX_S: - statef->idx = cmd_find_index(cmdq, flag, &statef->s); - if (statef->idx == -2) + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, + CMD_FIND_WINDOW_INDEX); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->idx = fs->idx; break; default: fatalx("too many -%c for %s", c, cmd->entry->name); @@ -567,21 +585,34 @@ complete_everything: if (statef->s == NULL) { if (state->c != NULL) statef->s = state->c->session; - if (statef->s == NULL) - statef->s = cmd_find_current(cmdq); + if (statef->s == NULL) { + fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, + CMD_FIND_QUIET); + if (fs != NULL) + statef->s = fs->s; + } if (statef->s == NULL) { if (flags & CMD_CANFAIL) return (0); - cmdq_error(cmdq, "no current session"); return (-1); } } - if (statef->wl == NULL) - statef->wl = cmd_find_window(cmdq, flag, &statef->s); - if (statef->wp == NULL) - statef->wl = cmd_find_pane(cmdq, flag, &statef->s, &statef->wp); - + if (statef->wl == NULL) { + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, 0); + if (fs != NULL) { + statef->s = fs->s; + statef->wl = fs->wl; + } + } + if (statef->wp == NULL) { + fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, 0); + if (fs != NULL) { + statef->s = fs->s; + statef->wl = fs->wl; + statef->wp = fs->wp; + } + } return (0); } diff --git a/tmux.h b/tmux.h index 5924470e..7a30fc71 100644 --- a/tmux.h +++ b/tmux.h @@ -1407,6 +1407,32 @@ struct cmd_entry { #define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) +/* Command find structures. */ +enum cmd_find_type { + CMD_FIND_PANE, + CMD_FIND_WINDOW, + CMD_FIND_SESSION, +}; +struct cmd_find_state { + struct cmd_q *cmdq; + int flags; + struct cmd_find_state *current; + + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + int idx; +}; + +/* Command fine flags. */ +#define CMD_FIND_PREFER_UNATTACHED 0x1 +#define CMD_FIND_QUIET 0x2 +#define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 +#define CMD_FIND_EXACT_SESSION 0x10 +#define CMD_FIND_EXACT_WINDOW 0x20 + /* Key binding and key table. */ struct key_binding { key_code key; @@ -1734,19 +1760,9 @@ long long args_strtonum(struct args *, u_char, long long, long long, char **); /* cmd-find.c */ -struct session *cmd_find_current(struct cmd_q *); -struct session *cmd_find_session(struct cmd_q *, const char *, int); -struct winlink *cmd_find_window(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_window_marked(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, - struct window_pane **); -struct winlink *cmd_find_pane_marked(struct cmd_q *, const char *, - struct session **, struct window_pane **); +struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, + enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); -int cmd_find_index(struct cmd_q *, const char *, - struct session **); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); From 208e2dad1e197d6256fe977d675809996e8bcf89 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 16:11:42 +0000 Subject: [PATCH 925/949] If command returns error, report it. --- cmd-queue.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-queue.c b/cmd-queue.c index 7b2675fa..c0fc26c6 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -199,6 +199,8 @@ cmdq_continue_one(struct cmd_q *cmdq) if (cmd_prepare_state(cmd, cmdq) != 0) goto error; retval = cmd->entry->exec(cmd, cmdq); + if (retval == CMD_RETURN_ERROR) + goto error; cmdq_guard(cmdq, "end", flags); return (retval); From 9b7697db62dc1c809955e8f8fea2868c230cc503 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 16:44:35 +0000 Subject: [PATCH 926/949] Change cmd_find_target to use a state struct from the caller. --- cmd-find.c | 102 ++++++++++++++++++++++++++++------------------------- cmd.c | 100 ++++++++++++++++++++++++++------------------------- tmux.h | 4 +-- 3 files changed, 108 insertions(+), 98 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 8059957b..139ec069 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -789,15 +789,18 @@ cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) fs->idx = -1; } -/* Split target into pieces and resolve for the given type. */ -struct cmd_find_state * -cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, - int flags) +/* + * Split target into pieces and resolve for the given type. Fills in the given + * state. Returns 0 on success or -1 on error. + */ +int +cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, + const char *target, enum cmd_find_type type, int flags) { - static struct cmd_find_state fs, current; - struct mouse_event *m; - char *colon, *period, *copy = NULL; - const char *session, *window, *pane; + struct cmd_find_state current; + struct mouse_event *m; + char *colon, *period, *copy = NULL; + const char *session, *window, *pane; /* Log the arguments. */ if (target == NULL) @@ -822,8 +825,8 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, } /* Clear new state. */ - cmd_find_clear_state(&fs, cmdq, flags); - fs.current = ¤t; + cmd_find_clear_state(fs, cmdq, flags); + fs->current = ¤t; /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') @@ -834,20 +837,20 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, m = &cmdq->item->mouse; switch (type) { case CMD_FIND_PANE: - fs.wp = cmd_mouse_pane(m, &fs.s, &fs.wl); - if (fs.wp != NULL) - fs.w = fs.wl->window; + fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); + if (fs->wp != NULL) + fs->w = fs->wl->window; break; case CMD_FIND_WINDOW: case CMD_FIND_SESSION: - fs.wl = cmd_mouse_window(m, &fs.s); - if (fs.wl != NULL) { - fs.w = fs.wl->window; - fs.wp = fs.w->active; + fs->wl = cmd_mouse_window(m, &fs->s); + if (fs->wl != NULL) { + fs->w = fs->wl->window; + fs->wp = fs->w->active; } break; } - if (fs.wp == NULL) { + if (fs->wp == NULL) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no mouse target"); goto error; @@ -862,11 +865,11 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, cmdq_error(cmdq, "no marked target"); goto error; } - fs.s = marked_session; - fs.wl = marked_winlink; - fs.idx = fs.wl->idx; - fs.w = fs.wl->window; - fs.wp = marked_window_pane; + fs->s = marked_session; + fs->wl = marked_winlink; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = marked_window_pane; goto found; } @@ -919,11 +922,11 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* Set exact match flags. */ if (session != NULL && *session == '=') { session++; - fs.flags |= CMD_FIND_EXACT_SESSION; + fs->flags |= CMD_FIND_EXACT_SESSION; } if (window != NULL && *window == '=') { window++; - fs.flags |= CMD_FIND_EXACT_WINDOW; + fs->flags |= CMD_FIND_EXACT_WINDOW; } /* Empty is the same as NULL. */ @@ -956,32 +959,32 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* If the session isn't NULL, look it up. */ if (session != NULL) { /* This will fill in session. */ - if (cmd_find_get_session(&fs, session) != 0) + if (cmd_find_get_session(fs, session) != 0) goto no_session; /* If window and pane are NULL, use that session's current. */ if (window == NULL && pane == NULL) { - fs.wl = fs.s->curw; - fs.idx = -1; - fs.w = fs.wl->window; - fs.wp = fs.w->active; + fs->wl = fs->s->curw; + fs->idx = -1; + fs->w = fs->wl->window; + fs->wp = fs->w->active; goto found; } /* If window is present but pane not, find window in session. */ if (window != NULL && pane == NULL) { /* This will fill in winlink and window. */ - if (cmd_find_get_window_with_session(&fs, window) != 0) + if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; if (~flags & CMD_FIND_WINDOW_INDEX) - fs.wp = fs.wl->window->active; + fs->wp = fs->wl->window->active; goto found; } /* If pane is present but window not, find pane. */ if (window == NULL && pane != NULL) { /* This will fill in winlink and window and pane. */ - if (cmd_find_get_pane_with_session(&fs, pane) != 0) + if (cmd_find_get_pane_with_session(fs, pane) != 0) goto no_pane; goto found; } @@ -990,10 +993,10 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, * If window and pane are present, find both in session. This * will fill in winlink and window. */ - if (cmd_find_get_window_with_session(&fs, window) != 0) + if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; /* This will fill in pane. */ - if (cmd_find_get_pane_with_window(&fs, pane) != 0) + if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } @@ -1001,10 +1004,10 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* No session. If window and pane, try them. */ if (window != NULL && pane != NULL) { /* This will fill in session, winlink and window. */ - if (cmd_find_get_window(&fs, window) != 0) + if (cmd_find_get_window(fs, window) != 0) goto no_window; /* This will fill in pane. */ - if (cmd_find_get_pane_with_window(&fs, pane) != 0) + if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } @@ -1012,38 +1015,41 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* If just window is present, try it. */ if (window != NULL && pane == NULL) { /* This will fill in session, winlink and window. */ - if (cmd_find_get_window(&fs, window) != 0) + if (cmd_find_get_window(fs, window) != 0) goto no_window; if (~flags & CMD_FIND_WINDOW_INDEX) - fs.wp = fs.wl->window->active; + fs->wp = fs->wl->window->active; goto found; } /* If just pane is present, try it. */ if (window == NULL && pane != NULL) { /* This will fill in session, winlink, window and pane. */ - if (cmd_find_get_pane(&fs, pane) != 0) + if (cmd_find_get_pane(fs, pane) != 0) goto no_pane; goto found; } current: - /* None is the current session. */ - free(copy); + /* Use the current session. */ if (flags & CMD_FIND_WINDOW_INDEX) current.idx = -1; - cmd_find_log_state(__func__, ¤t); - return (¤t); + memcpy(fs, ¤t, sizeof *fs); + goto found; error: - free(copy); + fs->current = NULL; log_debug(" error"); - return (NULL); + + free(copy); + return (-1); found: - cmd_find_log_state(__func__, &fs); + fs->current = NULL; + cmd_find_log_state(__func__, fs); + free(copy); - return (&fs); + return (0); no_session: if (~flags & CMD_FIND_QUIET) diff --git a/cmd.c b/cmd.c index 0bb804ef..43002eb3 100644 --- a/cmd.c +++ b/cmd.c @@ -433,12 +433,12 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) struct cmd_state_flag *statef = NULL; const char *flag; int flags = cmd->entry->flags, everything = 0; - int allflags = 0, targetflags; + int allflags = 0, targetflags, error; struct session *s; struct window *w; struct winlink *wl; struct window_pane *wp; - struct cmd_find_state *fs; + struct cmd_find_state fs; /* Set up state for either -t or -s. */ if (c == 't') { @@ -480,19 +480,19 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) case CMD_SESSION_T|CMD_PANE_T: case CMD_SESSION_S|CMD_PANE_S: if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, targetflags); - if (fs == NULL) + if (error != 0) return (-1); - statef->s = fs->s; - statef->wl = fs->wl; - statef->wp = fs->wp; + statef->s = fs.s; + statef->wl = fs.wl; + statef->wp = fs.wp; } else { - fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, - targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, + CMD_FIND_SESSION, targetflags); + if (error != 0) return (-1); - statef->s = fs->s; + statef->s = fs.s; if (flag == NULL) { statef->wl = statef->s->curw; @@ -516,24 +516,26 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) break; case CMD_MOVEW_R|CMD_INDEX_T: case CMD_MOVEW_R|CMD_INDEX_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); - if (fs != NULL) - statef->s = fs->s; + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, + targetflags); + if (error == 0) + statef->s = fs.s; else { - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, - CMD_FIND_WINDOW_INDEX); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, + CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); + if (error != 0) return (-1); - statef->s = fs->s; - statef->idx = fs->idx; + statef->s = fs.s; + statef->idx = fs.idx; } break; case CMD_SESSION_T: case CMD_SESSION_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, + targetflags); + if (error != 0) return (-1); - statef->s = fs->s; + statef->s = fs.s; break; case CMD_WINDOW_MARKED_T: case CMD_WINDOW_MARKED_S: @@ -541,11 +543,12 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_WINDOW_T: case CMD_WINDOW_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, + targetflags); + if (error != 0) return (-1); - statef->s = fs->s; - statef->wl = fs->wl; + statef->s = fs.s; + statef->wl = fs.wl; break; case CMD_PANE_MARKED_T: case CMD_PANE_MARKED_S: @@ -553,21 +556,22 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_PANE_T: case CMD_PANE_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, + targetflags); + if (error != 0) return (-1); - statef->s = fs->s; - statef->wl = fs->wl; - statef->wp = fs->wp; + statef->s = fs.s; + statef->wl = fs.wl; + statef->wp = fs.wp; break; case CMD_INDEX_T: case CMD_INDEX_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); - if (fs == NULL) + if (error != 0) return (-1); - statef->s = fs->s; - statef->idx = fs->idx; + statef->s = fs.s; + statef->idx = fs.idx; break; default: fatalx("too many -%c for %s", c, cmd->entry->name); @@ -586,10 +590,10 @@ complete_everything: if (state->c != NULL) statef->s = state->c->session; if (statef->s == NULL) { - fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, - CMD_FIND_QUIET); - if (fs != NULL) - statef->s = fs->s; + error = cmd_find_target(&fs, cmdq, NULL, + CMD_FIND_SESSION, CMD_FIND_QUIET); + if (error == 0) + statef->s = fs.s; } if (statef->s == NULL) { if (flags & CMD_CANFAIL) @@ -599,18 +603,18 @@ complete_everything: } } if (statef->wl == NULL) { - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, 0); - if (fs != NULL) { - statef->s = fs->s; - statef->wl = fs->wl; + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, 0); + if (error != 0) { + statef->s = fs.s; + statef->wl = fs.wl; } } if (statef->wp == NULL) { - fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, 0); - if (fs != NULL) { - statef->s = fs->s; - statef->wl = fs->wl; - statef->wp = fs->wp; + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, 0); + if (error != 0) { + statef->s = fs.s; + statef->wl = fs.wl; + statef->wp = fs.wp; } } return (0); diff --git a/tmux.h b/tmux.h index 7a30fc71..e6fb521e 100644 --- a/tmux.h +++ b/tmux.h @@ -1760,8 +1760,8 @@ long long args_strtonum(struct args *, u_char, long long, long long, char **); /* cmd-find.c */ -struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, - enum cmd_find_type, int); +int cmd_find_target(struct cmd_find_state *, struct cmd_q *, + const char *, enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); /* cmd.c */ From 9f5aca62a94f46be9c7637ebd3269d012c9346a5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 17:55:14 +0000 Subject: [PATCH 927/949] Use struct cmd_find_state directly and remove cmd_state_flag, also change so that winlink is set even if an index is too. --- cmd-find.c | 20 +++++++++------ cmd-new-window.c | 7 +----- tmux.h | 63 +++++++++++++++++++++--------------------------- 3 files changed, 41 insertions(+), 49 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 139ec069..582f1759 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -467,11 +467,10 @@ cmd_find_get_window(struct cmd_find_state *fs, const char *window) /* Otherwise try as a session itself. */ if (cmd_find_get_session(fs, window) == 0) { - if (~fs->flags & CMD_FIND_WINDOW_INDEX) { - fs->wl = fs->s->curw; - fs->w = fs->wl->window; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + if (~fs->flags & CMD_FIND_WINDOW_INDEX) fs->idx = fs->wl->idx; - } return (0); } @@ -493,6 +492,13 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) log_debug("%s: %s", __func__, window); exact = (fs->flags & CMD_FIND_EXACT_WINDOW); + /* + * Start with the current window as the default. So if only an index is + * found, the window will be the current. + */ + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + /* Check for window ids starting with @. */ if (*window == '@') { fs->w = window_find_by_id_str(window); @@ -976,8 +982,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, /* This will fill in winlink and window. */ if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; - if (~flags & CMD_FIND_WINDOW_INDEX) - fs->wp = fs->wl->window->active; + fs->wp = fs->wl->window->active; goto found; } @@ -1017,8 +1022,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, /* This will fill in session, winlink and window. */ if (cmd_find_get_window(fs, window) != 0) goto no_window; - if (~flags & CMD_FIND_WINDOW_INDEX) - fs->wp = fs->wl->window->active; + fs->wp = fs->wl->window->active; goto found; } diff --git a/cmd-new-window.c b/cmd-new-window.c index 8154bdfb..18d2952b 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -39,12 +39,7 @@ const struct cmd_entry cmd_new_window_entry = { "ac:dF:kn:Pt:", 0, -1, "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", - /* - * Using PREP_CANFAIL here ensures that the wl is filled in - * regardless; making PREP_INDEX the thing we want -t to be used for - * in the specific case. - */ - CMD_INDEX_T|CMD_CANFAIL, + CMD_INDEX_T, cmd_new_window_exec }; diff --git a/tmux.h b/tmux.h index e6fb521e..5724b536 100644 --- a/tmux.h +++ b/tmux.h @@ -1293,18 +1293,37 @@ struct args { char **argv; }; -/* Context for a command about to be executed. */ -struct cmd_state_flag { - struct session *s; - struct winlink *wl; - struct window_pane *wp; - int idx; - +/* Command find structures. */ +enum cmd_find_type { + CMD_FIND_PANE, + CMD_FIND_WINDOW, + CMD_FIND_SESSION, }; +struct cmd_find_state { + struct cmd_q *cmdq; + int flags; + struct cmd_find_state *current; + + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + int idx; +}; + +/* Command find flags. */ +#define CMD_FIND_PREFER_UNATTACHED 0x1 +#define CMD_FIND_QUIET 0x2 +#define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 +#define CMD_FIND_EXACT_SESSION 0x10 +#define CMD_FIND_EXACT_WINDOW 0x20 + +/* Context for command being executed. */ struct cmd_state { struct client *c; - struct cmd_state_flag tflag; - struct cmd_state_flag sflag; + struct cmd_find_state tflag; + struct cmd_find_state sflag; }; /* Command and list of commands. */ @@ -1407,32 +1426,6 @@ struct cmd_entry { #define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) -/* Command find structures. */ -enum cmd_find_type { - CMD_FIND_PANE, - CMD_FIND_WINDOW, - CMD_FIND_SESSION, -}; -struct cmd_find_state { - struct cmd_q *cmdq; - int flags; - struct cmd_find_state *current; - - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - int idx; -}; - -/* Command fine flags. */ -#define CMD_FIND_PREFER_UNATTACHED 0x1 -#define CMD_FIND_QUIET 0x2 -#define CMD_FIND_WINDOW_INDEX 0x4 -#define CMD_FIND_DEFAULT_MARKED 0x8 -#define CMD_FIND_EXACT_SESSION 0x10 -#define CMD_FIND_EXACT_WINDOW 0x20 - /* Key binding and key table. */ struct key_binding { key_code key; From 50f8ead4e602a8c47be7a715fff9d248a896f648 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 17:58:26 +0000 Subject: [PATCH 928/949] Don't log an error when doing the first check for move-window. --- cmd.c | 88 ++++++++++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/cmd.c b/cmd.c index 43002eb3..d7714ba2 100644 --- a/cmd.c +++ b/cmd.c @@ -430,7 +430,7 @@ static int cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) { struct cmd_state *state = &cmdq->state; - struct cmd_state_flag *statef = NULL; + struct cmd_find_state *fsf = NULL; const char *flag; int flags = cmd->entry->flags, everything = 0; int allflags = 0, targetflags, error; @@ -438,14 +438,13 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) struct window *w; struct winlink *wl; struct window_pane *wp; - struct cmd_find_state fs; /* Set up state for either -t or -s. */ if (c == 't') { - statef = &cmdq->state.tflag; + fsf = &cmdq->state.tflag; allflags = CMD_ALL_T; } else if (c == 's') { - statef = &cmdq->state.sflag; + fsf = &cmdq->state.sflag; allflags = CMD_ALL_S; } @@ -480,25 +479,21 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) case CMD_SESSION_T|CMD_PANE_T: case CMD_SESSION_S|CMD_PANE_S: if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, targetflags); if (error != 0) return (-1); - statef->s = fs.s; - statef->wl = fs.wl; - statef->wp = fs.wp; } else { - error = cmd_find_target(&fs, cmdq, flag, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, targetflags); if (error != 0) return (-1); - statef->s = fs.s; if (flag == NULL) { - statef->wl = statef->s->curw; - statef->wp = statef->s->curw->window->active; + fsf->wl = fsf->s->curw; + fsf->wp = fsf->s->curw->window->active; } else { - s = statef->s; + s = fsf->s; if ((w = window_find_by_id_str(flag)) != NULL) wp = w->active; else { @@ -508,34 +503,29 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) } wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { - statef->wl = wl; - statef->wp = wp; + fsf->wl = wl; + fsf->wp = wp; } } } break; case CMD_MOVEW_R|CMD_INDEX_T: case CMD_MOVEW_R|CMD_INDEX_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, - targetflags); - if (error == 0) - statef->s = fs.s; - else { - error = cmd_find_target(&fs, cmdq, flag, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, + targetflags|CMD_FIND_QUIET); + if (error != 0) { + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); if (error != 0) return (-1); - statef->s = fs.s; - statef->idx = fs.idx; } break; case CMD_SESSION_T: case CMD_SESSION_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, targetflags); if (error != 0) return (-1); - statef->s = fs.s; break; case CMD_WINDOW_MARKED_T: case CMD_WINDOW_MARKED_S: @@ -543,12 +533,10 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_WINDOW_T: case CMD_WINDOW_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, targetflags); if (error != 0) return (-1); - statef->s = fs.s; - statef->wl = fs.wl; break; case CMD_PANE_MARKED_T: case CMD_PANE_MARKED_S: @@ -556,22 +544,17 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_PANE_T: case CMD_PANE_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, targetflags); if (error != 0) return (-1); - statef->s = fs.s; - statef->wl = fs.wl; - statef->wp = fs.wp; break; case CMD_INDEX_T: case CMD_INDEX_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); if (error != 0) return (-1); - statef->s = fs.s; - statef->idx = fs.idx; break; default: fatalx("too many -%c for %s", c, cmd->entry->name); @@ -586,36 +569,31 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) return (0); complete_everything: - if (statef->s == NULL) { + if (fsf->s == NULL) { if (state->c != NULL) - statef->s = state->c->session; - if (statef->s == NULL) { - error = cmd_find_target(&fs, cmdq, NULL, + fsf->s = state->c->session; + if (fsf->s == NULL) { + error = cmd_find_target(fsf, cmdq, NULL, CMD_FIND_SESSION, CMD_FIND_QUIET); - if (error == 0) - statef->s = fs.s; + if (error != 0) + fsf->s = NULL; } - if (statef->s == NULL) { + if (fsf->s == NULL) { if (flags & CMD_CANFAIL) return (0); cmdq_error(cmdq, "no current session"); return (-1); } } - if (statef->wl == NULL) { - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, 0); - if (error != 0) { - statef->s = fs.s; - statef->wl = fs.wl; - } + if (fsf->wl == NULL) { + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, 0); + if (error != 0) + return (-1); } - if (statef->wp == NULL) { - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, 0); - if (error != 0) { - statef->s = fs.s; - statef->wl = fs.wl; - statef->wp = fs.wp; - } + if (fsf->wp == NULL) { + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, 0); + if (error != 0) + return (-1); } return (0); } From fd47084224ef1f0ddd22de1f40bd223bd7be5cf6 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 18:15:13 +0000 Subject: [PATCH 929/949] show-options and environment need CANFAIL flag. --- cmd-show-environment.c | 2 +- cmd-show-options.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 8feb2e22..723d3039 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", "gst:", 0, 1, "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - CMD_SESSION_T, + CMD_SESSION_T|CMD_CANFAIL, cmd_show_environment_exec }; diff --git a/cmd-show-options.c b/cmd-show-options.c index 5437ec73..3b39a532 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_show_options_entry = { "show-options", "show", "gqst:vw", 0, 1, "[-gqsvw] [-t target-session|target-window] [option]", - CMD_WINDOW_T, + CMD_WINDOW_T|CMD_CANFAIL, cmd_show_options_exec }; @@ -46,7 +46,7 @@ const struct cmd_entry cmd_show_window_options_entry = { "show-window-options", "showw", "gvt:", 0, 1, "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - CMD_WINDOW_T, + CMD_WINDOW_T|CMD_CANFAIL, cmd_show_options_exec }; From 66d1193a000e7ee0d6924f3c766b013ce0bc9e52 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 18:27:47 +0000 Subject: [PATCH 930/949] Remove an unnecessary function. --- cmd.c | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/cmd.c b/cmd.c index d7714ba2..789bcefb 100644 --- a/cmd.c +++ b/cmd.c @@ -207,9 +207,8 @@ const struct cmd_entry *cmd_table[] = { NULL }; -static void cmd_clear_state(struct cmd_state *); -static struct client *cmd_get_state_client(struct cmd_q *, int); -static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); +static void cmd_clear_state(struct cmd_state *); +static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) @@ -408,24 +407,6 @@ cmd_clear_state(struct cmd_state *state) state->sflag.idx = -1; } -static struct client * -cmd_get_state_client(struct cmd_q *cmdq, int quiet) -{ - struct cmd *cmd = cmdq->cmd; - struct args *args = cmd->args; - - switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { - case 0: - return (cmd_find_client(cmdq, NULL, 1)); - case CMD_CLIENT_C: - return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); - case CMD_CLIENT_T: - return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); - default: - fatalx("both -t and -c for %s", cmd->entry->name); - } -} - static int cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) { @@ -603,7 +584,6 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { struct cmd_state *state = &cmdq->state; struct args *args = cmd->args; - const char *cflag, *tflag; char *tmp; int error; @@ -621,23 +601,15 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) */ switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { case 0: - state->c = cmd_get_state_client(cmdq, 1); + state->c = cmd_find_client(cmdq, NULL, 1); break; case CMD_CLIENT_C: - cflag = args_get(args, 'c'); - if (cflag == NULL) - state->c = cmd_get_state_client(cmdq, 0); - else - state->c = cmd_find_client(cmdq, cflag, 0); + state->c = cmd_find_client(cmdq, args_get(args, 'c'), 0); if (state->c == NULL) return (-1); break; case CMD_CLIENT_T: - tflag = args_get(args, 't'); - if (tflag == NULL) - state->c = cmd_get_state_client(cmdq, 0); - else - state->c = cmd_find_client(cmdq, tflag, 0); + state->c = cmd_find_client(cmdq, args_get(args, 't'), 0); if (state->c == NULL) return (-1); break; From 72948d9f1d16dedf18dcb9b7ee96cc7ca803ef40 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 18:31:47 +0000 Subject: [PATCH 931/949] -c needs to be able for fail for display-message. --- cmd-display-message.c | 2 +- cmd.c | 15 ++++++++++----- tmux.h | 1 + 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 201f8b75..ddc55846 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_display_message_entry = { "c:pt:F:", 0, 1, "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - CMD_CLIENT_C|CMD_PANE_T, + CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, cmd_display_message_exec }; diff --git a/cmd.c b/cmd.c index 789bcefb..93bab204 100644 --- a/cmd.c +++ b/cmd.c @@ -585,7 +585,7 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) struct cmd_state *state = &cmdq->state; struct args *args = cmd->args; char *tmp; - int error; + int error, quiet; tmp = cmd_print(cmd); log_debug("preparing state for: %s (client %p)", tmp, cmdq->client); @@ -594,6 +594,11 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) /* Start with an empty state. */ cmd_clear_state(state); + /* No error messages if can fail. */ + quiet = 0; + if (cmd->entry->flags & CMD_CLIENT_CANFAIL) + quiet = 1; + /* * If the command wants a client and provides -c or -t, use it. If not, * try the base command instead via cmd_get_state_client. No client is @@ -604,13 +609,13 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) state->c = cmd_find_client(cmdq, NULL, 1); break; case CMD_CLIENT_C: - state->c = cmd_find_client(cmdq, args_get(args, 'c'), 0); - if (state->c == NULL) + state->c = cmd_find_client(cmdq, args_get(args, 'c'), quiet); + if (!quiet && state->c == NULL) return (-1); break; case CMD_CLIENT_T: - state->c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (state->c == NULL) + state->c = cmd_find_client(cmdq, args_get(args, 't'), quiet); + if (!quiet && state->c == NULL) return (-1); break; default: diff --git a/tmux.h b/tmux.h index 5724b536..8b26431d 100644 --- a/tmux.h +++ b/tmux.h @@ -1417,6 +1417,7 @@ struct cmd_entry { #define CMD_PANE_MARKED_T 0x10000 #define CMD_WINDOW_MARKED_T 0x20000 #define CMD_WINDOW_MARKED_S 0x40000 +#define CMD_CLIENT_CANFAIL 0x80000 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); From 899bee0056b2d90b1c40c800473014b458ebd63d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 21:17:37 +0000 Subject: [PATCH 932/949] Actually I thought cmd_get_state_client was unnecessary but it will be needed. --- cmd.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/cmd.c b/cmd.c index 93bab204..9f05a11e 100644 --- a/cmd.c +++ b/cmd.c @@ -207,8 +207,9 @@ const struct cmd_entry *cmd_table[] = { NULL }; -static void cmd_clear_state(struct cmd_state *); -static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); +static void cmd_clear_state(struct cmd_state *); +static struct client *cmd_get_state_client(struct cmd_q *, int); +static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) @@ -579,11 +580,30 @@ complete_everything: return (0); } +static struct client * +cmd_get_state_client(struct cmd_q *cmdq, int quiet) +{ + struct cmd *cmd = cmdq->cmd; + struct args *args = cmd->args; + + switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { + case 0: + return (cmd_find_client(cmdq, NULL, 1)); + case CMD_CLIENT_C: + return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); + case CMD_CLIENT_T: + return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); + default: + fatalx("both -t and -c for %s", cmd->entry->name); + } +} + int cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { struct cmd_state *state = &cmdq->state; struct args *args = cmd->args; + const char *cflag, *tflag; char *tmp; int error, quiet; @@ -606,14 +626,23 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) */ switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { case 0: - state->c = cmd_find_client(cmdq, NULL, 1); + state->c = cmd_get_state_client(cmdq, 1); break; case CMD_CLIENT_C: - state->c = cmd_find_client(cmdq, args_get(args, 'c'), quiet); + cflag = args_get(args, 'c'); + if (cflag == NULL) + state->c = cmd_get_state_client(cmdq, quiet); + else + state->c = cmd_find_client(cmdq, cflag, quiet); if (!quiet && state->c == NULL) return (-1); break; case CMD_CLIENT_T: + tflag = args_get(args, 't'); + if (tflag == NULL) + state->c = cmd_get_state_client(cmdq, 0); + else + state->c = cmd_find_client(cmdq, tflag, 0); state->c = cmd_find_client(cmdq, args_get(args, 't'), quiet); if (!quiet && state->c == NULL) return (-1); From ecfeee2e8255a77f82a07124c93c8dbc7683c421 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 21:53:57 +0000 Subject: [PATCH 933/949] Use member names in cmd_entry definitions so I stop getting confused about the order. --- cmd-attach-session.c | 13 +++++++---- cmd-bind-key.c | 14 ++++++++---- cmd-break-pane.c | 13 +++++++---- cmd-capture-pane.c | 15 +++++++----- cmd-choose-buffer.c | 13 +++++++---- cmd-choose-client.c | 13 +++++++---- cmd-choose-tree.c | 42 +++++++++++++++++++++------------- cmd-clear-history.c | 13 +++++++---- cmd-command-prompt.c | 14 ++++++++---- cmd-confirm-before.c | 13 +++++++---- cmd-copy-mode.c | 26 +++++++++++++-------- cmd-detach-client.c | 26 +++++++++++++-------- cmd-display-message.c | 15 +++++++----- cmd-display-panes.c | 13 +++++++---- cmd-find-window.c | 13 +++++++---- cmd-if-shell.c | 14 ++++++++---- cmd-join-pane.c | 26 +++++++++++++-------- cmd-kill-pane.c | 13 +++++++---- cmd-kill-server.c | 26 +++++++++++++-------- cmd-kill-session.c | 13 +++++++---- cmd-kill-window.c | 26 +++++++++++++-------- cmd-list-buffers.c | 13 +++++++---- cmd-list-clients.c | 13 +++++++---- cmd-list-keys.c | 26 +++++++++++++-------- cmd-list-panes.c | 13 +++++++---- cmd-list-sessions.c | 13 +++++++---- cmd-list-windows.c | 13 +++++++---- cmd-load-buffer.c | 13 +++++++---- cmd-lock-server.c | 39 +++++++++++++++++++------------ cmd-move-window.c | 26 +++++++++++++-------- cmd-new-session.c | 30 ++++++++++++++---------- cmd-new-window.c | 15 +++++++----- cmd-paste-buffer.c | 14 ++++++++---- cmd-pipe-pane.c | 13 +++++++---- cmd-refresh-client.c | 13 +++++++---- cmd-rename-session.c | 13 +++++++---- cmd-rename-window.c | 13 +++++++---- cmd-resize-pane.c | 14 ++++++++---- cmd-respawn-pane.c | 13 +++++++---- cmd-respawn-window.c | 13 +++++++---- cmd-rotate-window.c | 13 +++++++---- cmd-run-shell.c | 13 +++++++---- cmd-save-buffer.c | 26 +++++++++++++-------- cmd-select-layout.c | 39 +++++++++++++++++++------------ cmd-select-pane.c | 26 +++++++++++++-------- cmd-select-window.c | 52 ++++++++++++++++++++++++++---------------- cmd-send-keys.c | 26 +++++++++++++-------- cmd-set-buffer.c | 26 +++++++++++++-------- cmd-set-environment.c | 13 +++++++---- cmd-set-hook.c | 26 +++++++++++++-------- cmd-set-option.c | 26 +++++++++++++-------- cmd-show-environment.c | 13 +++++++---- cmd-show-messages.c | 26 +++++++++++++-------- cmd-show-options.c | 26 +++++++++++++-------- cmd-source-file.c | 13 +++++++---- cmd-split-window.c | 15 +++++++----- cmd-swap-pane.c | 13 +++++++---- cmd-swap-window.c | 13 +++++++---- cmd-switch-client.c | 16 ++++++++----- cmd-unbind-key.c | 13 +++++++---- cmd-wait-for.c | 13 +++++++---- cmd.c | 6 ++--- tmux.h | 18 ++++++++------- 63 files changed, 716 insertions(+), 449 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index c29e9d1a..976f180b 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -33,11 +33,14 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { - "attach-session", "attach", - "c:dErt:", 0, 0, - "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, - cmd_attach_session_exec + .name = "attach-session", + .alias = "attach", + + .args = { "c:dErt:", 0, 0 }, + .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, + .exec = cmd_attach_session_exec }; enum cmd_retval diff --git a/cmd-bind-key.c b/cmd-bind-key.c index b13409cb..df3285f7 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,11 +33,15 @@ enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, key_code); const struct cmd_entry cmd_bind_key_entry = { - "bind-key", "bind", - "cnrt:T:", 1, -1, - "[-cnr] [-t mode-table] [-T key-table] key command [arguments]", - 0, - cmd_bind_key_exec + .name = "bind-key", + .alias = "bind", + + .args = { "cnrt:T:", 1, -1 }, + .usage = "[-cnr] [-t mode-table] [-T key-table] key command " + "[arguments]", + + .flags = 0, + .exec = cmd_bind_key_exec }; enum cmd_retval diff --git a/cmd-break-pane.c b/cmd-break-pane.c index eb07fb87..62625c71 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { - "break-pane", "breakp", - "dPF:s:t:", 0, 0, - "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_S|CMD_INDEX_T, - cmd_break_pane_exec + .name = "break-pane", + .alias = "breakp", + + .args = { "dPF:s:t:", 0, 0 }, + .usage = "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_S|CMD_INDEX_T, + .exec = cmd_break_pane_exec }; enum cmd_retval diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 9d22a0f2..0e3644e9 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -36,12 +36,15 @@ char *cmd_capture_pane_history(struct args *, struct cmd_q *, struct window_pane *, size_t *); const struct cmd_entry cmd_capture_pane_entry = { - "capture-pane", "capturep", - "ab:CeE:JpPqS:t:", 0, 0, - "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" - CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_capture_pane_exec + .name = "capture-pane", + .alias = "capturep", + + .args = { "ab:CeE:JpPqS:t:", 0, 0 }, + .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " + "[-S start-line]" CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_capture_pane_exec }; char * diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 4418d415..fac792e9 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -33,11 +33,14 @@ enum cmd_retval cmd_choose_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_buffer_entry = { - "choose-buffer", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - CMD_WINDOW_T, - cmd_choose_buffer_exec + .name = "choose-buffer", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_buffer_exec }; enum cmd_retval diff --git a/cmd-choose-client.c b/cmd-choose-client.c index c58bc826..ab0f6c50 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -38,11 +38,14 @@ enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_q *); void cmd_choose_client_callback(struct window_choose_data *); const struct cmd_entry cmd_choose_client_entry = { - "choose-client", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - CMD_WINDOW_T, - cmd_choose_client_exec + .name = "choose-client", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_client_exec }; struct cmd_choose_client_data { diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 766978fe..faa9150c 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -44,28 +44,38 @@ enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_tree_entry = { - "choose-tree", NULL, - "S:W:swub:c:t:", 0, 1, - "[-suw] [-b session-template] [-c window template] [-S format] " \ - "[-W format] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_choose_tree_exec + .name = "choose-tree", + .alias = NULL, + + .args = { "S:W:swub:c:t:", 0, 1 }, + .usage = "[-suw] [-b session-template] [-c window template] " + "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_session_entry = { - "choose-session", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - CMD_WINDOW_T, - cmd_choose_tree_exec + .name = "choose-session", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + .flags = CMD_WINDOW_T, + + .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_window_entry = { - "choose-window", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE "[-F format] [template]", - CMD_WINDOW_T, - cmd_choose_tree_exec + .name = "choose-window", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]", + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_tree_exec }; enum cmd_retval diff --git a/cmd-clear-history.c b/cmd-clear-history.c index a76cab80..a8c2bfdc 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_clear_history_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clear_history_entry = { - "clear-history", "clearhist", - "t:", 0, 0, - CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_clear_history_exec + .name = "clear-history", + .alias = "clearhist", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_clear_history_exec }; enum cmd_retval diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 900fceb9..64b24bb5 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -35,11 +35,15 @@ int cmd_command_prompt_callback(void *, const char *); void cmd_command_prompt_free(void *); const struct cmd_entry cmd_command_prompt_entry = { - "command-prompt", NULL, - "I:p:t:", 0, 1, - "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", - CMD_CLIENT_T, - cmd_command_prompt_exec + .name = "command-prompt", + .alias = NULL, + + .args = { "I:p:t:", 0, 1 }, + .usage = "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " + "[template]", + + .flags = CMD_CLIENT_T, + .exec = cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 17a575a2..90e16992 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -34,11 +34,14 @@ int cmd_confirm_before_callback(void *, const char *); void cmd_confirm_before_free(void *); const struct cmd_entry cmd_confirm_before_entry = { - "confirm-before", "confirm", - "p:t:", 1, 1, - "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - CMD_CLIENT_T, - cmd_confirm_before_exec + .name = "confirm-before", + .alias = "confirm", + + .args = { "p:t:", 1, 1 }, + .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", + + .flags = CMD_CLIENT_T, + .exec = cmd_confirm_before_exec }; struct cmd_confirm_before_data { diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 79d06cee..7c6f6ca7 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -27,19 +27,25 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { - "copy-mode", NULL, - "Met:u", 0, 0, - "[-Mu] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_copy_mode_exec + .name = "copy-mode", + .alias = NULL, + + .args = { "Met:u", 0, 0 }, + .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_copy_mode_exec }; const struct cmd_entry cmd_clock_mode_entry = { - "clock-mode", NULL, - "t:", 0, 0, - CMD_TARGET_PANE_USAGE, - 0, - cmd_copy_mode_exec + .name = "clock-mode", + .alias = NULL, + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_PANE_USAGE, + + .flags = 0, + .exec = cmd_copy_mode_exec }; enum cmd_retval diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 86f8063c..6f6adbb9 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -29,19 +29,25 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { - "detach-client", "detach", - "as:t:P", 0, 0, - "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, - CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, - cmd_detach_client_exec + .name = "detach-client", + .alias = "detach", + + .args = { "as:t:P", 0, 0 }, + .usage = "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, + .exec = cmd_detach_client_exec }; const struct cmd_entry cmd_suspend_client_entry = { - "suspend-client", "suspendc", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_detach_client_exec + .name = "suspend-client", + .alias = "suspendc", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_detach_client_exec }; enum cmd_retval diff --git a/cmd-display-message.c b/cmd-display-message.c index ddc55846..fdfa6ae2 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -35,12 +35,15 @@ enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_message_entry = { - "display-message", "display", - "c:pt:F:", 0, 1, - "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE - " [message]", - CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, - cmd_display_message_exec + .name = "display-message", + .alias = "display", + + .args = { "c:pt:F:", 0, 1 }, + .usage = "[-p] [-c target-client] [-F format] " + CMD_TARGET_PANE_USAGE " [message]", + + .flags = CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, + .exec = cmd_display_message_exec }; enum cmd_retval diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 714ee19e..db1e4b08 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_display_panes_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_panes_entry = { - "display-panes", "displayp", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_display_panes_exec + .name = "display-panes", + .alias = "displayp", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_display_panes_exec }; enum cmd_retval diff --git a/cmd-find-window.c b/cmd-find-window.c index 1733b717..9c31cc0e 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -48,11 +48,14 @@ void cmd_find_window_callback(struct window_choose_data *); CMD_FIND_WINDOW_BY_NAME) const struct cmd_entry cmd_find_window_entry = { - "find-window", "findw", - "F:CNt:T", 1, 4, - "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", - CMD_WINDOW_T, - cmd_find_window_exec + .name = "find-window", + .alias = "findw", + + .args = { "F:CNt:T", 1, 4 }, + .usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", + + .flags = CMD_WINDOW_T, + .exec = cmd_find_window_exec }; struct cmd_find_window_data { diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 404f4671..4c2cacb1 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -36,11 +36,15 @@ void cmd_if_shell_done(struct cmd_q *); void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { - "if-shell", "if", - "bFt:", 2, 3, - "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command [command]", - CMD_PANE_T|CMD_CANFAIL, - cmd_if_shell_exec + .name = "if-shell", + .alias = "if", + + .args = { "bFt:", 2, 3 }, + .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " + "[command]", + + .flags = CMD_PANE_T|CMD_CANFAIL, + .exec = cmd_if_shell_exec }; struct cmd_if_shell_data { diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 6fc5b977..e5fbb423 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -34,19 +34,25 @@ enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmd_q *); enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { - "join-pane", "joinp", - "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_MARKED_S|CMD_PANE_T, - cmd_join_pane_exec + .name = "join-pane", + .alias = "joinp", + + .args = { "bdhvp:l:s:t:", 0, 0 }, + .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .exec = cmd_join_pane_exec }; const struct cmd_entry cmd_move_pane_entry = { - "move-pane", "movep", - "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_S|CMD_PANE_T, - cmd_join_pane_exec + .name = "move-pane", + .alias = "movep", + + .args = { "bdhvp:l:s:t:", 0, 0 }, + .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_S|CMD_PANE_T, + .exec = cmd_join_pane_exec }; enum cmd_retval diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index d5f69ea9..3e558d7d 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_pane_entry = { - "kill-pane", "killp", - "at:", 0, 0, - "[-a] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_kill_pane_exec + .name = "kill-pane", + .alias = "killp", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_kill_pane_exec }; enum cmd_retval diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 4107e6b6..6f84e959 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_server_entry = { - "kill-server", NULL, - "", 0, 0, - "", - 0, - cmd_kill_server_exec + .name = "kill-server", + .alias = NULL, + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_kill_server_exec }; const struct cmd_entry cmd_start_server_entry = { - "start-server", "start", - "", 0, 0, - "", - CMD_STARTSERVER, - cmd_kill_server_exec + .name = "start-server", + .alias = "start", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = CMD_STARTSERVER, + .exec = cmd_kill_server_exec }; enum cmd_retval diff --git a/cmd-kill-session.c b/cmd-kill-session.c index a4b0d5d2..a8d2d996 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -30,11 +30,14 @@ enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { - "kill-session", NULL, - "aCt:", 0, 0, - "[-aC] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_kill_session_exec + .name = "kill-session", + .alias = NULL, + + .args = { "aCt:", 0, 0 }, + .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_kill_session_exec }; enum cmd_retval diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 4ab17472..b8e1d5bc 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -27,19 +27,25 @@ enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_window_entry = { - "kill-window", "killw", - "at:", 0, 0, - "[-a] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_kill_window_exec + .name = "kill-window", + .alias = "killw", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_kill_window_exec }; const struct cmd_entry cmd_unlink_window_entry = { - "unlink-window", "unlinkw", - "kt:", 0, 0, - "[-k] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_kill_window_exec + .name = "unlink-window", + .alias = "unlinkw", + + .args = { "kt:", 0, 0 }, + .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_kill_window_exec }; enum cmd_retval diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 218eb6ff..a6007c33 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -33,11 +33,14 @@ enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_buffers_entry = { - "list-buffers", "lsb", - "F:", 0, 0, - "[-F format]", - 0, - cmd_list_buffers_exec + .name = "list-buffers", + .alias = "lsb", + + .args = { "F:", 0, 0 }, + .usage = "[-F format]", + + .flags = 0, + .exec = cmd_list_buffers_exec }; enum cmd_retval diff --git a/cmd-list-clients.c b/cmd-list-clients.c index effd8275..8a6fe8a9 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -36,11 +36,14 @@ enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_clients_entry = { - "list-clients", "lsc", - "F:t:", 0, 0, - "[-F format] " CMD_TARGET_SESSION_USAGE, - CMD_READONLY|CMD_SESSION_T, - cmd_list_clients_exec + .name = "list-clients", + .alias = "lsc", + + .args = { "F:t:", 0, 0 }, + .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_READONLY|CMD_SESSION_T, + .exec = cmd_list_clients_exec }; enum cmd_retval diff --git a/cmd-list-keys.c b/cmd-list-keys.c index f0a59c0b..08796e5d 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -33,19 +33,25 @@ enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); enum cmd_retval cmd_list_keys_commands(struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { - "list-keys", "lsk", - "t:T:", 0, 0, - "[-t mode-table] [-T key-table]", - 0, - cmd_list_keys_exec + .name = "list-keys", + .alias = "lsk", + + .args = { "t:T:", 0, 0 }, + .usage = "[-t mode-table] [-T key-table]", + + .flags = 0, + .exec = cmd_list_keys_exec }; const struct cmd_entry cmd_list_commands_entry = { - "list-commands", "lscm", - "", 0, 0, - "", - 0, - cmd_list_keys_exec + .name = "list-commands", + .alias = "lscm", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_list_keys_exec }; enum cmd_retval diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 2c9fa623..9f675b76 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -35,11 +35,14 @@ void cmd_list_panes_window(struct cmd *, struct session *, struct winlink *, struct cmd_q *, int); const struct cmd_entry cmd_list_panes_entry = { - "list-panes", "lsp", - "asF:t:", 0, 0, - "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_list_panes_exec + .name = "list-panes", + .alias = "lsp", + + .args = { "asF:t:", 0, 0 }, + .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_list_panes_exec }; enum cmd_retval diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index fed0c2ee..1fde7f86 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -39,11 +39,14 @@ enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_sessions_entry = { - "list-sessions", "ls", - "F:", 0, 0, - "[-F format]", - 0, - cmd_list_sessions_exec + .name = "list-sessions", + .alias = "ls", + + .args = { "F:", 0, 0 }, + .usage = "[-F format]", + + .flags = 0, + .exec = cmd_list_sessions_exec }; enum cmd_retval diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 992ba0de..d34f8b8c 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -46,11 +46,14 @@ void cmd_list_windows_session(struct cmd *, struct session *, struct cmd_q *, int); const struct cmd_entry cmd_list_windows_entry = { - "list-windows", "lsw", - "F:at:", 0, 0, - "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_list_windows_exec + .name = "list-windows", + .alias = "lsw", + + .args = { "F:at:", 0, 0 }, + .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_list_windows_exec }; enum cmd_retval diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 929e3bf2..6fd2a767 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -35,11 +35,14 @@ enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmd_q *); void cmd_load_buffer_callback(struct client *, int, void *); const struct cmd_entry cmd_load_buffer_entry = { - "load-buffer", "loadb", - "b:", 1, 1, - CMD_BUFFER_USAGE " path", - 0, - cmd_load_buffer_exec + .name = "load-buffer", + .alias = "loadb", + + .args = { "b:", 1, 1 }, + .usage = CMD_BUFFER_USAGE " path", + + .flags = 0, + .exec = cmd_load_buffer_exec }; enum cmd_retval diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 5d20ebd4..777311b3 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -27,27 +27,36 @@ enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_lock_server_entry = { - "lock-server", "lock", - "", 0, 0, - "", - 0, - cmd_lock_server_exec + .name = "lock-server", + .alias = "lock", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_session_entry = { - "lock-session", "locks", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_lock_server_exec + .name = "lock-session", + .alias = "locks", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_client_entry = { - "lock-client", "lockc", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_lock_server_exec + .name = "lock-client", + .alias = "lockc", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_lock_server_exec }; enum cmd_retval diff --git a/cmd-move-window.c b/cmd-move-window.c index 9e3a1c09..59a8538f 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -29,19 +29,25 @@ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { - "move-window", "movew", - "adkrs:t:", 0, 0, - "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, - cmd_move_window_exec + .name = "move-window", + .alias = "movew", + + .args = { "adkrs:t:", 0, 0 }, + .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, + + .flags = CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, + .exec = cmd_move_window_exec }; const struct cmd_entry cmd_link_window_entry = { - "link-window", "linkw", - "adks:t:", 0, 0, - "[-dk] " CMD_SRCDST_WINDOW_USAGE, - CMD_WINDOW_S|CMD_INDEX_T, - cmd_move_window_exec + .name = "link-window", + .alias = "linkw", + + .args = { "adks:t:", 0, 0 }, + .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, + + .flags = CMD_WINDOW_S|CMD_INDEX_T, + .exec = cmd_move_window_exec }; enum cmd_retval diff --git a/cmd-new-session.c b/cmd-new-session.c index e589a361..3cc07f46 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -36,21 +36,27 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { - "new-session", "new", - "Ac:dDEF:n:Ps:t:x:y:", 0, -1, - "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " - "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " - "[-y height] [command]", - CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, - cmd_new_session_exec + .name = "new-session", + .alias = "new", + + .args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 }, + .usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " + "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " + "[-y height] [command]", + + .flags = CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, + .exec = cmd_new_session_exec }; const struct cmd_entry cmd_has_session_entry = { - "has-session", "has", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_new_session_exec + .name = "has-session", + .alias = "has", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_new_session_exec }; enum cmd_retval diff --git a/cmd-new-window.c b/cmd-new-window.c index 18d2952b..b7a9c2be 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -35,12 +35,15 @@ enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { - "new-window", "neww", - "ac:dF:kn:Pt:", 0, -1, - "[-adkP] [-c start-directory] [-F format] [-n window-name] " - CMD_TARGET_WINDOW_USAGE " [command]", - CMD_INDEX_T, - cmd_new_window_exec + .name = "new-window", + .alias = "neww", + + .args = { "ac:dF:kn:Pt:", 0, -1 }, + .usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] " + CMD_TARGET_WINDOW_USAGE " [command]", + + .flags = CMD_INDEX_T, + .exec = cmd_new_window_exec }; enum cmd_retval diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 92a31c53..aa6e7805 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -34,11 +34,15 @@ void cmd_paste_buffer_filter(struct window_pane *, const char *, size_t, const char *, int); const struct cmd_entry cmd_paste_buffer_entry = { - "paste-buffer", "pasteb", - "db:prs:t:", 0, 0, - "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_paste_buffer_exec + .name = "paste-buffer", + .alias = "pasteb", + + .args = { "db:prs:t:", 0, 0 }, + .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " + CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_paste_buffer_exec }; enum cmd_retval diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index ad4b02e0..31b3a584 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -38,11 +38,14 @@ enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmd_q *); void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); const struct cmd_entry cmd_pipe_pane_entry = { - "pipe-pane", "pipep", - "ot:", 0, 1, - "[-o] " CMD_TARGET_PANE_USAGE " [command]", - CMD_PANE_T, - cmd_pipe_pane_exec + .name = "pipe-pane", + .alias = "pipep", + + .args = { "ot:", 0, 1 }, + .usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]", + + .flags = CMD_PANE_T, + .exec = cmd_pipe_pane_exec }; enum cmd_retval diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 9d1d0fce..2cf69ac5 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { - "refresh-client", "refresh", - "C:St:", 0, 0, - "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_refresh_client_exec + .name = "refresh-client", + .alias = "refresh", + + .args = { "C:St:", 0, 0 }, + .usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_refresh_client_exec }; enum cmd_retval diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 0c1a7e8c..c145dcb4 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_session_entry = { - "rename-session", "rename", - "t:", 1, 1, - CMD_TARGET_SESSION_USAGE " new-name", - CMD_SESSION_T, - cmd_rename_session_exec + .name = "rename-session", + .alias = "rename", + + .args = { "t:", 1, 1 }, + .usage = CMD_TARGET_SESSION_USAGE " new-name", + + .flags = CMD_SESSION_T, + .exec = cmd_rename_session_exec }; enum cmd_retval diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 6609ebab..6a61b486 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_window_entry = { - "rename-window", "renamew", - "t:", 1, 1, - CMD_TARGET_WINDOW_USAGE " new-name", - CMD_WINDOW_T, - cmd_rename_window_exec + .name = "rename-window", + .alias = "renamew", + + .args = { "t:", 1, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " new-name", + + .flags = CMD_WINDOW_T, + .exec = cmd_rename_window_exec }; enum cmd_retval diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index a4de32df..fb3302a2 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -31,11 +31,15 @@ enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); const struct cmd_entry cmd_resize_pane_entry = { - "resize-pane", "resizep", - "DLMRt:Ux:y:Z", 0, 1, - "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", - CMD_PANE_T, - cmd_resize_pane_exec + .name = "resize-pane", + .alias = "resizep", + + .args = { "DLMRt:Ux:y:Z", 0, 1 }, + .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " + "[adjustment]", + + .flags = CMD_PANE_T, + .exec = cmd_resize_pane_exec }; enum cmd_retval diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 45098d80..4a1ba60b 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { - "respawn-pane", "respawnp", - "kt:", 0, -1, - "[-k] " CMD_TARGET_PANE_USAGE " [command]", - CMD_PANE_T, - cmd_respawn_pane_exec + .name = "respawn-pane", + .alias = "respawnp", + + .args = { "kt:", 0, -1 }, + .usage = "[-k] " CMD_TARGET_PANE_USAGE " [command]", + + .flags = CMD_PANE_T, + .exec = cmd_respawn_pane_exec }; enum cmd_retval diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index f6550dee..2b2a674f 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -30,11 +30,14 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { - "respawn-window", "respawnw", - "kt:", 0, -1, - "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", - CMD_WINDOW_T, - cmd_respawn_window_exec + .name = "respawn-window", + .alias = "respawnw", + + .args = { "kt:", 0, -1 }, + .usage = "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", + + .flags = CMD_WINDOW_T, + .exec = cmd_respawn_window_exec }; enum cmd_retval diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 4122886a..9966cff6 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rotate_window_entry = { - "rotate-window", "rotatew", - "Dt:U", 0, 0, - "[-DU] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_rotate_window_exec + .name = "rotate-window", + .alias = "rotatew", + + .args = { "Dt:U", 0, 0 }, + .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_rotate_window_exec }; enum cmd_retval diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 0dae39ac..d84c3899 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -36,11 +36,14 @@ void cmd_run_shell_free(void *); void cmd_run_shell_print(struct job *, const char *); const struct cmd_entry cmd_run_shell_entry = { - "run-shell", "run", - "bt:", 1, 1, - "[-b] " CMD_TARGET_PANE_USAGE " shell-command", - CMD_PANE_T|CMD_CANFAIL, - cmd_run_shell_exec + .name = "run-shell", + .alias = "run", + + .args = { "bt:", 1, 1 }, + .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", + + .flags = CMD_PANE_T|CMD_CANFAIL, + .exec = cmd_run_shell_exec }; struct cmd_run_shell_data { diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index fc8d7bee..591390b5 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -35,19 +35,25 @@ enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_save_buffer_entry = { - "save-buffer", "saveb", - "ab:", 1, 1, - "[-a] " CMD_BUFFER_USAGE " path", - 0, - cmd_save_buffer_exec + .name = "save-buffer", + .alias = "saveb", + + .args = { "ab:", 1, 1 }, + .usage = "[-a] " CMD_BUFFER_USAGE " path", + + .flags = 0, + .exec = cmd_save_buffer_exec }; const struct cmd_entry cmd_show_buffer_entry = { - "show-buffer", "showb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - cmd_save_buffer_exec + .name = "show-buffer", + .alias = "showb", + + .args = { "b:", 0, 0 }, + .usage = CMD_BUFFER_USAGE, + + .flags = 0, + .exec = cmd_save_buffer_exec }; enum cmd_retval diff --git a/cmd-select-layout.c b/cmd-select-layout.c index aac60083..5bc0daad 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -29,27 +29,36 @@ enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { - "select-layout", "selectl", - "nopt:", 0, 1, - "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - CMD_WINDOW_T, - cmd_select_layout_exec + .name = "select-layout", + .alias = "selectl", + + .args = { "nopt:", 0, 1 }, + .usage = "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", + + .flags = CMD_WINDOW_T, + .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_next_layout_entry = { - "next-layout", "nextl", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_layout_exec + .name = "next-layout", + .alias = "nextl", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_previous_layout_entry = { - "previous-layout", "prevl", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_layout_exec + .name = "previous-layout", + .alias = "prevl", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_layout_exec }; enum cmd_retval diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e83dc609..aa12ae1e 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -27,19 +27,25 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { - "select-pane", "selectp", - "DdegLlMmP:Rt:U", 0, 0, - "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_select_pane_exec + .name = "select-pane", + .alias = "selectp", + + .args = { "DdegLlMmP:Rt:U", 0, 0 }, + .usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_select_pane_exec }; const struct cmd_entry cmd_last_pane_entry = { - "last-pane", "lastp", - "det:", 0, 0, - "[-de] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_pane_exec + .name = "last-pane", + .alias = "lastp", + + .args = { "det:", 0, 0 }, + .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_pane_exec }; enum cmd_retval diff --git a/cmd-select-window.c b/cmd-select-window.c index ede60dae..9b396cf9 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -29,35 +29,47 @@ enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_window_entry = { - "select-window", "selectw", - "lnpTt:", 0, 0, - "[-lnpT] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_window_exec + .name = "select-window", + .alias = "selectw", + + .args = { "lnpTt:", 0, 0 }, + .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_next_window_entry = { - "next-window", "next", - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_select_window_exec + .name = "next-window", + .alias = "next", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_previous_window_entry = { - "previous-window", "prev", - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_select_window_exec + .name = "previous-window", + .alias = "prev", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_last_window_entry = { - "last-window", "last", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_select_window_exec + .name = "last-window", + .alias = "last", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_select_window_exec }; enum cmd_retval diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 8f9b4ddf..aa78abca 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { - "send-keys", "send", - "lRMt:", 0, -1, - "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", - CMD_PANE_T, - cmd_send_keys_exec + .name = "send-keys", + .alias = "send", + + .args = { "lRMt:", 0, -1 }, + .usage = "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", + + .flags = CMD_PANE_T, + .exec = cmd_send_keys_exec }; const struct cmd_entry cmd_send_prefix_entry = { - "send-prefix", NULL, - "2t:", 0, 0, - "[-2] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_send_keys_exec + .name = "send-prefix", + .alias = NULL, + + .args = { "2t:", 0, 0 }, + .usage = "[-2] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_send_keys_exec }; enum cmd_retval diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index e7f7627e..1494cf26 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { - "set-buffer", "setb", - "ab:n:", 0, 1, - "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", - 0, - cmd_set_buffer_exec + .name = "set-buffer", + .alias = "setb", + + .args = { "ab:n:", 0, 1 }, + .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", + + .flags = 0, + .exec = cmd_set_buffer_exec }; const struct cmd_entry cmd_delete_buffer_entry = { - "delete-buffer", "deleteb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - cmd_set_buffer_exec + .name = "delete-buffer", + .alias = "deleteb", + + .args = { "b:", 0, 0 }, + .usage = CMD_BUFFER_USAGE, + + .flags = 0, + .exec = cmd_set_buffer_exec }; enum cmd_retval diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 4be967b7..61ab0470 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -30,11 +30,14 @@ enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_environment_entry = { - "set-environment", "setenv", - "grt:u", 1, 2, - "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - CMD_SESSION_T, - cmd_set_environment_exec + .name = "set-environment", + .alias = "setenv", + + .args = { "grt:u", 1, 2 }, + .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", + + .flags = CMD_SESSION_T, + .exec = cmd_set_environment_exec }; enum cmd_retval diff --git a/cmd-set-hook.c b/cmd-set-hook.c index d75b0ba1..e502fa79 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_hook_entry = { - "set-hook", NULL, - "gt:u", 1, 2, - "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", - CMD_SESSION_T, - cmd_set_hook_exec + .name = "set-hook", + .alias = NULL, + + .args = { "gt:u", 1, 2 }, + .usage = "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", + + .flags = CMD_SESSION_T, + .exec = cmd_set_hook_exec }; const struct cmd_entry cmd_show_hooks_entry = { - "show-hooks", NULL, - "gt:", 0, 1, - "[-g] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_set_hook_exec + .name = "show-hooks", + .alias = NULL, + + .args = { "gt:", 0, 1 }, + .usage = "[-g] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_set_hook_exec }; enum cmd_retval diff --git a/cmd-set-option.c b/cmd-set-option.c index 86856fbb..5190e181 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -65,19 +65,25 @@ struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, const char *); const struct cmd_entry cmd_set_option_entry = { - "set-option", "set", - "agoqst:uw", 1, 2, - "[-agosquw] [-t target-window] option [value]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_set_option_exec + .name = "set-option", + .alias = "set", + + .args = { "agoqst:uw", 1, 2 }, + .usage = "[-agosquw] [-t target-window] option [value]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_set_option_exec }; const struct cmd_entry cmd_set_window_option_entry = { - "set-window-option", "setw", - "agoqt:u", 1, 2, - "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_set_option_exec + .name = "set-window-option", + .alias = "setw", + + .args = { "agoqt:u", 1, 2 }, + .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_set_option_exec }; enum cmd_retval diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 723d3039..398a1e50 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -34,11 +34,14 @@ void cmd_show_environment_print(struct cmd *, struct cmd_q *, struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { - "show-environment", "showenv", - "gst:", 0, 1, - "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - CMD_SESSION_T|CMD_CANFAIL, - cmd_show_environment_exec + .name = "show-environment", + .alias = "showenv", + + .args = { "gst:", 0, 1 }, + .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", + + .flags = CMD_SESSION_T|CMD_CANFAIL, + .exec = cmd_show_environment_exec }; char * diff --git a/cmd-show-messages.c b/cmd-show-messages.c index b80b9fe7..e810abdc 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -32,19 +32,25 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { - "show-messages", "showmsgs", - "JTt:", 0, 0, - "[-JT] " CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_show_messages_exec + .name = "show-messages", + .alias = "showmsgs", + + .args = { "JTt:", 0, 0 }, + .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_show_messages_exec }; const struct cmd_entry cmd_server_info_entry = { - "server-info", "info", - "", 0, 0, - "", - 0, - cmd_show_messages_exec + .name = "server-info", + .alias = "info", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_show_messages_exec }; int cmd_show_messages_terminals(struct cmd_q *, int); diff --git a/cmd-show-options.c b/cmd-show-options.c index 3b39a532..4ef07e8f 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -35,19 +35,25 @@ enum cmd_retval cmd_show_options_all(struct cmd *, struct cmd_q *, struct options *, enum options_table_scope); const struct cmd_entry cmd_show_options_entry = { - "show-options", "show", - "gqst:vw", 0, 1, - "[-gqsvw] [-t target-session|target-window] [option]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_show_options_exec + .name = "show-options", + .alias = "show", + + .args = { "gqst:vw", 0, 1 }, + .usage = "[-gqsvw] [-t target-session|target-window] [option]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_show_options_exec }; const struct cmd_entry cmd_show_window_options_entry = { - "show-window-options", "showw", - "gvt:", 0, 1, - "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_show_options_exec + .name = "show-window-options", + .alias = "showw", + + .args = { "gvt:", 0, 1 }, + .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_show_options_exec }; enum cmd_retval diff --git a/cmd-source-file.c b/cmd-source-file.c index e776712c..9d2d6d68 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *); void cmd_source_file_done(struct cmd_q *); const struct cmd_entry cmd_source_file_entry = { - "source-file", "source", - "", 1, 1, - "path", - 0, - cmd_source_file_exec + .name = "source-file", + .alias = "source", + + .args = { "", 1, 1 }, + .usage = "path", + + .flags = 0, + .exec = cmd_source_file_exec }; enum cmd_retval diff --git a/cmd-split-window.c b/cmd-split-window.c index 59efae35..cb776d63 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -36,12 +36,15 @@ enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { - "split-window", "splitw", - "bc:dF:l:hp:Pt:v", 0, -1, - "[-bdhvP] [-c start-directory] [-F format] [-p percentage|-l size] " - CMD_TARGET_PANE_USAGE " [command]", - CMD_PANE_T, - cmd_split_window_exec + .name = "split-window", + .alias = "splitw", + + .args = { "bc:dF:l:hp:Pt:v", 0, -1 }, + .usage = "[-bdhvP] [-c start-directory] [-F format] " + "[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", + + .flags = CMD_PANE_T, + .exec = cmd_split_window_exec }; enum cmd_retval diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 62d6bad3..7b50e3be 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_pane_entry = { - "swap-pane", "swapp", - "dDs:t:U", 0, 0, - "[-dDU] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_MARKED_S|CMD_PANE_T, - cmd_swap_pane_exec + .name = "swap-pane", + .alias = "swapp", + + .args = { "dDs:t:U", 0, 0 }, + .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .exec = cmd_swap_pane_exec }; enum cmd_retval diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 0ed3ca81..38a3bf78 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_window_entry = { - "swap-window", "swapw", - "ds:t:", 0, 0, - "[-d] " CMD_SRCDST_WINDOW_USAGE, - CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, - cmd_swap_window_exec + .name = "swap-window", + .alias = "swapw", + + .args = { "ds:t:", 0, 0 }, + .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, + + .flags = CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, + .exec = cmd_swap_window_exec }; enum cmd_retval diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 919d0057..5a1fe33d 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -30,11 +30,16 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { - "switch-client", "switchc", - "lc:Enpt:rT:", 0, 0, - "[-Elnpr] [-c target-client] [-t target-session] [-T key-table]", - CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T|CMD_PREFERUNATTACHED, - cmd_switch_client_exec + .name = "switch-client", + .alias = "switchc", + + .args = { "lc:Enpt:rT:", 0, 0 }, + .usage = "[-Elnpr] [-c target-client] [-t target-session] " + "[-T key-table]", + + .flags = CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T| + CMD_PREFERUNATTACHED, + .exec = cmd_switch_client_exec }; enum cmd_retval @@ -48,7 +53,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) const char *tablename, *update; struct key_table *table; - if (args_has(args, 'r')) c->flags ^= CLIENT_READONLY; diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 66a4525e..8e89f21a 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, key_code); const struct cmd_entry cmd_unbind_key_entry = { - "unbind-key", "unbind", - "acnt:T:", 0, 1, - "[-acn] [-t mode-table] [-T key-table] key", - 0, - cmd_unbind_key_exec + .name = "unbind-key", + .alias = "unbind", + + .args = { "acnt:T:", 0, 1 }, + .usage = "[-acn] [-t mode-table] [-T key-table] key", + + .flags = 0, + .exec = cmd_unbind_key_exec }; enum cmd_retval diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 04316d5e..59f7dbfb 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_wait_for_entry = { - "wait-for", "wait", - "LSU", 1, 1, - "[-L|-S|-U] channel", - 0, - cmd_wait_for_exec + .name = "wait-for", + .alias = "wait", + + .args = { "LSU", 1, 1 }, + .usage = "[-L|-S|-U] channel", + + .flags = 0, + .exec = cmd_wait_for_exec }; struct wait_channel { diff --git a/cmd.c b/cmd.c index 9f05a11e..f47991a3 100644 --- a/cmd.c +++ b/cmd.c @@ -353,12 +353,12 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) return (NULL); } - args = args_parse(entry->args_template, argc, argv); + args = args_parse(entry->args.template, argc, argv); if (args == NULL) goto usage; - if (entry->args_lower != -1 && args->argc < entry->args_lower) + if (entry->args.lower != -1 && args->argc < entry->args.lower) goto usage; - if (entry->args_upper != -1 && args->argc > entry->args_upper) + if (entry->args.upper != -1 && args->argc > entry->args.upper) goto usage; cmd = xcalloc(1, sizeof *cmd); diff --git a/tmux.h b/tmux.h index 8b26431d..c9adb2ee 100644 --- a/tmux.h +++ b/tmux.h @@ -1389,14 +1389,16 @@ struct cmd_q { /* Command definition. */ struct cmd_entry { - const char *name; - const char *alias; + const char *name; + const char *alias; - const char *args_template; - int args_lower; - int args_upper; + struct { + const char *template; + int lower; + int upper; + } args; - const char *usage; + const char *usage; #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 @@ -1418,9 +1420,9 @@ struct cmd_entry { #define CMD_WINDOW_MARKED_T 0x20000 #define CMD_WINDOW_MARKED_S 0x40000 #define CMD_CLIENT_CANFAIL 0x80000 - int flags; + int flags; - enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); + enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; #define CMD_ALL_T (CMD_SESSION_T|CMD_WINDOW_T|CMD_PANE_T|CMD_INDEX_T| \ CMD_MOVEW_R|CMD_PANE_MARKED_T|CMD_WINDOW_MARKED_T) From a3129fd4e820d7ccb3797fed491e7c021b63c568 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Dec 2015 00:31:54 +0000 Subject: [PATCH 934/949] Instead of combined flags for -c, -s, -t, split into different sets using an enum and simplify the parsing code. --- cmd-attach-session.c | 4 +- cmd-break-pane.c | 5 +- cmd-capture-pane.c | 4 +- cmd-choose-buffer.c | 4 +- cmd-choose-client.c | 4 +- cmd-choose-tree.c | 13 +- cmd-clear-history.c | 4 +- cmd-command-prompt.c | 4 +- cmd-confirm-before.c | 4 +- cmd-copy-mode.c | 4 +- cmd-detach-client.c | 9 +- cmd-display-message.c | 5 +- cmd-display-panes.c | 4 +- cmd-find-window.c | 4 +- cmd-if-shell.c | 4 +- cmd-join-pane.c | 10 +- cmd-kill-pane.c | 4 +- cmd-kill-session.c | 4 +- cmd-kill-window.c | 8 +- cmd-list-clients.c | 4 +- cmd-list-panes.c | 4 +- cmd-list-windows.c | 4 +- cmd-lock-server.c | 8 +- cmd-move-window.c | 10 +- cmd-new-session.c | 8 +- cmd-new-window.c | 4 +- cmd-paste-buffer.c | 4 +- cmd-pipe-pane.c | 4 +- cmd-refresh-client.c | 4 +- cmd-rename-session.c | 4 +- cmd-rename-window.c | 4 +- cmd-resize-pane.c | 4 +- cmd-respawn-pane.c | 4 +- cmd-respawn-window.c | 4 +- cmd-rotate-window.c | 4 +- cmd-run-shell.c | 4 +- cmd-select-layout.c | 12 +- cmd-select-pane.c | 8 +- cmd-select-window.c | 16 ++- cmd-send-keys.c | 8 +- cmd-set-environment.c | 4 +- cmd-set-hook.c | 8 +- cmd-set-option.c | 8 +- cmd-show-environment.c | 4 +- cmd-show-messages.c | 4 +- cmd-show-options.c | 8 +- cmd-split-window.c | 4 +- cmd-swap-pane.c | 29 ++-- cmd-swap-window.c | 5 +- cmd-switch-client.c | 6 +- cmd.c | 319 +++++++++++++---------------------------- log.c | 1 + tmux.h | 53 +++---- 53 files changed, 353 insertions(+), 328 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 976f180b..3b3f9e68 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -39,7 +39,9 @@ const struct cmd_entry cmd_attach_session_entry = { .args = { "c:dErt:", 0, 0 }, .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, + .tflag = CMD_SESSION_WITHPANE, + + .flags = CMD_STARTSERVER, .exec = cmd_attach_session_exec }; diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 62625c71..c2b021fc 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -37,7 +37,10 @@ const struct cmd_entry cmd_break_pane_entry = { .args = { "dPF:s:t:", 0, 0 }, .usage = "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_S|CMD_INDEX_T, + .sflag = CMD_PANE, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, .exec = cmd_break_pane_exec }; diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 0e3644e9..33f6cf08 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -43,7 +43,9 @@ const struct cmd_entry cmd_capture_pane_entry = { .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line]" CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_capture_pane_exec }; diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index fac792e9..1f8fbfb2 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -39,7 +39,9 @@ const struct cmd_entry cmd_choose_buffer_entry = { .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_buffer_exec }; diff --git a/cmd-choose-client.c b/cmd-choose-client.c index ab0f6c50..7d5fc606 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_choose_client_entry = { .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_client_exec }; diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index faa9150c..db9222ba 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -51,7 +51,9 @@ const struct cmd_entry cmd_choose_tree_entry = { .usage = "[-suw] [-b session-template] [-c window template] " "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_tree_exec }; @@ -60,10 +62,11 @@ const struct cmd_entry cmd_choose_session_entry = { .alias = NULL, .args = { "F:t:", 0, 1 }, - .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_tree_exec }; @@ -74,7 +77,9 @@ const struct cmd_entry cmd_choose_window_entry = { .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_tree_exec }; diff --git a/cmd-clear-history.c b/cmd-clear-history.c index a8c2bfdc..1236e7f1 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_clear_history_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_clear_history_exec }; diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 64b24bb5..9200ada1 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_command_prompt_entry = { .usage = "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_command_prompt_exec }; diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 90e16992..b5a12821 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -40,7 +40,9 @@ const struct cmd_entry cmd_confirm_before_entry = { .args = { "p:t:", 1, 1 }, .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_confirm_before_exec }; diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 7c6f6ca7..1a006ebb 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_copy_mode_entry = { .args = { "Met:u", 0, 0 }, .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_copy_mode_exec }; diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 6f6adbb9..daf9a5c6 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -35,7 +35,10 @@ const struct cmd_entry cmd_detach_client_entry = { .args = { "as:t:P", 0, 0 }, .usage = "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, + .sflag = CMD_SESSION, + .tflag = CMD_CLIENT, + + .flags = CMD_READONLY, .exec = cmd_detach_client_exec }; @@ -46,7 +49,9 @@ const struct cmd_entry cmd_suspend_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_detach_client_exec }; diff --git a/cmd-display-message.c b/cmd-display-message.c index fdfa6ae2..a041b5a1 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -42,7 +42,10 @@ const struct cmd_entry cmd_display_message_entry = { .usage = "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - .flags = CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, + .cflag = CMD_CLIENT_CANFAIL, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_display_message_exec }; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index db1e4b08..d8db4066 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_display_panes_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_display_panes_exec }; diff --git a/cmd-find-window.c b/cmd-find-window.c index 9c31cc0e..eb940d8c 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -54,7 +54,9 @@ const struct cmd_entry cmd_find_window_entry = { .args = { "F:CNt:T", 1, 4 }, .usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_find_window_exec }; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 4c2cacb1..229289cd 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -43,7 +43,9 @@ const struct cmd_entry cmd_if_shell_entry = { .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " "[command]", - .flags = CMD_PANE_T|CMD_CANFAIL, + .tflag = CMD_PANE_CANFAIL, + + .flags = 0, .exec = cmd_if_shell_exec }; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index e5fbb423..8b4117fa 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -40,7 +40,10 @@ const struct cmd_entry cmd_join_pane_entry = { .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .sflag = CMD_PANE_MARKED, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_join_pane_exec }; @@ -51,7 +54,10 @@ const struct cmd_entry cmd_move_pane_entry = { .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_S|CMD_PANE_T, + .sflag = CMD_PANE, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_join_pane_exec }; diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 3e558d7d..f843bc58 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_kill_pane_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_kill_pane_exec }; diff --git a/cmd-kill-session.c b/cmd-kill-session.c index a8d2d996..4ca4e2c8 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_kill_session_entry = { .args = { "aCt:", 0, 0 }, .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_kill_session_exec }; diff --git a/cmd-kill-window.c b/cmd-kill-window.c index b8e1d5bc..7a8e9fa6 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_kill_window_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_kill_window_exec }; @@ -44,7 +46,9 @@ const struct cmd_entry cmd_unlink_window_entry = { .args = { "kt:", 0, 0 }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_kill_window_exec }; diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 8a6fe8a9..75c6f570 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_list_clients_entry = { .args = { "F:t:", 0, 0 }, .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_READONLY|CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = CMD_READONLY, .exec = cmd_list_clients_exec }; diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 9f675b76..da0e0962 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -41,7 +41,9 @@ const struct cmd_entry cmd_list_panes_entry = { .args = { "asF:t:", 0, 0 }, .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_list_panes_exec }; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index d34f8b8c..dd05ea85 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -52,7 +52,9 @@ const struct cmd_entry cmd_list_windows_entry = { .args = { "F:at:", 0, 0 }, .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_list_windows_exec }; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 777311b3..9cdd816f 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_lock_session_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_lock_server_exec }; @@ -55,7 +57,9 @@ const struct cmd_entry cmd_lock_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_lock_server_exec }; diff --git a/cmd-move-window.c b/cmd-move-window.c index 59a8538f..bb33ab38 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -35,7 +35,10 @@ const struct cmd_entry cmd_move_window_entry = { .args = { "adkrs:t:", 0, 0 }, .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - .flags = CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, + .sflag = CMD_WINDOW, + .tflag = CMD_MOVEW_R, + + .flags = 0, .exec = cmd_move_window_exec }; @@ -46,7 +49,10 @@ const struct cmd_entry cmd_link_window_entry = { .args = { "adks:t:", 0, 0 }, .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, - .flags = CMD_WINDOW_S|CMD_INDEX_T, + .sflag = CMD_WINDOW, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, .exec = cmd_move_window_exec }; diff --git a/cmd-new-session.c b/cmd-new-session.c index 3cc07f46..f96003c7 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_new_session_entry = { "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", - .flags = CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, + .tflag = CMD_SESSION_CANFAIL, + + .flags = CMD_STARTSERVER, .exec = cmd_new_session_exec }; @@ -55,7 +57,9 @@ const struct cmd_entry cmd_has_session_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_new_session_exec }; diff --git a/cmd-new-window.c b/cmd-new-window.c index b7a9c2be..33f68935 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_new_window_entry = { .usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", - .flags = CMD_INDEX_T, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, .exec = cmd_new_window_exec }; diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index aa6e7805..0728743a 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -41,7 +41,9 @@ const struct cmd_entry cmd_paste_buffer_entry = { .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_paste_buffer_exec }; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 31b3a584..a2653dc5 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_pipe_pane_entry = { .args = { "ot:", 0, 1 }, .usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_pipe_pane_exec }; diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 2cf69ac5..444e83fc 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_refresh_client_entry = { .args = { "C:St:", 0, 0 }, .usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_refresh_client_exec }; diff --git a/cmd-rename-session.c b/cmd-rename-session.c index c145dcb4..7fc6193d 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_rename_session_entry = { .args = { "t:", 1, 1 }, .usage = CMD_TARGET_SESSION_USAGE " new-name", - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_rename_session_exec }; diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 6a61b486..36c1bf31 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_rename_window_entry = { .args = { "t:", 1, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " new-name", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_rename_window_exec }; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index fb3302a2..bb29cef9 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -38,7 +38,9 @@ const struct cmd_entry cmd_resize_pane_entry = { .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " "[adjustment]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_resize_pane_exec }; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 4a1ba60b..bff6c11b 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -37,7 +37,9 @@ const struct cmd_entry cmd_respawn_pane_entry = { .args = { "kt:", 0, -1 }, .usage = "[-k] " CMD_TARGET_PANE_USAGE " [command]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_respawn_pane_exec }; diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 2b2a674f..aa4e169b 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_respawn_window_entry = { .args = { "kt:", 0, -1 }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_respawn_window_exec }; diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 9966cff6..014c1f2f 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_rotate_window_entry = { .args = { "Dt:U", 0, 0 }, .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_rotate_window_exec }; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index d84c3899..e857e9c9 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_run_shell_entry = { .args = { "bt:", 1, 1 }, .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", - .flags = CMD_PANE_T|CMD_CANFAIL, + .tflag = CMD_PANE_CANFAIL, + + .flags = 0, .exec = cmd_run_shell_exec }; diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 5bc0daad..e6ede1af 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_select_layout_entry = { .args = { "nopt:", 0, 1 }, .usage = "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_layout_exec }; @@ -46,7 +48,9 @@ const struct cmd_entry cmd_next_layout_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_layout_exec }; @@ -57,7 +61,9 @@ const struct cmd_entry cmd_previous_layout_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_layout_exec }; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index aa12ae1e..7652f608 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_select_pane_entry = { .args = { "DdegLlMmP:Rt:U", 0, 0 }, .usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_select_pane_exec }; @@ -44,7 +46,9 @@ const struct cmd_entry cmd_last_pane_entry = { .args = { "det:", 0, 0 }, .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_pane_exec }; diff --git a/cmd-select-window.c b/cmd-select-window.c index 9b396cf9..82acc859 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_select_window_entry = { .args = { "lnpTt:", 0, 0 }, .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_window_exec }; @@ -46,7 +48,9 @@ const struct cmd_entry cmd_next_window_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_select_window_exec }; @@ -57,7 +61,9 @@ const struct cmd_entry cmd_previous_window_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_select_window_exec }; @@ -68,7 +74,9 @@ const struct cmd_entry cmd_last_window_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_select_window_exec }; diff --git a/cmd-send-keys.c b/cmd-send-keys.c index aa78abca..377453b0 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_send_keys_entry = { .args = { "lRMt:", 0, -1 }, .usage = "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_send_keys_exec }; @@ -47,7 +49,9 @@ const struct cmd_entry cmd_send_prefix_entry = { .args = { "2t:", 0, 0 }, .usage = "[-2] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_send_keys_exec }; diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 61ab0470..0808dcb1 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_set_environment_entry = { .args = { "grt:u", 1, 2 }, .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_set_environment_exec }; diff --git a/cmd-set-hook.c b/cmd-set-hook.c index e502fa79..8ef02f8c 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_set_hook_entry = { .args = { "gt:u", 1, 2 }, .usage = "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_set_hook_exec }; @@ -47,7 +49,9 @@ const struct cmd_entry cmd_show_hooks_entry = { .args = { "gt:", 0, 1 }, .usage = "[-g] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_set_hook_exec }; diff --git a/cmd-set-option.c b/cmd-set-option.c index 5190e181..13de02a3 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -71,7 +71,9 @@ const struct cmd_entry cmd_set_option_entry = { .args = { "agoqst:uw", 1, 2 }, .usage = "[-agosquw] [-t target-window] option [value]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_set_option_exec }; @@ -82,7 +84,9 @@ const struct cmd_entry cmd_set_window_option_entry = { .args = { "agoqt:u", 1, 2 }, .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_set_option_exec }; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 398a1e50..ad9e9e37 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -40,7 +40,9 @@ const struct cmd_entry cmd_show_environment_entry = { .args = { "gst:", 0, 1 }, .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - .flags = CMD_SESSION_T|CMD_CANFAIL, + .tflag = CMD_SESSION_CANFAIL, + + .flags = 0, .exec = cmd_show_environment_exec }; diff --git a/cmd-show-messages.c b/cmd-show-messages.c index e810abdc..68ef6674 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -38,7 +38,9 @@ const struct cmd_entry cmd_show_messages_entry = { .args = { "JTt:", 0, 0 }, .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_show_messages_exec }; diff --git a/cmd-show-options.c b/cmd-show-options.c index 4ef07e8f..e99574f8 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -41,7 +41,9 @@ const struct cmd_entry cmd_show_options_entry = { .args = { "gqst:vw", 0, 1 }, .usage = "[-gqsvw] [-t target-session|target-window] [option]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_show_options_exec }; @@ -52,7 +54,9 @@ const struct cmd_entry cmd_show_window_options_entry = { .args = { "gvt:", 0, 1 }, .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_show_options_exec }; diff --git a/cmd-split-window.c b/cmd-split-window.c index cb776d63..aee323eb 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -43,7 +43,9 @@ const struct cmd_entry cmd_split_window_entry = { .usage = "[-bdhvP] [-c start-directory] [-F format] " "[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_split_window_exec }; diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 7b50e3be..84332c2f 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -35,14 +35,16 @@ const struct cmd_entry cmd_swap_pane_entry = { .args = { "dDs:t:U", 0, 0 }, .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .sflag = CMD_PANE_MARKED, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_swap_pane_exec }; enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; @@ -52,22 +54,23 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) dst_wl = cmdq->state.tflag.wl; dst_w = dst_wl->window; dst_wp = cmdq->state.tflag.wp; - src_wp = cmdq->state.sflag.wp; src_wl = cmdq->state.sflag.wl; src_w = src_wl->window; + src_wp = cmdq->state.sflag.wp; server_unzoom_window(dst_w); - if (!args_has(args, 's')) { + if (args_has(self->args, 'D')) { + src_wl = dst_wl; src_w = dst_w; - if (args_has(self->args, 'D')) { - src_wp = TAILQ_NEXT(dst_wp, entry); - if (src_wp == NULL) - src_wp = TAILQ_FIRST(&dst_w->panes); - } else if (args_has(self->args, 'U')) { - src_wp = TAILQ_PREV(dst_wp, window_panes, entry); - if (src_wp == NULL) - src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - } + src_wp = TAILQ_NEXT(dst_wp, entry); + if (src_wp == NULL) + src_wp = TAILQ_FIRST(&dst_w->panes); + } else if (args_has(self->args, 'U')) { + src_wl = dst_wl; + src_w = dst_w; + src_wp = TAILQ_PREV(dst_wp, window_panes, entry); + if (src_wp == NULL) + src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } server_unzoom_window(src_w); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 38a3bf78..e1835820 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -35,7 +35,10 @@ const struct cmd_entry cmd_swap_window_entry = { .args = { "ds:t:", 0, 0 }, .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, - .flags = CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, + .sflag = CMD_WINDOW_MARKED, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_swap_window_exec }; diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 5a1fe33d..0f6acbf4 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -37,8 +37,10 @@ const struct cmd_entry cmd_switch_client_entry = { .usage = "[-Elnpr] [-c target-client] [-t target-session] " "[-T key-table]", - .flags = CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T| - CMD_PREFERUNATTACHED, + .cflag = CMD_CLIENT, + .tflag = CMD_SESSION_WITHPANE, + + .flags = CMD_READONLY, .exec = cmd_switch_client_exec }; diff --git a/cmd.c b/cmd.c index f47991a3..62be3af1 100644 --- a/cmd.c +++ b/cmd.c @@ -207,10 +207,6 @@ const struct cmd_entry *cmd_table[] = { NULL }; -static void cmd_clear_state(struct cmd_state *); -static struct client *cmd_get_state_client(struct cmd_q *, int); -static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); - int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { @@ -409,252 +405,131 @@ cmd_clear_state(struct cmd_state *state) } static int -cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) +cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, + const char *target, struct cmd_q *cmdq) { - struct cmd_state *state = &cmdq->state; - struct cmd_find_state *fsf = NULL; - const char *flag; - int flags = cmd->entry->flags, everything = 0; - int allflags = 0, targetflags, error; - struct session *s; - struct window *w; - struct winlink *wl; - struct window_pane *wp; + int targetflags, error; - /* Set up state for either -t or -s. */ - if (c == 't') { - fsf = &cmdq->state.tflag; - allflags = CMD_ALL_T; - } else if (c == 's') { - fsf = &cmdq->state.sflag; - allflags = CMD_ALL_S; + if (flag == CMD_SESSION_WITHPANE) { + if (target != NULL && target[strcspn(target, ":.")] != '\0') + flag = CMD_PANE; + else + flag = CMD_SESSION; } - /* - * If the command wants something and no argument is present, use the - * base command instead. - */ - flag = args_get(cmd->args, c); - if (flag == NULL) { - if ((flags & allflags) == 0) - return (0); /* doesn't care about flag */ - cmd = cmdq->cmd; - everything = 1; - flag = args_get(cmd->args, c); - } - - /* - * If no flag and the current command is allowed to fail, just skip to - * fill in as much we can, otherwise continue and fail later if needed. - */ - if (flag == NULL && (flags & CMD_CANFAIL)) - goto complete_everything; - - /* Fill in state using command (current or base) flags. */ - if (flags & CMD_PREFERUNATTACHED) - targetflags = CMD_FIND_PREFER_UNATTACHED; - else - targetflags = 0; - switch (cmd->entry->flags & allflags) { - case 0: - break; - case CMD_SESSION_T|CMD_PANE_T: - case CMD_SESSION_S|CMD_PANE_S: - if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, - targetflags); - if (error != 0) - return (-1); - } else { - error = cmd_find_target(fsf, cmdq, flag, - CMD_FIND_SESSION, targetflags); - if (error != 0) - return (-1); - - if (flag == NULL) { - fsf->wl = fsf->s->curw; - fsf->wp = fsf->s->curw->window->active; - } else { - s = fsf->s; - if ((w = window_find_by_id_str(flag)) != NULL) - wp = w->active; - else { - wp = window_pane_find_by_id_str(flag); - if (wp != NULL) - w = wp->window; - } - wl = winlink_find_by_window(&s->windows, w); - if (wl != NULL) { - fsf->wl = wl; - fsf->wp = wp; - } - } - } - break; - case CMD_MOVEW_R|CMD_INDEX_T: - case CMD_MOVEW_R|CMD_INDEX_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, - targetflags|CMD_FIND_QUIET); - if (error != 0) { - error = cmd_find_target(fsf, cmdq, flag, - CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); - if (error != 0) - return (-1); - } - break; - case CMD_SESSION_T: - case CMD_SESSION_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, - targetflags); - if (error != 0) - return (-1); - break; - case CMD_WINDOW_MARKED_T: - case CMD_WINDOW_MARKED_S: - targetflags |= CMD_FIND_DEFAULT_MARKED; - /* FALLTHROUGH */ - case CMD_WINDOW_T: - case CMD_WINDOW_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, - targetflags); - if (error != 0) - return (-1); - break; - case CMD_PANE_MARKED_T: - case CMD_PANE_MARKED_S: - targetflags |= CMD_FIND_DEFAULT_MARKED; - /* FALLTHROUGH */ - case CMD_PANE_T: - case CMD_PANE_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, - targetflags); - if (error != 0) - return (-1); - break; - case CMD_INDEX_T: - case CMD_INDEX_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, - CMD_FIND_WINDOW_INDEX); - if (error != 0) - return (-1); - break; - default: - fatalx("too many -%c for %s", c, cmd->entry->name); - } - - /* - * If this is still the current command, it wants what it asked for and - * nothing more. If it's the base command, fill in as much as possible - * because the current command may have different flags. - */ - if (!everything) + switch (flag) { + case CMD_NONE: + case CMD_CLIENT: + case CMD_CLIENT_CANFAIL: return (0); + case CMD_SESSION: + case CMD_SESSION_CANFAIL: + case CMD_SESSION_PREFERUNATTACHED: + case CMD_SESSION_WITHPANE: + targetflags = 0; + if (flag == CMD_SESSION_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_SESSION_PREFERUNATTACHED) + targetflags |= CMD_FIND_PREFER_UNATTACHED; -complete_everything: - if (fsf->s == NULL) { - if (state->c != NULL) - fsf->s = state->c->session; - if (fsf->s == NULL) { - error = cmd_find_target(fsf, cmdq, NULL, - CMD_FIND_SESSION, CMD_FIND_QUIET); - if (error != 0) - fsf->s = NULL; - } - if (fsf->s == NULL) { - if (flags & CMD_CANFAIL) - return (0); - cmdq_error(cmdq, "no current session"); + error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, + targetflags); + if (error != 0 && flag != CMD_SESSION_CANFAIL) return (-1); - } - } - if (fsf->wl == NULL) { - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, 0); - if (error != 0) + break; + case CMD_MOVEW_R: + error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, + CMD_FIND_QUIET); + if (error == 0) + break; + flag = CMD_WINDOW_INDEX; + /* FALLTHROUGH */ + case CMD_WINDOW: + case CMD_WINDOW_CANFAIL: + case CMD_WINDOW_MARKED: + case CMD_WINDOW_INDEX: + targetflags = 0; + if (flag == CMD_WINDOW_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_WINDOW_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + if (flag == CMD_WINDOW_INDEX) + targetflags |= CMD_FIND_WINDOW_INDEX; + + error = cmd_find_target(fs, cmdq, target, CMD_FIND_WINDOW, + targetflags); + if (error != 0 && flag != CMD_WINDOW_CANFAIL) return (-1); - } - if (fsf->wp == NULL) { - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, 0); - if (error != 0) + break; + case CMD_PANE: + case CMD_PANE_CANFAIL: + case CMD_PANE_MARKED: + targetflags = 0; + if (flag == CMD_PANE_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_PANE_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + + error = cmd_find_target(fs, cmdq, target, CMD_FIND_PANE, + targetflags); + if (error != 0 && flag != CMD_PANE_CANFAIL) return (-1); + break; } return (0); } -static struct client * -cmd_get_state_client(struct cmd_q *cmdq, int quiet) -{ - struct cmd *cmd = cmdq->cmd; - struct args *args = cmd->args; - - switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { - case 0: - return (cmd_find_client(cmdq, NULL, 1)); - case CMD_CLIENT_C: - return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); - case CMD_CLIENT_T: - return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); - default: - fatalx("both -t and -c for %s", cmd->entry->name); - } -} - int cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { - struct cmd_state *state = &cmdq->state; - struct args *args = cmd->args; - const char *cflag, *tflag; - char *tmp; - int error, quiet; + const struct cmd_entry *entry = cmd->entry; + struct cmd_state *state = &cmdq->state; + char *tmp; + enum cmd_entry_flag flag; + const char *s; + int error; tmp = cmd_print(cmd); - log_debug("preparing state for: %s (client %p)", tmp, cmdq->client); + log_debug("preparing state for %s (client %p)", tmp, cmdq->client); free(tmp); - /* Start with an empty state. */ cmd_clear_state(state); - /* No error messages if can fail. */ - quiet = 0; - if (cmd->entry->flags & CMD_CLIENT_CANFAIL) - quiet = 1; - - /* - * If the command wants a client and provides -c or -t, use it. If not, - * try the base command instead via cmd_get_state_client. No client is - * allowed if no flags, otherwise it must be available. - */ - switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { - case 0: - state->c = cmd_get_state_client(cmdq, 1); - break; - case CMD_CLIENT_C: - cflag = args_get(args, 'c'); - if (cflag == NULL) - state->c = cmd_get_state_client(cmdq, quiet); + flag = cmd->entry->cflag; + if (flag == CMD_NONE) { + flag = cmd->entry->tflag; + if (flag == CMD_CLIENT || flag == CMD_CLIENT_CANFAIL) + s = args_get(cmd->args, 't'); else - state->c = cmd_find_client(cmdq, cflag, quiet); - if (!quiet && state->c == NULL) - return (-1); - break; - case CMD_CLIENT_T: - tflag = args_get(args, 't'); - if (tflag == NULL) - state->c = cmd_get_state_client(cmdq, 0); - else - state->c = cmd_find_client(cmdq, tflag, 0); - state->c = cmd_find_client(cmdq, args_get(args, 't'), quiet); - if (!quiet && state->c == NULL) + s = NULL; + } else + s = args_get(cmd->args, 'c'); + switch (flag) { + case CMD_CLIENT: + state->c = cmd_find_client(cmdq, s, 0); + if (state->c == NULL) return (-1); break; default: - fatalx("both -c and -t for %s", cmd->entry->name); + state->c = cmd_find_client(cmdq, s, 1); + break; } - error = cmd_set_state_flag(cmd, cmdq, 't'); - if (error == 0) - error = cmd_set_state_flag(cmd, cmdq, 's'); - return (error); + s = args_get(cmd->args, 't'); + log_debug("preparing -t state: target %s", s == NULL ? "none" : s); + + error = cmd_prepare_state_flag(&state->tflag, entry->tflag, s, cmdq); + if (error != 0) + return (error); + + s = args_get(cmd->args, 's'); + log_debug("preparing -s state: target %s", s == NULL ? "none" : s); + + error = cmd_prepare_state_flag(&state->sflag, entry->sflag, s, cmdq); + if (error != 0) + return (error); + + return (0); } char * diff --git a/log.c b/log.c index 46f1673c..200f9119 100644 --- a/log.c +++ b/log.c @@ -144,6 +144,7 @@ fatalx(const char *msg, ...) char *fmt; va_list ap; + abort(); va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); diff --git a/tmux.h b/tmux.h index c9adb2ee..04d1fba2 100644 --- a/tmux.h +++ b/tmux.h @@ -1387,6 +1387,30 @@ struct cmd_q { TAILQ_ENTRY(cmd_q) waitentry; }; +/* Command -c, -t or -s flags. */ +enum cmd_entry_flag { + CMD_NONE, + + CMD_CLIENT, + CMD_CLIENT_CANFAIL, + + CMD_SESSION, + CMD_SESSION_CANFAIL, + CMD_SESSION_PREFERUNATTACHED, + CMD_SESSION_WITHPANE, + + CMD_WINDOW, + CMD_WINDOW_CANFAIL, + CMD_WINDOW_MARKED, + CMD_WINDOW_INDEX, + + CMD_PANE, + CMD_PANE_CANFAIL, + CMD_PANE_MARKED, + + CMD_MOVEW_R, +}; + /* Command definition. */ struct cmd_entry { const char *name; @@ -1397,37 +1421,18 @@ struct cmd_entry { int lower; int upper; } args; - const char *usage; + enum cmd_entry_flag tflag; + enum cmd_entry_flag sflag; + enum cmd_entry_flag cflag; + #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 -#define CMD_SESSION_T 0x4 -#define CMD_SESSION_S 0x8 -#define CMD_WINDOW_T 0x10 -#define CMD_WINDOW_S 0x20 -#define CMD_PANE_T 0x40 -#define CMD_PANE_S 0x80 -#define CMD_CLIENT_T 0x100 -#define CMD_CLIENT_C 0x200 -#define CMD_INDEX_T 0x400 -#define CMD_INDEX_S 0x800 -#define CMD_CANFAIL 0x1000 -#define CMD_PREFERUNATTACHED 0x2000 -#define CMD_MOVEW_R 0x4000 /* for movew -r only */ -#define CMD_PANE_MARKED_S 0x8000 -#define CMD_PANE_MARKED_T 0x10000 -#define CMD_WINDOW_MARKED_T 0x20000 -#define CMD_WINDOW_MARKED_S 0x40000 -#define CMD_CLIENT_CANFAIL 0x80000 - int flags; + int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; -#define CMD_ALL_T (CMD_SESSION_T|CMD_WINDOW_T|CMD_PANE_T|CMD_INDEX_T| \ - CMD_MOVEW_R|CMD_PANE_MARKED_T|CMD_WINDOW_MARKED_T) -#define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ - CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) /* Key binding and key table. */ struct key_binding { From a585a1b81a9122c2f7d7083304a4eb47d4e1c7ec Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Dec 2015 00:32:19 +0000 Subject: [PATCH 935/949] Remove some stray debug code. --- log.c | 1 - 1 file changed, 1 deletion(-) diff --git a/log.c b/log.c index 200f9119..46f1673c 100644 --- a/log.c +++ b/log.c @@ -144,7 +144,6 @@ fatalx(const char *msg, ...) char *fmt; va_list ap; - abort(); va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); From d5999f8b5cbbd3648d7a6c00100e5eab279a90bb Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Dec 2015 23:30:58 +0000 Subject: [PATCH 936/949] Use cmd_find_clear_state instead of an extra function doing the same. --- cmd-find.c | 3 --- cmd.c | 20 +++----------------- tmux.h | 3 +++ 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 582f1759..8e118c00 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -50,9 +50,6 @@ int cmd_find_get_pane(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); -void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); -void cmd_find_log_state(const char *, struct cmd_find_state *); - const char *cmd_find_session_table[][2] = { { NULL, NULL } }; diff --git a/cmd.c b/cmd.c index 62be3af1..4f1e1b90 100644 --- a/cmd.c +++ b/cmd.c @@ -388,22 +388,6 @@ usage: return (NULL); } -static void -cmd_clear_state(struct cmd_state *state) -{ - state->c = NULL; - - state->tflag.s = NULL; - state->tflag.wl = NULL; - state->tflag.wp = NULL; - state->tflag.idx = -1; - - state->sflag.s = NULL; - state->sflag.wl = NULL; - state->sflag.wp = NULL; - state->sflag.idx = -1; -} - static int cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, const char *target, struct cmd_q *cmdq) @@ -493,7 +477,9 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) log_debug("preparing state for %s (client %p)", tmp, cmdq->client); free(tmp); - cmd_clear_state(state); + state->c = NULL; + cmd_find_clear_state(&state->tflag, NULL, 0); + cmd_find_clear_state(&state->sflag, NULL, 0); flag = cmd->entry->cflag; if (flag == CMD_NONE) { diff --git a/tmux.h b/tmux.h index 04d1fba2..c6bd48cf 100644 --- a/tmux.h +++ b/tmux.h @@ -1764,6 +1764,9 @@ long long args_strtonum(struct args *, u_char, long long, long long, int cmd_find_target(struct cmd_find_state *, struct cmd_q *, const char *, enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); +void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, + int); +void cmd_find_log_state(const char *, struct cmd_find_state *); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); From 12da13c9d1e0e015ed19761e352ee6b877b23aa4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:00:01 +0000 Subject: [PATCH 937/949] Make the marked pane a cmd_find_state. --- cmd-find.c | 109 +++++++++++++++++++++++++++++----------------- cmd-select-pane.c | 4 +- screen-redraw.c | 4 +- server.c | 56 +++++++----------------- tmux.h | 7 +-- window.c | 4 +- 6 files changed, 95 insertions(+), 89 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 8e118c00..b7728ea2 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -792,6 +792,67 @@ cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) fs->idx = -1; } +/* Check if a state if valid. */ +int +cmd_find_valid_state(struct cmd_find_state *fs) +{ + struct winlink *wl; + + if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL) + return (0); + + if (!session_alive(fs->s)) + return (0); + + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (wl->window == fs->w && wl == fs->wl) + break; + } + if (wl == NULL) + return (0); + + if (fs->w != fs->wl->window) + return (0); + + if (!window_has_pane(fs->w, fs->wp)) + return (0); + return (window_pane_visible(fs->wp)); +} + +/* Copy a state. */ +void +cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src) +{ + dst->s = src->s; + dst->wl = src->wl; + dst->idx = dst->wl->idx; + dst->w = dst->wl->window; + dst->wp = src->wp; +} + +/* Log the result. */ +void +cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) +{ + if (fs->s != NULL) + log_debug("%s: s=$%u", prefix, fs->s->id); + else + log_debug("%s: s=none", prefix); + if (fs->wl != NULL) { + log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, + fs->wl->window == fs->w, fs->w->id, fs->w->name); + } else + log_debug("%s: wl=none", prefix); + if (fs->wp != NULL) + log_debug("%s: wp=%%%u", prefix, fs->wp->id); + else + log_debug("%s: wp=none", prefix); + if (fs->idx != -1) + log_debug("%s: idx=%d", prefix, fs->idx); + else + log_debug("%s: idx=none", prefix); +} + /* * Split target into pieces and resolve for the given type. Fills in the given * state. Returns 0 on success or -1 on error. @@ -814,17 +875,14 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, /* Find current state. */ cmd_find_clear_state(¤t, cmdq, flags); - if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { - current.s = marked_session; - current.wl = marked_winlink; - current.idx = current.wl->idx; - current.w = current.wl->window; - current.wp = marked_window_pane; - } - if (current.s == NULL && cmd_find_current_session(¤t) != 0) { - if (~flags & CMD_FIND_QUIET) - cmdq_error(cmdq, "no current session"); - goto error; + if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) + cmd_find_copy_state(¤t, &marked_pane); + else { + if (cmd_find_current_session(¤t) != 0) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no current session"); + goto error; + } } /* Clear new state. */ @@ -868,11 +926,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, cmdq_error(cmdq, "no marked target"); goto error; } - fs->s = marked_session; - fs->wl = marked_winlink; - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - fs->wp = marked_window_pane; + cmd_find_copy_state(fs, &marked_pane); goto found; } @@ -1068,29 +1122,6 @@ no_pane: goto error; } -/* Log the result. */ -void -cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) -{ - if (fs->s != NULL) - log_debug("%s: s=$%u", prefix, fs->s->id); - else - log_debug("%s: s=none", prefix); - if (fs->wl != NULL) { - log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, - fs->wl->window == fs->w, fs->w->id, fs->w->name); - } else - log_debug("%s: wl=none", prefix); - if (fs->wp != NULL) - log_debug("%s: wp=%%%u", prefix, fs->wp->id); - else - log_debug("%s: wp=none", prefix); - if (fs->idx != -1) - log_debug("%s: idx=%d", prefix, fs->idx); - else - log_debug("%s: idx=none", prefix); -} - /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 7652f608..6ebe753c 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -94,13 +94,13 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) return (CMD_RETURN_NORMAL); - lastwp = marked_window_pane; + lastwp = marked_pane.wp; if (args_has(args, 'M') || server_is_marked(s, wl, wp)) server_clear_marked(); else server_set_marked(s, wl, wp); - markedwp = marked_window_pane; + markedwp = marked_pane.wp; if (lastwp != NULL) { server_redraw_window_borders(lastwp->window); diff --git a/screen-redraw.c b/screen-redraw.c index fd4536d0..9958d04a 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -331,9 +331,9 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) continue; active = screen_redraw_check_is(i, j, type, w, w->active, wp); - if (server_is_marked(s, s->curw, marked_window_pane) && + if (server_is_marked(s, s->curw, marked_pane.wp) && screen_redraw_check_is(i, j, type, w, - marked_window_pane, wp)) { + marked_pane.wp, wp)) { if (active) tty_attributes(tty, &m_active_gc, NULL); else diff --git a/server.c b/server.c index c45e8a2c..0a1146f3 100644 --- a/server.c +++ b/server.c @@ -41,18 +41,14 @@ * Main server functions. */ -struct clients clients; +struct clients clients; -struct tmuxproc *server_proc; -int server_fd; -int server_exit; -struct event server_ev_accept; +struct tmuxproc *server_proc; +int server_fd; +int server_exit; +struct event server_ev_accept; -struct session *marked_session; -struct winlink *marked_winlink; -struct window *marked_window; -struct window_pane *marked_window_pane; -struct layout_cell *marked_layout_cell; +struct cmd_find_state marked_pane; int server_create_socket(void); int server_loop(void); @@ -68,22 +64,18 @@ void server_child_stopped(pid_t, int); void server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { - marked_session = s; - marked_winlink = wl; - marked_window = wl->window; - marked_window_pane = wp; - marked_layout_cell = wp->layout_cell; + cmd_find_clear_state(&marked_pane, NULL, 0); + marked_pane.s = s; + marked_pane.wl = wl; + marked_pane.w = wl->window; + marked_pane.wp = wp; } /* Clear marked pane. */ void server_clear_marked(void) { - marked_session = NULL; - marked_winlink = NULL; - marked_window = NULL; - marked_window_pane = NULL; - marked_layout_cell = NULL; + cmd_find_clear_state(&marked_pane, NULL, 0); } /* Is this the marked pane? */ @@ -92,9 +84,9 @@ server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { if (s == NULL || wl == NULL || wp == NULL) return (0); - if (marked_session != s || marked_winlink != wl) + if (marked_pane.s != s || marked_pane.wl != wl) return (0); - if (marked_window_pane != wp) + if (marked_pane.wp != wp) return (0); return (server_check_marked()); } @@ -103,25 +95,7 @@ server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) int server_check_marked(void) { - struct winlink *wl; - - if (marked_window_pane == NULL) - return (0); - if (marked_layout_cell != marked_window_pane->layout_cell) - return (0); - - if (!session_alive(marked_session)) - return (0); - RB_FOREACH(wl, winlinks, &marked_session->windows) { - if (wl->window == marked_window && wl == marked_winlink) - break; - } - if (wl == NULL) - return (0); - - if (!window_has_pane(marked_window, marked_window_pane)) - return (0); - return (window_pane_visible(marked_window_pane)); + return (cmd_find_valid_state(&marked_pane)); } /* Create server socket. */ diff --git a/tmux.h b/tmux.h index c6bd48cf..651990f0 100644 --- a/tmux.h +++ b/tmux.h @@ -1766,6 +1766,9 @@ int cmd_find_target(struct cmd_find_state *, struct cmd_q *, struct client *cmd_find_client(struct cmd_q *, const char *, int); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); +int cmd_find_valid_state(struct cmd_find_state *); +void cmd_find_copy_state(struct cmd_find_state *, + struct cmd_find_state *); void cmd_find_log_state(const char *, struct cmd_find_state *); /* cmd.c */ @@ -1844,9 +1847,7 @@ void alerts_check_session(struct session *); /* server.c */ extern struct tmuxproc *server_proc; extern struct clients clients; -extern struct session *marked_session; -extern struct winlink *marked_winlink; -extern struct window_pane *marked_window_pane; +extern struct cmd_find_state marked_pane; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); diff --git a/window.c b/window.c index 75b99672..f0e57190 100644 --- a/window.c +++ b/window.c @@ -568,7 +568,7 @@ window_add_pane(struct window *w, u_int hlimit) void window_lost_pane(struct window *w, struct window_pane *wp) { - if (wp == marked_window_pane) + if (wp == marked_pane.wp) server_clear_marked(); if (wp == w->active) { @@ -691,7 +691,7 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; - if (server_check_marked() && wl == marked_winlink) + if (server_check_marked() && wl == marked_pane.wl) flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; From 56d097cfe053f6cd50db9607503c25a80e3cd989 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:11:24 +0000 Subject: [PATCH 938/949] Don't copy marked pane when can just point to it. --- cmd-find.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index b7728ea2..ea1389a9 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -873,22 +873,23 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, log_debug("%s: target %s, type %d", __func__, target, type); log_debug("%s: cmdq %p, flags %#x", __func__, cmdq, flags); + /* Clear new state. */ + cmd_find_clear_state(fs, cmdq, flags); + /* Find current state. */ - cmd_find_clear_state(¤t, cmdq, flags); + fs->current = NULL; if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) - cmd_find_copy_state(¤t, &marked_pane); - else { + fs->current = &marked_pane; + if (fs->current == NULL) { + cmd_find_clear_state(¤t, cmdq, flags); if (cmd_find_current_session(¤t) != 0) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no current session"); goto error; } + fs->current = ¤t; } - /* Clear new state. */ - cmd_find_clear_state(fs, cmdq, flags); - fs->current = ¤t; - /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') goto current; @@ -1087,9 +1088,9 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, current: /* Use the current session. */ + cmd_find_copy_state(fs, fs->current); if (flags & CMD_FIND_WINDOW_INDEX) - current.idx = -1; - memcpy(fs, ¤t, sizeof *fs); + fs->idx = -1; goto found; error: From 6f417ec9438de4440522483be02c51803d628313 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:45:02 +0000 Subject: [PATCH 939/949] We changed somewhat recently to us the pty when tmux was run inside itself to work out the current pane. This is confusing in many cases (particularly notable is that "tmux neww\; splitw" would not split the new window), and the few advantages do not make up for the confusion. So drop this behaviour and return to using the current window and pane; keep the pty check but only use it to limit the list of possible current sessions. --- cmd-find.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index ea1389a9..dbfe99ef 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -249,7 +249,11 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) { struct window_pane *wp; - /* If this is running in a pane, that's great. */ + /* + * If this is running in a pane, we can use that to limit the list of + * sessions to those containing that pane (we still use the current + * window in the best session). + */ if (fs->cmdq->client->tty.path != NULL) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) @@ -262,11 +266,8 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) if (wp == NULL) goto unknown_pane; - /* We now know the window and pane. */ + /* Find the best session and winlink containing this pane. */ fs->w = wp->window; - fs->wp = wp; - - /* Find the best session and winlink. */ if (cmd_find_best_session_with_window(fs) != 0) { if (wp != NULL) { /* @@ -278,6 +279,13 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) } return (-1); } + + /* Use the current window and pane from this session. */ + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + return (0); unknown_pane: @@ -290,6 +298,7 @@ unknown_pane: fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = fs->w->active; + return (0); } From 9d88d82d5e8caa7882a28ac95fda19754e5553e7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:52:17 +0000 Subject: [PATCH 940/949] Allow list-keys and list-commands to be run without a running server. --- cmd-list-keys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 08796e5d..4abe2473 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_list_keys_entry = { .args = { "t:T:", 0, 0 }, .usage = "[-t mode-table] [-T key-table]", - .flags = 0, + .flags = CMD_STARTSERVER, .exec = cmd_list_keys_exec }; @@ -50,7 +50,7 @@ const struct cmd_entry cmd_list_commands_entry = { .args = { "", 0, 0 }, .usage = "", - .flags = 0, + .flags = CMD_STARTSERVER, .exec = cmd_list_keys_exec }; From ac9778395feb8551e0acbd2f32465b65aeba5e05 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 13:43:07 +0000 Subject: [PATCH 941/949] Some hooks API changes to fire a hook while waiting another cmdq and infrastructure that will be needed soon. --- cmd-attach-session.c | 2 +- hooks.c | 77 +++++++++++++++++++++++++++++++++++++++----- server-client.c | 4 +-- tmux.h | 8 ++++- 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 3b3f9e68..586863aa 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -144,7 +144,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); - hooks_run(c->session->hooks, "client-attached", c); + hooks_run(c->session->hooks, c, "client-attached"); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/hooks.c b/hooks.c index 6f548d23..424faf0c 100644 --- a/hooks.c +++ b/hooks.c @@ -34,6 +34,7 @@ RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); static struct hook *hooks_find1(struct hooks *, const char *); static void hooks_free1(struct hooks *, struct hook *); +static void hooks_emptyfn(struct cmd_q *); static int hooks_cmp(struct hook *hook1, struct hook *hook2) @@ -132,18 +133,78 @@ hooks_find(struct hooks *hooks, const char *name) return (hook); } -void -hooks_run(struct hooks *hooks, const char *name, struct client *c) +static void +hooks_emptyfn(struct cmd_q *hooks_cmdq) +{ + struct cmd_q *cmdq = hooks_cmdq->data; + + if (cmdq != NULL) { + if (hooks_cmdq->client_exit >= 0) + cmdq->client_exit = hooks_cmdq->client_exit; + if (!cmdq_free(cmdq)) + cmdq_continue(cmdq); + } + cmdq_free(hooks_cmdq); +} + +int +hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) { struct hook *hook; - struct cmd_q *cmdq; + struct cmd_q *hooks_cmdq; + va_list ap; + char *name; + + va_start(ap, fmt); + xvasprintf(&name, fmt, ap); + va_end(ap); hook = hooks_find(hooks, name); - if (hook == NULL) - return; + if (hook == NULL) { + free(name); + return (-1); + } log_debug("running hook %s", name); + free(name); - cmdq = cmdq_new(c); - cmdq_run(cmdq, hook->cmdlist, NULL); - cmdq_free(cmdq); + hooks_cmdq = cmdq_new(c); + hooks_cmdq->flags |= CMD_Q_NOHOOKS; + hooks_cmdq->parent = NULL; + + cmdq_run(hooks_cmdq, hook->cmdlist, NULL); + cmdq_free(hooks_cmdq); + return (0); +} + +int +hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, const char *fmt, ...) +{ + struct hook *hook; + struct cmd_q *hooks_cmdq; + va_list ap; + char *name; + + va_start(ap, fmt); + xvasprintf(&name, fmt, ap); + va_end(ap); + + hook = hooks_find(hooks, name); + if (hook == NULL) { + free(name); + return (-1); + } + log_debug("running hook %s (parent %p)", name, cmdq); + free(name); + + hooks_cmdq = cmdq_new(cmdq->client); + hooks_cmdq->flags |= CMD_Q_NOHOOKS; + hooks_cmdq->parent = cmdq; + + hooks_cmdq->emptyfn = hooks_emptyfn; + hooks_cmdq->data = cmdq; + + if (cmdq != NULL) + cmdq->references++; + cmdq_run(hooks_cmdq, hook->cmdlist, NULL); + return (0); } diff --git a/server-client.c b/server-client.c index c2b43f38..ec16340b 100644 --- a/server-client.c +++ b/server-client.c @@ -283,7 +283,7 @@ server_client_detach(struct client *c, enum msgtype msgtype) if (s == NULL) return; - hooks_run(c->session->hooks, "client-detached", c); + hooks_run(c->session->hooks, c, "client-detached"); proc_send_s(c->peer, msgtype, s->name); } @@ -1027,7 +1027,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_redraw_client(c); } if (c->session != NULL) - hooks_run(c->session->hooks, "client-resized", c); + hooks_run(c->session->hooks, c, "client-resized"); break; case MSG_EXITING: if (datalen != 0) diff --git a/tmux.h b/tmux.h index 651990f0..2b1b29b0 100644 --- a/tmux.h +++ b/tmux.h @@ -1368,6 +1368,8 @@ struct cmd_q { int references; int flags; #define CMD_Q_DEAD 0x1 +#define CMD_Q_REENTRY 0x2 +#define CMD_Q_NOHOOKS 0x4 struct client *client; int client_exit; @@ -1375,6 +1377,7 @@ struct cmd_q { struct cmd_q_items queue; struct cmd_q_item *item; struct cmd *cmd; + struct cmd_q *parent; struct cmd_state state; @@ -1581,7 +1584,10 @@ void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); -void hooks_run(struct hooks *, const char *, struct client *); +int printflike(3, 4) hooks_run(struct hooks *, struct client *, const char *, + ...); +int printflike(3, 4) hooks_wait(struct hooks *, struct cmd_q *, const char *, + ...); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; From 909b737289ee24f1c78c13c10d728a1cf990912d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 14:32:55 +0000 Subject: [PATCH 942/949] Copy state directly rather than dereferencing wl (which could be NULL). --- cmd-find.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index dbfe99ef..cf39240a 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -834,8 +834,8 @@ cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src) { dst->s = src->s; dst->wl = src->wl; - dst->idx = dst->wl->idx; - dst->w = dst->wl->window; + dst->idx = src->idx; + dst->w = src->w; dst->wp = src->wp; } From 8eb1a7d5dc8d66ca7d17c72e4d8d0e58d6fd2824 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Dec 2015 21:47:00 +0000 Subject: [PATCH 943/949] showenv and setenv need to be CANFAIL. --- cmd-set-environment.c | 4 ++-- cmd-show-environment.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 0808dcb1..f701d7d9 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_set_environment_entry = { .args = { "grt:u", 1, 2 }, .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - .tflag = CMD_SESSION, + .tflag = CMD_SESSION_CANFAIL, .flags = 0, .exec = cmd_set_environment_exec @@ -64,7 +64,7 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) else value = args->argv[1]; - if (args_has(self->args, 'g')) + if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) env = global_environ; else env = cmdq->state.tflag.s->environ; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index ad9e9e37..54baafe4 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -94,7 +94,7 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) struct environ *env; struct environ_entry *envent; - if (args_has(self->args, 'g')) + if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) env = global_environ; else env = cmdq->state.tflag.s->environ; From 021c64310daa4ae4c4e8af08f17c994f21237758 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Dec 2015 21:50:37 +0000 Subject: [PATCH 944/949] Add infrastructure to work out the best target given a pane or window alone and use it to add pane_died and pane_exited hooks. --- cmd-attach-session.c | 2 +- cmd-find.c | 53 ++++++++++++++++++++++++++++++++++++++++---- cmd-queue.c | 4 ++++ cmd-respawn-window.c | 2 +- hooks.c | 20 +++++++++++++++-- server-client.c | 4 ++-- server-fn.c | 9 +++++++- server.c | 2 +- tmux.1 | 6 +++++ window.c | 2 +- 10 files changed, 91 insertions(+), 13 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 586863aa..4d8a75f8 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -144,7 +144,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); - hooks_run(c->session->hooks, c, "client-attached"); + hooks_run(c->session->hooks, c, NULL, "client-attached"); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/cmd-find.c b/cmd-find.c index cf39240a..15dfa596 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -190,7 +190,7 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) u_int ssize; struct session *s; - if (fs->cmdq->client != NULL) { + if (fs->cmdq != NULL && fs->cmdq->client != NULL) { fs->s = cmd_find_try_TMUX(fs->cmdq->client, fs->w); if (fs->s != NULL) return (cmd_find_best_winlink_with_window(fs)); @@ -254,7 +254,7 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) * sessions to those containing that pane (we still use the current * window in the best session). */ - if (fs->cmdq->client->tty.path != NULL) { + if (fs->cmdq != NULL && fs->cmdq->client->tty.path != NULL) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) break; @@ -289,7 +289,9 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) return (0); unknown_pane: - fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); + fs->s = NULL; + if (fs->cmdq != NULL) + fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); if (fs->s == NULL) fs->s = cmd_find_best_session(NULL, 0, fs->flags); if (fs->s == NULL) @@ -310,7 +312,7 @@ int cmd_find_current_session(struct cmd_find_state *fs) { /* If we know the current client, use it. */ - if (fs->cmdq->client != NULL) { + if (fs->cmdq != NULL && fs->cmdq->client != NULL) { log_debug("%s: have client %p%s", __func__, fs->cmdq->client, fs->cmdq->client->session == NULL ? "" : " (with session)"); if (fs->cmdq->client->session == NULL) @@ -862,6 +864,49 @@ cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) log_debug("%s: idx=none", prefix); } +/* Find state from a session. */ +int +cmd_find_from_session(struct cmd_find_state *fs, struct session *s) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->s = s; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + + cmd_find_log_state(__func__, fs); + return (0); +} + +/* Find state from a window. */ +int +cmd_find_from_window(struct cmd_find_state *fs, struct window *w) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->w = w; + if (cmd_find_best_session_with_window(fs) != 0) + return (-1); + if (cmd_find_best_winlink_with_window(fs) != 0) + return (-1); + + cmd_find_log_state(__func__, fs); + return (0); +} + +/* Find state from a pane. */ +int +cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp) +{ + if (cmd_find_from_window(fs, wp->window) != 0) + return (-1); + fs->wp = wp; + + cmd_find_log_state(__func__, fs); + return (0); +} + /* * Split target into pieces and resolve for the given type. Fills in the given * state. Returns 0 on success or -1 on error. diff --git a/cmd-queue.c b/cmd-queue.c index c0fc26c6..5bc3226a 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -44,6 +44,9 @@ cmdq_new(struct client *c) cmdq->item = NULL; cmdq->cmd = NULL; + cmd_find_clear_state(&cmdq->current, NULL, 0); + cmdq->parent = NULL; + return (cmdq); } @@ -286,3 +289,4 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } + diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index aa4e169b..da9a365a 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -90,7 +90,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(env); - server_destroy_pane(wp); + server_destroy_pane(wp, 0); return (CMD_RETURN_ERROR); } layout_init(w, wp); diff --git a/hooks.c b/hooks.c index 424faf0c..c1a016ae 100644 --- a/hooks.c +++ b/hooks.c @@ -42,6 +42,14 @@ hooks_cmp(struct hook *hook1, struct hook *hook2) return (strcmp(hook1->name, hook2->name)); } +struct hooks * +hooks_get(struct session *s) +{ + if (s != NULL) + return (s->hooks); + return (global_hooks); +} + struct hooks * hooks_create(struct hooks *parent) { @@ -148,7 +156,8 @@ hooks_emptyfn(struct cmd_q *hooks_cmdq) } int -hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) +hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs, + const char *fmt, ...) { struct hook *hook; struct cmd_q *hooks_cmdq; @@ -169,6 +178,9 @@ hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) hooks_cmdq = cmdq_new(c); hooks_cmdq->flags |= CMD_Q_NOHOOKS; + + if (fs != NULL) + cmd_find_copy_state(&hooks_cmdq->current, fs); hooks_cmdq->parent = NULL; cmdq_run(hooks_cmdq, hook->cmdlist, NULL); @@ -177,7 +189,8 @@ hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) } int -hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, const char *fmt, ...) +hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, + const char *fmt, ...) { struct hook *hook; struct cmd_q *hooks_cmdq; @@ -198,6 +211,9 @@ hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, const char *fmt, ...) hooks_cmdq = cmdq_new(cmdq->client); hooks_cmdq->flags |= CMD_Q_NOHOOKS; + + if (fs != NULL) + cmd_find_copy_state(&hooks_cmdq->current, fs); hooks_cmdq->parent = cmdq; hooks_cmdq->emptyfn = hooks_emptyfn; diff --git a/server-client.c b/server-client.c index ec16340b..9b88a165 100644 --- a/server-client.c +++ b/server-client.c @@ -283,7 +283,7 @@ server_client_detach(struct client *c, enum msgtype msgtype) if (s == NULL) return; - hooks_run(c->session->hooks, c, "client-detached"); + hooks_run(c->session->hooks, c, NULL, "client-detached"); proc_send_s(c->peer, msgtype, s->name); } @@ -1027,7 +1027,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_redraw_client(c); } if (c->session != NULL) - hooks_run(c->session->hooks, c, "client-resized"); + hooks_run(c->session->hooks, c, NULL, "client-resized"); break; case MSG_EXITING: if (datalen != 0) diff --git a/server-fn.c b/server-fn.c index a4c2dfea..0f35aaaf 100644 --- a/server-fn.c +++ b/server-fn.c @@ -293,12 +293,13 @@ server_unlink_window(struct session *s, struct winlink *wl) } void -server_destroy_pane(struct window_pane *wp) +server_destroy_pane(struct window_pane *wp, int hooks) { struct window *w = wp->window; int old_fd; struct screen_write_ctx ctx; struct grid_cell gc; + struct cmd_find_state fs; old_fd = wp->fd; if (wp->fd != -1) { @@ -319,6 +320,9 @@ server_destroy_pane(struct window_pane *wp) screen_write_puts(&ctx, &gc, "Pane is dead"); screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; + + if (hooks && cmd_find_from_pane(&fs, wp) == 0) + hooks_run(hooks_get(fs.s), NULL, &fs, "pane-died"); return; } @@ -326,6 +330,9 @@ server_destroy_pane(struct window_pane *wp) layout_close_pane(wp); window_remove_pane(w, wp); + if (hooks && cmd_find_from_window(&fs, w) == 0) + hooks_run(hooks_get(fs.s), NULL, &fs, "pane-exited"); + if (TAILQ_EMPTY(&w->panes)) server_kill_window(w); else diff --git a/server.c b/server.c index 0a1146f3..170244c7 100644 --- a/server.c +++ b/server.c @@ -387,7 +387,7 @@ server_child_exited(pid_t pid, int status) TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { wp->status = status; - server_destroy_pane(wp); + server_destroy_pane(wp, 1); break; } } diff --git a/tmux.1 b/tmux.1 index 39e9bd4b..9017b4e7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3230,6 +3230,12 @@ Run when a client is attached. Run when a client is detached .It client-resized Run when a client is resized. +.It pane-died +Run when the program running in a pane exits, but +.Ic remain-on-exit +is on so the pane has not closed. +.It pane-exited +Run when the program running in a pane exits. .El .Pp Hooks are managed with these commands: diff --git a/window.c b/window.c index f0e57190..01d5f973 100644 --- a/window.c +++ b/window.c @@ -974,7 +974,7 @@ window_pane_error_callback(__unused struct bufferevent *bufev, { struct window_pane *wp = data; - server_destroy_pane(wp); + server_destroy_pane(wp, 1); } void From 99e9a4c7864c188857fe57b411312ee669f16b54 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Dec 2015 22:05:35 +0000 Subject: [PATCH 945/949] send-keys -R should reset the input parser to ground state (so it can be used to escape from, for example, printf '\033]2;'). --- cmd-send-keys.c | 2 +- input.c | 44 +++++++++++++++++++++++--------------------- tmux.h | 19 +++++++++++++------ 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 377453b0..7b0b952c 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -86,7 +86,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 'R')) - input_reset(wp); + input_reset(wp, 1); for (i = 0; i < args->argc; i++) { literal = args_has(args, 'l'); diff --git a/input.c b/input.c index d8e80afb..ed1ebffc 100644 --- a/input.c +++ b/input.c @@ -762,24 +762,12 @@ input_init(struct window_pane *wp) ictx = wp->ictx = xcalloc(1, sizeof *ictx); - input_reset_cell(ictx); - - *ictx->interm_buf = '\0'; - ictx->interm_len = 0; - - *ictx->param_buf = '\0'; - ictx->param_len = 0; - ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); - *ictx->input_buf = '\0'; - ictx->input_len = 0; - - ictx->state = &input_state_ground; - ictx->flags = 0; - ictx->since_ground = evbuffer_new(); + + input_reset(wp, 0); } /* Destroy input parser. */ @@ -797,18 +785,32 @@ input_free(struct window_pane *wp) /* Reset input state and clear screen. */ void -input_reset(struct window_pane *wp) +input_reset(struct window_pane *wp, int clear) { struct input_ctx *ictx = wp->ictx; input_reset_cell(ictx); - if (wp->mode == NULL) - screen_write_start(&ictx->ctx, wp, &wp->base); - else - screen_write_start(&ictx->ctx, NULL, &wp->base); - screen_write_reset(&ictx->ctx); - screen_write_stop(&ictx->ctx); + if (clear) { + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + screen_write_reset(&ictx->ctx); + screen_write_stop(&ictx->ctx); + } + + *ictx->interm_buf = '\0'; + ictx->interm_len = 0; + + *ictx->param_buf = '\0'; + ictx->param_len = 0; + + *ictx->input_buf = '\0'; + ictx->input_len = 0; + + ictx->state = &input_state_ground; + ictx->flags = 0; } /* Return pending data. */ diff --git a/tmux.h b/tmux.h index 2b1b29b0..194d01d8 100644 --- a/tmux.h +++ b/tmux.h @@ -1379,6 +1379,7 @@ struct cmd_q { struct cmd *cmd; struct cmd_q *parent; + struct cmd_find_state current; struct cmd_state state; time_t time; @@ -1576,6 +1577,7 @@ void format_defaults_paste_buffer(struct format_tree *, /* hooks.c */ struct hook; +struct hooks *hooks_get(struct session *); struct hooks *hooks_create(struct hooks *); void hooks_free(struct hooks *); struct hook *hooks_first(struct hooks *); @@ -1584,10 +1586,10 @@ void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); -int printflike(3, 4) hooks_run(struct hooks *, struct client *, const char *, - ...); -int printflike(3, 4) hooks_wait(struct hooks *, struct cmd_q *, const char *, - ...); +int printflike(4, 5) hooks_run(struct hooks *, struct client *, + struct cmd_find_state *, const char *, ...); +int printflike(4, 5) hooks_wait(struct hooks *, struct cmd_q *, + struct cmd_find_state *, const char *, ...); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -1776,6 +1778,11 @@ int cmd_find_valid_state(struct cmd_find_state *); void cmd_find_copy_state(struct cmd_find_state *, struct cmd_find_state *); void cmd_find_log_state(const char *, struct cmd_find_state *); +int cmd_find_from_session(struct cmd_find_state *, + struct session *); +int cmd_find_from_window(struct cmd_find_state *, struct window *); +int cmd_find_from_pane(struct cmd_find_state *, + struct window_pane *); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); @@ -1896,7 +1903,7 @@ void server_kill_window(struct window *); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); void server_unlink_window(struct session *, struct winlink *); -void server_destroy_pane(struct window_pane *); +void server_destroy_pane(struct window_pane *, int); void server_destroy_session_group(struct session *); void server_destroy_session(struct session *); void server_check_unattached(void); @@ -1930,7 +1937,7 @@ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); -void input_reset(struct window_pane *); +void input_reset(struct window_pane *, int); struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); From a337403868cb55f2d7f197cce6e5b18c1154540c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Dec 2015 23:08:22 +0000 Subject: [PATCH 946/949] As well as setting up the state, actually use it in cmd_find_target. --- cmd-find.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 15dfa596..9b1ca517 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -931,10 +931,11 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, cmd_find_clear_state(fs, cmdq, flags); /* Find current state. */ - fs->current = NULL; if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) fs->current = &marked_pane; - if (fs->current == NULL) { + else if (cmd_find_valid_state(&cmdq->current)) + fs->current = &cmdq->current; + else { cmd_find_clear_state(¤t, cmdq, flags); if (cmd_find_current_session(¤t) != 0) { if (~flags & CMD_FIND_QUIET) From bdbec099cc858e8150884b3bcd984d2586531537 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 19 Dec 2015 08:43:04 +0000 Subject: [PATCH 947/949] Make input off flag (selectp -d) apply to synchronize-panes too. --- window.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/window.c b/window.c index 01d5f973..67d047b9 100644 --- a/window.c +++ b/window.c @@ -1127,7 +1127,9 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; - if (wp2->fd != -1 && window_pane_visible(wp2)) + if (wp2->fd == -1 || wp2->flags & PANE_INPUTOFF) + continue; + if (window_pane_visible(wp2)) input_key(wp2, key, NULL); } } From 83c96d2685a36166f2826c43e644c4b3a6c6d535 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 20 Dec 2015 11:25:13 +0000 Subject: [PATCH 948/949] No need to set cwd on Cygwin now, from Yuya Adachi. --- server-client.c | 1 - 1 file changed, 1 deletion(-) diff --git a/server-client.c b/server-client.c index 611b2212..4f094a77 100644 --- a/server-client.c +++ b/server-client.c @@ -1203,7 +1203,6 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) #ifdef __CYGWIN__ c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); - c->cwd = open(".", O_RDONLY); #endif if (c->flags & CLIENT_CONTROL) { From f2ec911b8a0bb88d23f16b7d9e0f41f29d4ac541 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 21 Dec 2015 09:20:13 +0000 Subject: [PATCH 949/949] Detach the right session with -d. --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 4d8a75f8..993f4c75 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -91,7 +91,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_client_detach(c, MSG_DETACH); + server_client_detach(c_loop, MSG_DETACH); } }