xref: /openbsd-src/usr.bin/tmux/status.c (revision fb8aa7497fded39583f40e800732f9c046411717)
1 /* $OpenBSD: status.c,v 1.149 2016/06/06 07:23:36 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 char   *status_redraw_get_left(struct client *, time_t, struct grid_cell *,
33 	    size_t *);
34 char   *status_redraw_get_right(struct client *, time_t, struct grid_cell *,
35 	    size_t *);
36 char   *status_print(struct client *, struct winlink *, time_t,
37 	    struct grid_cell *);
38 char   *status_replace(struct client *, struct winlink *, const char *, time_t);
39 void	status_message_callback(int, short, void *);
40 void	status_timer_callback(int, short, void *);
41 
42 const char *status_prompt_up_history(u_int *);
43 const char *status_prompt_down_history(u_int *);
44 void	status_prompt_add_history(const char *);
45 
46 const char **status_prompt_complete_list(u_int *, const char *);
47 char   *status_prompt_complete_prefix(const char **, u_int);
48 char   *status_prompt_complete(struct session *, const char *);
49 
50 char   *status_prompt_find_history_file(void);
51 
52 /* Status prompt history. */
53 #define PROMPT_HISTORY 100
54 char	**status_prompt_hlist;
55 u_int	  status_prompt_hsize;
56 
57 /* Find the history file to load/save from/to. */
58 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 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 /* Get screen line of status line. -1 means off. */
196 int
197 status_at_line(struct client *c)
198 {
199 	struct session	*s = c->session;
200 
201 	if (!options_get_number(s->options, "status"))
202 		return (-1);
203 
204 	if (options_get_number(s->options, "status-position") == 0)
205 		return (0);
206 	return (c->tty.sy - 1);
207 }
208 
209 /* Retrieve options for left string. */
210 char *
211 status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc,
212     size_t *size)
213 {
214 	struct session	*s = c->session;
215 	const char	*template;
216 	char		*left;
217 	size_t		 leftlen;
218 
219 	style_apply_update(gc, s->options, "status-left-style");
220 
221 	template = options_get_string(s->options, "status-left");
222 	left = status_replace(c, NULL, template, t);
223 
224 	*size = options_get_number(s->options, "status-left-length");
225 	leftlen = screen_write_cstrlen("%s", left);
226 	if (leftlen < *size)
227 		*size = leftlen;
228 	return (left);
229 }
230 
231 /* Retrieve options for right string. */
232 char *
233 status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc,
234     size_t *size)
235 {
236 	struct session	*s = c->session;
237 	const char	*template;
238 	char		*right;
239 	size_t		 rightlen;
240 
241 	style_apply_update(gc, s->options, "status-right-style");
242 
243 	template = options_get_string(s->options, "status-right");
244 	right = status_replace(c, NULL, template, t);
245 
246 	*size = options_get_number(s->options, "status-right-length");
247 	rightlen = screen_write_cstrlen("%s", right);
248 	if (rightlen < *size)
249 		*size = rightlen;
250 	return (right);
251 }
252 
253 /* Get window at window list position. */
254 struct window *
255 status_get_window_at(struct client *c, u_int x)
256 {
257 	struct session	*s = c->session;
258 	struct winlink	*wl;
259 	struct options	*oo;
260 	const char	*sep;
261 	size_t		 seplen;
262 
263 	x += c->wlmouse;
264 	RB_FOREACH(wl, winlinks, &s->windows) {
265 		oo = wl->window->options;
266 
267 		sep = options_get_string(oo, "window-status-separator");
268 		seplen = screen_write_cstrlen("%s", sep);
269 
270 		if (x < wl->status_width)
271 			return (wl->window);
272 		x -= wl->status_width + seplen;
273 	}
274 	return (NULL);
275 }
276 
277 /* Draw status for client on the last lines of given context. */
278 int
279 status_redraw(struct client *c)
280 {
281 	struct screen_write_ctx	ctx;
282 	struct session	       *s = c->session;
283 	struct winlink	       *wl;
284 	struct screen		old_status, window_list;
285 	struct grid_cell	stdgc, lgc, rgc, gc;
286 	struct options	       *oo;
287 	time_t			t;
288 	char		       *left, *right, *sep;
289 	u_int			offset, needed;
290 	u_int			wlstart, wlwidth, wlavailable, wloffset, wlsize;
291 	size_t			llen, rlen, seplen;
292 	int			larrow, rarrow;
293 
294 	/* No status line? */
295 	if (c->tty.sy == 0 || !options_get_number(s->options, "status"))
296 		return (1);
297 	left = right = NULL;
298 	larrow = rarrow = 0;
299 
300 	/* Store current time. */
301 	t = time(NULL);
302 
303 	/* Set up default colour. */
304 	style_apply(&stdgc, s->options, "status-style");
305 
306 	/* Create the target screen. */
307 	memcpy(&old_status, &c->status, sizeof old_status);
308 	screen_init(&c->status, c->tty.sx, 1, 0);
309 	screen_write_start(&ctx, NULL, &c->status);
310 	for (offset = 0; offset < c->tty.sx; offset++)
311 		screen_write_putc(&ctx, &stdgc, ' ');
312 	screen_write_stop(&ctx);
313 
314 	/* If the height is one line, blank status line. */
315 	if (c->tty.sy <= 1)
316 		goto out;
317 
318 	/* Work out left and right strings. */
319 	memcpy(&lgc, &stdgc, sizeof lgc);
320 	left = status_redraw_get_left(c, t, &lgc, &llen);
321 	memcpy(&rgc, &stdgc, sizeof rgc);
322 	right = status_redraw_get_right(c, t, &rgc, &rlen);
323 
324 	/*
325 	 * Figure out how much space we have for the window list. If there
326 	 * isn't enough space, just show a blank status line.
327 	 */
328 	needed = 0;
329 	if (llen != 0)
330 		needed += llen;
331 	if (rlen != 0)
332 		needed += rlen;
333 	if (c->tty.sx == 0 || c->tty.sx <= needed)
334 		goto out;
335 	wlavailable = c->tty.sx - needed;
336 
337 	/* Calculate the total size needed for the window list. */
338 	wlstart = wloffset = wlwidth = 0;
339 	RB_FOREACH(wl, winlinks, &s->windows) {
340 		free(wl->status_text);
341 		memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
342 		wl->status_text = status_print(c, wl, t, &wl->status_cell);
343 		wl->status_width = screen_write_cstrlen("%s", wl->status_text);
344 
345 		if (wl == s->curw)
346 			wloffset = wlwidth;
347 
348 		oo = wl->window->options;
349 		sep = options_get_string(oo, "window-status-separator");
350 		seplen = screen_write_cstrlen("%s", sep);
351 		wlwidth += wl->status_width + seplen;
352 	}
353 
354 	/* Create a new screen for the window list. */
355 	screen_init(&window_list, wlwidth, 1, 0);
356 
357 	/* And draw the window list into it. */
358 	screen_write_start(&ctx, NULL, &window_list);
359 	RB_FOREACH(wl, winlinks, &s->windows) {
360 		screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s",
361 		    wl->status_text);
362 
363 		oo = wl->window->options;
364 		sep = options_get_string(oo, "window-status-separator");
365 		screen_write_cnputs(&ctx, -1, &stdgc, "%s", sep);
366 	}
367 	screen_write_stop(&ctx);
368 
369 	/* If there is enough space for the total width, skip to draw now. */
370 	if (wlwidth <= wlavailable)
371 		goto draw;
372 
373 	/* Find size of current window text. */
374 	wlsize = s->curw->status_width;
375 
376 	/*
377 	 * If the current window is already on screen, good to draw from the
378 	 * start and just leave off the end.
379 	 */
380 	if (wloffset + wlsize < wlavailable) {
381 		if (wlavailable > 0) {
382 			rarrow = 1;
383 			wlavailable--;
384 		}
385 		wlwidth = wlavailable;
386 	} else {
387 		/*
388 		 * Work out how many characters we need to omit from the
389 		 * start. There are wlavailable characters to fill, and
390 		 * wloffset + wlsize must be the last. So, the start character
391 		 * is wloffset + wlsize - wlavailable.
392 		 */
393 		if (wlavailable > 0) {
394 			larrow = 1;
395 			wlavailable--;
396 		}
397 
398 		wlstart = wloffset + wlsize - wlavailable;
399 		if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
400 			rarrow = 1;
401 			wlstart++;
402 			wlavailable--;
403 		}
404 		wlwidth = wlavailable;
405 	}
406 
407 	/* Bail if anything is now too small too. */
408 	if (wlwidth == 0 || wlavailable == 0) {
409 		screen_free(&window_list);
410 		goto out;
411 	}
412 
413 	/*
414 	 * Now the start position is known, work out the state of the left and
415 	 * right arrows.
416 	 */
417 	offset = 0;
418 	RB_FOREACH(wl, winlinks, &s->windows) {
419 		if (wl->flags & WINLINK_ALERTFLAGS &&
420 		    larrow == 1 && offset < wlstart)
421 			larrow = -1;
422 
423 		offset += wl->status_width;
424 
425 		if (wl->flags & WINLINK_ALERTFLAGS &&
426 		    rarrow == 1 && offset > wlstart + wlwidth)
427 			rarrow = -1;
428 	}
429 
430 draw:
431 	/* Begin drawing. */
432 	screen_write_start(&ctx, NULL, &c->status);
433 
434 	/* Draw the left string and arrow. */
435 	screen_write_cursormove(&ctx, 0, 0);
436 	if (llen != 0)
437 		screen_write_cnputs(&ctx, llen, &lgc, "%s", left);
438 	if (larrow != 0) {
439 		memcpy(&gc, &stdgc, sizeof gc);
440 		if (larrow == -1)
441 			gc.attr ^= GRID_ATTR_REVERSE;
442 		screen_write_putc(&ctx, &gc, '<');
443 	}
444 
445 	/* Draw the right string and arrow. */
446 	if (rarrow != 0) {
447 		screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
448 		memcpy(&gc, &stdgc, sizeof gc);
449 		if (rarrow == -1)
450 			gc.attr ^= GRID_ATTR_REVERSE;
451 		screen_write_putc(&ctx, &gc, '>');
452 	} else
453 		screen_write_cursormove(&ctx, c->tty.sx - rlen, 0);
454 	if (rlen != 0)
455 		screen_write_cnputs(&ctx, rlen, &rgc, "%s", right);
456 
457 	/* Figure out the offset for the window list. */
458 	if (llen != 0)
459 		wloffset = llen;
460 	else
461 		wloffset = 0;
462 	if (wlwidth < wlavailable) {
463 		switch (options_get_number(s->options, "status-justify")) {
464 		case 1:	/* centred */
465 			wloffset += (wlavailable - wlwidth) / 2;
466 			break;
467 		case 2:	/* right */
468 			wloffset += (wlavailable - wlwidth);
469 			break;
470 		}
471 	}
472 	if (larrow != 0)
473 		wloffset++;
474 
475 	/* Copy the window list. */
476 	c->wlmouse = -wloffset + wlstart;
477 	screen_write_cursormove(&ctx, wloffset, 0);
478 	screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
479 	screen_free(&window_list);
480 
481 	screen_write_stop(&ctx);
482 
483 out:
484 	free(left);
485 	free(right);
486 
487 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
488 		screen_free(&old_status);
489 		return (0);
490 	}
491 	screen_free(&old_status);
492 	return (1);
493 }
494 
495 /* Replace special sequences in fmt. */
496 char *
497 status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
498 {
499 	struct format_tree	*ft;
500 	char			*expanded;
501 
502 	if (fmt == NULL)
503 		return (xstrdup(""));
504 
505 	if (c->flags & CLIENT_STATUSFORCE)
506 		ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE);
507 	else
508 		ft = format_create(NULL, FORMAT_STATUS);
509 	format_defaults(ft, c, NULL, wl, NULL);
510 
511 	expanded = format_expand_time(ft, fmt, t);
512 
513 	format_free(ft);
514 	return (expanded);
515 }
516 
517 /* Return winlink status line entry and adjust gc as necessary. */
518 char *
519 status_print(struct client *c, struct winlink *wl, time_t t,
520     struct grid_cell *gc)
521 {
522 	struct options	*oo = wl->window->options;
523 	struct session	*s = c->session;
524 	const char	*fmt;
525 	char   		*text;
526 
527 	style_apply_update(gc, oo, "window-status-style");
528 	fmt = options_get_string(oo, "window-status-format");
529 	if (wl == s->curw) {
530 		style_apply_update(gc, oo, "window-status-current-style");
531 		fmt = options_get_string(oo, "window-status-current-format");
532 	}
533 	if (wl == TAILQ_FIRST(&s->lastw))
534 		style_apply_update(gc, oo, "window-status-last-style");
535 
536 	if (wl->flags & WINLINK_BELL)
537 		style_apply_update(gc, oo, "window-status-bell-style");
538 	else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
539 		style_apply_update(gc, oo, "window-status-activity-style");
540 
541 	text = status_replace(c, wl, fmt, t);
542 	return (text);
543 }
544 
545 /* Set a status line message. */
546 void
547 status_message_set(struct client *c, const char *fmt, ...)
548 {
549 	struct timeval		 tv;
550 	struct message_entry	*msg, *msg1;
551 	va_list			 ap;
552 	int			 delay;
553 	u_int			 limit;
554 
555 	limit = options_get_number(global_options, "message-limit");
556 
557 	status_prompt_clear(c);
558 	status_message_clear(c);
559 
560 	va_start(ap, fmt);
561 	xvasprintf(&c->message_string, fmt, ap);
562 	va_end(ap);
563 
564 	msg = xcalloc(1, sizeof *msg);
565 	msg->msg_time = time(NULL);
566 	msg->msg_num = c->message_next++;
567 	msg->msg = xstrdup(c->message_string);
568 	TAILQ_INSERT_TAIL(&c->message_log, msg, entry);
569 
570 	TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
571 		if (msg->msg_num + limit >= c->message_next)
572 			break;
573 		free(msg->msg);
574 		TAILQ_REMOVE(&c->message_log, msg, entry);
575 		free(msg);
576 	}
577 
578 	delay = options_get_number(c->session->options, "display-time");
579 	if (delay > 0) {
580 		tv.tv_sec = delay / 1000;
581 		tv.tv_usec = (delay % 1000) * 1000L;
582 
583 		if (event_initialized(&c->message_timer))
584 			evtimer_del(&c->message_timer);
585 		evtimer_set(&c->message_timer, status_message_callback, c);
586 		evtimer_add(&c->message_timer, &tv);
587 	}
588 
589 	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
590 	c->flags |= CLIENT_STATUS;
591 }
592 
593 /* Clear status line message. */
594 void
595 status_message_clear(struct client *c)
596 {
597 	if (c->message_string == NULL)
598 		return;
599 
600 	free(c->message_string);
601 	c->message_string = NULL;
602 
603 	c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
604 	c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
605 
606 	screen_reinit(&c->status);
607 }
608 
609 /* Clear status line message after timer expires. */
610 void
611 status_message_callback(__unused int fd, __unused short event, void *data)
612 {
613 	struct client	*c = data;
614 
615 	status_message_clear(c);
616 }
617 
618 /* Draw client message on status line of present else on last line. */
619 int
620 status_message_redraw(struct client *c)
621 {
622 	struct screen_write_ctx		ctx;
623 	struct session		       *s = c->session;
624 	struct screen		        old_status;
625 	size_t			        len;
626 	struct grid_cell		gc;
627 
628 	if (c->tty.sx == 0 || c->tty.sy == 0)
629 		return (0);
630 	memcpy(&old_status, &c->status, sizeof old_status);
631 	screen_init(&c->status, c->tty.sx, 1, 0);
632 
633 	len = screen_write_strlen("%s", c->message_string);
634 	if (len > c->tty.sx)
635 		len = c->tty.sx;
636 
637 	style_apply(&gc, s->options, "message-style");
638 
639 	screen_write_start(&ctx, NULL, &c->status);
640 
641 	screen_write_cursormove(&ctx, 0, 0);
642 	screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
643 	for (; len < c->tty.sx; len++)
644 		screen_write_putc(&ctx, &gc, ' ');
645 
646 	screen_write_stop(&ctx);
647 
648 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
649 		screen_free(&old_status);
650 		return (0);
651 	}
652 	screen_free(&old_status);
653 	return (1);
654 }
655 
656 /* Enable status line prompt. */
657 void
658 status_prompt_set(struct client *c, const char *msg, const char *input,
659     int (*callbackfn)(void *, const char *), void (*freefn)(void *),
660     void *data, int flags)
661 {
662 	struct format_tree	*ft;
663 	int			 keys;
664 	time_t			 t;
665 
666 	ft = format_create(NULL, 0);
667 	format_defaults(ft, c, NULL, NULL, NULL);
668 	t = time(NULL);
669 
670 	status_message_clear(c);
671 	status_prompt_clear(c);
672 
673 	c->prompt_string = format_expand_time(ft, msg, t);
674 
675 	c->prompt_buffer = format_expand_time(ft, input, t);
676 	c->prompt_index = strlen(c->prompt_buffer);
677 
678 	c->prompt_callbackfn = callbackfn;
679 	c->prompt_freefn = freefn;
680 	c->prompt_data = data;
681 
682 	c->prompt_hindex = 0;
683 
684 	c->prompt_flags = flags;
685 
686 	keys = options_get_number(c->session->options, "status-keys");
687 	if (keys == MODEKEY_EMACS)
688 		mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit);
689 	else
690 		mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit);
691 
692 	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
693 	c->flags |= CLIENT_STATUS;
694 
695 	format_free(ft);
696 }
697 
698 /* Remove status line prompt. */
699 void
700 status_prompt_clear(struct client *c)
701 {
702 	if (c->prompt_string == NULL)
703 		return;
704 
705 	if (c->prompt_freefn != NULL && c->prompt_data != NULL)
706 		c->prompt_freefn(c->prompt_data);
707 
708 	free(c->prompt_string);
709 	c->prompt_string = NULL;
710 
711 	free(c->prompt_buffer);
712 	c->prompt_buffer = NULL;
713 
714 	c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
715 	c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
716 
717 	screen_reinit(&c->status);
718 }
719 
720 /* Update status line prompt with a new prompt string. */
721 void
722 status_prompt_update(struct client *c, const char *msg, const char *input)
723 {
724 	struct format_tree	*ft;
725 	time_t			 t;
726 
727 	ft = format_create(NULL, 0);
728 	format_defaults(ft, c, NULL, NULL, NULL);
729 	t = time(NULL);
730 
731 	free(c->prompt_string);
732 	c->prompt_string = format_expand_time(ft, msg, t);
733 
734 	free(c->prompt_buffer);
735 	c->prompt_buffer = format_expand_time(ft, input, t);
736 	c->prompt_index = strlen(c->prompt_buffer);
737 
738 	c->prompt_hindex = 0;
739 
740 	c->flags |= CLIENT_STATUS;
741 
742 	format_free(ft);
743 }
744 
745 /* Draw client prompt on status line of present else on last line. */
746 int
747 status_prompt_redraw(struct client *c)
748 {
749 	struct screen_write_ctx		ctx;
750 	struct session		       *s = c->session;
751 	struct screen		        old_status;
752 	size_t			        i, size, left, len, off;
753 	struct grid_cell		gc;
754 
755 	if (c->tty.sx == 0 || c->tty.sy == 0)
756 		return (0);
757 	memcpy(&old_status, &c->status, sizeof old_status);
758 	screen_init(&c->status, c->tty.sx, 1, 0);
759 
760 	len = screen_write_strlen("%s", c->prompt_string);
761 	if (len > c->tty.sx)
762 		len = c->tty.sx;
763 	off = 0;
764 
765 	/* Change colours for command mode. */
766 	if (c->prompt_mdata.mode == 1)
767 		style_apply(&gc, s->options, "message-command-style");
768 	else
769 		style_apply(&gc, s->options, "message-style");
770 
771 	screen_write_start(&ctx, NULL, &c->status);
772 
773 	screen_write_cursormove(&ctx, 0, 0);
774 	screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string);
775 
776 	left = c->tty.sx - len;
777 	if (left != 0) {
778 		size = screen_write_strlen("%s", c->prompt_buffer);
779 		if (c->prompt_index >= left) {
780 			off = c->prompt_index - left + 1;
781 			if (c->prompt_index == size)
782 				left--;
783 			size = left;
784 		}
785 		screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer +
786 		    off);
787 
788 		for (i = len + size; i < c->tty.sx; i++)
789 			screen_write_putc(&ctx, &gc, ' ');
790 	}
791 
792 	screen_write_stop(&ctx);
793 
794 	/* Apply fake cursor. */
795 	off = len + c->prompt_index - off;
796 	grid_view_get_cell(c->status.grid, off, 0, &gc);
797 	gc.attr ^= GRID_ATTR_REVERSE;
798 	grid_view_set_cell(c->status.grid, off, 0, &gc);
799 
800 	if (grid_compare(c->status.grid, old_status.grid) == 0) {
801 		screen_free(&old_status);
802 		return (0);
803 	}
804 	screen_free(&old_status);
805 	return (1);
806 }
807 
808 /* Handle keys in prompt. */
809 void
810 status_prompt_key(struct client *c, key_code key)
811 {
812 	struct session		*sess = c->session;
813 	struct options		*oo = sess->options;
814 	struct paste_buffer	*pb;
815 	char			*s, *first, *last, word[64], swapc;
816 	const char		*histstr, *bufdata, *wsep = NULL;
817 	u_char			 ch;
818 	size_t			 size, n, off, idx, bufsize;
819 
820 	size = strlen(c->prompt_buffer);
821 	switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) {
822 	case MODEKEYEDIT_CURSORLEFT:
823 		if (c->prompt_index > 0) {
824 			c->prompt_index--;
825 			c->flags |= CLIENT_STATUS;
826 		}
827 		break;
828 	case MODEKEYEDIT_SWITCHMODE:
829 		c->flags |= CLIENT_STATUS;
830 		break;
831 	case MODEKEYEDIT_SWITCHMODEAPPEND:
832 		c->flags |= CLIENT_STATUS;
833 		/* FALLTHROUGH */
834 	case MODEKEYEDIT_CURSORRIGHT:
835 		if (c->prompt_index < size) {
836 			c->prompt_index++;
837 			c->flags |= CLIENT_STATUS;
838 		}
839 		break;
840 	case MODEKEYEDIT_SWITCHMODEBEGINLINE:
841 		c->flags |= CLIENT_STATUS;
842 		/* FALLTHROUGH */
843 	case MODEKEYEDIT_STARTOFLINE:
844 		if (c->prompt_index != 0) {
845 			c->prompt_index = 0;
846 			c->flags |= CLIENT_STATUS;
847 		}
848 		break;
849 	case MODEKEYEDIT_SWITCHMODEAPPENDLINE:
850 		c->flags |= CLIENT_STATUS;
851 		/* FALLTHROUGH */
852 	case MODEKEYEDIT_ENDOFLINE:
853 		if (c->prompt_index != size) {
854 			c->prompt_index = size;
855 			c->flags |= CLIENT_STATUS;
856 		}
857 		break;
858 	case MODEKEYEDIT_COMPLETE:
859 		if (*c->prompt_buffer == '\0')
860 			break;
861 
862 		idx = c->prompt_index;
863 		if (idx != 0)
864 			idx--;
865 
866 		/* Find the word we are in. */
867 		first = c->prompt_buffer + idx;
868 		while (first > c->prompt_buffer && *first != ' ')
869 			first--;
870 		while (*first == ' ')
871 			first++;
872 		last = c->prompt_buffer + idx;
873 		while (*last != '\0' && *last != ' ')
874 			last++;
875 		while (*last == ' ')
876 			last--;
877 		if (*last != '\0')
878 			last++;
879 		if (last <= first ||
880 		    ((size_t) (last - first)) > (sizeof word) - 1)
881 			break;
882 		memcpy(word, first, last - first);
883 		word[last - first] = '\0';
884 
885 		/* And try to complete it. */
886 		if ((s = status_prompt_complete(sess, word)) == NULL)
887 			break;
888 
889 		/* Trim out word. */
890 		n = size - (last - c->prompt_buffer) + 1; /* with \0 */
891 		memmove(first, last, n);
892 		size -= last - first;
893 
894 		/* Insert the new word. */
895 		size += strlen(s);
896 		off = first - c->prompt_buffer;
897 		c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1);
898 		first = c->prompt_buffer + off;
899 		memmove(first + strlen(s), first, n);
900 		memcpy(first, s, strlen(s));
901 
902 		c->prompt_index = (first - c->prompt_buffer) + strlen(s);
903 		free(s);
904 
905 		c->flags |= CLIENT_STATUS;
906 		break;
907 	case MODEKEYEDIT_BACKSPACE:
908 		if (c->prompt_index != 0) {
909 			if (c->prompt_index == size)
910 				c->prompt_buffer[--c->prompt_index] = '\0';
911 			else {
912 				memmove(c->prompt_buffer + c->prompt_index - 1,
913 				    c->prompt_buffer + c->prompt_index,
914 				    size + 1 - c->prompt_index);
915 				c->prompt_index--;
916 			}
917 			c->flags |= CLIENT_STATUS;
918 		}
919 		break;
920 	case MODEKEYEDIT_DELETE:
921 	case MODEKEYEDIT_SWITCHMODESUBSTITUTE:
922 		if (c->prompt_index != size) {
923 			memmove(c->prompt_buffer + c->prompt_index,
924 			    c->prompt_buffer + c->prompt_index + 1,
925 			    size + 1 - c->prompt_index);
926 			c->flags |= CLIENT_STATUS;
927 		}
928 		break;
929 	case MODEKEYEDIT_DELETELINE:
930 	case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
931 		*c->prompt_buffer = '\0';
932 		c->prompt_index = 0;
933 		c->flags |= CLIENT_STATUS;
934 		break;
935 	case MODEKEYEDIT_DELETETOENDOFLINE:
936 	case MODEKEYEDIT_SWITCHMODECHANGELINE:
937 		if (c->prompt_index < size) {
938 			c->prompt_buffer[c->prompt_index] = '\0';
939 			c->flags |= CLIENT_STATUS;
940 		}
941 		break;
942 	case MODEKEYEDIT_DELETEWORD:
943 		wsep = options_get_string(oo, "word-separators");
944 		idx = c->prompt_index;
945 
946 		/* Find a non-separator. */
947 		while (idx != 0) {
948 			idx--;
949 			if (!strchr(wsep, c->prompt_buffer[idx]))
950 				break;
951 		}
952 
953 		/* Find the separator at the beginning of the word. */
954 		while (idx != 0) {
955 			idx--;
956 			if (strchr(wsep, c->prompt_buffer[idx])) {
957 				/* Go back to the word. */
958 				idx++;
959 				break;
960 			}
961 		}
962 
963 		memmove(c->prompt_buffer + idx,
964 		    c->prompt_buffer + c->prompt_index,
965 		    size + 1 - c->prompt_index);
966 		memset(c->prompt_buffer + size - (c->prompt_index - idx),
967 		    '\0', c->prompt_index - idx);
968 		c->prompt_index = idx;
969 		c->flags |= CLIENT_STATUS;
970 		break;
971 	case MODEKEYEDIT_NEXTSPACE:
972 		wsep = " ";
973 		/* FALLTHROUGH */
974 	case MODEKEYEDIT_NEXTWORD:
975 		if (wsep == NULL)
976 			wsep = options_get_string(oo, "word-separators");
977 
978 		/* Find a separator. */
979 		while (c->prompt_index != size) {
980 			c->prompt_index++;
981 			if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
982 				break;
983 		}
984 
985 		/* Find the word right after the separation. */
986 		while (c->prompt_index != size) {
987 			c->prompt_index++;
988 			if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
989 				break;
990 		}
991 
992 		c->flags |= CLIENT_STATUS;
993 		break;
994 	case MODEKEYEDIT_NEXTSPACEEND:
995 		wsep = " ";
996 		/* FALLTHROUGH */
997 	case MODEKEYEDIT_NEXTWORDEND:
998 		if (wsep == NULL)
999 			wsep = options_get_string(oo, "word-separators");
1000 
1001 		/* Find a word. */
1002 		while (c->prompt_index != size) {
1003 			c->prompt_index++;
1004 			if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1005 				break;
1006 		}
1007 
1008 		/* Find the separator at the end of the word. */
1009 		while (c->prompt_index != size) {
1010 			c->prompt_index++;
1011 			if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
1012 				break;
1013 		}
1014 
1015 		/* Back up to the end-of-word like vi. */
1016 		if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
1017 		    c->prompt_index != 0)
1018 			c->prompt_index--;
1019 
1020 		c->flags |= CLIENT_STATUS;
1021 		break;
1022 	case MODEKEYEDIT_PREVIOUSSPACE:
1023 		wsep = " ";
1024 		/* FALLTHROUGH */
1025 	case MODEKEYEDIT_PREVIOUSWORD:
1026 		if (wsep == NULL)
1027 			wsep = options_get_string(oo, "word-separators");
1028 
1029 		/* Find a non-separator. */
1030 		while (c->prompt_index != 0) {
1031 			c->prompt_index--;
1032 			if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1033 				break;
1034 		}
1035 
1036 		/* Find the separator at the beginning of the word. */
1037 		while (c->prompt_index != 0) {
1038 			c->prompt_index--;
1039 			if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
1040 				/* Go back to the word. */
1041 				c->prompt_index++;
1042 				break;
1043 			}
1044 		}
1045 
1046 		c->flags |= CLIENT_STATUS;
1047 		break;
1048 	case MODEKEYEDIT_HISTORYUP:
1049 		histstr = status_prompt_up_history(&c->prompt_hindex);
1050 		if (histstr == NULL)
1051 			break;
1052 		free(c->prompt_buffer);
1053 		c->prompt_buffer = xstrdup(histstr);
1054 		c->prompt_index = strlen(c->prompt_buffer);
1055 		c->flags |= CLIENT_STATUS;
1056 		break;
1057 	case MODEKEYEDIT_HISTORYDOWN:
1058 		histstr = status_prompt_down_history(&c->prompt_hindex);
1059 		if (histstr == NULL)
1060 			break;
1061 		free(c->prompt_buffer);
1062 		c->prompt_buffer = xstrdup(histstr);
1063 		c->prompt_index = strlen(c->prompt_buffer);
1064 		c->flags |= CLIENT_STATUS;
1065 		break;
1066 	case MODEKEYEDIT_PASTE:
1067 		if ((pb = paste_get_top(NULL)) == NULL)
1068 			break;
1069 		bufdata = paste_buffer_data(pb, &bufsize);
1070 		for (n = 0; n < bufsize; n++) {
1071 			ch = (u_char)bufdata[n];
1072 			if (ch < 32 || ch == 127)
1073 				break;
1074 		}
1075 
1076 		c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1);
1077 		if (c->prompt_index == size) {
1078 			memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
1079 			c->prompt_index += n;
1080 			c->prompt_buffer[c->prompt_index] = '\0';
1081 		} else {
1082 			memmove(c->prompt_buffer + c->prompt_index + n,
1083 			    c->prompt_buffer + c->prompt_index,
1084 			    size + 1 - c->prompt_index);
1085 			memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
1086 			c->prompt_index += n;
1087 		}
1088 
1089 		c->flags |= CLIENT_STATUS;
1090 		break;
1091 	case MODEKEYEDIT_TRANSPOSECHARS:
1092 		idx = c->prompt_index;
1093 		if (idx < size)
1094 			idx++;
1095 		if (idx >= 2) {
1096 			swapc = c->prompt_buffer[idx - 2];
1097 			c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
1098 			c->prompt_buffer[idx - 1] = swapc;
1099 			c->prompt_index = idx;
1100 			c->flags |= CLIENT_STATUS;
1101 		}
1102 		break;
1103 	case MODEKEYEDIT_ENTER:
1104 		if (*c->prompt_buffer != '\0')
1105 			status_prompt_add_history(c->prompt_buffer);
1106 		if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
1107 			status_prompt_clear(c);
1108 		break;
1109 	case MODEKEYEDIT_CANCEL:
1110 		if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
1111 			status_prompt_clear(c);
1112 		break;
1113 	case MODEKEY_OTHER:
1114 		if (key <= 0x1f || key >= 0x7f)
1115 			break;
1116 		c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2);
1117 
1118 		if (c->prompt_index == size) {
1119 			c->prompt_buffer[c->prompt_index++] = key;
1120 			c->prompt_buffer[c->prompt_index] = '\0';
1121 		} else {
1122 			memmove(c->prompt_buffer + c->prompt_index + 1,
1123 			    c->prompt_buffer + c->prompt_index,
1124 			    size + 1 - c->prompt_index);
1125 			c->prompt_buffer[c->prompt_index++] = key;
1126 		}
1127 
1128 		if (c->prompt_flags & PROMPT_SINGLE) {
1129 			if (c->prompt_callbackfn(c->prompt_data,
1130 			    c->prompt_buffer) == 0)
1131 				status_prompt_clear(c);
1132 		}
1133 
1134 		c->flags |= CLIENT_STATUS;
1135 		break;
1136 	default:
1137 		break;
1138 	}
1139 }
1140 
1141 /* Get previous line from the history. */
1142 const char *
1143 status_prompt_up_history(u_int *idx)
1144 {
1145 	/*
1146 	 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1147 	 * empty.
1148 	 */
1149 
1150 	if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
1151 		return (NULL);
1152 	(*idx)++;
1153 	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1154 }
1155 
1156 /* Get next line from the history. */
1157 const char *
1158 status_prompt_down_history(u_int *idx)
1159 {
1160 	if (status_prompt_hsize == 0 || *idx == 0)
1161 		return ("");
1162 	(*idx)--;
1163 	if (*idx == 0)
1164 		return ("");
1165 	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1166 }
1167 
1168 /* Add line to the history. */
1169 void
1170 status_prompt_add_history(const char *line)
1171 {
1172 	size_t	size;
1173 
1174 	if (status_prompt_hsize > 0 &&
1175 	    strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
1176 		return;
1177 
1178 	if (status_prompt_hsize == PROMPT_HISTORY) {
1179 		free(status_prompt_hlist[0]);
1180 
1181 		size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
1182 		memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
1183 
1184 		status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
1185 		return;
1186 	}
1187 
1188 	status_prompt_hlist = xreallocarray(status_prompt_hlist,
1189 	    status_prompt_hsize + 1, sizeof *status_prompt_hlist);
1190 	status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
1191 }
1192 
1193 /* Build completion list. */
1194 const char **
1195 status_prompt_complete_list(u_int *size, const char *s)
1196 {
1197 	const char				**list = NULL, **layout;
1198 	const struct cmd_entry			**cmdent;
1199 	const struct options_table_entry	 *oe;
1200 	const char				 *layouts[] = {
1201 		"even-horizontal", "even-vertical", "main-horizontal",
1202 		"main-vertical", "tiled", NULL
1203 	};
1204 
1205 	*size = 0;
1206 	for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1207 		if (strncmp((*cmdent)->name, s, strlen(s)) == 0) {
1208 			list = xreallocarray(list, (*size) + 1, sizeof *list);
1209 			list[(*size)++] = (*cmdent)->name;
1210 		}
1211 	}
1212 	for (oe = options_table; oe->name != NULL; oe++) {
1213 		if (strncmp(oe->name, s, strlen(s)) == 0) {
1214 			list = xreallocarray(list, (*size) + 1, sizeof *list);
1215 			list[(*size)++] = oe->name;
1216 		}
1217 	}
1218 	for (layout = layouts; *layout != NULL; layout++) {
1219 		if (strncmp(*layout, s, strlen(s)) == 0) {
1220 			list = xreallocarray(list, (*size) + 1, sizeof *list);
1221 			list[(*size)++] = *layout;
1222 		}
1223 	}
1224 	return (list);
1225 }
1226 
1227 /* Find longest prefix. */
1228 char *
1229 status_prompt_complete_prefix(const char **list, u_int size)
1230 {
1231 	char	 *out;
1232 	u_int	  i;
1233 	size_t	  j;
1234 
1235 	out = xstrdup(list[0]);
1236 	for (i = 1; i < size; i++) {
1237 		j = strlen(list[i]);
1238 		if (j > strlen(out))
1239 			j = strlen(out);
1240 		for (; j > 0; j--) {
1241 			if (out[j - 1] != list[i][j - 1])
1242 				out[j - 1] = '\0';
1243 		}
1244 	}
1245 	return (out);
1246 }
1247 
1248 /* Complete word. */
1249 char *
1250 status_prompt_complete(struct session *sess, const char *s)
1251 {
1252 	const char	**list = NULL, *colon;
1253 	u_int		  size = 0, i;
1254 	struct session	 *s_loop;
1255 	struct winlink	 *wl;
1256 	struct window	 *w;
1257 	char		 *copy, *out, *tmp;
1258 
1259 	if (*s == '\0')
1260 		return (NULL);
1261 	out = NULL;
1262 
1263 	if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
1264 		list = status_prompt_complete_list(&size, s);
1265 		if (size == 0)
1266 			out = NULL;
1267 		else if (size == 1)
1268 			xasprintf(&out, "%s ", list[0]);
1269 		else
1270 			out = status_prompt_complete_prefix(list, size);
1271 		free(list);
1272 		return (out);
1273 	}
1274 	copy = xstrdup(s);
1275 
1276 	colon = ":";
1277 	if (copy[strlen(copy) - 1] == ':')
1278 		copy[strlen(copy) - 1] = '\0';
1279 	else
1280 		colon = "";
1281 	s = copy + 2;
1282 
1283 	RB_FOREACH(s_loop, sessions, &sessions) {
1284 		if (strncmp(s_loop->name, s, strlen(s)) == 0) {
1285 			list = xreallocarray(list, size + 2, sizeof *list);
1286 			list[size++] = s_loop->name;
1287 		}
1288 	}
1289 	if (size == 1) {
1290 		out = xstrdup(list[0]);
1291 		if (session_find(list[0]) != NULL)
1292 			colon = ":";
1293 	} else if (size != 0)
1294 		out = status_prompt_complete_prefix(list, size);
1295 	if (out != NULL) {
1296 		xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1297 		out = tmp;
1298 		goto found;
1299 	}
1300 
1301 	colon = "";
1302 	if (*s == ':') {
1303 		RB_FOREACH(wl, winlinks, &sess->windows) {
1304 			xasprintf(&tmp, ":%s", wl->window->name);
1305 			if (strncmp(tmp, s, strlen(s)) == 0){
1306 				list = xreallocarray(list, size + 1,
1307 				    sizeof *list);
1308 				list[size++] = tmp;
1309 				continue;
1310 			}
1311 			free(tmp);
1312 
1313 			xasprintf(&tmp, ":%d", wl->idx);
1314 			if (strncmp(tmp, s, strlen(s)) == 0) {
1315 				list = xreallocarray(list, size + 1,
1316 				    sizeof *list);
1317 				list[size++] = tmp;
1318 				continue;
1319 			}
1320 			free(tmp);
1321 		}
1322 	} else {
1323 		RB_FOREACH(s_loop, sessions, &sessions) {
1324 			RB_FOREACH(wl, winlinks, &s_loop->windows) {
1325 				w = wl->window;
1326 
1327 				xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
1328 				if (strncmp(tmp, s, strlen(s)) == 0) {
1329 					list = xreallocarray(list, size + 1,
1330 					    sizeof *list);
1331 					list[size++] = tmp;
1332 					continue;
1333 				}
1334 				free(tmp);
1335 
1336 				xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
1337 				if (strncmp(tmp, s, strlen(s)) == 0) {
1338 					list = xreallocarray(list, size + 1,
1339 					    sizeof *list);
1340 					list[size++] = tmp;
1341 					continue;
1342 				}
1343 				free(tmp);
1344 			}
1345 		}
1346 	}
1347 	if (size == 1) {
1348 		out = xstrdup(list[0]);
1349 		colon = " ";
1350 	} else if (size != 0)
1351 		out = status_prompt_complete_prefix(list, size);
1352 	if (out != NULL) {
1353 		xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1354 		out = tmp;
1355 	}
1356 
1357 	for (i = 0; i < size; i++)
1358 		free((void *)list[i]);
1359 
1360 found:
1361 	free(copy);
1362 	free(list);
1363 	return (out);
1364 }
1365