xref: /openbsd-src/usr.bin/tmux/status.c (revision c7e8ea31cd41a963f06f0a8ba93948b06aa6b4a4)
1 /* $OpenBSD: status.c,v 1.168 2017/05/29 20:42:53 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/time.h>
21 
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 
30 #include "tmux.h"
31 
32 static char	*status_redraw_get_left(struct client *, time_t,
33 		     struct grid_cell *, size_t *);
34 static char	*status_redraw_get_right(struct client *, time_t,
35 		     struct grid_cell *, size_t *);
36 static char	*status_print(struct client *, struct winlink *, time_t,
37 		     struct grid_cell *);
38 static char	*status_replace(struct client *, struct winlink *, const char *,
39 		     time_t);
40 static void	 status_message_callback(int, short, void *);
41 static void	 status_timer_callback(int, short, void *);
42 
43 static char	*status_prompt_find_history_file(void);
44 static const char *status_prompt_up_history(u_int *);
45 static const char *status_prompt_down_history(u_int *);
46 static void	 status_prompt_add_history(const char *);
47 
48 static const char **status_prompt_complete_list(u_int *, const char *);
49 static char	*status_prompt_complete_prefix(const char **, u_int);
50 static char	*status_prompt_complete(struct session *, const char *);
51 
52 /* Status prompt history. */
53 #define PROMPT_HISTORY 100
54 static char	**status_prompt_hlist;
55 static u_int	  status_prompt_hsize;
56 
57 /* Find the history file to load/save from/to. */
58 static char *
59 status_prompt_find_history_file(void)
60 {
61 	const char	*home, *history_file;
62 	char		*path;
63 
64 	history_file = options_get_string(global_options, "history-file");
65 	if (*history_file == '\0')
66 		return (NULL);
67 	if (*history_file == '/')
68 		return (xstrdup(history_file));
69 
70 	if (history_file[0] != '~' || history_file[1] != '/')
71 		return (NULL);
72 	if ((home = find_home()) == NULL)
73 		return (NULL);
74 	xasprintf(&path, "%s%s", home, history_file + 1);
75 	return (path);
76 }
77 
78 /* Load status prompt history from file. */
79 void
80 status_prompt_load_history(void)
81 {
82 	FILE	*f;
83 	char	*history_file, *line, *tmp;
84 	size_t	 length;
85 
86 	if ((history_file = status_prompt_find_history_file()) == NULL)
87 		return;
88 	log_debug("loading history from %s", history_file);
89 
90 	f = fopen(history_file, "r");
91 	if (f == NULL) {
92 		log_debug("%s: %s", history_file, strerror(errno));
93 		free(history_file);
94 		return;
95 	}
96 	free(history_file);
97 
98 	for (;;) {
99 		if ((line = fgetln(f, &length)) == NULL)
100 			break;
101 
102 		if (length > 0) {
103 			if (line[length - 1] == '\n') {
104 				line[length - 1] = '\0';
105 				status_prompt_add_history(line);
106 			} else {
107 				tmp = xmalloc(length + 1);
108 				memcpy(tmp, line, length);
109 				tmp[length] = '\0';
110 				status_prompt_add_history(tmp);
111 				free(tmp);
112 			}
113 		}
114 	}
115 	fclose(f);
116 }
117 
118 /* Save status prompt history to file. */
119 void
120 status_prompt_save_history(void)
121 {
122 	FILE	*f;
123 	u_int	 i;
124 	char	*history_file;
125 
126 	if ((history_file = status_prompt_find_history_file()) == NULL)
127 		return;
128 	log_debug("saving history to %s", history_file);
129 
130 	f = fopen(history_file, "w");
131 	if (f == NULL) {
132 		log_debug("%s: %s", history_file, strerror(errno));
133 		free(history_file);
134 		return;
135 	}
136 	free(history_file);
137 
138 	for (i = 0; i < status_prompt_hsize; i++) {
139 		fputs(status_prompt_hlist[i], f);
140 		fputc('\n', f);
141 	}
142 	fclose(f);
143 
144 }
145 
146 /* Status timer callback. */
147 static void
148 status_timer_callback(__unused int fd, __unused short events, void *arg)
149 {
150 	struct client	*c = arg;
151 	struct session	*s = c->session;
152 	struct timeval	 tv;
153 
154 	evtimer_del(&c->status_timer);
155 
156 	if (s == NULL)
157 		return;
158 
159 	if (c->message_string == NULL && c->prompt_string == NULL)
160 		c->flags |= CLIENT_STATUS;
161 
162 	timerclear(&tv);
163 	tv.tv_sec = options_get_number(s->options, "status-interval");
164 
165 	if (tv.tv_sec != 0)
166 		evtimer_add(&c->status_timer, &tv);
167 	log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
168 }
169 
170 /* Start status timer for client. */
171 void
172 status_timer_start(struct client *c)
173 {
174 	struct session	*s = c->session;
175 
176 	if (event_initialized(&c->status_timer))
177 		evtimer_del(&c->status_timer);
178 	else
179 		evtimer_set(&c->status_timer, status_timer_callback, c);
180 
181 	if (s != NULL && options_get_number(s->options, "status"))
182 		status_timer_callback(-1, 0, c);
183 }
184 
185 /* Start status timer for all clients. */
186 void
187 status_timer_start_all(void)
188 {
189 	struct client	*c;
190 
191 	TAILQ_FOREACH(c, &clients, entry)
192 		status_timer_start(c);
193 }
194 
195 /* Update status cache. */
196 void
197 status_update_saved(struct session *s)
198 {
199 	if (!options_get_number(s->options, "status"))
200 		s->statusat = -1;
201 	else if (options_get_number(s->options, "status-position") == 0)
202 		s->statusat = 0;
203 	else
204 		s->statusat = 1;
205 }
206 
207 /* Get screen line of status line. -1 means off. */
208 int
209 status_at_line(struct client *c)
210 {
211 	struct session	*s = c->session;
212 
213 	if (s->statusat != 1)
214 		return (s->statusat);
215 	return (c->tty.sy - 1);
216 }
217 
218 /* Retrieve options for left string. */
219 static char *
220 status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc,
221     size_t *size)
222 {
223 	struct session	*s = c->session;
224 	const char	*template;
225 	char		*left;
226 	size_t		 leftlen;
227 
228 	style_apply_update(gc, s->options, "status-left-style");
229 
230 	template = options_get_string(s->options, "status-left");
231 	left = status_replace(c, NULL, template, t);
232 
233 	*size = options_get_number(s->options, "status-left-length");
234 	leftlen = screen_write_cstrlen("%s", left);
235 	if (leftlen < *size)
236 		*size = leftlen;
237 	return (left);
238 }
239 
240 /* Retrieve options for right string. */
241 static char *
242 status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc,
243     size_t *size)
244 {
245 	struct session	*s = c->session;
246 	const char	*template;
247 	char		*right;
248 	size_t		 rightlen;
249 
250 	style_apply_update(gc, s->options, "status-right-style");
251 
252 	template = options_get_string(s->options, "status-right");
253 	right = status_replace(c, NULL, template, t);
254 
255 	*size = options_get_number(s->options, "status-right-length");
256 	rightlen = screen_write_cstrlen("%s", right);
257 	if (rightlen < *size)
258 		*size = rightlen;
259 	return (right);
260 }
261 
262 /* Get window at window list position. */
263 struct window *
264 status_get_window_at(struct client *c, u_int x)
265 {
266 	struct session	*s = c->session;
267 	struct winlink	*wl;
268 	struct options	*oo;
269 	const char	*sep;
270 	size_t		 seplen;
271 
272 	x += c->wlmouse;
273 	RB_FOREACH(wl, winlinks, &s->windows) {
274 		oo = wl->window->options;
275 
276 		sep = options_get_string(oo, "window-status-separator");
277 		seplen = screen_write_cstrlen("%s", sep);
278 
279 		if (x < wl->status_width)
280 			return (wl->window);
281 		x -= wl->status_width + seplen;
282 	}
283 	return (NULL);
284 }
285 
286 /* Draw status for client on the last lines of given context. */
287 int
288 status_redraw(struct client *c)
289 {
290 	struct screen_write_ctx	 ctx;
291 	struct session		*s = c->session;
292 	struct winlink		*wl;
293 	struct screen		 old_status, window_list;
294 	struct grid_cell	 stdgc, lgc, rgc, gc;
295 	struct options		*oo;
296 	time_t			 t;
297 	char			*left, *right;
298 	const char		*sep;
299 	u_int			 offset, needed;
300 	u_int			 wlstart, wlwidth, wlavailable, wloffset, wlsize;
301 	size_t			 llen, rlen, seplen;
302 	int			 larrow, rarrow;
303 
304 	/* Delete the saved status line, if any. */
305 	if (c->old_status != NULL) {
306 		screen_free(c->old_status);
307 		free(c->old_status);
308 		c->old_status = NULL;
309 	}
310 
311 	/* No status line? */
312 	if (c->tty.sy == 0 || !options_get_number(s->options, "status"))
313 		return (1);
314 	left = right = NULL;
315 	larrow = rarrow = 0;
316 
317 	/* Store current time. */
318 	t = time(NULL);
319 
320 	/* Set up default colour. */
321 	style_apply(&stdgc, s->options, "status-style");
322 
323 	/* Create the target screen. */
324 	memcpy(&old_status, &c->status, sizeof old_status);
325 	screen_init(&c->status, c->tty.sx, 1, 0);
326 	screen_write_start(&ctx, NULL, &c->status);
327 	for (offset = 0; offset < c->tty.sx; offset++)
328 		screen_write_putc(&ctx, &stdgc, ' ');
329 	screen_write_stop(&ctx);
330 
331 	/* If the height is one line, blank status line. */
332 	if (c->tty.sy <= 1)
333 		goto out;
334 
335 	/* Work out left and right strings. */
336 	memcpy(&lgc, &stdgc, sizeof lgc);
337 	left = status_redraw_get_left(c, t, &lgc, &llen);
338 	memcpy(&rgc, &stdgc, sizeof rgc);
339 	right = status_redraw_get_right(c, t, &rgc, &rlen);
340 
341 	/*
342 	 * Figure out how much space we have for the window list. If there
343 	 * isn't enough space, just show a blank status line.
344 	 */
345 	needed = 0;
346 	if (llen != 0)
347 		needed += llen;
348 	if (rlen != 0)
349 		needed += rlen;
350 	if (c->tty.sx == 0 || c->tty.sx <= needed)
351 		goto out;
352 	wlavailable = c->tty.sx - needed;
353 
354 	/* Calculate the total size needed for the window list. */
355 	wlstart = wloffset = wlwidth = 0;
356 	RB_FOREACH(wl, winlinks, &s->windows) {
357 		free(wl->status_text);
358 		memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
359 		wl->status_text = status_print(c, wl, t, &wl->status_cell);
360 		wl->status_width = screen_write_cstrlen("%s", wl->status_text);
361 
362 		if (wl == s->curw)
363 			wloffset = wlwidth;
364 
365 		oo = wl->window->options;
366 		sep = options_get_string(oo, "window-status-separator");
367 		seplen = screen_write_cstrlen("%s", sep);
368 		wlwidth += wl->status_width + seplen;
369 	}
370 
371 	/* Create a new screen for the window list. */
372 	screen_init(&window_list, wlwidth, 1, 0);
373 
374 	/* And draw the window list into it. */
375 	screen_write_start(&ctx, NULL, &window_list);
376 	RB_FOREACH(wl, winlinks, &s->windows) {
377 		screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s",
378 		    wl->status_text);
379 
380 		oo = wl->window->options;
381 		sep = options_get_string(oo, "window-status-separator");
382 		screen_write_cnputs(&ctx, -1, &stdgc, "%s", sep);
383 	}
384 	screen_write_stop(&ctx);
385 
386 	/* If there is enough space for the total width, skip to draw now. */
387 	if (wlwidth <= wlavailable)
388 		goto draw;
389 
390 	/* Find size of current window text. */
391 	wlsize = s->curw->status_width;
392 
393 	/*
394 	 * If the current window is already on screen, good to draw from the
395 	 * start and just leave off the end.
396 	 */
397 	if (wloffset + wlsize < wlavailable) {
398 		if (wlavailable > 0) {
399 			rarrow = 1;
400 			wlavailable--;
401 		}
402 		wlwidth = wlavailable;
403 	} else {
404 		/*
405 		 * Work out how many characters we need to omit from the
406 		 * start. There are wlavailable characters to fill, and
407 		 * wloffset + wlsize must be the last. So, the start character
408 		 * is wloffset + wlsize - wlavailable.
409 		 */
410 		if (wlavailable > 0) {
411 			larrow = 1;
412 			wlavailable--;
413 		}
414 
415 		wlstart = wloffset + wlsize - wlavailable;
416 		if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
417 			rarrow = 1;
418 			wlstart++;
419 			wlavailable--;
420 		}
421 		wlwidth = wlavailable;
422 	}
423 
424 	/* Bail if anything is now too small too. */
425 	if (wlwidth == 0 || wlavailable == 0) {
426 		screen_free(&window_list);
427 		goto out;
428 	}
429 
430 	/*
431 	 * Now the start position is known, work out the state of the left and
432 	 * right arrows.
433 	 */
434 	offset = 0;
435 	RB_FOREACH(wl, winlinks, &s->windows) {
436 		if (wl->flags & WINLINK_ALERTFLAGS &&
437 		    larrow == 1 && offset < wlstart)
438 			larrow = -1;
439 
440 		offset += wl->status_width;
441 
442 		if (wl->flags & WINLINK_ALERTFLAGS &&
443 		    rarrow == 1 && offset > wlstart + wlwidth)
444 			rarrow = -1;
445 	}
446 
447 draw:
448 	/* Begin drawing. */
449 	screen_write_start(&ctx, NULL, &c->status);
450 
451 	/* Draw the left string and arrow. */
452 	screen_write_cursormove(&ctx, 0, 0);
453 	if (llen != 0)
454 		screen_write_cnputs(&ctx, llen, &lgc, "%s", left);
455 	if (larrow != 0) {
456 		memcpy(&gc, &stdgc, sizeof gc);
457 		if (larrow == -1)
458 			gc.attr ^= GRID_ATTR_REVERSE;
459 		screen_write_putc(&ctx, &gc, '<');
460 	}
461 
462 	/* Draw the right string and arrow. */
463 	if (rarrow != 0) {
464 		screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
465 		memcpy(&gc, &stdgc, sizeof gc);
466 		if (rarrow == -1)
467 			gc.attr ^= GRID_ATTR_REVERSE;
468 		screen_write_putc(&ctx, &gc, '>');
469 	} else
470 		screen_write_cursormove(&ctx, c->tty.sx - rlen, 0);
471 	if (rlen != 0)
472 		screen_write_cnputs(&ctx, rlen, &rgc, "%s", right);
473 
474 	/* Figure out the offset for the window list. */
475 	if (llen != 0)
476 		wloffset = llen;
477 	else
478 		wloffset = 0;
479 	if (wlwidth < wlavailable) {
480 		switch (options_get_number(s->options, "status-justify")) {
481 		case 1:	/* centred */
482 			wloffset += (wlavailable - wlwidth) / 2;
483 			break;
484 		case 2:	/* right */
485 			wloffset += (wlavailable - wlwidth);
486 			break;
487 		}
488 	}
489 	if (larrow != 0)
490 		wloffset++;
491 
492 	/* Copy the window list. */
493 	c->wlmouse = -wloffset + wlstart;
494 	screen_write_cursormove(&ctx, wloffset, 0);
495 	screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1, NULL,
496 	    NULL);
497 	screen_free(&window_list);
498 
499 	screen_write_stop(&ctx);
500 
501 out:
502 	free(left);
503 	free(right);
504 
505 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
506 		screen_free(&old_status);
507 		return (0);
508 	}
509 	screen_free(&old_status);
510 	return (1);
511 }
512 
513 /* Replace special sequences in fmt. */
514 static char *
515 status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
516 {
517 	struct format_tree	*ft;
518 	char			*expanded;
519 	u_int			 tag;
520 
521 	if (fmt == NULL)
522 		return (xstrdup(""));
523 
524 	if (wl != NULL)
525 		tag = FORMAT_WINDOW|wl->window->id;
526 	else
527 		tag = FORMAT_NONE;
528 	if (c->flags & CLIENT_STATUSFORCE)
529 		ft = format_create(c, NULL, tag, FORMAT_STATUS|FORMAT_FORCE);
530 	else
531 		ft = format_create(c, NULL, tag, FORMAT_STATUS);
532 	format_defaults(ft, c, NULL, wl, NULL);
533 
534 	expanded = format_expand_time(ft, fmt, t);
535 
536 	format_free(ft);
537 	return (expanded);
538 }
539 
540 /* Return winlink status line entry and adjust gc as necessary. */
541 static char *
542 status_print(struct client *c, struct winlink *wl, time_t t,
543     struct grid_cell *gc)
544 {
545 	struct options	*oo = wl->window->options;
546 	struct session	*s = c->session;
547 	const char	*fmt;
548 	char   		*text;
549 
550 	style_apply_update(gc, oo, "window-status-style");
551 	fmt = options_get_string(oo, "window-status-format");
552 	if (wl == s->curw) {
553 		style_apply_update(gc, oo, "window-status-current-style");
554 		fmt = options_get_string(oo, "window-status-current-format");
555 	}
556 	if (wl == TAILQ_FIRST(&s->lastw))
557 		style_apply_update(gc, oo, "window-status-last-style");
558 
559 	if (wl->flags & WINLINK_BELL)
560 		style_apply_update(gc, oo, "window-status-bell-style");
561 	else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
562 		style_apply_update(gc, oo, "window-status-activity-style");
563 
564 	text = status_replace(c, wl, fmt, t);
565 	return (text);
566 }
567 
568 /* Set a status line message. */
569 void
570 status_message_set(struct client *c, const char *fmt, ...)
571 {
572 	struct timeval	tv;
573 	va_list		ap;
574 	int		delay;
575 
576 	status_message_clear(c);
577 
578 	if (c->old_status == NULL) {
579 		c->old_status = xmalloc(sizeof *c->old_status);
580 		memcpy(c->old_status, &c->status, sizeof *c->old_status);
581 		screen_init(&c->status, c->tty.sx, 1, 0);
582 	}
583 
584 	va_start(ap, fmt);
585 	xvasprintf(&c->message_string, fmt, ap);
586 	va_end(ap);
587 
588 	server_client_add_message(c, "%s", c->message_string);
589 
590 	delay = options_get_number(c->session->options, "display-time");
591 	if (delay > 0) {
592 		tv.tv_sec = delay / 1000;
593 		tv.tv_usec = (delay % 1000) * 1000L;
594 
595 		if (event_initialized(&c->message_timer))
596 			evtimer_del(&c->message_timer);
597 		evtimer_set(&c->message_timer, status_message_callback, c);
598 		evtimer_add(&c->message_timer, &tv);
599 	}
600 
601 	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
602 	c->flags |= CLIENT_STATUS;
603 }
604 
605 /* Clear status line message. */
606 void
607 status_message_clear(struct client *c)
608 {
609 	if (c->message_string == NULL)
610 		return;
611 
612 	free(c->message_string);
613 	c->message_string = NULL;
614 
615 	if (c->prompt_string == NULL)
616 		c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
617 	c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
618 
619 	screen_reinit(&c->status);
620 }
621 
622 /* Clear status line message after timer expires. */
623 static void
624 status_message_callback(__unused int fd, __unused short event, void *data)
625 {
626 	struct client	*c = data;
627 
628 	status_message_clear(c);
629 }
630 
631 /* Draw client message on status line of present else on last line. */
632 int
633 status_message_redraw(struct client *c)
634 {
635 	struct screen_write_ctx		ctx;
636 	struct session		       *s = c->session;
637 	struct screen		        old_status;
638 	size_t			        len;
639 	struct grid_cell		gc;
640 
641 	if (c->tty.sx == 0 || c->tty.sy == 0)
642 		return (0);
643 	memcpy(&old_status, &c->status, sizeof old_status);
644 	screen_init(&c->status, c->tty.sx, 1, 0);
645 
646 	len = screen_write_strlen("%s", c->message_string);
647 	if (len > c->tty.sx)
648 		len = c->tty.sx;
649 
650 	style_apply(&gc, s->options, "message-style");
651 
652 	screen_write_start(&ctx, NULL, &c->status);
653 
654 	screen_write_cursormove(&ctx, 0, 0);
655 	screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
656 	for (; len < c->tty.sx; len++)
657 		screen_write_putc(&ctx, &gc, ' ');
658 
659 	screen_write_stop(&ctx);
660 
661 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
662 		screen_free(&old_status);
663 		return (0);
664 	}
665 	screen_free(&old_status);
666 	return (1);
667 }
668 
669 /* Enable status line prompt. */
670 void
671 status_prompt_set(struct client *c, const char *msg, const char *input,
672     prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags)
673 {
674 	struct format_tree	*ft;
675 	time_t			 t;
676 	char			*tmp, *cp;
677 
678 	ft = format_create(c, NULL, FORMAT_NONE, 0);
679 	format_defaults(ft, c, NULL, NULL, NULL);
680 	t = time(NULL);
681 
682 	if (input == NULL)
683 		input = "";
684 	if (flags & PROMPT_NOFORMAT)
685 		tmp = xstrdup(input);
686 	else
687 		tmp = format_expand_time(ft, input, t);
688 
689 	status_message_clear(c);
690 	status_prompt_clear(c);
691 
692 	if (c->old_status == NULL) {
693 		c->old_status = xmalloc(sizeof *c->old_status);
694 		memcpy(c->old_status, &c->status, sizeof *c->old_status);
695 		screen_init(&c->status, c->tty.sx, 1, 0);
696 	}
697 
698 	c->prompt_string = format_expand_time(ft, msg, t);
699 
700 	c->prompt_buffer = utf8_fromcstr(tmp);
701 	c->prompt_index = utf8_strlen(c->prompt_buffer);
702 
703 	c->prompt_inputcb = inputcb;
704 	c->prompt_freecb = freecb;
705 	c->prompt_data = data;
706 
707 	c->prompt_hindex = 0;
708 
709 	c->prompt_flags = flags;
710 	c->prompt_mode = PROMPT_ENTRY;
711 
712 	if (~flags & PROMPT_INCREMENTAL)
713 		c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
714 	c->flags |= CLIENT_STATUS;
715 
716 	if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') {
717 		xasprintf(&cp, "=%s", tmp);
718 		c->prompt_inputcb(c, c->prompt_data, cp, 0);
719 		free(cp);
720 	}
721 
722 	free(tmp);
723 	format_free(ft);
724 }
725 
726 /* Remove status line prompt. */
727 void
728 status_prompt_clear(struct client *c)
729 {
730 	if (c->prompt_string == NULL)
731 		return;
732 
733 	if (c->prompt_freecb != NULL && c->prompt_data != NULL)
734 		c->prompt_freecb(c->prompt_data);
735 
736 	free(c->prompt_string);
737 	c->prompt_string = NULL;
738 
739 	free(c->prompt_buffer);
740 	c->prompt_buffer = NULL;
741 
742 	c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
743 	c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
744 
745 	screen_reinit(&c->status);
746 }
747 
748 /* Update status line prompt with a new prompt string. */
749 void
750 status_prompt_update(struct client *c, const char *msg, const char *input)
751 {
752 	struct format_tree	*ft;
753 	time_t			 t;
754 	char			*tmp;
755 
756 	ft = format_create(c, NULL, FORMAT_NONE, 0);
757 	format_defaults(ft, c, NULL, NULL, NULL);
758 
759 	t = time(NULL);
760 	tmp = format_expand_time(ft, input, t);
761 
762 	free(c->prompt_string);
763 	c->prompt_string = format_expand_time(ft, msg, t);
764 
765 	free(c->prompt_buffer);
766 	c->prompt_buffer = utf8_fromcstr(tmp);
767 	c->prompt_index = utf8_strlen(c->prompt_buffer);
768 
769 	c->prompt_hindex = 0;
770 
771 	c->flags |= CLIENT_STATUS;
772 
773 	free(tmp);
774 	format_free(ft);
775 }
776 
777 /* Draw client prompt on status line of present else on last line. */
778 int
779 status_prompt_redraw(struct client *c)
780 {
781 	struct screen_write_ctx	 ctx;
782 	struct session		*s = c->session;
783 	struct screen		 old_status;
784 	u_int			 i, offset, left, start, pcursor, pwidth, width;
785 	struct grid_cell	 gc, cursorgc;
786 
787 	if (c->tty.sx == 0 || c->tty.sy == 0)
788 		return (0);
789 	memcpy(&old_status, &c->status, sizeof old_status);
790 	screen_init(&c->status, c->tty.sx, 1, 0);
791 
792 	if (c->prompt_mode == PROMPT_COMMAND)
793 		style_apply(&gc, s->options, "message-command-style");
794 	else
795 		style_apply(&gc, s->options, "message-style");
796 
797 	memcpy(&cursorgc, &gc, sizeof cursorgc);
798 	cursorgc.attr ^= GRID_ATTR_REVERSE;
799 
800 	start = screen_write_strlen("%s", c->prompt_string);
801 	if (start > c->tty.sx)
802 		start = c->tty.sx;
803 
804 	screen_write_start(&ctx, NULL, &c->status);
805 	screen_write_cursormove(&ctx, 0, 0);
806 	screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
807 	while (c->status.cx < screen_size_x(&c->status))
808 		screen_write_putc(&ctx, &gc, ' ');
809 	screen_write_cursormove(&ctx, start, 0);
810 
811 	left = c->tty.sx - start;
812 	if (left == 0)
813 		goto finished;
814 
815 	pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index);
816 	pwidth = utf8_strwidth(c->prompt_buffer, -1);
817 	if (pcursor >= left) {
818 		/*
819 		 * The cursor would be outside the screen so start drawing
820 		 * with it on the right.
821 		 */
822 		offset = (pcursor - left) + 1;
823 		pwidth = left;
824 	} else
825 		offset = 0;
826 	if (pwidth > left)
827 		pwidth = left;
828 
829 	width = 0;
830 	for (i = 0; c->prompt_buffer[i].size != 0; i++) {
831 		if (width < offset) {
832 			width += c->prompt_buffer[i].width;
833 			continue;
834 		}
835 		if (width >= offset + pwidth)
836 			break;
837 		width += c->prompt_buffer[i].width;
838 		if (width > offset + pwidth)
839 			break;
840 
841 		if (i != c->prompt_index) {
842 			utf8_copy(&gc.data, &c->prompt_buffer[i]);
843 			screen_write_cell(&ctx, &gc);
844 		} else {
845 			utf8_copy(&cursorgc.data, &c->prompt_buffer[i]);
846 			screen_write_cell(&ctx, &cursorgc);
847 		}
848 	}
849 	if (c->status.cx < screen_size_x(&c->status) && c->prompt_index >= i)
850 		screen_write_putc(&ctx, &cursorgc, ' ');
851 
852 finished:
853 	screen_write_stop(&ctx);
854 
855 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
856 		screen_free(&old_status);
857 		return (0);
858 	}
859 	screen_free(&old_status);
860 	return (1);
861 }
862 
863 /* Is this a separator? */
864 static int
865 status_prompt_in_list(const char *ws, const struct utf8_data *ud)
866 {
867 	if (ud->size != 1 || ud->width != 1)
868 		return (0);
869 	return (strchr(ws, *ud->data) != NULL);
870 }
871 
872 /* Is this a space? */
873 static int
874 status_prompt_space(const struct utf8_data *ud)
875 {
876 	if (ud->size != 1 || ud->width != 1)
877 		return (0);
878 	return (*ud->data == ' ');
879 }
880 
881 /*
882  * Translate key from emacs to vi. Return 0 to drop key, 1 to process the key
883  * as an emacs key; return 2 to append to the buffer.
884  */
885 static int
886 status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
887 {
888 	if (c->prompt_mode == PROMPT_ENTRY) {
889 		switch (key) {
890 		case '\003': /* C-c */
891 		case '\010': /* C-h */
892 		case '\011': /* Tab */
893 		case '\025': /* C-u */
894 		case '\027': /* C-w */
895 		case '\n':
896 		case '\r':
897 		case KEYC_BSPACE:
898 		case KEYC_DC:
899 		case KEYC_DOWN:
900 		case KEYC_END:
901 		case KEYC_HOME:
902 		case KEYC_LEFT:
903 		case KEYC_RIGHT:
904 		case KEYC_UP:
905 			*new_key = key;
906 			return (1);
907 		case '\033': /* Escape */
908 			c->prompt_mode = PROMPT_COMMAND;
909 			c->flags |= CLIENT_STATUS;
910 			return (0);
911 		}
912 		*new_key = key;
913 		return (2);
914 	}
915 
916 	switch (key) {
917 	case 'A':
918 	case 'I':
919 	case 'C':
920 	case 's':
921 	case 'a':
922 		c->prompt_mode = PROMPT_ENTRY;
923 		c->flags |= CLIENT_STATUS;
924 		break; /* switch mode and... */
925 	case 'S':
926 		c->prompt_mode = PROMPT_ENTRY;
927 		c->flags |= CLIENT_STATUS;
928 		*new_key = '\025'; /* C-u */
929 		return (1);
930 	case 'i':
931 	case '\033': /* Escape */
932 		c->prompt_mode = PROMPT_ENTRY;
933 		c->flags |= CLIENT_STATUS;
934 		return (0);
935 	}
936 
937 	switch (key) {
938 	case 'A':
939 	case '$':
940 		*new_key = KEYC_END;
941 		return (1);
942 	case 'I':
943 	case '0':
944 	case '^':
945 		*new_key = KEYC_HOME;
946 		return (1);
947 	case 'C':
948 	case 'D':
949 		*new_key = '\013'; /* C-k */
950 		return (1);
951 	case KEYC_BSPACE:
952 	case 'X':
953 		*new_key = KEYC_BSPACE;
954 		return (1);
955 	case 'b':
956 	case 'B':
957 		*new_key = 'b'|KEYC_ESCAPE;
958 		return (1);
959 	case 'd':
960 		*new_key = '\025';
961 		return (1);
962 	case 'e':
963 	case 'E':
964 	case 'w':
965 	case 'W':
966 		*new_key = 'f'|KEYC_ESCAPE;
967 		return (1);
968 	case 'p':
969 		*new_key = '\031'; /* C-y */
970 		return (1);
971 	case 's':
972 	case KEYC_DC:
973 	case 'x':
974 		*new_key = KEYC_DC;
975 		return (1);
976 	case KEYC_DOWN:
977 	case 'j':
978 		*new_key = KEYC_DOWN;
979 		return (1);
980 	case KEYC_LEFT:
981 	case 'h':
982 		*new_key = KEYC_LEFT;
983 		return (1);
984 	case 'a':
985 	case KEYC_RIGHT:
986 	case 'l':
987 		*new_key = KEYC_RIGHT;
988 		return (1);
989 	case KEYC_UP:
990 	case 'k':
991 		*new_key = KEYC_UP;
992 		return (1);
993 	case '\010' /* C-h */:
994 	case '\003' /* C-c */:
995 	case '\n':
996 	case '\r':
997 		return (1);
998 	}
999 	return (0);
1000 }
1001 
1002 /* Handle keys in prompt. */
1003 int
1004 status_prompt_key(struct client *c, key_code key)
1005 {
1006 	struct options		*oo = c->session->options;
1007 	struct paste_buffer	*pb;
1008 	char			*s, *cp, word[64], prefix = '=';
1009 	const char		*histstr, *bufdata, *ws = NULL;
1010 	u_char			 ch;
1011 	size_t			 size, n, off, idx, bufsize, used;
1012 	struct utf8_data	 tmp, *first, *last, *ud;
1013 	int			 keys;
1014 
1015 	size = utf8_strlen(c->prompt_buffer);
1016 
1017 	if (c->prompt_flags & PROMPT_NUMERIC) {
1018 		if (key >= '0' && key <= '9')
1019 			goto append_key;
1020 		s = utf8_tocstr(c->prompt_buffer);
1021 		c->prompt_inputcb(c, c->prompt_data, s, 1);
1022 		status_prompt_clear(c);
1023 		free(s);
1024 		return (1);
1025 	}
1026 
1027 	keys = options_get_number(c->session->options, "status-keys");
1028 	if (keys == MODEKEY_VI) {
1029 		switch (status_prompt_translate_key(c, key, &key)) {
1030 		case 1:
1031 			goto process_key;
1032 		case 2:
1033 			goto append_key;
1034 		default:
1035 			return (0);
1036 		}
1037 	}
1038 
1039 process_key:
1040 	switch (key) {
1041 	case KEYC_LEFT:
1042 	case '\002': /* C-b */
1043 		if (c->prompt_index > 0) {
1044 			c->prompt_index--;
1045 			break;
1046 		}
1047 		break;
1048 	case KEYC_RIGHT:
1049 	case '\006': /* C-f */
1050 		if (c->prompt_index < size) {
1051 			c->prompt_index++;
1052 			break;
1053 		}
1054 		break;
1055 	case KEYC_HOME:
1056 	case '\001': /* C-a */
1057 		if (c->prompt_index != 0) {
1058 			c->prompt_index = 0;
1059 			break;
1060 		}
1061 		break;
1062 	case KEYC_END:
1063 	case '\005': /* C-e */
1064 		if (c->prompt_index != size) {
1065 			c->prompt_index = size;
1066 			break;
1067 		}
1068 		break;
1069 	case '\011': /* Tab */
1070 		if (c->prompt_buffer[0].size == 0)
1071 			break;
1072 
1073 		idx = c->prompt_index;
1074 		if (idx != 0)
1075 			idx--;
1076 
1077 		/* Find the word we are in. */
1078 		first = &c->prompt_buffer[idx];
1079 		while (first > c->prompt_buffer && !status_prompt_space(first))
1080 			first--;
1081 		while (first->size != 0 && status_prompt_space(first))
1082 			first++;
1083 		last = &c->prompt_buffer[idx];
1084 		while (last->size != 0 && !status_prompt_space(last))
1085 			last++;
1086 		while (last > c->prompt_buffer && status_prompt_space(last))
1087 			last--;
1088 		if (last->size != 0)
1089 			last++;
1090 		if (last <= first)
1091 			break;
1092 
1093 		used = 0;
1094 		for (ud = first; ud < last; ud++) {
1095 			if (used + ud->size >= sizeof word)
1096 				break;
1097 			memcpy(word + used, ud->data, ud->size);
1098 			used += ud->size;
1099 		}
1100 		if (ud != last)
1101 			break;
1102 		word[used] = '\0';
1103 
1104 		/* And try to complete it. */
1105 		if ((s = status_prompt_complete(c->session, word)) == NULL)
1106 			break;
1107 
1108 		/* Trim out word. */
1109 		n = size - (last - c->prompt_buffer) + 1; /* with \0 */
1110 		memmove(first, last, n * sizeof *c->prompt_buffer);
1111 		size -= last - first;
1112 
1113 		/* Insert the new word. */
1114 		size += strlen(s);
1115 		off = first - c->prompt_buffer;
1116 		c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
1117 		    sizeof *c->prompt_buffer);
1118 		first = c->prompt_buffer + off;
1119 		memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
1120 		for (idx = 0; idx < strlen(s); idx++)
1121 			utf8_set(&first[idx], s[idx]);
1122 
1123 		c->prompt_index = (first - c->prompt_buffer) + strlen(s);
1124 		free(s);
1125 
1126 		goto changed;
1127 	case KEYC_BSPACE:
1128 	case '\010': /* C-h */
1129 		if (c->prompt_index != 0) {
1130 			if (c->prompt_index == size)
1131 				c->prompt_buffer[--c->prompt_index].size = 0;
1132 			else {
1133 				memmove(c->prompt_buffer + c->prompt_index - 1,
1134 				    c->prompt_buffer + c->prompt_index,
1135 				    (size + 1 - c->prompt_index) *
1136 				    sizeof *c->prompt_buffer);
1137 				c->prompt_index--;
1138 			}
1139 			goto changed;
1140 		}
1141 		break;
1142 	case KEYC_DC:
1143 	case '\004': /* C-d */
1144 		if (c->prompt_index != size) {
1145 			memmove(c->prompt_buffer + c->prompt_index,
1146 			    c->prompt_buffer + c->prompt_index + 1,
1147 			    (size + 1 - c->prompt_index) *
1148 			    sizeof *c->prompt_buffer);
1149 			goto changed;
1150 		}
1151 		break;
1152 	case '\025': /* C-u */
1153 		c->prompt_buffer[0].size = 0;
1154 		c->prompt_index = 0;
1155 		goto changed;
1156 	case '\013': /* C-k */
1157 		if (c->prompt_index < size) {
1158 			c->prompt_buffer[c->prompt_index].size = 0;
1159 			goto changed;
1160 		}
1161 		break;
1162 	case '\027': /* C-w */
1163 		ws = options_get_string(oo, "word-separators");
1164 		idx = c->prompt_index;
1165 
1166 		/* Find a non-separator. */
1167 		while (idx != 0) {
1168 			idx--;
1169 			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1170 				break;
1171 		}
1172 
1173 		/* Find the separator at the beginning of the word. */
1174 		while (idx != 0) {
1175 			idx--;
1176 			if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
1177 				/* Go back to the word. */
1178 				idx++;
1179 				break;
1180 			}
1181 		}
1182 
1183 		memmove(c->prompt_buffer + idx,
1184 		    c->prompt_buffer + c->prompt_index,
1185 		    (size + 1 - c->prompt_index) *
1186 		    sizeof *c->prompt_buffer);
1187 		memset(c->prompt_buffer + size - (c->prompt_index - idx),
1188 		    '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer);
1189 		c->prompt_index = idx;
1190 
1191 		goto changed;
1192 	case 'f'|KEYC_ESCAPE:
1193 		ws = options_get_string(oo, "word-separators");
1194 
1195 		/* Find a word. */
1196 		while (c->prompt_index != size) {
1197 			idx = ++c->prompt_index;
1198 			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1199 				break;
1200 		}
1201 
1202 		/* Find the separator at the end of the word. */
1203 		while (c->prompt_index != size) {
1204 			idx = ++c->prompt_index;
1205 			if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1206 				break;
1207 		}
1208 
1209 		/* Back up to the end-of-word like vi. */
1210 		if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
1211 		    c->prompt_index != 0)
1212 			c->prompt_index--;
1213 
1214 		goto changed;
1215 	case 'b'|KEYC_ESCAPE:
1216 		ws = options_get_string(oo, "word-separators");
1217 
1218 		/* Find a non-separator. */
1219 		while (c->prompt_index != 0) {
1220 			idx = --c->prompt_index;
1221 			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1222 				break;
1223 		}
1224 
1225 		/* Find the separator at the beginning of the word. */
1226 		while (c->prompt_index != 0) {
1227 			idx = --c->prompt_index;
1228 			if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
1229 				/* Go back to the word. */
1230 				c->prompt_index++;
1231 				break;
1232 			}
1233 		}
1234 		goto changed;
1235 	case KEYC_UP:
1236 	case '\020': /* C-p */
1237 		histstr = status_prompt_up_history(&c->prompt_hindex);
1238 		if (histstr == NULL)
1239 			break;
1240 		free(c->prompt_buffer);
1241 		c->prompt_buffer = utf8_fromcstr(histstr);
1242 		c->prompt_index = utf8_strlen(c->prompt_buffer);
1243 		goto changed;
1244 	case KEYC_DOWN:
1245 	case '\016': /* C-n */
1246 		histstr = status_prompt_down_history(&c->prompt_hindex);
1247 		if (histstr == NULL)
1248 			break;
1249 		free(c->prompt_buffer);
1250 		c->prompt_buffer = utf8_fromcstr(histstr);
1251 		c->prompt_index = utf8_strlen(c->prompt_buffer);
1252 		goto changed;
1253 	case '\031': /* C-y */
1254 		if ((pb = paste_get_top(NULL)) == NULL)
1255 			break;
1256 		bufdata = paste_buffer_data(pb, &bufsize);
1257 		for (n = 0; n < bufsize; n++) {
1258 			ch = (u_char)bufdata[n];
1259 			if (ch < 32 || ch >= 127)
1260 				break;
1261 		}
1262 
1263 		c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
1264 		    sizeof *c->prompt_buffer);
1265 		if (c->prompt_index == size) {
1266 			for (idx = 0; idx < n; idx++) {
1267 				ud = &c->prompt_buffer[c->prompt_index + idx];
1268 				utf8_set(ud, bufdata[idx]);
1269 			}
1270 			c->prompt_index += n;
1271 			c->prompt_buffer[c->prompt_index].size = 0;
1272 		} else {
1273 			memmove(c->prompt_buffer + c->prompt_index + n,
1274 			    c->prompt_buffer + c->prompt_index,
1275 			    (size + 1 - c->prompt_index) *
1276 			    sizeof *c->prompt_buffer);
1277 			for (idx = 0; idx < n; idx++) {
1278 				ud = &c->prompt_buffer[c->prompt_index + idx];
1279 				utf8_set(ud, bufdata[idx]);
1280 			}
1281 			c->prompt_index += n;
1282 		}
1283 		goto changed;
1284 	case '\024': /* C-t */
1285 		idx = c->prompt_index;
1286 		if (idx < size)
1287 			idx++;
1288 		if (idx >= 2) {
1289 			utf8_copy(&tmp, &c->prompt_buffer[idx - 2]);
1290 			utf8_copy(&c->prompt_buffer[idx - 2],
1291 			    &c->prompt_buffer[idx - 1]);
1292 			utf8_copy(&c->prompt_buffer[idx - 1], &tmp);
1293 			c->prompt_index = idx;
1294 			goto changed;
1295 		}
1296 		break;
1297 	case '\r':
1298 	case '\n':
1299 		s = utf8_tocstr(c->prompt_buffer);
1300 		if (*s != '\0')
1301 			status_prompt_add_history(s);
1302 		if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
1303 			status_prompt_clear(c);
1304 		free(s);
1305 		break;
1306 	case '\033': /* Escape */
1307 	case '\003': /* C-c */
1308 		if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
1309 			status_prompt_clear(c);
1310 		break;
1311 	case '\022': /* C-r */
1312 		if (c->prompt_flags & PROMPT_INCREMENTAL) {
1313 			prefix = '-';
1314 			goto changed;
1315 		}
1316 		break;
1317 	case '\023': /* C-s */
1318 		if (c->prompt_flags & PROMPT_INCREMENTAL) {
1319 			prefix = '+';
1320 			goto changed;
1321 		}
1322 		break;
1323 	default:
1324 		goto append_key;
1325 	}
1326 
1327 	c->flags |= CLIENT_STATUS;
1328 	return (0);
1329 
1330 append_key:
1331 	if (key <= 0x1f || key >= KEYC_BASE)
1332 		return (0);
1333 	if (utf8_split(key, &tmp) != UTF8_DONE)
1334 		return (0);
1335 
1336 	c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2,
1337 	    sizeof *c->prompt_buffer);
1338 
1339 	if (c->prompt_index == size) {
1340 		utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
1341 		c->prompt_index++;
1342 		c->prompt_buffer[c->prompt_index].size = 0;
1343 	} else {
1344 		memmove(c->prompt_buffer + c->prompt_index + 1,
1345 		    c->prompt_buffer + c->prompt_index,
1346 		    (size + 1 - c->prompt_index) *
1347 		    sizeof *c->prompt_buffer);
1348 		utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
1349 		c->prompt_index++;
1350 	}
1351 
1352 	if (c->prompt_flags & PROMPT_SINGLE) {
1353 		s = utf8_tocstr(c->prompt_buffer);
1354 		if (strlen(s) != 1)
1355 			status_prompt_clear(c);
1356 		else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
1357 			status_prompt_clear(c);
1358 		free(s);
1359 	}
1360 
1361 changed:
1362 	c->flags |= CLIENT_STATUS;
1363 	if (c->prompt_flags & PROMPT_INCREMENTAL) {
1364 		s = utf8_tocstr(c->prompt_buffer);
1365 		xasprintf(&cp, "%c%s", prefix, s);
1366 		c->prompt_inputcb(c, c->prompt_data, cp, 0);
1367 		free(cp);
1368 		free(s);
1369 	}
1370 	return (0);
1371 }
1372 
1373 /* Get previous line from the history. */
1374 static const char *
1375 status_prompt_up_history(u_int *idx)
1376 {
1377 	/*
1378 	 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1379 	 * empty.
1380 	 */
1381 
1382 	if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
1383 		return (NULL);
1384 	(*idx)++;
1385 	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1386 }
1387 
1388 /* Get next line from the history. */
1389 static const char *
1390 status_prompt_down_history(u_int *idx)
1391 {
1392 	if (status_prompt_hsize == 0 || *idx == 0)
1393 		return ("");
1394 	(*idx)--;
1395 	if (*idx == 0)
1396 		return ("");
1397 	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1398 }
1399 
1400 /* Add line to the history. */
1401 static void
1402 status_prompt_add_history(const char *line)
1403 {
1404 	size_t	size;
1405 
1406 	if (status_prompt_hsize > 0 &&
1407 	    strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
1408 		return;
1409 
1410 	if (status_prompt_hsize == PROMPT_HISTORY) {
1411 		free(status_prompt_hlist[0]);
1412 
1413 		size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
1414 		memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
1415 
1416 		status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
1417 		return;
1418 	}
1419 
1420 	status_prompt_hlist = xreallocarray(status_prompt_hlist,
1421 	    status_prompt_hsize + 1, sizeof *status_prompt_hlist);
1422 	status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
1423 }
1424 
1425 /* Build completion list. */
1426 static const char **
1427 status_prompt_complete_list(u_int *size, const char *s)
1428 {
1429 	const char				**list = NULL, **layout;
1430 	const struct cmd_entry			**cmdent;
1431 	const struct options_table_entry	 *oe;
1432 	const char				 *layouts[] = {
1433 		"even-horizontal", "even-vertical", "main-horizontal",
1434 		"main-vertical", "tiled", NULL
1435 	};
1436 
1437 	*size = 0;
1438 	for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1439 		if (strncmp((*cmdent)->name, s, strlen(s)) == 0) {
1440 			list = xreallocarray(list, (*size) + 1, sizeof *list);
1441 			list[(*size)++] = (*cmdent)->name;
1442 		}
1443 	}
1444 	for (oe = options_table; oe->name != NULL; oe++) {
1445 		if (strncmp(oe->name, s, strlen(s)) == 0) {
1446 			list = xreallocarray(list, (*size) + 1, sizeof *list);
1447 			list[(*size)++] = oe->name;
1448 		}
1449 	}
1450 	for (layout = layouts; *layout != NULL; layout++) {
1451 		if (strncmp(*layout, s, strlen(s)) == 0) {
1452 			list = xreallocarray(list, (*size) + 1, sizeof *list);
1453 			list[(*size)++] = *layout;
1454 		}
1455 	}
1456 	return (list);
1457 }
1458 
1459 /* Find longest prefix. */
1460 static char *
1461 status_prompt_complete_prefix(const char **list, u_int size)
1462 {
1463 	char	 *out;
1464 	u_int	  i;
1465 	size_t	  j;
1466 
1467 	out = xstrdup(list[0]);
1468 	for (i = 1; i < size; i++) {
1469 		j = strlen(list[i]);
1470 		if (j > strlen(out))
1471 			j = strlen(out);
1472 		for (; j > 0; j--) {
1473 			if (out[j - 1] != list[i][j - 1])
1474 				out[j - 1] = '\0';
1475 		}
1476 	}
1477 	return (out);
1478 }
1479 
1480 /* Complete word. */
1481 static char *
1482 status_prompt_complete(struct session *session, const char *s)
1483 {
1484 	const char	**list = NULL, *colon;
1485 	u_int		  size = 0, i;
1486 	struct session	 *s_loop;
1487 	struct winlink	 *wl;
1488 	struct window	 *w;
1489 	char		 *copy, *out, *tmp;
1490 
1491 	if (*s == '\0')
1492 		return (NULL);
1493 	out = NULL;
1494 
1495 	if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
1496 		list = status_prompt_complete_list(&size, s);
1497 		if (size == 0)
1498 			out = NULL;
1499 		else if (size == 1)
1500 			xasprintf(&out, "%s ", list[0]);
1501 		else
1502 			out = status_prompt_complete_prefix(list, size);
1503 		free(list);
1504 		return (out);
1505 	}
1506 	copy = xstrdup(s);
1507 
1508 	colon = ":";
1509 	if (copy[strlen(copy) - 1] == ':')
1510 		copy[strlen(copy) - 1] = '\0';
1511 	else
1512 		colon = "";
1513 	s = copy + 2;
1514 
1515 	RB_FOREACH(s_loop, sessions, &sessions) {
1516 		if (strncmp(s_loop->name, s, strlen(s)) == 0) {
1517 			list = xreallocarray(list, size + 2, sizeof *list);
1518 			list[size++] = s_loop->name;
1519 		}
1520 	}
1521 	if (size == 1) {
1522 		out = xstrdup(list[0]);
1523 		if (session_find(list[0]) != NULL)
1524 			colon = ":";
1525 	} else if (size != 0)
1526 		out = status_prompt_complete_prefix(list, size);
1527 	if (out != NULL) {
1528 		xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1529 		free(out);
1530 		out = tmp;
1531 		goto found;
1532 	}
1533 
1534 	colon = "";
1535 	if (*s == ':') {
1536 		RB_FOREACH(wl, winlinks, &session->windows) {
1537 			xasprintf(&tmp, ":%s", wl->window->name);
1538 			if (strncmp(tmp, s, strlen(s)) == 0){
1539 				list = xreallocarray(list, size + 1,
1540 				    sizeof *list);
1541 				list[size++] = tmp;
1542 				continue;
1543 			}
1544 			free(tmp);
1545 
1546 			xasprintf(&tmp, ":%d", wl->idx);
1547 			if (strncmp(tmp, s, strlen(s)) == 0) {
1548 				list = xreallocarray(list, size + 1,
1549 				    sizeof *list);
1550 				list[size++] = tmp;
1551 				continue;
1552 			}
1553 			free(tmp);
1554 		}
1555 	} else {
1556 		RB_FOREACH(s_loop, sessions, &sessions) {
1557 			RB_FOREACH(wl, winlinks, &s_loop->windows) {
1558 				w = wl->window;
1559 
1560 				xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
1561 				if (strncmp(tmp, s, strlen(s)) == 0) {
1562 					list = xreallocarray(list, size + 1,
1563 					    sizeof *list);
1564 					list[size++] = tmp;
1565 					continue;
1566 				}
1567 				free(tmp);
1568 
1569 				xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
1570 				if (strncmp(tmp, s, strlen(s)) == 0) {
1571 					list = xreallocarray(list, size + 1,
1572 					    sizeof *list);
1573 					list[size++] = tmp;
1574 					continue;
1575 				}
1576 				free(tmp);
1577 			}
1578 		}
1579 	}
1580 	if (size == 1) {
1581 		out = xstrdup(list[0]);
1582 		colon = " ";
1583 	} else if (size != 0)
1584 		out = status_prompt_complete_prefix(list, size);
1585 	if (out != NULL) {
1586 		xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1587 		out = tmp;
1588 	}
1589 
1590 	for (i = 0; i < size; i++)
1591 		free((void *)list[i]);
1592 
1593 found:
1594 	free(copy);
1595 	free(list);
1596 	return (out);
1597 }
1598