xref: /minix/external/bsd/tmux/dist/status.c (revision 0a6a1f1d)
1*0a6a1f1dSLionel Sambuc /* Id */
2eda6f593SDavid van Moolenbroek 
3eda6f593SDavid van Moolenbroek /*
4eda6f593SDavid van Moolenbroek  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5eda6f593SDavid van Moolenbroek  *
6eda6f593SDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
7eda6f593SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
8eda6f593SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
9eda6f593SDavid van Moolenbroek  *
10eda6f593SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eda6f593SDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eda6f593SDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eda6f593SDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eda6f593SDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15eda6f593SDavid van Moolenbroek  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16eda6f593SDavid van Moolenbroek  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eda6f593SDavid van Moolenbroek  */
18eda6f593SDavid van Moolenbroek 
19eda6f593SDavid van Moolenbroek #include <sys/types.h>
20eda6f593SDavid van Moolenbroek #include <sys/time.h>
21eda6f593SDavid van Moolenbroek 
22eda6f593SDavid van Moolenbroek #include <errno.h>
23eda6f593SDavid van Moolenbroek #include <limits.h>
24eda6f593SDavid van Moolenbroek #include <stdarg.h>
25eda6f593SDavid van Moolenbroek #include <stdlib.h>
26eda6f593SDavid van Moolenbroek #include <string.h>
27eda6f593SDavid van Moolenbroek #include <time.h>
28eda6f593SDavid van Moolenbroek #include <unistd.h>
29eda6f593SDavid van Moolenbroek 
30eda6f593SDavid van Moolenbroek #include "tmux.h"
31eda6f593SDavid van Moolenbroek 
32eda6f593SDavid van Moolenbroek char   *status_redraw_get_left(
33eda6f593SDavid van Moolenbroek 	    struct client *, time_t, int, struct grid_cell *, size_t *);
34eda6f593SDavid van Moolenbroek char   *status_redraw_get_right(
35eda6f593SDavid van Moolenbroek 	    struct client *, time_t, int, struct grid_cell *, size_t *);
36eda6f593SDavid van Moolenbroek char   *status_find_job(struct client *, char **);
37eda6f593SDavid van Moolenbroek void	status_job_free(void *);
38eda6f593SDavid van Moolenbroek void	status_job_callback(struct job *);
39eda6f593SDavid van Moolenbroek char   *status_print(
40eda6f593SDavid van Moolenbroek 	    struct client *, struct winlink *, time_t, struct grid_cell *);
41*0a6a1f1dSLionel Sambuc void	status_replace1(struct client *, char **, char **, char *, size_t, int);
42eda6f593SDavid van Moolenbroek void	status_message_callback(int, short, void *);
43eda6f593SDavid van Moolenbroek 
44eda6f593SDavid van Moolenbroek const char *status_prompt_up_history(u_int *);
45eda6f593SDavid van Moolenbroek const char *status_prompt_down_history(u_int *);
46eda6f593SDavid van Moolenbroek void	status_prompt_add_history(const char *);
47eda6f593SDavid van Moolenbroek char   *status_prompt_complete(const char *);
48eda6f593SDavid van Moolenbroek 
49eda6f593SDavid van Moolenbroek /* Status prompt history. */
50eda6f593SDavid van Moolenbroek ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER;
51eda6f593SDavid van Moolenbroek 
52eda6f593SDavid van Moolenbroek /* Status output tree. */
53eda6f593SDavid van Moolenbroek RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp);
54eda6f593SDavid van Moolenbroek 
55eda6f593SDavid van Moolenbroek /* Output tree comparison function. */
56eda6f593SDavid van Moolenbroek int
status_out_cmp(struct status_out * so1,struct status_out * so2)57eda6f593SDavid van Moolenbroek status_out_cmp(struct status_out *so1, struct status_out *so2)
58eda6f593SDavid van Moolenbroek {
59eda6f593SDavid van Moolenbroek 	return (strcmp(so1->cmd, so2->cmd));
60eda6f593SDavid van Moolenbroek }
61eda6f593SDavid van Moolenbroek 
62*0a6a1f1dSLionel Sambuc /* Get screen line of status line. -1 means off. */
63*0a6a1f1dSLionel Sambuc int
status_at_line(struct client * c)64*0a6a1f1dSLionel Sambuc status_at_line(struct client *c)
65*0a6a1f1dSLionel Sambuc {
66*0a6a1f1dSLionel Sambuc 	struct session	*s = c->session;
67*0a6a1f1dSLionel Sambuc 
68*0a6a1f1dSLionel Sambuc 	if (!options_get_number(&s->options, "status"))
69*0a6a1f1dSLionel Sambuc 		return (-1);
70*0a6a1f1dSLionel Sambuc 
71*0a6a1f1dSLionel Sambuc 	if (options_get_number(&s->options, "status-position") == 0)
72*0a6a1f1dSLionel Sambuc 		return (0);
73*0a6a1f1dSLionel Sambuc 	return (c->tty.sy - 1);
74*0a6a1f1dSLionel Sambuc }
75*0a6a1f1dSLionel Sambuc 
76eda6f593SDavid van Moolenbroek /* Retrieve options for left string. */
77eda6f593SDavid van Moolenbroek char *
status_redraw_get_left(struct client * c,time_t t,int utf8flag,struct grid_cell * gc,size_t * size)78eda6f593SDavid van Moolenbroek status_redraw_get_left(struct client *c,
79eda6f593SDavid van Moolenbroek     time_t t, int utf8flag, struct grid_cell *gc, size_t *size)
80eda6f593SDavid van Moolenbroek {
81eda6f593SDavid van Moolenbroek 	struct session	*s = c->session;
82eda6f593SDavid van Moolenbroek 	char		*left;
83eda6f593SDavid van Moolenbroek 	size_t		 leftlen;
84eda6f593SDavid van Moolenbroek 
85*0a6a1f1dSLionel Sambuc 	style_apply_update(gc, &s->options, "status-left-style");
86eda6f593SDavid van Moolenbroek 
87eda6f593SDavid van Moolenbroek 	left = status_replace(c, NULL,
88eda6f593SDavid van Moolenbroek 	    NULL, NULL, options_get_string(&s->options, "status-left"), t, 1);
89eda6f593SDavid van Moolenbroek 
90eda6f593SDavid van Moolenbroek 	*size = options_get_number(&s->options, "status-left-length");
91eda6f593SDavid van Moolenbroek 	leftlen = screen_write_cstrlen(utf8flag, "%s", left);
92eda6f593SDavid van Moolenbroek 	if (leftlen < *size)
93eda6f593SDavid van Moolenbroek 		*size = leftlen;
94eda6f593SDavid van Moolenbroek 	return (left);
95eda6f593SDavid van Moolenbroek }
96eda6f593SDavid van Moolenbroek 
97eda6f593SDavid van Moolenbroek /* Retrieve options for right string. */
98eda6f593SDavid van Moolenbroek char *
status_redraw_get_right(struct client * c,time_t t,int utf8flag,struct grid_cell * gc,size_t * size)99eda6f593SDavid van Moolenbroek status_redraw_get_right(struct client *c,
100eda6f593SDavid van Moolenbroek     time_t t, int utf8flag, struct grid_cell *gc, size_t *size)
101eda6f593SDavid van Moolenbroek {
102eda6f593SDavid van Moolenbroek 	struct session	*s = c->session;
103eda6f593SDavid van Moolenbroek 	char		*right;
104eda6f593SDavid van Moolenbroek 	size_t		 rightlen;
105eda6f593SDavid van Moolenbroek 
106*0a6a1f1dSLionel Sambuc 	style_apply_update(gc, &s->options, "status-right-style");
107eda6f593SDavid van Moolenbroek 
108eda6f593SDavid van Moolenbroek 	right = status_replace(c, NULL,
109eda6f593SDavid van Moolenbroek 	    NULL, NULL, options_get_string(&s->options, "status-right"), t, 1);
110eda6f593SDavid van Moolenbroek 
111eda6f593SDavid van Moolenbroek 	*size = options_get_number(&s->options, "status-right-length");
112eda6f593SDavid van Moolenbroek 	rightlen = screen_write_cstrlen(utf8flag, "%s", right);
113eda6f593SDavid van Moolenbroek 	if (rightlen < *size)
114eda6f593SDavid van Moolenbroek 		*size = rightlen;
115eda6f593SDavid van Moolenbroek 	return (right);
116eda6f593SDavid van Moolenbroek }
117eda6f593SDavid van Moolenbroek 
118eda6f593SDavid van Moolenbroek /* Set window at window list position. */
119eda6f593SDavid van Moolenbroek void
status_set_window_at(struct client * c,u_int x)120eda6f593SDavid van Moolenbroek status_set_window_at(struct client *c, u_int x)
121eda6f593SDavid van Moolenbroek {
122eda6f593SDavid van Moolenbroek 	struct session	*s = c->session;
123eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
124eda6f593SDavid van Moolenbroek 
125*0a6a1f1dSLionel Sambuc 	x += c->wlmouse;
126eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, &s->windows) {
127*0a6a1f1dSLionel Sambuc 		if (x < wl->status_width && session_select(s, wl->idx) == 0)
128eda6f593SDavid van Moolenbroek 			server_redraw_session(s);
129eda6f593SDavid van Moolenbroek 		x -= wl->status_width + 1;
130eda6f593SDavid van Moolenbroek 	}
131eda6f593SDavid van Moolenbroek }
132eda6f593SDavid van Moolenbroek 
133eda6f593SDavid van Moolenbroek /* Draw status for client on the last lines of given context. */
134eda6f593SDavid van Moolenbroek int
status_redraw(struct client * c)135eda6f593SDavid van Moolenbroek status_redraw(struct client *c)
136eda6f593SDavid van Moolenbroek {
137eda6f593SDavid van Moolenbroek 	struct screen_write_ctx	ctx;
138eda6f593SDavid van Moolenbroek 	struct session	       *s = c->session;
139eda6f593SDavid van Moolenbroek 	struct winlink	       *wl;
140eda6f593SDavid van Moolenbroek 	struct screen		old_status, window_list;
141eda6f593SDavid van Moolenbroek 	struct grid_cell	stdgc, lgc, rgc, gc;
142*0a6a1f1dSLionel Sambuc 	struct options	       *oo;
143eda6f593SDavid van Moolenbroek 	time_t			t;
144*0a6a1f1dSLionel Sambuc 	char		       *left, *right, *sep;
145eda6f593SDavid van Moolenbroek 	u_int			offset, needed;
146eda6f593SDavid van Moolenbroek 	u_int			wlstart, wlwidth, wlavailable, wloffset, wlsize;
147*0a6a1f1dSLionel Sambuc 	size_t			llen, rlen, seplen;
148eda6f593SDavid van Moolenbroek 	int			larrow, rarrow, utf8flag;
149eda6f593SDavid van Moolenbroek 
150eda6f593SDavid van Moolenbroek 	/* No status line? */
151eda6f593SDavid van Moolenbroek 	if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
152eda6f593SDavid van Moolenbroek 		return (1);
153eda6f593SDavid van Moolenbroek 	left = right = NULL;
154eda6f593SDavid van Moolenbroek 	larrow = rarrow = 0;
155eda6f593SDavid van Moolenbroek 
156eda6f593SDavid van Moolenbroek 	/* Update status timer. */
157eda6f593SDavid van Moolenbroek 	if (gettimeofday(&c->status_timer, NULL) != 0)
158eda6f593SDavid van Moolenbroek 		fatal("gettimeofday failed");
159eda6f593SDavid van Moolenbroek 	t = c->status_timer.tv_sec;
160eda6f593SDavid van Moolenbroek 
161eda6f593SDavid van Moolenbroek 	/* Set up default colour. */
162*0a6a1f1dSLionel Sambuc 	style_apply(&stdgc, &s->options, "status-style");
163eda6f593SDavid van Moolenbroek 
164eda6f593SDavid van Moolenbroek 	/* Create the target screen. */
165eda6f593SDavid van Moolenbroek 	memcpy(&old_status, &c->status, sizeof old_status);
166eda6f593SDavid van Moolenbroek 	screen_init(&c->status, c->tty.sx, 1, 0);
167eda6f593SDavid van Moolenbroek 	screen_write_start(&ctx, NULL, &c->status);
168eda6f593SDavid van Moolenbroek 	for (offset = 0; offset < c->tty.sx; offset++)
169eda6f593SDavid van Moolenbroek 		screen_write_putc(&ctx, &stdgc, ' ');
170eda6f593SDavid van Moolenbroek 	screen_write_stop(&ctx);
171eda6f593SDavid van Moolenbroek 
172eda6f593SDavid van Moolenbroek 	/* If the height is one line, blank status line. */
173eda6f593SDavid van Moolenbroek 	if (c->tty.sy <= 1)
174eda6f593SDavid van Moolenbroek 		goto out;
175eda6f593SDavid van Moolenbroek 
176eda6f593SDavid van Moolenbroek 	/* Get UTF-8 flag. */
177eda6f593SDavid van Moolenbroek 	utf8flag = options_get_number(&s->options, "status-utf8");
178eda6f593SDavid van Moolenbroek 
179eda6f593SDavid van Moolenbroek 	/* Work out left and right strings. */
180eda6f593SDavid van Moolenbroek 	memcpy(&lgc, &stdgc, sizeof lgc);
181eda6f593SDavid van Moolenbroek 	left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen);
182eda6f593SDavid van Moolenbroek 	memcpy(&rgc, &stdgc, sizeof rgc);
183eda6f593SDavid van Moolenbroek 	right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen);
184eda6f593SDavid van Moolenbroek 
185eda6f593SDavid van Moolenbroek 	/*
186eda6f593SDavid van Moolenbroek 	 * Figure out how much space we have for the window list. If there
187eda6f593SDavid van Moolenbroek 	 * isn't enough space, just show a blank status line.
188eda6f593SDavid van Moolenbroek 	 */
189eda6f593SDavid van Moolenbroek 	needed = 0;
190eda6f593SDavid van Moolenbroek 	if (llen != 0)
191eda6f593SDavid van Moolenbroek 		needed += llen + 1;
192eda6f593SDavid van Moolenbroek 	if (rlen != 0)
193eda6f593SDavid van Moolenbroek 		needed += rlen + 1;
194eda6f593SDavid van Moolenbroek 	if (c->tty.sx == 0 || c->tty.sx <= needed)
195eda6f593SDavid van Moolenbroek 		goto out;
196eda6f593SDavid van Moolenbroek 	wlavailable = c->tty.sx - needed;
197eda6f593SDavid van Moolenbroek 
198eda6f593SDavid van Moolenbroek 	/* Calculate the total size needed for the window list. */
199eda6f593SDavid van Moolenbroek 	wlstart = wloffset = wlwidth = 0;
200eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, &s->windows) {
201*0a6a1f1dSLionel Sambuc 		free(wl->status_text);
202eda6f593SDavid van Moolenbroek 		memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
203eda6f593SDavid van Moolenbroek 		wl->status_text = status_print(c, wl, t, &wl->status_cell);
204eda6f593SDavid van Moolenbroek 		wl->status_width =
205eda6f593SDavid van Moolenbroek 		    screen_write_cstrlen(utf8flag, "%s", wl->status_text);
206eda6f593SDavid van Moolenbroek 
207eda6f593SDavid van Moolenbroek 		if (wl == s->curw)
208eda6f593SDavid van Moolenbroek 			wloffset = wlwidth;
209*0a6a1f1dSLionel Sambuc 
210*0a6a1f1dSLionel Sambuc 		oo = &wl->window->options;
211*0a6a1f1dSLionel Sambuc 		sep = options_get_string(oo, "window-status-separator");
212*0a6a1f1dSLionel Sambuc 		seplen = screen_write_strlen(utf8flag, "%s", sep);
213*0a6a1f1dSLionel Sambuc 		wlwidth += wl->status_width + seplen;
214eda6f593SDavid van Moolenbroek 	}
215eda6f593SDavid van Moolenbroek 
216eda6f593SDavid van Moolenbroek 	/* Create a new screen for the window list. */
217eda6f593SDavid van Moolenbroek 	screen_init(&window_list, wlwidth, 1, 0);
218eda6f593SDavid van Moolenbroek 
219eda6f593SDavid van Moolenbroek 	/* And draw the window list into it. */
220eda6f593SDavid van Moolenbroek 	screen_write_start(&ctx, NULL, &window_list);
221eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, &s->windows) {
222eda6f593SDavid van Moolenbroek 		screen_write_cnputs(&ctx,
223eda6f593SDavid van Moolenbroek 		    -1, &wl->status_cell, utf8flag, "%s", wl->status_text);
224*0a6a1f1dSLionel Sambuc 
225*0a6a1f1dSLionel Sambuc 		oo = &wl->window->options;
226*0a6a1f1dSLionel Sambuc 		sep = options_get_string(oo, "window-status-separator");
227*0a6a1f1dSLionel Sambuc 		screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep);
228eda6f593SDavid van Moolenbroek 	}
229eda6f593SDavid van Moolenbroek 	screen_write_stop(&ctx);
230eda6f593SDavid van Moolenbroek 
231eda6f593SDavid van Moolenbroek 	/* If there is enough space for the total width, skip to draw now. */
232eda6f593SDavid van Moolenbroek 	if (wlwidth <= wlavailable)
233eda6f593SDavid van Moolenbroek 		goto draw;
234eda6f593SDavid van Moolenbroek 
235eda6f593SDavid van Moolenbroek 	/* Find size of current window text. */
236eda6f593SDavid van Moolenbroek 	wlsize = s->curw->status_width;
237eda6f593SDavid van Moolenbroek 
238eda6f593SDavid van Moolenbroek 	/*
239eda6f593SDavid van Moolenbroek 	 * If the current window is already on screen, good to draw from the
240eda6f593SDavid van Moolenbroek 	 * start and just leave off the end.
241eda6f593SDavid van Moolenbroek 	 */
242eda6f593SDavid van Moolenbroek 	if (wloffset + wlsize < wlavailable) {
243eda6f593SDavid van Moolenbroek 		if (wlavailable > 0) {
244eda6f593SDavid van Moolenbroek 			rarrow = 1;
245eda6f593SDavid van Moolenbroek 			wlavailable--;
246eda6f593SDavid van Moolenbroek 		}
247eda6f593SDavid van Moolenbroek 		wlwidth = wlavailable;
248eda6f593SDavid van Moolenbroek 	} else {
249eda6f593SDavid van Moolenbroek 		/*
250eda6f593SDavid van Moolenbroek 		 * Work out how many characters we need to omit from the
251eda6f593SDavid van Moolenbroek 		 * start. There are wlavailable characters to fill, and
252eda6f593SDavid van Moolenbroek 		 * wloffset + wlsize must be the last. So, the start character
253eda6f593SDavid van Moolenbroek 		 * is wloffset + wlsize - wlavailable.
254eda6f593SDavid van Moolenbroek 		 */
255eda6f593SDavid van Moolenbroek 		if (wlavailable > 0) {
256eda6f593SDavid van Moolenbroek 			larrow = 1;
257eda6f593SDavid van Moolenbroek 			wlavailable--;
258eda6f593SDavid van Moolenbroek 		}
259eda6f593SDavid van Moolenbroek 
260eda6f593SDavid van Moolenbroek 		wlstart = wloffset + wlsize - wlavailable;
261eda6f593SDavid van Moolenbroek 		if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
262eda6f593SDavid van Moolenbroek 			rarrow = 1;
263eda6f593SDavid van Moolenbroek 			wlstart++;
264eda6f593SDavid van Moolenbroek 			wlavailable--;
265eda6f593SDavid van Moolenbroek 		}
266eda6f593SDavid van Moolenbroek 		wlwidth = wlavailable;
267eda6f593SDavid van Moolenbroek 	}
268eda6f593SDavid van Moolenbroek 
269eda6f593SDavid van Moolenbroek 	/* Bail if anything is now too small too. */
270eda6f593SDavid van Moolenbroek 	if (wlwidth == 0 || wlavailable == 0) {
271eda6f593SDavid van Moolenbroek 		screen_free(&window_list);
272eda6f593SDavid van Moolenbroek 		goto out;
273eda6f593SDavid van Moolenbroek 	}
274eda6f593SDavid van Moolenbroek 
275eda6f593SDavid van Moolenbroek 	/*
276eda6f593SDavid van Moolenbroek 	 * Now the start position is known, work out the state of the left and
277eda6f593SDavid van Moolenbroek 	 * right arrows.
278eda6f593SDavid van Moolenbroek 	 */
279eda6f593SDavid van Moolenbroek 	offset = 0;
280eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, &s->windows) {
281eda6f593SDavid van Moolenbroek 		if (wl->flags & WINLINK_ALERTFLAGS &&
282eda6f593SDavid van Moolenbroek 		    larrow == 1 && offset < wlstart)
283eda6f593SDavid van Moolenbroek 			larrow = -1;
284eda6f593SDavid van Moolenbroek 
285eda6f593SDavid van Moolenbroek 		offset += wl->status_width;
286eda6f593SDavid van Moolenbroek 
287eda6f593SDavid van Moolenbroek 		if (wl->flags & WINLINK_ALERTFLAGS &&
288eda6f593SDavid van Moolenbroek 		    rarrow == 1 && offset > wlstart + wlwidth)
289eda6f593SDavid van Moolenbroek 			rarrow = -1;
290eda6f593SDavid van Moolenbroek 	}
291eda6f593SDavid van Moolenbroek 
292eda6f593SDavid van Moolenbroek draw:
293eda6f593SDavid van Moolenbroek 	/* Begin drawing. */
294eda6f593SDavid van Moolenbroek 	screen_write_start(&ctx, NULL, &c->status);
295eda6f593SDavid van Moolenbroek 
296eda6f593SDavid van Moolenbroek 	/* Draw the left string and arrow. */
297eda6f593SDavid van Moolenbroek 	screen_write_cursormove(&ctx, 0, 0);
298eda6f593SDavid van Moolenbroek 	if (llen != 0) {
299eda6f593SDavid van Moolenbroek 		screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left);
300eda6f593SDavid van Moolenbroek 		screen_write_putc(&ctx, &stdgc, ' ');
301eda6f593SDavid van Moolenbroek 	}
302eda6f593SDavid van Moolenbroek 	if (larrow != 0) {
303eda6f593SDavid van Moolenbroek 		memcpy(&gc, &stdgc, sizeof gc);
304eda6f593SDavid van Moolenbroek 		if (larrow == -1)
305eda6f593SDavid van Moolenbroek 			gc.attr ^= GRID_ATTR_REVERSE;
306eda6f593SDavid van Moolenbroek 		screen_write_putc(&ctx, &gc, '<');
307eda6f593SDavid van Moolenbroek 	}
308eda6f593SDavid van Moolenbroek 
309eda6f593SDavid van Moolenbroek 	/* Draw the right string and arrow. */
310eda6f593SDavid van Moolenbroek 	if (rarrow != 0) {
311eda6f593SDavid van Moolenbroek 		screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, 0);
312eda6f593SDavid van Moolenbroek 		memcpy(&gc, &stdgc, sizeof gc);
313eda6f593SDavid van Moolenbroek 		if (rarrow == -1)
314eda6f593SDavid van Moolenbroek 			gc.attr ^= GRID_ATTR_REVERSE;
315eda6f593SDavid van Moolenbroek 		screen_write_putc(&ctx, &gc, '>');
316eda6f593SDavid van Moolenbroek 	} else
317eda6f593SDavid van Moolenbroek 		screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
318eda6f593SDavid van Moolenbroek 	if (rlen != 0) {
319eda6f593SDavid van Moolenbroek 		screen_write_putc(&ctx, &stdgc, ' ');
320eda6f593SDavid van Moolenbroek 		screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right);
321eda6f593SDavid van Moolenbroek 	}
322eda6f593SDavid van Moolenbroek 
323eda6f593SDavid van Moolenbroek 	/* Figure out the offset for the window list. */
324eda6f593SDavid van Moolenbroek 	if (llen != 0)
325eda6f593SDavid van Moolenbroek 		wloffset = llen + 1;
326eda6f593SDavid van Moolenbroek 	else
327eda6f593SDavid van Moolenbroek 		wloffset = 0;
328eda6f593SDavid van Moolenbroek 	if (wlwidth < wlavailable) {
329eda6f593SDavid van Moolenbroek 		switch (options_get_number(&s->options, "status-justify")) {
330eda6f593SDavid van Moolenbroek 		case 1:	/* centered */
331eda6f593SDavid van Moolenbroek 			wloffset += (wlavailable - wlwidth) / 2;
332eda6f593SDavid van Moolenbroek 			break;
333eda6f593SDavid van Moolenbroek 		case 2:	/* right */
334eda6f593SDavid van Moolenbroek 			wloffset += (wlavailable - wlwidth);
335eda6f593SDavid van Moolenbroek 			break;
336eda6f593SDavid van Moolenbroek 		}
337eda6f593SDavid van Moolenbroek 	}
338eda6f593SDavid van Moolenbroek 	if (larrow != 0)
339eda6f593SDavid van Moolenbroek 		wloffset++;
340eda6f593SDavid van Moolenbroek 
341eda6f593SDavid van Moolenbroek 	/* Copy the window list. */
342*0a6a1f1dSLionel Sambuc 	c->wlmouse = -wloffset + wlstart;
343eda6f593SDavid van Moolenbroek 	screen_write_cursormove(&ctx, wloffset, 0);
344eda6f593SDavid van Moolenbroek 	screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
345eda6f593SDavid van Moolenbroek 	screen_free(&window_list);
346eda6f593SDavid van Moolenbroek 
347eda6f593SDavid van Moolenbroek 	screen_write_stop(&ctx);
348eda6f593SDavid van Moolenbroek 
349eda6f593SDavid van Moolenbroek out:
350*0a6a1f1dSLionel Sambuc 	free(left);
351*0a6a1f1dSLionel Sambuc 	free(right);
352eda6f593SDavid van Moolenbroek 
353eda6f593SDavid van Moolenbroek 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
354eda6f593SDavid van Moolenbroek 		screen_free(&old_status);
355eda6f593SDavid van Moolenbroek 		return (0);
356eda6f593SDavid van Moolenbroek 	}
357eda6f593SDavid van Moolenbroek 	screen_free(&old_status);
358eda6f593SDavid van Moolenbroek 	return (1);
359eda6f593SDavid van Moolenbroek }
360eda6f593SDavid van Moolenbroek 
361eda6f593SDavid van Moolenbroek /* Replace a single special sequence (prefixed by #). */
362eda6f593SDavid van Moolenbroek void
status_replace1(struct client * c,char ** iptr,char ** optr,char * out,size_t outsize,int jobsflag)363*0a6a1f1dSLionel Sambuc status_replace1(struct client *c, char **iptr, char **optr, char *out,
364eda6f593SDavid van Moolenbroek     size_t outsize, int jobsflag)
365eda6f593SDavid van Moolenbroek {
366*0a6a1f1dSLionel Sambuc 	char	ch, tmp[256], *ptr, *endptr;
367eda6f593SDavid van Moolenbroek 	size_t	ptrlen;
368eda6f593SDavid van Moolenbroek 	long	limit;
369eda6f593SDavid van Moolenbroek 
370eda6f593SDavid van Moolenbroek 	errno = 0;
371eda6f593SDavid van Moolenbroek 	limit = strtol(*iptr, &endptr, 10);
372eda6f593SDavid van Moolenbroek 	if ((limit == 0 && errno != EINVAL) ||
373eda6f593SDavid van Moolenbroek 	    (limit == LONG_MIN && errno != ERANGE) ||
374eda6f593SDavid van Moolenbroek 	    (limit == LONG_MAX && errno != ERANGE) ||
375eda6f593SDavid van Moolenbroek 	    limit != 0)
376eda6f593SDavid van Moolenbroek 		*iptr = endptr;
377eda6f593SDavid van Moolenbroek 	if (limit <= 0)
378eda6f593SDavid van Moolenbroek 		limit = LONG_MAX;
379eda6f593SDavid van Moolenbroek 
380eda6f593SDavid van Moolenbroek 	switch (*(*iptr)++) {
381eda6f593SDavid van Moolenbroek 	case '(':
382eda6f593SDavid van Moolenbroek 		if (!jobsflag) {
383eda6f593SDavid van Moolenbroek 			ch = ')';
384eda6f593SDavid van Moolenbroek 			goto skip_to;
385eda6f593SDavid van Moolenbroek 		}
386eda6f593SDavid van Moolenbroek 		if ((ptr = status_find_job(c, iptr)) == NULL)
387eda6f593SDavid van Moolenbroek 			return;
388eda6f593SDavid van Moolenbroek 		goto do_replace;
389eda6f593SDavid van Moolenbroek 	case '[':
390eda6f593SDavid van Moolenbroek 		/*
391eda6f593SDavid van Moolenbroek 		 * Embedded style, handled at display time. Leave present and
392eda6f593SDavid van Moolenbroek 		 * skip input until ].
393eda6f593SDavid van Moolenbroek 		 */
394eda6f593SDavid van Moolenbroek 		ch = ']';
395eda6f593SDavid van Moolenbroek 		goto skip_to;
396*0a6a1f1dSLionel Sambuc 	case '{':
397*0a6a1f1dSLionel Sambuc 		ptr = __UNCONST("#{");
398*0a6a1f1dSLionel Sambuc 		goto do_replace;
399eda6f593SDavid van Moolenbroek 	case '#':
400eda6f593SDavid van Moolenbroek 		*(*optr)++ = '#';
401eda6f593SDavid van Moolenbroek 		break;
402*0a6a1f1dSLionel Sambuc 	default:
403*0a6a1f1dSLionel Sambuc 		xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1));
404*0a6a1f1dSLionel Sambuc 		ptr = tmp;
405*0a6a1f1dSLionel Sambuc 		goto do_replace;
406eda6f593SDavid van Moolenbroek 	}
407eda6f593SDavid van Moolenbroek 
408eda6f593SDavid van Moolenbroek 	return;
409eda6f593SDavid van Moolenbroek 
410eda6f593SDavid van Moolenbroek do_replace:
411eda6f593SDavid van Moolenbroek 	ptrlen = strlen(ptr);
412eda6f593SDavid van Moolenbroek 	if ((size_t) limit < ptrlen)
413eda6f593SDavid van Moolenbroek 		ptrlen = limit;
414eda6f593SDavid van Moolenbroek 
415eda6f593SDavid van Moolenbroek 	if (*optr + ptrlen >= out + outsize - 1)
416eda6f593SDavid van Moolenbroek 		return;
417eda6f593SDavid van Moolenbroek 	while (ptrlen > 0 && *ptr != '\0') {
418eda6f593SDavid van Moolenbroek 		*(*optr)++ = *ptr++;
419eda6f593SDavid van Moolenbroek 		ptrlen--;
420eda6f593SDavid van Moolenbroek 	}
421eda6f593SDavid van Moolenbroek 
422eda6f593SDavid van Moolenbroek 	return;
423eda6f593SDavid van Moolenbroek 
424eda6f593SDavid van Moolenbroek skip_to:
425eda6f593SDavid van Moolenbroek 	*(*optr)++ = '#';
426eda6f593SDavid van Moolenbroek 
427eda6f593SDavid van Moolenbroek 	(*iptr)--;	/* include ch */
428eda6f593SDavid van Moolenbroek 	while (**iptr != ch && **iptr != '\0') {
429eda6f593SDavid van Moolenbroek 		if (*optr >=  out + outsize - 1)
430eda6f593SDavid van Moolenbroek 			break;
431eda6f593SDavid van Moolenbroek 		*(*optr)++ = *(*iptr)++;
432eda6f593SDavid van Moolenbroek 	}
433eda6f593SDavid van Moolenbroek }
434eda6f593SDavid van Moolenbroek 
435eda6f593SDavid van Moolenbroek /* Replace special sequences in fmt. */
436eda6f593SDavid van Moolenbroek char *
status_replace(struct client * c,struct session * s,struct winlink * wl,struct window_pane * wp,const char * fmt,time_t t,int jobsflag)437eda6f593SDavid van Moolenbroek status_replace(struct client *c, struct session *s, struct winlink *wl,
438eda6f593SDavid van Moolenbroek     struct window_pane *wp, const char *fmt, time_t t, int jobsflag)
439eda6f593SDavid van Moolenbroek {
440eda6f593SDavid van Moolenbroek 	static char		 out[BUFSIZ];
441*0a6a1f1dSLionel Sambuc 	char			 in[BUFSIZ], ch, *iptr, *optr, *expanded;
442*0a6a1f1dSLionel Sambuc 	size_t			 len;
443*0a6a1f1dSLionel Sambuc 	struct format_tree	*ft;
444eda6f593SDavid van Moolenbroek 
445*0a6a1f1dSLionel Sambuc 	if (fmt == NULL)
446*0a6a1f1dSLionel Sambuc 		return (xstrdup(""));
447*0a6a1f1dSLionel Sambuc 
448*0a6a1f1dSLionel Sambuc 	if (s == NULL && c != NULL)
449*0a6a1f1dSLionel Sambuc 		s = c->session;
450*0a6a1f1dSLionel Sambuc 	if (wl == NULL && s != NULL)
451*0a6a1f1dSLionel Sambuc 		wl = s->curw;
452*0a6a1f1dSLionel Sambuc 	if (wp == NULL && wl != NULL)
453*0a6a1f1dSLionel Sambuc 		wp = wl->window->active;
454*0a6a1f1dSLionel Sambuc 
455*0a6a1f1dSLionel Sambuc 	len = strftime(in, sizeof in, fmt, localtime(&t));
456*0a6a1f1dSLionel Sambuc 	in[len] = '\0';
457eda6f593SDavid van Moolenbroek 
458eda6f593SDavid van Moolenbroek 	iptr = in;
459eda6f593SDavid van Moolenbroek 	optr = out;
460eda6f593SDavid van Moolenbroek 
461eda6f593SDavid van Moolenbroek 	while (*iptr != '\0') {
462eda6f593SDavid van Moolenbroek 		if (optr >= out + (sizeof out) - 1)
463eda6f593SDavid van Moolenbroek 			break;
464eda6f593SDavid van Moolenbroek 		ch = *iptr++;
465eda6f593SDavid van Moolenbroek 
466eda6f593SDavid van Moolenbroek 		if (ch != '#' || *iptr == '\0') {
467eda6f593SDavid van Moolenbroek 			*optr++ = ch;
468eda6f593SDavid van Moolenbroek 			continue;
469eda6f593SDavid van Moolenbroek 		}
470*0a6a1f1dSLionel Sambuc 		status_replace1(c, &iptr, &optr, out, sizeof out, jobsflag);
471eda6f593SDavid van Moolenbroek 	}
472eda6f593SDavid van Moolenbroek 	*optr = '\0';
473eda6f593SDavid van Moolenbroek 
474*0a6a1f1dSLionel Sambuc 	ft = format_create();
475*0a6a1f1dSLionel Sambuc 	if (c != NULL)
476*0a6a1f1dSLionel Sambuc 		format_client(ft, c);
477*0a6a1f1dSLionel Sambuc 	if (s != NULL)
478*0a6a1f1dSLionel Sambuc 		format_session(ft, s);
479*0a6a1f1dSLionel Sambuc 	if (s != NULL && wl != NULL)
480*0a6a1f1dSLionel Sambuc 		format_winlink(ft, s, wl);
481*0a6a1f1dSLionel Sambuc 	if (wp != NULL)
482*0a6a1f1dSLionel Sambuc 		format_window_pane(ft, wp);
483*0a6a1f1dSLionel Sambuc 	expanded = format_expand(ft, out);
484*0a6a1f1dSLionel Sambuc 	format_free(ft);
485*0a6a1f1dSLionel Sambuc 	return (expanded);
486eda6f593SDavid van Moolenbroek }
487eda6f593SDavid van Moolenbroek 
488eda6f593SDavid van Moolenbroek /* Figure out job name and get its result, starting it off if necessary. */
489eda6f593SDavid van Moolenbroek char *
status_find_job(struct client * c,char ** iptr)490eda6f593SDavid van Moolenbroek status_find_job(struct client *c, char **iptr)
491eda6f593SDavid van Moolenbroek {
492eda6f593SDavid van Moolenbroek 	struct status_out	*so, so_find;
493eda6f593SDavid van Moolenbroek 	char   			*cmd;
494eda6f593SDavid van Moolenbroek 	int			 lastesc;
495eda6f593SDavid van Moolenbroek 	size_t			 len;
496eda6f593SDavid van Moolenbroek 
497eda6f593SDavid van Moolenbroek 	if (**iptr == '\0')
498eda6f593SDavid van Moolenbroek 		return (NULL);
499eda6f593SDavid van Moolenbroek 	if (**iptr == ')') {		/* no command given */
500eda6f593SDavid van Moolenbroek 		(*iptr)++;
501eda6f593SDavid van Moolenbroek 		return (NULL);
502eda6f593SDavid van Moolenbroek 	}
503eda6f593SDavid van Moolenbroek 
504eda6f593SDavid van Moolenbroek 	cmd = xmalloc(strlen(*iptr) + 1);
505eda6f593SDavid van Moolenbroek 	len = 0;
506eda6f593SDavid van Moolenbroek 
507eda6f593SDavid van Moolenbroek 	lastesc = 0;
508eda6f593SDavid van Moolenbroek 	for (; **iptr != '\0'; (*iptr)++) {
509eda6f593SDavid van Moolenbroek 		if (!lastesc && **iptr == ')')
510eda6f593SDavid van Moolenbroek 			break;		/* unescaped ) is the end */
511eda6f593SDavid van Moolenbroek 		if (!lastesc && **iptr == '\\') {
512eda6f593SDavid van Moolenbroek 			lastesc = 1;
513eda6f593SDavid van Moolenbroek 			continue;	/* skip \ if not escaped */
514eda6f593SDavid van Moolenbroek 		}
515eda6f593SDavid van Moolenbroek 		lastesc = 0;
516eda6f593SDavid van Moolenbroek 		cmd[len++] = **iptr;
517eda6f593SDavid van Moolenbroek 	}
518eda6f593SDavid van Moolenbroek 	if (**iptr == '\0')		/* no terminating ) */ {
519*0a6a1f1dSLionel Sambuc 		free(cmd);
520eda6f593SDavid van Moolenbroek 		return (NULL);
521eda6f593SDavid van Moolenbroek 	}
522eda6f593SDavid van Moolenbroek 	(*iptr)++;			/* skip final ) */
523eda6f593SDavid van Moolenbroek 	cmd[len] = '\0';
524eda6f593SDavid van Moolenbroek 
525eda6f593SDavid van Moolenbroek 	/* First try in the new tree. */
526eda6f593SDavid van Moolenbroek 	so_find.cmd = cmd;
527eda6f593SDavid van Moolenbroek 	so = RB_FIND(status_out_tree, &c->status_new, &so_find);
528*0a6a1f1dSLionel Sambuc 	if (so != NULL && so->out != NULL) {
529*0a6a1f1dSLionel Sambuc 		free(cmd);
530eda6f593SDavid van Moolenbroek 		return (so->out);
531*0a6a1f1dSLionel Sambuc 	}
532eda6f593SDavid van Moolenbroek 
533eda6f593SDavid van Moolenbroek 	/* If not found at all, start the job and add to the tree. */
534eda6f593SDavid van Moolenbroek 	if (so == NULL) {
535*0a6a1f1dSLionel Sambuc 		job_run(cmd, NULL, status_job_callback, status_job_free, c);
536eda6f593SDavid van Moolenbroek 		c->references++;
537eda6f593SDavid van Moolenbroek 
538eda6f593SDavid van Moolenbroek 		so = xmalloc(sizeof *so);
539eda6f593SDavid van Moolenbroek 		so->cmd = xstrdup(cmd);
540eda6f593SDavid van Moolenbroek 		so->out = NULL;
541eda6f593SDavid van Moolenbroek 		RB_INSERT(status_out_tree, &c->status_new, so);
542eda6f593SDavid van Moolenbroek 	}
543eda6f593SDavid van Moolenbroek 
544eda6f593SDavid van Moolenbroek 	/* Lookup in the old tree. */
545eda6f593SDavid van Moolenbroek 	so_find.cmd = cmd;
546eda6f593SDavid van Moolenbroek 	so = RB_FIND(status_out_tree, &c->status_old, &so_find);
547*0a6a1f1dSLionel Sambuc 	free(cmd);
548eda6f593SDavid van Moolenbroek 	if (so != NULL)
549eda6f593SDavid van Moolenbroek 		return (so->out);
550eda6f593SDavid van Moolenbroek 	return (NULL);
551eda6f593SDavid van Moolenbroek }
552eda6f593SDavid van Moolenbroek 
553eda6f593SDavid van Moolenbroek /* Free job tree. */
554eda6f593SDavid van Moolenbroek void
status_free_jobs(struct status_out_tree * sotree)555eda6f593SDavid van Moolenbroek status_free_jobs(struct status_out_tree *sotree)
556eda6f593SDavid van Moolenbroek {
557eda6f593SDavid van Moolenbroek 	struct status_out	*so, *so_next;
558eda6f593SDavid van Moolenbroek 
559eda6f593SDavid van Moolenbroek 	so_next = RB_MIN(status_out_tree, sotree);
560eda6f593SDavid van Moolenbroek 	while (so_next != NULL) {
561eda6f593SDavid van Moolenbroek 		so = so_next;
562eda6f593SDavid van Moolenbroek 		so_next = RB_NEXT(status_out_tree, sotree, so);
563eda6f593SDavid van Moolenbroek 
564eda6f593SDavid van Moolenbroek 		RB_REMOVE(status_out_tree, sotree, so);
565*0a6a1f1dSLionel Sambuc 		free(so->out);
566*0a6a1f1dSLionel Sambuc 		free(so->cmd);
567*0a6a1f1dSLionel Sambuc 		free(so);
568eda6f593SDavid van Moolenbroek 	}
569eda6f593SDavid van Moolenbroek }
570eda6f593SDavid van Moolenbroek 
571eda6f593SDavid van Moolenbroek /* Update jobs on status interval. */
572eda6f593SDavid van Moolenbroek void
status_update_jobs(struct client * c)573eda6f593SDavid van Moolenbroek status_update_jobs(struct client *c)
574eda6f593SDavid van Moolenbroek {
575eda6f593SDavid van Moolenbroek 	/* Free the old tree. */
576eda6f593SDavid van Moolenbroek 	status_free_jobs(&c->status_old);
577eda6f593SDavid van Moolenbroek 
578eda6f593SDavid van Moolenbroek 	/* Move the new to old. */
579eda6f593SDavid van Moolenbroek 	memcpy(&c->status_old, &c->status_new, sizeof c->status_old);
580eda6f593SDavid van Moolenbroek 	RB_INIT(&c->status_new);
581eda6f593SDavid van Moolenbroek }
582eda6f593SDavid van Moolenbroek 
583eda6f593SDavid van Moolenbroek /* Free status job. */
584eda6f593SDavid van Moolenbroek void
status_job_free(void * data)585eda6f593SDavid van Moolenbroek status_job_free(void *data)
586eda6f593SDavid van Moolenbroek {
587eda6f593SDavid van Moolenbroek 	struct client	*c = data;
588eda6f593SDavid van Moolenbroek 
589eda6f593SDavid van Moolenbroek 	c->references--;
590eda6f593SDavid van Moolenbroek }
591eda6f593SDavid van Moolenbroek 
592eda6f593SDavid van Moolenbroek /* Job has finished: save its result. */
593eda6f593SDavid van Moolenbroek void
status_job_callback(struct job * job)594eda6f593SDavid van Moolenbroek status_job_callback(struct job *job)
595eda6f593SDavid van Moolenbroek {
596eda6f593SDavid van Moolenbroek 	struct client		*c = job->data;
597eda6f593SDavid van Moolenbroek 	struct status_out	*so, so_find;
598eda6f593SDavid van Moolenbroek 	char			*line, *buf;
599eda6f593SDavid van Moolenbroek 	size_t			 len;
600eda6f593SDavid van Moolenbroek 
601eda6f593SDavid van Moolenbroek 	if (c->flags & CLIENT_DEAD)
602eda6f593SDavid van Moolenbroek 		return;
603eda6f593SDavid van Moolenbroek 
604eda6f593SDavid van Moolenbroek 	so_find.cmd = job->cmd;
605eda6f593SDavid van Moolenbroek 	so = RB_FIND(status_out_tree, &c->status_new, &so_find);
606eda6f593SDavid van Moolenbroek 	if (so == NULL || so->out != NULL)
607eda6f593SDavid van Moolenbroek 		return;
608eda6f593SDavid van Moolenbroek 
609eda6f593SDavid van Moolenbroek 	buf = NULL;
610eda6f593SDavid van Moolenbroek 	if ((line = evbuffer_readline(job->event->input)) == NULL) {
611eda6f593SDavid van Moolenbroek 		len = EVBUFFER_LENGTH(job->event->input);
612eda6f593SDavid van Moolenbroek 		buf = xmalloc(len + 1);
613eda6f593SDavid van Moolenbroek 		if (len != 0)
614eda6f593SDavid van Moolenbroek 			memcpy(buf, EVBUFFER_DATA(job->event->input), len);
615eda6f593SDavid van Moolenbroek 		buf[len] = '\0';
616eda6f593SDavid van Moolenbroek 	} else
617*0a6a1f1dSLionel Sambuc 		buf = line;
618eda6f593SDavid van Moolenbroek 
619eda6f593SDavid van Moolenbroek 	so->out = buf;
620eda6f593SDavid van Moolenbroek 	server_status_client(c);
621eda6f593SDavid van Moolenbroek }
622eda6f593SDavid van Moolenbroek 
623eda6f593SDavid van Moolenbroek /* Return winlink status line entry and adjust gc as necessary. */
624eda6f593SDavid van Moolenbroek char *
status_print(struct client * c,struct winlink * wl,time_t t,struct grid_cell * gc)625eda6f593SDavid van Moolenbroek status_print(
626eda6f593SDavid van Moolenbroek     struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc)
627eda6f593SDavid van Moolenbroek {
628eda6f593SDavid van Moolenbroek 	struct options	*oo = &wl->window->options;
629eda6f593SDavid van Moolenbroek 	struct session	*s = c->session;
630eda6f593SDavid van Moolenbroek 	const char	*fmt;
631eda6f593SDavid van Moolenbroek 	char   		*text;
632eda6f593SDavid van Moolenbroek 
633*0a6a1f1dSLionel Sambuc 	style_apply_update(gc, oo, "window-status-style");
634eda6f593SDavid van Moolenbroek 	fmt = options_get_string(oo, "window-status-format");
635eda6f593SDavid van Moolenbroek 	if (wl == s->curw) {
636*0a6a1f1dSLionel Sambuc 		style_apply_update(gc, oo, "window-status-current-style");
637eda6f593SDavid van Moolenbroek 		fmt = options_get_string(oo, "window-status-current-format");
638eda6f593SDavid van Moolenbroek 	}
639*0a6a1f1dSLionel Sambuc 	if (wl == TAILQ_FIRST(&s->lastw))
640*0a6a1f1dSLionel Sambuc 		style_apply_update(gc, oo, "window-status-last-style");
641eda6f593SDavid van Moolenbroek 
642*0a6a1f1dSLionel Sambuc 	if (wl->flags & WINLINK_BELL)
643*0a6a1f1dSLionel Sambuc 		style_apply_update(gc, oo, "window-status-bell-style");
644*0a6a1f1dSLionel Sambuc 	else if (wl->flags & WINLINK_CONTENT)
645*0a6a1f1dSLionel Sambuc 		style_apply_update(gc, oo, "window-status-content-style");
646*0a6a1f1dSLionel Sambuc 	else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
647*0a6a1f1dSLionel Sambuc 		style_apply_update(gc, oo, "window-status-activity-style");
648eda6f593SDavid van Moolenbroek 
649eda6f593SDavid van Moolenbroek 	text = status_replace(c, NULL, wl, NULL, fmt, t, 1);
650eda6f593SDavid van Moolenbroek 	return (text);
651eda6f593SDavid van Moolenbroek }
652eda6f593SDavid van Moolenbroek 
653eda6f593SDavid van Moolenbroek /* Set a status line message. */
654eda6f593SDavid van Moolenbroek void printflike2
status_message_set(struct client * c,const char * fmt,...)655eda6f593SDavid van Moolenbroek status_message_set(struct client *c, const char *fmt, ...)
656eda6f593SDavid van Moolenbroek {
657eda6f593SDavid van Moolenbroek 	struct timeval		 tv;
658eda6f593SDavid van Moolenbroek 	struct session		*s = c->session;
659eda6f593SDavid van Moolenbroek 	struct message_entry	*msg;
660eda6f593SDavid van Moolenbroek 	va_list			 ap;
661eda6f593SDavid van Moolenbroek 	int			 delay;
662eda6f593SDavid van Moolenbroek 	u_int			 i, limit;
663eda6f593SDavid van Moolenbroek 
664eda6f593SDavid van Moolenbroek 	status_prompt_clear(c);
665eda6f593SDavid van Moolenbroek 	status_message_clear(c);
666eda6f593SDavid van Moolenbroek 
667eda6f593SDavid van Moolenbroek 	va_start(ap, fmt);
668eda6f593SDavid van Moolenbroek 	xvasprintf(&c->message_string, fmt, ap);
669eda6f593SDavid van Moolenbroek 	va_end(ap);
670eda6f593SDavid van Moolenbroek 
671eda6f593SDavid van Moolenbroek 	ARRAY_EXPAND(&c->message_log, 1);
672eda6f593SDavid van Moolenbroek 	msg = &ARRAY_LAST(&c->message_log);
673eda6f593SDavid van Moolenbroek 	msg->msg_time = time(NULL);
674eda6f593SDavid van Moolenbroek 	msg->msg = xstrdup(c->message_string);
675eda6f593SDavid van Moolenbroek 
676eda6f593SDavid van Moolenbroek 	if (s == NULL)
677eda6f593SDavid van Moolenbroek 		limit = 0;
678eda6f593SDavid van Moolenbroek 	else
679eda6f593SDavid van Moolenbroek 		limit = options_get_number(&s->options, "message-limit");
680eda6f593SDavid van Moolenbroek 	if (ARRAY_LENGTH(&c->message_log) > limit) {
681eda6f593SDavid van Moolenbroek 		limit = ARRAY_LENGTH(&c->message_log) - limit;
682eda6f593SDavid van Moolenbroek 		for (i = 0; i < limit; i++) {
683eda6f593SDavid van Moolenbroek 			msg = &ARRAY_FIRST(&c->message_log);
684*0a6a1f1dSLionel Sambuc 			free(msg->msg);
685eda6f593SDavid van Moolenbroek 			ARRAY_REMOVE(&c->message_log, 0);
686eda6f593SDavid van Moolenbroek 		}
687eda6f593SDavid van Moolenbroek 	}
688eda6f593SDavid van Moolenbroek 
689eda6f593SDavid van Moolenbroek 	delay = options_get_number(&c->session->options, "display-time");
690eda6f593SDavid van Moolenbroek 	tv.tv_sec = delay / 1000;
691eda6f593SDavid van Moolenbroek 	tv.tv_usec = (delay % 1000) * 1000L;
692eda6f593SDavid van Moolenbroek 
693*0a6a1f1dSLionel Sambuc 	if (event_initialized(&c->message_timer))
694eda6f593SDavid van Moolenbroek 		evtimer_del(&c->message_timer);
695eda6f593SDavid van Moolenbroek 	evtimer_set(&c->message_timer, status_message_callback, c);
696eda6f593SDavid van Moolenbroek 	evtimer_add(&c->message_timer, &tv);
697eda6f593SDavid van Moolenbroek 
698eda6f593SDavid van Moolenbroek 	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
699eda6f593SDavid van Moolenbroek 	c->flags |= CLIENT_STATUS;
700eda6f593SDavid van Moolenbroek }
701eda6f593SDavid van Moolenbroek 
702eda6f593SDavid van Moolenbroek /* Clear status line message. */
703eda6f593SDavid van Moolenbroek void
status_message_clear(struct client * c)704eda6f593SDavid van Moolenbroek status_message_clear(struct client *c)
705eda6f593SDavid van Moolenbroek {
706eda6f593SDavid van Moolenbroek 	if (c->message_string == NULL)
707eda6f593SDavid van Moolenbroek 		return;
708eda6f593SDavid van Moolenbroek 
709*0a6a1f1dSLionel Sambuc 	free(c->message_string);
710eda6f593SDavid van Moolenbroek 	c->message_string = NULL;
711eda6f593SDavid van Moolenbroek 
712eda6f593SDavid van Moolenbroek 	c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
713eda6f593SDavid van Moolenbroek 	c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
714eda6f593SDavid van Moolenbroek 
715eda6f593SDavid van Moolenbroek 	screen_reinit(&c->status);
716eda6f593SDavid van Moolenbroek }
717eda6f593SDavid van Moolenbroek 
718eda6f593SDavid van Moolenbroek /* Clear status line message after timer expires. */
719eda6f593SDavid van Moolenbroek void
status_message_callback(unused int fd,unused short event,void * data)720eda6f593SDavid van Moolenbroek status_message_callback(unused int fd, unused short event, void *data)
721eda6f593SDavid van Moolenbroek {
722eda6f593SDavid van Moolenbroek 	struct client	*c = data;
723eda6f593SDavid van Moolenbroek 
724eda6f593SDavid van Moolenbroek 	status_message_clear(c);
725eda6f593SDavid van Moolenbroek }
726eda6f593SDavid van Moolenbroek 
727eda6f593SDavid van Moolenbroek /* Draw client message on status line of present else on last line. */
728eda6f593SDavid van Moolenbroek int
status_message_redraw(struct client * c)729eda6f593SDavid van Moolenbroek status_message_redraw(struct client *c)
730eda6f593SDavid van Moolenbroek {
731eda6f593SDavid van Moolenbroek 	struct screen_write_ctx		ctx;
732eda6f593SDavid van Moolenbroek 	struct session		       *s = c->session;
733eda6f593SDavid van Moolenbroek 	struct screen		        old_status;
734eda6f593SDavid van Moolenbroek 	size_t			        len;
735eda6f593SDavid van Moolenbroek 	struct grid_cell		gc;
736eda6f593SDavid van Moolenbroek 	int				utf8flag;
737eda6f593SDavid van Moolenbroek 
738eda6f593SDavid van Moolenbroek 	if (c->tty.sx == 0 || c->tty.sy == 0)
739eda6f593SDavid van Moolenbroek 		return (0);
740eda6f593SDavid van Moolenbroek 	memcpy(&old_status, &c->status, sizeof old_status);
741eda6f593SDavid van Moolenbroek 	screen_init(&c->status, c->tty.sx, 1, 0);
742eda6f593SDavid van Moolenbroek 
743eda6f593SDavid van Moolenbroek 	utf8flag = options_get_number(&s->options, "status-utf8");
744eda6f593SDavid van Moolenbroek 
745eda6f593SDavid van Moolenbroek 	len = screen_write_strlen(utf8flag, "%s", c->message_string);
746eda6f593SDavid van Moolenbroek 	if (len > c->tty.sx)
747eda6f593SDavid van Moolenbroek 		len = c->tty.sx;
748eda6f593SDavid van Moolenbroek 
749*0a6a1f1dSLionel Sambuc 	style_apply(&gc, &s->options, "message-style");
750eda6f593SDavid van Moolenbroek 
751eda6f593SDavid van Moolenbroek 	screen_write_start(&ctx, NULL, &c->status);
752eda6f593SDavid van Moolenbroek 
753eda6f593SDavid van Moolenbroek 	screen_write_cursormove(&ctx, 0, 0);
754eda6f593SDavid van Moolenbroek 	screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string);
755eda6f593SDavid van Moolenbroek 	for (; len < c->tty.sx; len++)
756eda6f593SDavid van Moolenbroek 		screen_write_putc(&ctx, &gc, ' ');
757eda6f593SDavid van Moolenbroek 
758eda6f593SDavid van Moolenbroek 	screen_write_stop(&ctx);
759eda6f593SDavid van Moolenbroek 
760eda6f593SDavid van Moolenbroek 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
761eda6f593SDavid van Moolenbroek 		screen_free(&old_status);
762eda6f593SDavid van Moolenbroek 		return (0);
763eda6f593SDavid van Moolenbroek 	}
764eda6f593SDavid van Moolenbroek 	screen_free(&old_status);
765eda6f593SDavid van Moolenbroek 	return (1);
766eda6f593SDavid van Moolenbroek }
767eda6f593SDavid van Moolenbroek 
768eda6f593SDavid van Moolenbroek /* Enable status line prompt. */
769eda6f593SDavid van Moolenbroek void
status_prompt_set(struct client * c,const char * msg,const char * input,int (* callbackfn)(void *,const char *),void (* freefn)(void *),void * data,int flags)770eda6f593SDavid van Moolenbroek status_prompt_set(struct client *c, const char *msg, const char *input,
771eda6f593SDavid van Moolenbroek     int (*callbackfn)(void *, const char *), void (*freefn)(void *),
772eda6f593SDavid van Moolenbroek     void *data, int flags)
773eda6f593SDavid van Moolenbroek {
774eda6f593SDavid van Moolenbroek 	int	keys;
775eda6f593SDavid van Moolenbroek 
776eda6f593SDavid van Moolenbroek 	status_message_clear(c);
777eda6f593SDavid van Moolenbroek 	status_prompt_clear(c);
778eda6f593SDavid van Moolenbroek 
779eda6f593SDavid van Moolenbroek 	c->prompt_string = status_replace(c, NULL, NULL, NULL, msg,
780eda6f593SDavid van Moolenbroek 	    time(NULL), 0);
781eda6f593SDavid van Moolenbroek 
782eda6f593SDavid van Moolenbroek 	c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input,
783eda6f593SDavid van Moolenbroek 	    time(NULL), 0);
784eda6f593SDavid van Moolenbroek 	c->prompt_index = strlen(c->prompt_buffer);
785eda6f593SDavid van Moolenbroek 
786eda6f593SDavid van Moolenbroek 	c->prompt_callbackfn = callbackfn;
787eda6f593SDavid van Moolenbroek 	c->prompt_freefn = freefn;
788eda6f593SDavid van Moolenbroek 	c->prompt_data = data;
789eda6f593SDavid van Moolenbroek 
790eda6f593SDavid van Moolenbroek 	c->prompt_hindex = 0;
791eda6f593SDavid van Moolenbroek 
792eda6f593SDavid van Moolenbroek 	c->prompt_flags = flags;
793eda6f593SDavid van Moolenbroek 
794eda6f593SDavid van Moolenbroek 	keys = options_get_number(&c->session->options, "status-keys");
795eda6f593SDavid van Moolenbroek 	if (keys == MODEKEY_EMACS)
796eda6f593SDavid van Moolenbroek 		mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit);
797eda6f593SDavid van Moolenbroek 	else
798eda6f593SDavid van Moolenbroek 		mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit);
799eda6f593SDavid van Moolenbroek 
800eda6f593SDavid van Moolenbroek 	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
801eda6f593SDavid van Moolenbroek 	c->flags |= CLIENT_STATUS;
802eda6f593SDavid van Moolenbroek }
803eda6f593SDavid van Moolenbroek 
804eda6f593SDavid van Moolenbroek /* Remove status line prompt. */
805eda6f593SDavid van Moolenbroek void
status_prompt_clear(struct client * c)806eda6f593SDavid van Moolenbroek status_prompt_clear(struct client *c)
807eda6f593SDavid van Moolenbroek {
808eda6f593SDavid van Moolenbroek 	if (c->prompt_string == NULL)
809eda6f593SDavid van Moolenbroek 		return;
810eda6f593SDavid van Moolenbroek 
811eda6f593SDavid van Moolenbroek 	if (c->prompt_freefn != NULL && c->prompt_data != NULL)
812eda6f593SDavid van Moolenbroek 		c->prompt_freefn(c->prompt_data);
813eda6f593SDavid van Moolenbroek 
814*0a6a1f1dSLionel Sambuc 	free(c->prompt_string);
815eda6f593SDavid van Moolenbroek 	c->prompt_string = NULL;
816eda6f593SDavid van Moolenbroek 
817*0a6a1f1dSLionel Sambuc 	free(c->prompt_buffer);
818eda6f593SDavid van Moolenbroek 	c->prompt_buffer = NULL;
819eda6f593SDavid van Moolenbroek 
820eda6f593SDavid van Moolenbroek 	c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
821eda6f593SDavid van Moolenbroek 	c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
822eda6f593SDavid van Moolenbroek 
823eda6f593SDavid van Moolenbroek 	screen_reinit(&c->status);
824eda6f593SDavid van Moolenbroek }
825eda6f593SDavid van Moolenbroek 
826eda6f593SDavid van Moolenbroek /* Update status line prompt with a new prompt string. */
827eda6f593SDavid van Moolenbroek void
status_prompt_update(struct client * c,const char * msg,const char * input)828eda6f593SDavid van Moolenbroek status_prompt_update(struct client *c, const char *msg, const char *input)
829eda6f593SDavid van Moolenbroek {
830*0a6a1f1dSLionel Sambuc 	free(c->prompt_string);
831eda6f593SDavid van Moolenbroek 	c->prompt_string = status_replace(c, NULL, NULL, NULL, msg,
832eda6f593SDavid van Moolenbroek 	    time(NULL), 0);
833eda6f593SDavid van Moolenbroek 
834*0a6a1f1dSLionel Sambuc 	free(c->prompt_buffer);
835eda6f593SDavid van Moolenbroek 	c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input,
836eda6f593SDavid van Moolenbroek 	    time(NULL), 0);
837eda6f593SDavid van Moolenbroek 	c->prompt_index = strlen(c->prompt_buffer);
838eda6f593SDavid van Moolenbroek 
839eda6f593SDavid van Moolenbroek 	c->prompt_hindex = 0;
840eda6f593SDavid van Moolenbroek 
841eda6f593SDavid van Moolenbroek 	c->flags |= CLIENT_STATUS;
842eda6f593SDavid van Moolenbroek }
843eda6f593SDavid van Moolenbroek 
844eda6f593SDavid van Moolenbroek /* Draw client prompt on status line of present else on last line. */
845eda6f593SDavid van Moolenbroek int
status_prompt_redraw(struct client * c)846eda6f593SDavid van Moolenbroek status_prompt_redraw(struct client *c)
847eda6f593SDavid van Moolenbroek {
848eda6f593SDavid van Moolenbroek 	struct screen_write_ctx		ctx;
849eda6f593SDavid van Moolenbroek 	struct session		       *s = c->session;
850eda6f593SDavid van Moolenbroek 	struct screen		        old_status;
851eda6f593SDavid van Moolenbroek 	size_t			        i, size, left, len, off;
852eda6f593SDavid van Moolenbroek 	struct grid_cell		gc, *gcp;
853eda6f593SDavid van Moolenbroek 	int				utf8flag;
854eda6f593SDavid van Moolenbroek 
855eda6f593SDavid van Moolenbroek 	if (c->tty.sx == 0 || c->tty.sy == 0)
856eda6f593SDavid van Moolenbroek 		return (0);
857eda6f593SDavid van Moolenbroek 	memcpy(&old_status, &c->status, sizeof old_status);
858eda6f593SDavid van Moolenbroek 	screen_init(&c->status, c->tty.sx, 1, 0);
859eda6f593SDavid van Moolenbroek 
860eda6f593SDavid van Moolenbroek 	utf8flag = options_get_number(&s->options, "status-utf8");
861eda6f593SDavid van Moolenbroek 
862eda6f593SDavid van Moolenbroek 	len = screen_write_strlen(utf8flag, "%s", c->prompt_string);
863eda6f593SDavid van Moolenbroek 	if (len > c->tty.sx)
864eda6f593SDavid van Moolenbroek 		len = c->tty.sx;
865eda6f593SDavid van Moolenbroek 	off = 0;
866eda6f593SDavid van Moolenbroek 
867*0a6a1f1dSLionel Sambuc 	/* Change colours for command mode. */
868*0a6a1f1dSLionel Sambuc 	if (c->prompt_mdata.mode == 1)
869*0a6a1f1dSLionel Sambuc 		style_apply(&gc, &s->options, "message-command-style");
870*0a6a1f1dSLionel Sambuc 	else
871*0a6a1f1dSLionel Sambuc 		style_apply(&gc, &s->options, "message-style");
872eda6f593SDavid van Moolenbroek 
873eda6f593SDavid van Moolenbroek 	screen_write_start(&ctx, NULL, &c->status);
874eda6f593SDavid van Moolenbroek 
875eda6f593SDavid van Moolenbroek 	screen_write_cursormove(&ctx, 0, 0);
876eda6f593SDavid van Moolenbroek 	screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string);
877eda6f593SDavid van Moolenbroek 
878eda6f593SDavid van Moolenbroek 	left = c->tty.sx - len;
879eda6f593SDavid van Moolenbroek 	if (left != 0) {
880eda6f593SDavid van Moolenbroek 		size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer);
881eda6f593SDavid van Moolenbroek 		if (c->prompt_index >= left) {
882eda6f593SDavid van Moolenbroek 			off = c->prompt_index - left + 1;
883eda6f593SDavid van Moolenbroek 			if (c->prompt_index == size)
884eda6f593SDavid van Moolenbroek 				left--;
885eda6f593SDavid van Moolenbroek 			size = left;
886eda6f593SDavid van Moolenbroek 		}
887eda6f593SDavid van Moolenbroek 		screen_write_nputs(
888eda6f593SDavid van Moolenbroek 		    &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off);
889eda6f593SDavid van Moolenbroek 
890eda6f593SDavid van Moolenbroek 		for (i = len + size; i < c->tty.sx; i++)
891eda6f593SDavid van Moolenbroek 			screen_write_putc(&ctx, &gc, ' ');
892eda6f593SDavid van Moolenbroek 	}
893eda6f593SDavid van Moolenbroek 
894eda6f593SDavid van Moolenbroek 	screen_write_stop(&ctx);
895eda6f593SDavid van Moolenbroek 
896eda6f593SDavid van Moolenbroek 	/* Apply fake cursor. */
897eda6f593SDavid van Moolenbroek 	off = len + c->prompt_index - off;
898eda6f593SDavid van Moolenbroek 	gcp = grid_view_get_cell(c->status.grid, off, 0);
899eda6f593SDavid van Moolenbroek 	gcp->attr ^= GRID_ATTR_REVERSE;
900eda6f593SDavid van Moolenbroek 
901eda6f593SDavid van Moolenbroek 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
902eda6f593SDavid van Moolenbroek 		screen_free(&old_status);
903eda6f593SDavid van Moolenbroek 		return (0);
904eda6f593SDavid van Moolenbroek 	}
905eda6f593SDavid van Moolenbroek 	screen_free(&old_status);
906eda6f593SDavid van Moolenbroek 	return (1);
907eda6f593SDavid van Moolenbroek }
908eda6f593SDavid van Moolenbroek 
909eda6f593SDavid van Moolenbroek /* Handle keys in prompt. */
910eda6f593SDavid van Moolenbroek void
status_prompt_key(struct client * c,int key)911eda6f593SDavid van Moolenbroek status_prompt_key(struct client *c, int key)
912eda6f593SDavid van Moolenbroek {
913*0a6a1f1dSLionel Sambuc 	struct session		*sess = c->session;
914*0a6a1f1dSLionel Sambuc 	struct options		*oo = &sess->options;
915eda6f593SDavid van Moolenbroek 	struct paste_buffer	*pb;
916eda6f593SDavid van Moolenbroek 	char			*s, *first, *last, word[64], swapc;
917eda6f593SDavid van Moolenbroek 	const char		*histstr;
918*0a6a1f1dSLionel Sambuc 	const char		*wsep = NULL;
919eda6f593SDavid van Moolenbroek 	u_char			 ch;
920eda6f593SDavid van Moolenbroek 	size_t			 size, n, off, idx;
921eda6f593SDavid van Moolenbroek 
922eda6f593SDavid van Moolenbroek 	size = strlen(c->prompt_buffer);
923*0a6a1f1dSLionel Sambuc 	switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) {
924eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_CURSORLEFT:
925eda6f593SDavid van Moolenbroek 		if (c->prompt_index > 0) {
926eda6f593SDavid van Moolenbroek 			c->prompt_index--;
927eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
928eda6f593SDavid van Moolenbroek 		}
929eda6f593SDavid van Moolenbroek 		break;
930*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_SWITCHMODE:
931*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
932*0a6a1f1dSLionel Sambuc 		break;
933eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_SWITCHMODEAPPEND:
934*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
935*0a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
936eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_CURSORRIGHT:
937eda6f593SDavid van Moolenbroek 		if (c->prompt_index < size) {
938eda6f593SDavid van Moolenbroek 			c->prompt_index++;
939eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
940eda6f593SDavid van Moolenbroek 		}
941eda6f593SDavid van Moolenbroek 		break;
942*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_SWITCHMODEBEGINLINE:
943*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
944*0a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
945eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_STARTOFLINE:
946eda6f593SDavid van Moolenbroek 		if (c->prompt_index != 0) {
947eda6f593SDavid van Moolenbroek 			c->prompt_index = 0;
948eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
949eda6f593SDavid van Moolenbroek 		}
950eda6f593SDavid van Moolenbroek 		break;
951*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_SWITCHMODEAPPENDLINE:
952*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
953*0a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
954eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_ENDOFLINE:
955eda6f593SDavid van Moolenbroek 		if (c->prompt_index != size) {
956eda6f593SDavid van Moolenbroek 			c->prompt_index = size;
957eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
958eda6f593SDavid van Moolenbroek 		}
959eda6f593SDavid van Moolenbroek 		break;
960eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_COMPLETE:
961eda6f593SDavid van Moolenbroek 		if (*c->prompt_buffer == '\0')
962eda6f593SDavid van Moolenbroek 			break;
963eda6f593SDavid van Moolenbroek 
964eda6f593SDavid van Moolenbroek 		idx = c->prompt_index;
965eda6f593SDavid van Moolenbroek 		if (idx != 0)
966eda6f593SDavid van Moolenbroek 			idx--;
967eda6f593SDavid van Moolenbroek 
968eda6f593SDavid van Moolenbroek 		/* Find the word we are in. */
969eda6f593SDavid van Moolenbroek 		first = c->prompt_buffer + idx;
970eda6f593SDavid van Moolenbroek 		while (first > c->prompt_buffer && *first != ' ')
971eda6f593SDavid van Moolenbroek 			first--;
972eda6f593SDavid van Moolenbroek 		while (*first == ' ')
973eda6f593SDavid van Moolenbroek 			first++;
974eda6f593SDavid van Moolenbroek 		last = c->prompt_buffer + idx;
975eda6f593SDavid van Moolenbroek 		while (*last != '\0' && *last != ' ')
976eda6f593SDavid van Moolenbroek 			last++;
977eda6f593SDavid van Moolenbroek 		while (*last == ' ')
978eda6f593SDavid van Moolenbroek 			last--;
979eda6f593SDavid van Moolenbroek 		if (*last != '\0')
980eda6f593SDavid van Moolenbroek 			last++;
981eda6f593SDavid van Moolenbroek 		if (last <= first ||
982eda6f593SDavid van Moolenbroek 		    ((size_t) (last - first)) > (sizeof word) - 1)
983eda6f593SDavid van Moolenbroek 			break;
984eda6f593SDavid van Moolenbroek 		memcpy(word, first, last - first);
985eda6f593SDavid van Moolenbroek 		word[last - first] = '\0';
986eda6f593SDavid van Moolenbroek 
987eda6f593SDavid van Moolenbroek 		/* And try to complete it. */
988eda6f593SDavid van Moolenbroek 		if ((s = status_prompt_complete(word)) == NULL)
989eda6f593SDavid van Moolenbroek 			break;
990eda6f593SDavid van Moolenbroek 
991eda6f593SDavid van Moolenbroek 		/* Trim out word. */
992eda6f593SDavid van Moolenbroek 		n = size - (last - c->prompt_buffer) + 1; /* with \0 */
993eda6f593SDavid van Moolenbroek 		memmove(first, last, n);
994eda6f593SDavid van Moolenbroek 		size -= last - first;
995eda6f593SDavid van Moolenbroek 
996eda6f593SDavid van Moolenbroek 		/* Insert the new word. */
997eda6f593SDavid van Moolenbroek 		size += strlen(s);
998eda6f593SDavid van Moolenbroek 		off = first - c->prompt_buffer;
999eda6f593SDavid van Moolenbroek 		c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1);
1000eda6f593SDavid van Moolenbroek 		first = c->prompt_buffer + off;
1001eda6f593SDavid van Moolenbroek 		memmove(first + strlen(s), first, n);
1002eda6f593SDavid van Moolenbroek 		memcpy(first, s, strlen(s));
1003eda6f593SDavid van Moolenbroek 
1004eda6f593SDavid van Moolenbroek 		c->prompt_index = (first - c->prompt_buffer) + strlen(s);
1005*0a6a1f1dSLionel Sambuc 		free(s);
1006eda6f593SDavid van Moolenbroek 
1007eda6f593SDavid van Moolenbroek 		c->flags |= CLIENT_STATUS;
1008eda6f593SDavid van Moolenbroek 		break;
1009eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_BACKSPACE:
1010eda6f593SDavid van Moolenbroek 		if (c->prompt_index != 0) {
1011eda6f593SDavid van Moolenbroek 			if (c->prompt_index == size)
1012eda6f593SDavid van Moolenbroek 				c->prompt_buffer[--c->prompt_index] = '\0';
1013eda6f593SDavid van Moolenbroek 			else {
1014eda6f593SDavid van Moolenbroek 				memmove(c->prompt_buffer + c->prompt_index - 1,
1015eda6f593SDavid van Moolenbroek 				    c->prompt_buffer + c->prompt_index,
1016eda6f593SDavid van Moolenbroek 				    size + 1 - c->prompt_index);
1017eda6f593SDavid van Moolenbroek 				c->prompt_index--;
1018eda6f593SDavid van Moolenbroek 			}
1019eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
1020eda6f593SDavid van Moolenbroek 		}
1021eda6f593SDavid van Moolenbroek 		break;
1022eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_DELETE:
1023*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_SWITCHMODESUBSTITUTE:
1024eda6f593SDavid van Moolenbroek 		if (c->prompt_index != size) {
1025eda6f593SDavid van Moolenbroek 			memmove(c->prompt_buffer + c->prompt_index,
1026eda6f593SDavid van Moolenbroek 			    c->prompt_buffer + c->prompt_index + 1,
1027eda6f593SDavid van Moolenbroek 			    size + 1 - c->prompt_index);
1028eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
1029eda6f593SDavid van Moolenbroek 		}
1030eda6f593SDavid van Moolenbroek 		break;
1031eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_DELETELINE:
1032*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
1033eda6f593SDavid van Moolenbroek 		*c->prompt_buffer = '\0';
1034eda6f593SDavid van Moolenbroek 		c->prompt_index = 0;
1035eda6f593SDavid van Moolenbroek 		c->flags |= CLIENT_STATUS;
1036eda6f593SDavid van Moolenbroek 		break;
1037eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_DELETETOENDOFLINE:
1038*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_SWITCHMODECHANGELINE:
1039eda6f593SDavid van Moolenbroek 		if (c->prompt_index < size) {
1040eda6f593SDavid van Moolenbroek 			c->prompt_buffer[c->prompt_index] = '\0';
1041eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
1042eda6f593SDavid van Moolenbroek 		}
1043eda6f593SDavid van Moolenbroek 		break;
1044*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_DELETEWORD:
1045*0a6a1f1dSLionel Sambuc 		wsep = options_get_string(oo, "word-separators");
1046*0a6a1f1dSLionel Sambuc 		idx = c->prompt_index;
1047*0a6a1f1dSLionel Sambuc 
1048*0a6a1f1dSLionel Sambuc 		/* Find a non-separator. */
1049*0a6a1f1dSLionel Sambuc 		while (idx != 0) {
1050*0a6a1f1dSLionel Sambuc 			idx--;
1051*0a6a1f1dSLionel Sambuc 			if (!strchr(wsep, c->prompt_buffer[idx]))
1052*0a6a1f1dSLionel Sambuc 				break;
1053*0a6a1f1dSLionel Sambuc 		}
1054*0a6a1f1dSLionel Sambuc 
1055*0a6a1f1dSLionel Sambuc 		/* Find the separator at the beginning of the word. */
1056*0a6a1f1dSLionel Sambuc 		while (idx != 0) {
1057*0a6a1f1dSLionel Sambuc 			idx--;
1058*0a6a1f1dSLionel Sambuc 			if (strchr(wsep, c->prompt_buffer[idx])) {
1059*0a6a1f1dSLionel Sambuc 				/* Go back to the word. */
1060*0a6a1f1dSLionel Sambuc 				idx++;
1061*0a6a1f1dSLionel Sambuc 				break;
1062*0a6a1f1dSLionel Sambuc 			}
1063*0a6a1f1dSLionel Sambuc 		}
1064*0a6a1f1dSLionel Sambuc 
1065*0a6a1f1dSLionel Sambuc 		memmove(c->prompt_buffer + idx,
1066*0a6a1f1dSLionel Sambuc 		    c->prompt_buffer + c->prompt_index,
1067*0a6a1f1dSLionel Sambuc 		    size + 1 - c->prompt_index);
1068*0a6a1f1dSLionel Sambuc 		memset(c->prompt_buffer + size - (c->prompt_index - idx),
1069*0a6a1f1dSLionel Sambuc 		    '\0', c->prompt_index - idx);
1070*0a6a1f1dSLionel Sambuc 		c->prompt_index = idx;
1071*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
1072*0a6a1f1dSLionel Sambuc 		break;
1073*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_NEXTSPACE:
1074*0a6a1f1dSLionel Sambuc 		wsep = " ";
1075*0a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
1076*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_NEXTWORD:
1077*0a6a1f1dSLionel Sambuc 		if (wsep == NULL)
1078*0a6a1f1dSLionel Sambuc 			wsep = options_get_string(oo, "word-separators");
1079*0a6a1f1dSLionel Sambuc 
1080*0a6a1f1dSLionel Sambuc 		/* Find a separator. */
1081*0a6a1f1dSLionel Sambuc 		while (c->prompt_index != size) {
1082*0a6a1f1dSLionel Sambuc 			c->prompt_index++;
1083*0a6a1f1dSLionel Sambuc 			if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
1084*0a6a1f1dSLionel Sambuc 				break;
1085*0a6a1f1dSLionel Sambuc 		}
1086*0a6a1f1dSLionel Sambuc 
1087*0a6a1f1dSLionel Sambuc 		/* Find the word right after the separation. */
1088*0a6a1f1dSLionel Sambuc 		while (c->prompt_index != size) {
1089*0a6a1f1dSLionel Sambuc 			c->prompt_index++;
1090*0a6a1f1dSLionel Sambuc 			if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1091*0a6a1f1dSLionel Sambuc 				break;
1092*0a6a1f1dSLionel Sambuc 		}
1093*0a6a1f1dSLionel Sambuc 
1094*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
1095*0a6a1f1dSLionel Sambuc 		break;
1096*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_NEXTSPACEEND:
1097*0a6a1f1dSLionel Sambuc 		wsep = " ";
1098*0a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
1099*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_NEXTWORDEND:
1100*0a6a1f1dSLionel Sambuc 		if (wsep == NULL)
1101*0a6a1f1dSLionel Sambuc 			wsep = options_get_string(oo, "word-separators");
1102*0a6a1f1dSLionel Sambuc 
1103*0a6a1f1dSLionel Sambuc 		/* Find a word. */
1104*0a6a1f1dSLionel Sambuc 		while (c->prompt_index != size) {
1105*0a6a1f1dSLionel Sambuc 			c->prompt_index++;
1106*0a6a1f1dSLionel Sambuc 			if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1107*0a6a1f1dSLionel Sambuc 				break;
1108*0a6a1f1dSLionel Sambuc 		}
1109*0a6a1f1dSLionel Sambuc 
1110*0a6a1f1dSLionel Sambuc 		/* Find the separator at the end of the word. */
1111*0a6a1f1dSLionel Sambuc 		while (c->prompt_index != size) {
1112*0a6a1f1dSLionel Sambuc 			c->prompt_index++;
1113*0a6a1f1dSLionel Sambuc 			if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
1114*0a6a1f1dSLionel Sambuc 				break;
1115*0a6a1f1dSLionel Sambuc 		}
1116*0a6a1f1dSLionel Sambuc 
1117*0a6a1f1dSLionel Sambuc 		/* Back up to the end-of-word like vi. */
1118*0a6a1f1dSLionel Sambuc 		if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
1119*0a6a1f1dSLionel Sambuc 		    c->prompt_index != 0)
1120*0a6a1f1dSLionel Sambuc 			c->prompt_index--;
1121*0a6a1f1dSLionel Sambuc 
1122*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
1123*0a6a1f1dSLionel Sambuc 		break;
1124*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_PREVIOUSSPACE:
1125*0a6a1f1dSLionel Sambuc 		wsep = " ";
1126*0a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
1127*0a6a1f1dSLionel Sambuc 	case MODEKEYEDIT_PREVIOUSWORD:
1128*0a6a1f1dSLionel Sambuc 		if (wsep == NULL)
1129*0a6a1f1dSLionel Sambuc 			wsep = options_get_string(oo, "word-separators");
1130*0a6a1f1dSLionel Sambuc 
1131*0a6a1f1dSLionel Sambuc 		/* Find a non-separator. */
1132*0a6a1f1dSLionel Sambuc 		while (c->prompt_index != 0) {
1133*0a6a1f1dSLionel Sambuc 			c->prompt_index--;
1134*0a6a1f1dSLionel Sambuc 			if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1135*0a6a1f1dSLionel Sambuc 				break;
1136*0a6a1f1dSLionel Sambuc 		}
1137*0a6a1f1dSLionel Sambuc 
1138*0a6a1f1dSLionel Sambuc 		/* Find the separator at the beginning of the word. */
1139*0a6a1f1dSLionel Sambuc 		while (c->prompt_index != 0) {
1140*0a6a1f1dSLionel Sambuc 			c->prompt_index--;
1141*0a6a1f1dSLionel Sambuc 			if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
1142*0a6a1f1dSLionel Sambuc 				/* Go back to the word. */
1143*0a6a1f1dSLionel Sambuc 				c->prompt_index++;
1144*0a6a1f1dSLionel Sambuc 				break;
1145*0a6a1f1dSLionel Sambuc 			}
1146*0a6a1f1dSLionel Sambuc 		}
1147*0a6a1f1dSLionel Sambuc 
1148*0a6a1f1dSLionel Sambuc 		c->flags |= CLIENT_STATUS;
1149*0a6a1f1dSLionel Sambuc 		break;
1150eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_HISTORYUP:
1151eda6f593SDavid van Moolenbroek 		histstr = status_prompt_up_history(&c->prompt_hindex);
1152eda6f593SDavid van Moolenbroek 		if (histstr == NULL)
1153eda6f593SDavid van Moolenbroek 			break;
1154*0a6a1f1dSLionel Sambuc 		free(c->prompt_buffer);
1155eda6f593SDavid van Moolenbroek 		c->prompt_buffer = xstrdup(histstr);
1156eda6f593SDavid van Moolenbroek 		c->prompt_index = strlen(c->prompt_buffer);
1157eda6f593SDavid van Moolenbroek 		c->flags |= CLIENT_STATUS;
1158eda6f593SDavid van Moolenbroek 		break;
1159eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_HISTORYDOWN:
1160eda6f593SDavid van Moolenbroek 		histstr = status_prompt_down_history(&c->prompt_hindex);
1161eda6f593SDavid van Moolenbroek 		if (histstr == NULL)
1162eda6f593SDavid van Moolenbroek 			break;
1163*0a6a1f1dSLionel Sambuc 		free(c->prompt_buffer);
1164eda6f593SDavid van Moolenbroek 		c->prompt_buffer = xstrdup(histstr);
1165eda6f593SDavid van Moolenbroek 		c->prompt_index = strlen(c->prompt_buffer);
1166eda6f593SDavid van Moolenbroek 		c->flags |= CLIENT_STATUS;
1167eda6f593SDavid van Moolenbroek 		break;
1168eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_PASTE:
1169eda6f593SDavid van Moolenbroek 		if ((pb = paste_get_top(&global_buffers)) == NULL)
1170eda6f593SDavid van Moolenbroek 			break;
1171eda6f593SDavid van Moolenbroek 		for (n = 0; n < pb->size; n++) {
1172eda6f593SDavid van Moolenbroek 			ch = (u_char) pb->data[n];
1173eda6f593SDavid van Moolenbroek 			if (ch < 32 || ch == 127)
1174eda6f593SDavid van Moolenbroek 				break;
1175eda6f593SDavid van Moolenbroek 		}
1176eda6f593SDavid van Moolenbroek 
1177eda6f593SDavid van Moolenbroek 		c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1);
1178eda6f593SDavid van Moolenbroek 		if (c->prompt_index == size) {
1179eda6f593SDavid van Moolenbroek 			memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
1180eda6f593SDavid van Moolenbroek 			c->prompt_index += n;
1181eda6f593SDavid van Moolenbroek 			c->prompt_buffer[c->prompt_index] = '\0';
1182eda6f593SDavid van Moolenbroek 		} else {
1183eda6f593SDavid van Moolenbroek 			memmove(c->prompt_buffer + c->prompt_index + n,
1184eda6f593SDavid van Moolenbroek 			    c->prompt_buffer + c->prompt_index,
1185eda6f593SDavid van Moolenbroek 			    size + 1 - c->prompt_index);
1186eda6f593SDavid van Moolenbroek 			memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
1187eda6f593SDavid van Moolenbroek 			c->prompt_index += n;
1188eda6f593SDavid van Moolenbroek 		}
1189eda6f593SDavid van Moolenbroek 
1190eda6f593SDavid van Moolenbroek 		c->flags |= CLIENT_STATUS;
1191eda6f593SDavid van Moolenbroek 		break;
1192eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_TRANSPOSECHARS:
1193eda6f593SDavid van Moolenbroek 		idx = c->prompt_index;
1194eda6f593SDavid van Moolenbroek 		if (idx < size)
1195eda6f593SDavid van Moolenbroek 			idx++;
1196eda6f593SDavid van Moolenbroek 		if (idx >= 2) {
1197eda6f593SDavid van Moolenbroek 			swapc = c->prompt_buffer[idx - 2];
1198eda6f593SDavid van Moolenbroek 			c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
1199eda6f593SDavid van Moolenbroek 			c->prompt_buffer[idx - 1] = swapc;
1200eda6f593SDavid van Moolenbroek 			c->prompt_index = idx;
1201eda6f593SDavid van Moolenbroek 			c->flags |= CLIENT_STATUS;
1202eda6f593SDavid van Moolenbroek 		}
1203eda6f593SDavid van Moolenbroek 		break;
1204eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_ENTER:
1205eda6f593SDavid van Moolenbroek 		if (*c->prompt_buffer != '\0')
1206eda6f593SDavid van Moolenbroek 			status_prompt_add_history(c->prompt_buffer);
1207eda6f593SDavid van Moolenbroek 		if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
1208eda6f593SDavid van Moolenbroek 			status_prompt_clear(c);
1209eda6f593SDavid van Moolenbroek 		break;
1210eda6f593SDavid van Moolenbroek 	case MODEKEYEDIT_CANCEL:
1211eda6f593SDavid van Moolenbroek 		if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
1212eda6f593SDavid van Moolenbroek 			status_prompt_clear(c);
1213eda6f593SDavid van Moolenbroek 		break;
1214eda6f593SDavid van Moolenbroek 	case MODEKEY_OTHER:
1215eda6f593SDavid van Moolenbroek 		if ((key & 0xff00) != 0 || key < 32 || key == 127)
1216eda6f593SDavid van Moolenbroek 			break;
1217eda6f593SDavid van Moolenbroek 		c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2);
1218eda6f593SDavid van Moolenbroek 
1219eda6f593SDavid van Moolenbroek 		if (c->prompt_index == size) {
1220eda6f593SDavid van Moolenbroek 			c->prompt_buffer[c->prompt_index++] = key;
1221eda6f593SDavid van Moolenbroek 			c->prompt_buffer[c->prompt_index] = '\0';
1222eda6f593SDavid van Moolenbroek 		} else {
1223eda6f593SDavid van Moolenbroek 			memmove(c->prompt_buffer + c->prompt_index + 1,
1224eda6f593SDavid van Moolenbroek 			    c->prompt_buffer + c->prompt_index,
1225eda6f593SDavid van Moolenbroek 			    size + 1 - c->prompt_index);
1226eda6f593SDavid van Moolenbroek 			c->prompt_buffer[c->prompt_index++] = key;
1227eda6f593SDavid van Moolenbroek 		}
1228eda6f593SDavid van Moolenbroek 
1229eda6f593SDavid van Moolenbroek 		if (c->prompt_flags & PROMPT_SINGLE) {
1230eda6f593SDavid van Moolenbroek 			if (c->prompt_callbackfn(
1231eda6f593SDavid van Moolenbroek 			    c->prompt_data, c->prompt_buffer) == 0)
1232eda6f593SDavid van Moolenbroek 				status_prompt_clear(c);
1233eda6f593SDavid van Moolenbroek 		}
1234eda6f593SDavid van Moolenbroek 
1235eda6f593SDavid van Moolenbroek 		c->flags |= CLIENT_STATUS;
1236eda6f593SDavid van Moolenbroek 		break;
1237eda6f593SDavid van Moolenbroek 	default:
1238eda6f593SDavid van Moolenbroek 		break;
1239eda6f593SDavid van Moolenbroek 	}
1240eda6f593SDavid van Moolenbroek }
1241eda6f593SDavid van Moolenbroek 
1242eda6f593SDavid van Moolenbroek /* Get previous line from the history. */
1243eda6f593SDavid van Moolenbroek const char *
status_prompt_up_history(u_int * idx)1244eda6f593SDavid van Moolenbroek status_prompt_up_history(u_int *idx)
1245eda6f593SDavid van Moolenbroek {
1246eda6f593SDavid van Moolenbroek 	u_int size;
1247eda6f593SDavid van Moolenbroek 
1248eda6f593SDavid van Moolenbroek 	/*
1249eda6f593SDavid van Moolenbroek 	 * History runs from 0 to size - 1.
1250eda6f593SDavid van Moolenbroek 	 *
1251eda6f593SDavid van Moolenbroek 	 * Index is from 0 to size. Zero is empty.
1252eda6f593SDavid van Moolenbroek 	 */
1253eda6f593SDavid van Moolenbroek 
1254eda6f593SDavid van Moolenbroek 	size = ARRAY_LENGTH(&status_prompt_history);
1255eda6f593SDavid van Moolenbroek 	if (size == 0 || *idx == size)
1256eda6f593SDavid van Moolenbroek 		return (NULL);
1257eda6f593SDavid van Moolenbroek 	(*idx)++;
1258eda6f593SDavid van Moolenbroek 	return (ARRAY_ITEM(&status_prompt_history, size - *idx));
1259eda6f593SDavid van Moolenbroek }
1260eda6f593SDavid van Moolenbroek 
1261eda6f593SDavid van Moolenbroek /* Get next line from the history. */
1262eda6f593SDavid van Moolenbroek const char *
status_prompt_down_history(u_int * idx)1263eda6f593SDavid van Moolenbroek status_prompt_down_history(u_int *idx)
1264eda6f593SDavid van Moolenbroek {
1265eda6f593SDavid van Moolenbroek 	u_int size;
1266eda6f593SDavid van Moolenbroek 
1267eda6f593SDavid van Moolenbroek 	size = ARRAY_LENGTH(&status_prompt_history);
1268eda6f593SDavid van Moolenbroek 	if (size == 0 || *idx == 0)
1269eda6f593SDavid van Moolenbroek 		return ("");
1270eda6f593SDavid van Moolenbroek 	(*idx)--;
1271eda6f593SDavid van Moolenbroek 	if (*idx == 0)
1272eda6f593SDavid van Moolenbroek 		return ("");
1273eda6f593SDavid van Moolenbroek 	return (ARRAY_ITEM(&status_prompt_history, size - *idx));
1274eda6f593SDavid van Moolenbroek }
1275eda6f593SDavid van Moolenbroek 
1276eda6f593SDavid van Moolenbroek /* Add line to the history. */
1277eda6f593SDavid van Moolenbroek void
status_prompt_add_history(const char * line)1278eda6f593SDavid van Moolenbroek status_prompt_add_history(const char *line)
1279eda6f593SDavid van Moolenbroek {
1280eda6f593SDavid van Moolenbroek 	u_int size;
1281eda6f593SDavid van Moolenbroek 
1282eda6f593SDavid van Moolenbroek 	size = ARRAY_LENGTH(&status_prompt_history);
1283eda6f593SDavid van Moolenbroek 	if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0)
1284eda6f593SDavid van Moolenbroek 		return;
1285eda6f593SDavid van Moolenbroek 
1286eda6f593SDavid van Moolenbroek 	if (size == PROMPT_HISTORY) {
1287*0a6a1f1dSLionel Sambuc 		free(ARRAY_FIRST(&status_prompt_history));
1288eda6f593SDavid van Moolenbroek 		ARRAY_REMOVE(&status_prompt_history, 0);
1289eda6f593SDavid van Moolenbroek 	}
1290eda6f593SDavid van Moolenbroek 
1291eda6f593SDavid van Moolenbroek 	ARRAY_ADD(&status_prompt_history, xstrdup(line));
1292eda6f593SDavid van Moolenbroek }
1293eda6f593SDavid van Moolenbroek 
1294eda6f593SDavid van Moolenbroek /* Complete word. */
1295eda6f593SDavid van Moolenbroek char *
status_prompt_complete(const char * s)1296eda6f593SDavid van Moolenbroek status_prompt_complete(const char *s)
1297eda6f593SDavid van Moolenbroek {
1298eda6f593SDavid van Moolenbroek 	const struct cmd_entry 	  	       **cmdent;
1299eda6f593SDavid van Moolenbroek 	const struct options_table_entry	*oe;
1300eda6f593SDavid van Moolenbroek 	ARRAY_DECL(, const char *)		 list;
1301eda6f593SDavid van Moolenbroek 	char					*prefix, *s2;
1302eda6f593SDavid van Moolenbroek 	u_int					 i;
1303eda6f593SDavid van Moolenbroek 	size_t				 	 j;
1304eda6f593SDavid van Moolenbroek 
1305eda6f593SDavid van Moolenbroek 	if (*s == '\0')
1306eda6f593SDavid van Moolenbroek 		return (NULL);
1307eda6f593SDavid van Moolenbroek 
1308eda6f593SDavid van Moolenbroek 	/* First, build a list of all the possible matches. */
1309eda6f593SDavid van Moolenbroek 	ARRAY_INIT(&list);
1310eda6f593SDavid van Moolenbroek 	for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1311eda6f593SDavid van Moolenbroek 		if (strncmp((*cmdent)->name, s, strlen(s)) == 0)
1312eda6f593SDavid van Moolenbroek 			ARRAY_ADD(&list, (*cmdent)->name);
1313eda6f593SDavid van Moolenbroek 	}
1314eda6f593SDavid van Moolenbroek 	for (oe = server_options_table; oe->name != NULL; oe++) {
1315eda6f593SDavid van Moolenbroek 		if (strncmp(oe->name, s, strlen(s)) == 0)
1316eda6f593SDavid van Moolenbroek 			ARRAY_ADD(&list, oe->name);
1317eda6f593SDavid van Moolenbroek 	}
1318eda6f593SDavid van Moolenbroek 	for (oe = session_options_table; oe->name != NULL; oe++) {
1319eda6f593SDavid van Moolenbroek 		if (strncmp(oe->name, s, strlen(s)) == 0)
1320eda6f593SDavid van Moolenbroek 			ARRAY_ADD(&list, oe->name);
1321eda6f593SDavid van Moolenbroek 	}
1322eda6f593SDavid van Moolenbroek 	for (oe = window_options_table; oe->name != NULL; oe++) {
1323eda6f593SDavid van Moolenbroek 		if (strncmp(oe->name, s, strlen(s)) == 0)
1324eda6f593SDavid van Moolenbroek 			ARRAY_ADD(&list, oe->name);
1325eda6f593SDavid van Moolenbroek 	}
1326eda6f593SDavid van Moolenbroek 
1327eda6f593SDavid van Moolenbroek 	/* If none, bail now. */
1328eda6f593SDavid van Moolenbroek 	if (ARRAY_LENGTH(&list) == 0) {
1329eda6f593SDavid van Moolenbroek 		ARRAY_FREE(&list);
1330eda6f593SDavid van Moolenbroek 		return (NULL);
1331eda6f593SDavid van Moolenbroek 	}
1332eda6f593SDavid van Moolenbroek 
1333eda6f593SDavid van Moolenbroek 	/* If an exact match, return it, with a trailing space. */
1334eda6f593SDavid van Moolenbroek 	if (ARRAY_LENGTH(&list) == 1) {
1335eda6f593SDavid van Moolenbroek 		xasprintf(&s2, "%s ", ARRAY_FIRST(&list));
1336eda6f593SDavid van Moolenbroek 		ARRAY_FREE(&list);
1337eda6f593SDavid van Moolenbroek 		return (s2);
1338eda6f593SDavid van Moolenbroek 	}
1339eda6f593SDavid van Moolenbroek 
1340eda6f593SDavid van Moolenbroek 	/* Now loop through the list and find the longest common prefix. */
1341eda6f593SDavid van Moolenbroek 	prefix = xstrdup(ARRAY_FIRST(&list));
1342eda6f593SDavid van Moolenbroek 	for (i = 1; i < ARRAY_LENGTH(&list); i++) {
1343eda6f593SDavid van Moolenbroek 		s = ARRAY_ITEM(&list, i);
1344eda6f593SDavid van Moolenbroek 
1345eda6f593SDavid van Moolenbroek 		j = strlen(s);
1346eda6f593SDavid van Moolenbroek 		if (j > strlen(prefix))
1347eda6f593SDavid van Moolenbroek 			j = strlen(prefix);
1348eda6f593SDavid van Moolenbroek 		for (; j > 0; j--) {
1349eda6f593SDavid van Moolenbroek 			if (prefix[j - 1] != s[j - 1])
1350eda6f593SDavid van Moolenbroek 				prefix[j - 1] = '\0';
1351eda6f593SDavid van Moolenbroek 		}
1352eda6f593SDavid van Moolenbroek 	}
1353eda6f593SDavid van Moolenbroek 
1354eda6f593SDavid van Moolenbroek 	ARRAY_FREE(&list);
1355eda6f593SDavid van Moolenbroek 	return (prefix);
1356eda6f593SDavid van Moolenbroek }
1357