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