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