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