status.c revision 1.221
1/* $OpenBSD: status.c,v 1.221 2021/04/12 09:36:12 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
32static void	 status_message_callback(int, short, void *);
33static void	 status_timer_callback(int, short, void *);
34
35static char	*status_prompt_find_history_file(void);
36static const char *status_prompt_up_history(u_int *);
37static const char *status_prompt_down_history(u_int *);
38static void	 status_prompt_add_history(const char *);
39
40static char	*status_prompt_complete(struct client *, const char *, u_int);
41static char	*status_prompt_complete_window_menu(struct client *,
42		     struct session *, const char *, u_int, char);
43
44struct status_prompt_menu {
45	struct client	 *c;
46	u_int		  start;
47	u_int		  size;
48	char		**list;
49	char		  flag;
50};
51
52/* Status prompt history. */
53#define PROMPT_HISTORY 100
54static char	**status_prompt_hlist;
55static u_int	  status_prompt_hsize;
56
57/* Find the history file to load/save from/to. */
58static char *
59status_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. */
79void
80status_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. */
119void
120status_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. */
147static void
148status_timer_callback(__unused int fd, __unused short events, void *arg)
149{
150	struct client	*c = arg;
151	struct session	*s = c->session;
152	struct timeval	 tv;
153
154	evtimer_del(&c->status.timer);
155
156	if (s == NULL)
157		return;
158
159	if (c->message_string == NULL && c->prompt_string == NULL)
160		c->flags |= CLIENT_REDRAWSTATUS;
161
162	timerclear(&tv);
163	tv.tv_sec = options_get_number(s->options, "status-interval");
164
165	if (tv.tv_sec != 0)
166		evtimer_add(&c->status.timer, &tv);
167	log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
168}
169
170/* Start status timer for client. */
171void
172status_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. */
186void
187status_timer_start_all(void)
188{
189	struct client	*c;
190
191	TAILQ_FOREACH(c, &clients, entry)
192		status_timer_start(c);
193}
194
195/* Update status cache. */
196void
197status_update_cache(struct session *s)
198{
199	s->statuslines = options_get_number(s->options, "status");
200	if (s->statuslines == 0)
201		s->statusat = -1;
202	else if (options_get_number(s->options, "status-position") == 0)
203		s->statusat = 0;
204	else
205		s->statusat = 1;
206}
207
208/* Get screen line of status line. -1 means off. */
209int
210status_at_line(struct client *c)
211{
212	struct session	*s = c->session;
213
214	if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
215		return (-1);
216	if (s->statusat != 1)
217		return (s->statusat);
218	return (c->tty.sy - status_line_size(c));
219}
220
221/* Get size of status line for client's session. 0 means off. */
222u_int
223status_line_size(struct client *c)
224{
225	struct session	*s = c->session;
226
227	if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
228		return (0);
229	return (s->statuslines);
230}
231
232/* Get window at window list position. */
233struct style_range *
234status_get_range(struct client *c, u_int x, u_int y)
235{
236	struct status_line	*sl = &c->status;
237	struct style_range	*sr;
238
239	if (y >= nitems(sl->entries))
240		return (NULL);
241	TAILQ_FOREACH(sr, &sl->entries[y].ranges, entry) {
242		if (x >= sr->start && x < sr->end)
243			return (sr);
244	}
245	return (NULL);
246}
247
248/* Free all ranges. */
249static void
250status_free_ranges(struct style_ranges *srs)
251{
252	struct style_range	*sr, *sr1;
253
254	TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) {
255		TAILQ_REMOVE(srs, sr, entry);
256		free(sr);
257	}
258}
259
260/* Save old status line. */
261static void
262status_push_screen(struct client *c)
263{
264	struct status_line *sl = &c->status;
265
266	if (sl->active == &sl->screen) {
267		sl->active = xmalloc(sizeof *sl->active);
268		screen_init(sl->active, c->tty.sx, status_line_size(c), 0);
269	}
270	sl->references++;
271}
272
273/* Restore old status line. */
274static void
275status_pop_screen(struct client *c)
276{
277	struct status_line *sl = &c->status;
278
279	if (--sl->references == 0) {
280		screen_free(sl->active);
281		free(sl->active);
282		sl->active = &sl->screen;
283	}
284}
285
286/* Initialize status line. */
287void
288status_init(struct client *c)
289{
290	struct status_line	*sl = &c->status;
291	u_int			 i;
292
293	for (i = 0; i < nitems(sl->entries); i++)
294		TAILQ_INIT(&sl->entries[i].ranges);
295
296	screen_init(&sl->screen, c->tty.sx, 1, 0);
297	sl->active = &sl->screen;
298}
299
300/* Free status line. */
301void
302status_free(struct client *c)
303{
304	struct status_line	*sl = &c->status;
305	u_int			 i;
306
307	for (i = 0; i < nitems(sl->entries); i++) {
308		status_free_ranges(&sl->entries[i].ranges);
309		free((void *)sl->entries[i].expanded);
310	}
311
312	if (event_initialized(&sl->timer))
313		evtimer_del(&sl->timer);
314
315	if (sl->active != &sl->screen) {
316		screen_free(sl->active);
317		free(sl->active);
318	}
319	screen_free(&sl->screen);
320}
321
322/* Draw status line for client. */
323int
324status_redraw(struct client *c)
325{
326	struct status_line		*sl = &c->status;
327	struct status_line_entry	*sle;
328	struct session			*s = c->session;
329	struct screen_write_ctx		 ctx;
330	struct grid_cell		 gc;
331	u_int				 lines, i, n, width = c->tty.sx;
332	int				 flags, force = 0, changed = 0, fg, bg;
333	struct options_entry		*o;
334	union options_value		*ov;
335	struct format_tree		*ft;
336	char				*expanded;
337
338	log_debug("%s enter", __func__);
339
340	/* Shouldn't get here if not the active screen. */
341	if (sl->active != &sl->screen)
342		fatalx("not the active screen");
343
344	/* No status line? */
345	lines = status_line_size(c);
346	if (c->tty.sy == 0 || lines == 0)
347		return (1);
348
349	/* Create format tree. */
350	flags = FORMAT_STATUS;
351	if (c->flags & CLIENT_STATUSFORCE)
352		flags |= FORMAT_FORCE;
353	ft = format_create(c, NULL, FORMAT_NONE, flags);
354	format_defaults(ft, c, NULL, NULL, NULL);
355
356	/* Set up default colour. */
357	style_apply(&gc, s->options, "status-style", ft);
358	fg = options_get_number(s->options, "status-fg");
359	if (fg != 8)
360		gc.fg = fg;
361	bg = options_get_number(s->options, "status-bg");
362	if (bg != 8)
363		gc.bg = bg;
364	if (!grid_cells_equal(&gc, &sl->style)) {
365		force = 1;
366		memcpy(&sl->style, &gc, sizeof sl->style);
367	}
368
369	/* Resize the target screen. */
370	if (screen_size_x(&sl->screen) != width ||
371	    screen_size_y(&sl->screen) != lines) {
372		screen_resize(&sl->screen, width, lines, 0);
373		changed = force = 1;
374	}
375	screen_write_start(&ctx, &sl->screen);
376
377	/* Write the status lines. */
378	o = options_get(s->options, "status-format");
379	if (o == NULL) {
380		for (n = 0; n < width * lines; n++)
381			screen_write_putc(&ctx, &gc, ' ');
382	} else {
383		for (i = 0; i < lines; i++) {
384			screen_write_cursormove(&ctx, 0, i, 0);
385
386			ov = options_array_get(o, i);
387			if (ov == NULL) {
388				for (n = 0; n < width; n++)
389					screen_write_putc(&ctx, &gc, ' ');
390				continue;
391			}
392			sle = &sl->entries[i];
393
394			expanded = format_expand_time(ft, ov->string);
395			if (!force &&
396			    sle->expanded != NULL &&
397			    strcmp(expanded, sle->expanded) == 0) {
398				free(expanded);
399				continue;
400			}
401			changed = 1;
402
403			for (n = 0; n < width; n++)
404				screen_write_putc(&ctx, &gc, ' ');
405			screen_write_cursormove(&ctx, 0, i, 0);
406
407			status_free_ranges(&sle->ranges);
408			format_draw(&ctx, &gc, width, expanded, &sle->ranges);
409
410			free(sle->expanded);
411			sle->expanded = expanded;
412		}
413	}
414	screen_write_stop(&ctx);
415
416	/* Free the format tree. */
417	format_free(ft);
418
419	/* Return if the status line has changed. */
420	log_debug("%s exit: force=%d, changed=%d", __func__, force, changed);
421	return (force || changed);
422}
423
424/* Set a status line message. */
425void
426status_message_set(struct client *c, int delay, int ignore_styles,
427    int ignore_keys, const char *fmt, ...)
428{
429	struct timeval	tv;
430	va_list		ap;
431
432	status_message_clear(c);
433	status_push_screen(c);
434
435	va_start(ap, fmt);
436	xvasprintf(&c->message_string, fmt, ap);
437	va_end(ap);
438
439	server_add_message("%s message: %s", c->name, c->message_string);
440
441	/*
442	 * With delay -1, the display-time option is used; zero means wait for
443	 * key press; more than zero is the actual delay time in milliseconds.
444	 */
445	if (delay == -1)
446		delay = options_get_number(c->session->options, "display-time");
447	if (delay > 0) {
448		tv.tv_sec = delay / 1000;
449		tv.tv_usec = (delay % 1000) * 1000L;
450
451		if (event_initialized(&c->message_timer))
452			evtimer_del(&c->message_timer);
453		evtimer_set(&c->message_timer, status_message_callback, c);
454
455		evtimer_add(&c->message_timer, &tv);
456	}
457
458	if (delay != 0)
459		c->message_ignore_keys = ignore_keys;
460	c->message_ignore_styles = ignore_styles;
461
462	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
463	c->flags |= CLIENT_REDRAWSTATUS;
464}
465
466/* Clear status line message. */
467void
468status_message_clear(struct client *c)
469{
470	if (c->message_string == NULL)
471		return;
472
473	free(c->message_string);
474	c->message_string = NULL;
475
476	if (c->prompt_string == NULL)
477		c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
478	c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */
479
480	status_pop_screen(c);
481}
482
483/* Clear status line message after timer expires. */
484static void
485status_message_callback(__unused int fd, __unused short event, void *data)
486{
487	struct client	*c = data;
488
489	status_message_clear(c);
490}
491
492/* Draw client message on status line of present else on last line. */
493int
494status_message_redraw(struct client *c)
495{
496	struct status_line	*sl = &c->status;
497	struct screen_write_ctx	 ctx;
498	struct session		*s = c->session;
499	struct screen		 old_screen;
500	size_t			 len;
501	u_int			 lines, offset;
502	struct grid_cell	 gc;
503	struct format_tree	*ft;
504
505	if (c->tty.sx == 0 || c->tty.sy == 0)
506		return (0);
507	memcpy(&old_screen, sl->active, sizeof old_screen);
508
509	lines = status_line_size(c);
510	if (lines <= 1)
511		lines = 1;
512	screen_init(sl->active, c->tty.sx, lines, 0);
513
514	len = screen_write_strlen("%s", c->message_string);
515	if (len > c->tty.sx)
516		len = c->tty.sx;
517
518	ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
519	style_apply(&gc, s->options, "message-style", ft);
520	format_free(ft);
521
522	screen_write_start(&ctx, sl->active);
523	screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
524	screen_write_cursormove(&ctx, 0, lines - 1, 0);
525	for (offset = 0; offset < c->tty.sx; offset++)
526		screen_write_putc(&ctx, &gc, ' ');
527	screen_write_cursormove(&ctx, 0, lines - 1, 0);
528	if (c->message_ignore_styles)
529		screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
530	else
531		format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL);
532	screen_write_stop(&ctx);
533
534	if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
535		screen_free(&old_screen);
536		return (0);
537	}
538	screen_free(&old_screen);
539	return (1);
540}
541
542/* Enable status line prompt. */
543void
544status_prompt_set(struct client *c, struct cmd_find_state *fs,
545    const char *msg, const char *input, prompt_input_cb inputcb,
546    prompt_free_cb freecb, void *data, int flags)
547{
548	struct format_tree	*ft;
549	char			*tmp;
550
551	if (fs != NULL)
552		ft = format_create_from_state(NULL, c, fs);
553	else
554		ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
555
556	if (input == NULL)
557		input = "";
558	if (flags & PROMPT_NOFORMAT)
559		tmp = xstrdup(input);
560	else
561		tmp = format_expand_time(ft, input);
562
563	status_message_clear(c);
564	status_prompt_clear(c);
565	status_push_screen(c);
566
567	c->prompt_string = format_expand_time(ft, msg);
568
569	if (flags & PROMPT_INCREMENTAL) {
570		c->prompt_last = xstrdup(tmp);
571		c->prompt_buffer = utf8_fromcstr("");
572	} else {
573		c->prompt_last = NULL;
574		c->prompt_buffer = utf8_fromcstr(tmp);
575	}
576	c->prompt_index = utf8_strlen(c->prompt_buffer);
577
578	c->prompt_inputcb = inputcb;
579	c->prompt_freecb = freecb;
580	c->prompt_data = data;
581
582	c->prompt_hindex = 0;
583
584	c->prompt_flags = flags;
585	c->prompt_mode = PROMPT_ENTRY;
586
587	if (~flags & PROMPT_INCREMENTAL)
588		c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
589	c->flags |= CLIENT_REDRAWSTATUS;
590
591	if (flags & PROMPT_INCREMENTAL)
592		c->prompt_inputcb(c, c->prompt_data, "=", 0);
593
594	free(tmp);
595	format_free(ft);
596}
597
598/* Remove status line prompt. */
599void
600status_prompt_clear(struct client *c)
601{
602	if (c->prompt_string == NULL)
603		return;
604
605	if (c->prompt_freecb != NULL && c->prompt_data != NULL)
606		c->prompt_freecb(c->prompt_data);
607
608	free(c->prompt_last);
609	c->prompt_last = NULL;
610
611	free(c->prompt_string);
612	c->prompt_string = NULL;
613
614	free(c->prompt_buffer);
615	c->prompt_buffer = NULL;
616
617	free(c->prompt_saved);
618	c->prompt_saved = NULL;
619
620	c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
621	c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */
622
623	status_pop_screen(c);
624}
625
626/* Update status line prompt with a new prompt string. */
627void
628status_prompt_update(struct client *c, const char *msg, const char *input)
629{
630	struct format_tree	*ft;
631	char			*tmp;
632
633	ft = format_create(c, NULL, FORMAT_NONE, 0);
634	format_defaults(ft, c, NULL, NULL, NULL);
635
636	tmp = format_expand_time(ft, input);
637
638	free(c->prompt_string);
639	c->prompt_string = format_expand_time(ft, msg);
640
641	free(c->prompt_buffer);
642	c->prompt_buffer = utf8_fromcstr(tmp);
643	c->prompt_index = utf8_strlen(c->prompt_buffer);
644
645	c->prompt_hindex = 0;
646
647	c->flags |= CLIENT_REDRAWSTATUS;
648
649	free(tmp);
650	format_free(ft);
651}
652
653/* Draw client prompt on status line of present else on last line. */
654int
655status_prompt_redraw(struct client *c)
656{
657	struct status_line	*sl = &c->status;
658	struct screen_write_ctx	 ctx;
659	struct session		*s = c->session;
660	struct screen		 old_screen;
661	u_int			 i, lines, offset, left, start, width;
662	u_int			 pcursor, pwidth;
663	struct grid_cell	 gc, cursorgc;
664	struct format_tree	*ft;
665
666	if (c->tty.sx == 0 || c->tty.sy == 0)
667		return (0);
668	memcpy(&old_screen, sl->active, sizeof old_screen);
669
670	lines = status_line_size(c);
671	if (lines <= 1)
672		lines = 1;
673	screen_init(sl->active, c->tty.sx, lines, 0);
674
675	ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
676	if (c->prompt_mode == PROMPT_COMMAND)
677		style_apply(&gc, s->options, "message-command-style", ft);
678	else
679		style_apply(&gc, s->options, "message-style", ft);
680	format_free(ft);
681
682	memcpy(&cursorgc, &gc, sizeof cursorgc);
683	cursorgc.attr ^= GRID_ATTR_REVERSE;
684
685	start = screen_write_strlen("%s", c->prompt_string);
686	if (start > c->tty.sx)
687		start = c->tty.sx;
688
689	screen_write_start(&ctx, sl->active);
690	screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
691	screen_write_cursormove(&ctx, 0, lines - 1, 0);
692	for (offset = 0; offset < c->tty.sx; offset++)
693		screen_write_putc(&ctx, &gc, ' ');
694	screen_write_cursormove(&ctx, 0, lines - 1, 0);
695	screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
696	screen_write_cursormove(&ctx, start, lines - 1, 0);
697
698	left = c->tty.sx - start;
699	if (left == 0)
700		goto finished;
701
702	pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index);
703	pwidth = utf8_strwidth(c->prompt_buffer, -1);
704	if (pcursor >= left) {
705		/*
706		 * The cursor would be outside the screen so start drawing
707		 * with it on the right.
708		 */
709		offset = (pcursor - left) + 1;
710		pwidth = left;
711	} else
712		offset = 0;
713	if (pwidth > left)
714		pwidth = left;
715
716	width = 0;
717	for (i = 0; c->prompt_buffer[i].size != 0; i++) {
718		if (width < offset) {
719			width += c->prompt_buffer[i].width;
720			continue;
721		}
722		if (width >= offset + pwidth)
723			break;
724		width += c->prompt_buffer[i].width;
725		if (width > offset + pwidth)
726			break;
727
728		if (i != c->prompt_index) {
729			utf8_copy(&gc.data, &c->prompt_buffer[i]);
730			screen_write_cell(&ctx, &gc);
731		} else {
732			utf8_copy(&cursorgc.data, &c->prompt_buffer[i]);
733			screen_write_cell(&ctx, &cursorgc);
734		}
735	}
736	if (sl->active->cx < screen_size_x(sl->active) && c->prompt_index >= i)
737		screen_write_putc(&ctx, &cursorgc, ' ');
738
739finished:
740	screen_write_stop(&ctx);
741
742	if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
743		screen_free(&old_screen);
744		return (0);
745	}
746	screen_free(&old_screen);
747	return (1);
748}
749
750/* Is this a separator? */
751static int
752status_prompt_in_list(const char *ws, const struct utf8_data *ud)
753{
754	if (ud->size != 1 || ud->width != 1)
755		return (0);
756	return (strchr(ws, *ud->data) != NULL);
757}
758
759/* Is this a space? */
760static int
761status_prompt_space(const struct utf8_data *ud)
762{
763	if (ud->size != 1 || ud->width != 1)
764		return (0);
765	return (*ud->data == ' ');
766}
767
768/*
769 * Translate key from emacs to vi. Return 0 to drop key, 1 to process the key
770 * as an emacs key; return 2 to append to the buffer.
771 */
772static int
773status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
774{
775	if (c->prompt_mode == PROMPT_ENTRY) {
776		switch (key) {
777		case '\003': /* C-c */
778		case '\007': /* C-g */
779		case '\010': /* C-h */
780		case '\011': /* Tab */
781		case '\025': /* C-u */
782		case '\027': /* C-w */
783		case '\n':
784		case '\r':
785		case KEYC_BSPACE:
786		case KEYC_DC:
787		case KEYC_DOWN:
788		case KEYC_END:
789		case KEYC_HOME:
790		case KEYC_LEFT:
791		case KEYC_RIGHT:
792		case KEYC_UP:
793			*new_key = key;
794			return (1);
795		case '\033': /* Escape */
796			c->prompt_mode = PROMPT_COMMAND;
797			c->flags |= CLIENT_REDRAWSTATUS;
798			return (0);
799		}
800		*new_key = key;
801		return (2);
802	}
803
804	switch (key) {
805	case 'A':
806	case 'I':
807	case 'C':
808	case 's':
809	case 'a':
810		c->prompt_mode = PROMPT_ENTRY;
811		c->flags |= CLIENT_REDRAWSTATUS;
812		break; /* switch mode and... */
813	case 'S':
814		c->prompt_mode = PROMPT_ENTRY;
815		c->flags |= CLIENT_REDRAWSTATUS;
816		*new_key = '\025'; /* C-u */
817		return (1);
818	case 'i':
819	case '\033': /* Escape */
820		c->prompt_mode = PROMPT_ENTRY;
821		c->flags |= CLIENT_REDRAWSTATUS;
822		return (0);
823	}
824
825	switch (key) {
826	case 'A':
827	case '$':
828		*new_key = KEYC_END;
829		return (1);
830	case 'I':
831	case '0':
832	case '^':
833		*new_key = KEYC_HOME;
834		return (1);
835	case 'C':
836	case 'D':
837		*new_key = '\013'; /* C-k */
838		return (1);
839	case KEYC_BSPACE:
840	case 'X':
841		*new_key = KEYC_BSPACE;
842		return (1);
843	case 'b':
844	case 'B':
845		*new_key = 'b'|KEYC_META;
846		return (1);
847	case 'd':
848		*new_key = '\025';
849		return (1);
850	case 'e':
851	case 'E':
852	case 'w':
853	case 'W':
854		*new_key = 'f'|KEYC_META;
855		return (1);
856	case 'p':
857		*new_key = '\031'; /* C-y */
858		return (1);
859	case 'q':
860		*new_key = '\003'; /* C-c */
861		return (1);
862	case 's':
863	case KEYC_DC:
864	case 'x':
865		*new_key = KEYC_DC;
866		return (1);
867	case KEYC_DOWN:
868	case 'j':
869		*new_key = KEYC_DOWN;
870		return (1);
871	case KEYC_LEFT:
872	case 'h':
873		*new_key = KEYC_LEFT;
874		return (1);
875	case 'a':
876	case KEYC_RIGHT:
877	case 'l':
878		*new_key = KEYC_RIGHT;
879		return (1);
880	case KEYC_UP:
881	case 'k':
882		*new_key = KEYC_UP;
883		return (1);
884	case '\010' /* C-h */:
885	case '\003' /* C-c */:
886	case '\n':
887	case '\r':
888		return (1);
889	}
890	return (0);
891}
892
893/* Paste into prompt. */
894static int
895status_prompt_paste(struct client *c)
896{
897	struct paste_buffer	*pb;
898	const char		*bufdata;
899	size_t			 size, n, bufsize;
900	u_int			 i;
901	struct utf8_data	*ud, *udp;
902	enum utf8_state		 more;
903
904	size = utf8_strlen(c->prompt_buffer);
905	if (c->prompt_saved != NULL) {
906		ud = c->prompt_saved;
907		n = utf8_strlen(c->prompt_saved);
908	} else {
909		if ((pb = paste_get_top(NULL)) == NULL)
910			return (0);
911		bufdata = paste_buffer_data(pb, &bufsize);
912		ud = xreallocarray(NULL, bufsize + 1, sizeof *ud);
913		udp = ud;
914		for (i = 0; i != bufsize; /* nothing */) {
915			more = utf8_open(udp, bufdata[i]);
916			if (more == UTF8_MORE) {
917				while (++i != bufsize && more == UTF8_MORE)
918					more = utf8_append(udp, bufdata[i]);
919				if (more == UTF8_DONE) {
920					udp++;
921					continue;
922				}
923				i -= udp->have;
924			}
925			if (bufdata[i] <= 31 || bufdata[i] >= 127)
926				break;
927			utf8_set(udp, bufdata[i]);
928			udp++;
929			i++;
930		}
931		udp->size = 0;
932		n = udp - ud;
933	}
934	if (n == 0)
935		return (0);
936
937	c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
938	    sizeof *c->prompt_buffer);
939	if (c->prompt_index == size) {
940		memcpy(c->prompt_buffer + c->prompt_index, ud,
941		    n * sizeof *c->prompt_buffer);
942		c->prompt_index += n;
943		c->prompt_buffer[c->prompt_index].size = 0;
944	} else {
945		memmove(c->prompt_buffer + c->prompt_index + n,
946		    c->prompt_buffer + c->prompt_index,
947		    (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer);
948		memcpy(c->prompt_buffer + c->prompt_index, ud,
949		    n * sizeof *c->prompt_buffer);
950		c->prompt_index += n;
951	}
952
953	if (ud != c->prompt_saved)
954		free(ud);
955	return (1);
956}
957
958/* Finish completion. */
959static int
960status_prompt_replace_complete(struct client *c, const char *s)
961{
962	char			 word[64], *allocated = NULL;
963	size_t			 size, n, off, idx, used;
964	struct utf8_data	*first, *last, *ud;
965
966	/* Work out where the cursor currently is. */
967	idx = c->prompt_index;
968	if (idx != 0)
969		idx--;
970	size = utf8_strlen(c->prompt_buffer);
971
972	/* Find the word we are in. */
973	first = &c->prompt_buffer[idx];
974	while (first > c->prompt_buffer && !status_prompt_space(first))
975		first--;
976	while (first->size != 0 && status_prompt_space(first))
977		first++;
978	last = &c->prompt_buffer[idx];
979	while (last->size != 0 && !status_prompt_space(last))
980		last++;
981	while (last > c->prompt_buffer && status_prompt_space(last))
982		last--;
983	if (last->size != 0)
984		last++;
985	if (last < first)
986		return (0);
987	if (s == NULL) {
988		used = 0;
989		for (ud = first; ud < last; ud++) {
990			if (used + ud->size >= sizeof word)
991				break;
992			memcpy(word + used, ud->data, ud->size);
993			used += ud->size;
994		}
995		if (ud != last)
996			return (0);
997		word[used] = '\0';
998	}
999
1000	/* Try to complete it. */
1001	if (s == NULL) {
1002		allocated = status_prompt_complete(c, word,
1003		    first - c->prompt_buffer);
1004		if (allocated == NULL)
1005			return (0);
1006		s = allocated;
1007	}
1008
1009	/* Trim out word. */
1010	n = size - (last - c->prompt_buffer) + 1; /* with \0 */
1011	memmove(first, last, n * sizeof *c->prompt_buffer);
1012	size -= last - first;
1013
1014	/* Insert the new word. */
1015	size += strlen(s);
1016	off = first - c->prompt_buffer;
1017	c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
1018	    sizeof *c->prompt_buffer);
1019	first = c->prompt_buffer + off;
1020	memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
1021	for (idx = 0; idx < strlen(s); idx++)
1022		utf8_set(&first[idx], s[idx]);
1023	c->prompt_index = (first - c->prompt_buffer) + strlen(s);
1024
1025	free(allocated);
1026	return (1);
1027}
1028
1029/* Handle keys in prompt. */
1030int
1031status_prompt_key(struct client *c, key_code key)
1032{
1033	struct options		*oo = c->session->options;
1034	char			*s, *cp, prefix = '=';
1035	const char		*histstr, *ws = NULL, *keystring;
1036	size_t			 size, idx;
1037	struct utf8_data	 tmp;
1038	int			 keys;
1039
1040	if (c->prompt_flags & PROMPT_KEY) {
1041		keystring = key_string_lookup_key(key, 0);
1042		c->prompt_inputcb(c, c->prompt_data, keystring, 1);
1043		status_prompt_clear(c);
1044		return (0);
1045	}
1046	size = utf8_strlen(c->prompt_buffer);
1047
1048	if (c->prompt_flags & PROMPT_NUMERIC) {
1049		if (key >= '0' && key <= '9')
1050			goto append_key;
1051		s = utf8_tocstr(c->prompt_buffer);
1052		c->prompt_inputcb(c, c->prompt_data, s, 1);
1053		status_prompt_clear(c);
1054		free(s);
1055		return (1);
1056	}
1057	key &= ~KEYC_MASK_FLAGS;
1058
1059	keys = options_get_number(c->session->options, "status-keys");
1060	if (keys == MODEKEY_VI) {
1061		switch (status_prompt_translate_key(c, key, &key)) {
1062		case 1:
1063			goto process_key;
1064		case 2:
1065			goto append_key;
1066		default:
1067			return (0);
1068		}
1069	}
1070
1071process_key:
1072	switch (key) {
1073	case KEYC_LEFT:
1074	case '\002': /* C-b */
1075		if (c->prompt_index > 0) {
1076			c->prompt_index--;
1077			break;
1078		}
1079		break;
1080	case KEYC_RIGHT:
1081	case '\006': /* C-f */
1082		if (c->prompt_index < size) {
1083			c->prompt_index++;
1084			break;
1085		}
1086		break;
1087	case KEYC_HOME:
1088	case '\001': /* C-a */
1089		if (c->prompt_index != 0) {
1090			c->prompt_index = 0;
1091			break;
1092		}
1093		break;
1094	case KEYC_END:
1095	case '\005': /* C-e */
1096		if (c->prompt_index != size) {
1097			c->prompt_index = size;
1098			break;
1099		}
1100		break;
1101	case '\011': /* Tab */
1102		if (status_prompt_replace_complete(c, NULL))
1103			goto changed;
1104		break;
1105	case KEYC_BSPACE:
1106	case '\010': /* C-h */
1107		if (c->prompt_index != 0) {
1108			if (c->prompt_index == size)
1109				c->prompt_buffer[--c->prompt_index].size = 0;
1110			else {
1111				memmove(c->prompt_buffer + c->prompt_index - 1,
1112				    c->prompt_buffer + c->prompt_index,
1113				    (size + 1 - c->prompt_index) *
1114				    sizeof *c->prompt_buffer);
1115				c->prompt_index--;
1116			}
1117			goto changed;
1118		}
1119		break;
1120	case KEYC_DC:
1121	case '\004': /* C-d */
1122		if (c->prompt_index != size) {
1123			memmove(c->prompt_buffer + c->prompt_index,
1124			    c->prompt_buffer + c->prompt_index + 1,
1125			    (size + 1 - c->prompt_index) *
1126			    sizeof *c->prompt_buffer);
1127			goto changed;
1128		}
1129		break;
1130	case '\025': /* C-u */
1131		c->prompt_buffer[0].size = 0;
1132		c->prompt_index = 0;
1133		goto changed;
1134	case '\013': /* C-k */
1135		if (c->prompt_index < size) {
1136			c->prompt_buffer[c->prompt_index].size = 0;
1137			goto changed;
1138		}
1139		break;
1140	case '\027': /* C-w */
1141		ws = options_get_string(oo, "word-separators");
1142		idx = c->prompt_index;
1143
1144		/* Find a non-separator. */
1145		while (idx != 0) {
1146			idx--;
1147			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1148				break;
1149		}
1150
1151		/* Find the separator at the beginning of the word. */
1152		while (idx != 0) {
1153			idx--;
1154			if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
1155				/* Go back to the word. */
1156				idx++;
1157				break;
1158			}
1159		}
1160
1161		free(c->prompt_saved);
1162		c->prompt_saved = xcalloc(sizeof *c->prompt_buffer,
1163		    (c->prompt_index - idx) + 1);
1164		memcpy(c->prompt_saved, c->prompt_buffer + idx,
1165		    (c->prompt_index - idx) * sizeof *c->prompt_buffer);
1166
1167		memmove(c->prompt_buffer + idx,
1168		    c->prompt_buffer + c->prompt_index,
1169		    (size + 1 - c->prompt_index) *
1170		    sizeof *c->prompt_buffer);
1171		memset(c->prompt_buffer + size - (c->prompt_index - idx),
1172		    '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer);
1173		c->prompt_index = idx;
1174
1175		goto changed;
1176	case 'f'|KEYC_META:
1177	case KEYC_RIGHT|KEYC_CTRL:
1178		ws = options_get_string(oo, "word-separators");
1179
1180		/* Find a word. */
1181		while (c->prompt_index != size) {
1182			idx = ++c->prompt_index;
1183			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1184				break;
1185		}
1186
1187		/* Find the separator at the end of the word. */
1188		while (c->prompt_index != size) {
1189			idx = ++c->prompt_index;
1190			if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1191				break;
1192		}
1193
1194		/* Back up to the end-of-word like vi. */
1195		if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
1196		    c->prompt_index != 0)
1197			c->prompt_index--;
1198
1199		goto changed;
1200	case 'b'|KEYC_META:
1201	case KEYC_LEFT|KEYC_CTRL:
1202		ws = options_get_string(oo, "word-separators");
1203
1204		/* Find a non-separator. */
1205		while (c->prompt_index != 0) {
1206			idx = --c->prompt_index;
1207			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1208				break;
1209		}
1210
1211		/* Find the separator at the beginning of the word. */
1212		while (c->prompt_index != 0) {
1213			idx = --c->prompt_index;
1214			if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
1215				/* Go back to the word. */
1216				c->prompt_index++;
1217				break;
1218			}
1219		}
1220		goto changed;
1221	case KEYC_UP:
1222	case '\020': /* C-p */
1223		histstr = status_prompt_up_history(&c->prompt_hindex);
1224		if (histstr == NULL)
1225			break;
1226		free(c->prompt_buffer);
1227		c->prompt_buffer = utf8_fromcstr(histstr);
1228		c->prompt_index = utf8_strlen(c->prompt_buffer);
1229		goto changed;
1230	case KEYC_DOWN:
1231	case '\016': /* C-n */
1232		histstr = status_prompt_down_history(&c->prompt_hindex);
1233		if (histstr == NULL)
1234			break;
1235		free(c->prompt_buffer);
1236		c->prompt_buffer = utf8_fromcstr(histstr);
1237		c->prompt_index = utf8_strlen(c->prompt_buffer);
1238		goto changed;
1239	case '\031': /* C-y */
1240		if (status_prompt_paste(c))
1241			goto changed;
1242		break;
1243	case '\024': /* C-t */
1244		idx = c->prompt_index;
1245		if (idx < size)
1246			idx++;
1247		if (idx >= 2) {
1248			utf8_copy(&tmp, &c->prompt_buffer[idx - 2]);
1249			utf8_copy(&c->prompt_buffer[idx - 2],
1250			    &c->prompt_buffer[idx - 1]);
1251			utf8_copy(&c->prompt_buffer[idx - 1], &tmp);
1252			c->prompt_index = idx;
1253			goto changed;
1254		}
1255		break;
1256	case '\r':
1257	case '\n':
1258		s = utf8_tocstr(c->prompt_buffer);
1259		if (*s != '\0')
1260			status_prompt_add_history(s);
1261		if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
1262			status_prompt_clear(c);
1263		free(s);
1264		break;
1265	case '\033': /* Escape */
1266	case '\003': /* C-c */
1267	case '\007': /* C-g */
1268		if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
1269			status_prompt_clear(c);
1270		break;
1271	case '\022': /* C-r */
1272		if (~c->prompt_flags & PROMPT_INCREMENTAL)
1273			break;
1274		if (c->prompt_buffer[0].size == 0) {
1275			prefix = '=';
1276			free (c->prompt_buffer);
1277			c->prompt_buffer = utf8_fromcstr(c->prompt_last);
1278			c->prompt_index = utf8_strlen(c->prompt_buffer);
1279		} else
1280			prefix = '-';
1281		goto changed;
1282	case '\023': /* C-s */
1283		if (~c->prompt_flags & PROMPT_INCREMENTAL)
1284			break;
1285		if (c->prompt_buffer[0].size == 0) {
1286			prefix = '=';
1287			free (c->prompt_buffer);
1288			c->prompt_buffer = utf8_fromcstr(c->prompt_last);
1289			c->prompt_index = utf8_strlen(c->prompt_buffer);
1290		} else
1291			prefix = '+';
1292		goto changed;
1293	default:
1294		goto append_key;
1295	}
1296
1297	c->flags |= CLIENT_REDRAWSTATUS;
1298	return (0);
1299
1300append_key:
1301	if (key <= 0x1f || key >= KEYC_BASE)
1302		return (0);
1303	if (key <= 0x7f)
1304		utf8_set(&tmp, key);
1305	else
1306		utf8_to_data(key, &tmp);
1307
1308	c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2,
1309	    sizeof *c->prompt_buffer);
1310
1311	if (c->prompt_index == size) {
1312		utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
1313		c->prompt_index++;
1314		c->prompt_buffer[c->prompt_index].size = 0;
1315	} else {
1316		memmove(c->prompt_buffer + c->prompt_index + 1,
1317		    c->prompt_buffer + c->prompt_index,
1318		    (size + 1 - c->prompt_index) *
1319		    sizeof *c->prompt_buffer);
1320		utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
1321		c->prompt_index++;
1322	}
1323
1324	if (c->prompt_flags & PROMPT_SINGLE) {
1325		if (utf8_strlen(c->prompt_buffer) != 1)
1326			status_prompt_clear(c);
1327		else {
1328			s = utf8_tocstr(c->prompt_buffer);
1329			if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
1330				status_prompt_clear(c);
1331			free(s);
1332		}
1333	}
1334
1335changed:
1336	c->flags |= CLIENT_REDRAWSTATUS;
1337	if (c->prompt_flags & PROMPT_INCREMENTAL) {
1338		s = utf8_tocstr(c->prompt_buffer);
1339		xasprintf(&cp, "%c%s", prefix, s);
1340		c->prompt_inputcb(c, c->prompt_data, cp, 0);
1341		free(cp);
1342		free(s);
1343	}
1344	return (0);
1345}
1346
1347/* Get previous line from the history. */
1348static const char *
1349status_prompt_up_history(u_int *idx)
1350{
1351	/*
1352	 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1353	 * empty.
1354	 */
1355
1356	if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
1357		return (NULL);
1358	(*idx)++;
1359	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1360}
1361
1362/* Get next line from the history. */
1363static const char *
1364status_prompt_down_history(u_int *idx)
1365{
1366	if (status_prompt_hsize == 0 || *idx == 0)
1367		return ("");
1368	(*idx)--;
1369	if (*idx == 0)
1370		return ("");
1371	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1372}
1373
1374/* Add line to the history. */
1375static void
1376status_prompt_add_history(const char *line)
1377{
1378	size_t	size;
1379
1380	if (status_prompt_hsize > 0 &&
1381	    strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
1382		return;
1383
1384	if (status_prompt_hsize == PROMPT_HISTORY) {
1385		free(status_prompt_hlist[0]);
1386
1387		size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
1388		memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
1389
1390		status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
1391		return;
1392	}
1393
1394	status_prompt_hlist = xreallocarray(status_prompt_hlist,
1395	    status_prompt_hsize + 1, sizeof *status_prompt_hlist);
1396	status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
1397}
1398
1399/* Build completion list. */
1400static char **
1401status_prompt_complete_list(u_int *size, const char *s, int at_start)
1402{
1403	char					**list = NULL;
1404	const char				**layout, *value, *cp;
1405	const struct cmd_entry			**cmdent;
1406	const struct options_table_entry	 *oe;
1407	size_t					  slen = strlen(s), valuelen;
1408	struct options_entry			 *o;
1409	struct options_array_item		 *a;
1410	const char				 *layouts[] = {
1411		"even-horizontal", "even-vertical", "main-horizontal",
1412		"main-vertical", "tiled", NULL
1413	};
1414
1415	*size = 0;
1416	for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1417		if (strncmp((*cmdent)->name, s, slen) == 0) {
1418			list = xreallocarray(list, (*size) + 1, sizeof *list);
1419			list[(*size)++] = xstrdup((*cmdent)->name);
1420		}
1421		if ((*cmdent)->alias != NULL &&
1422		    strncmp((*cmdent)->alias, s, slen) == 0) {
1423			list = xreallocarray(list, (*size) + 1, sizeof *list);
1424			list[(*size)++] = xstrdup((*cmdent)->alias);
1425		}
1426	}
1427	o = options_get_only(global_options, "command-alias");
1428	if (o != NULL) {
1429		a = options_array_first(o);
1430		while (a != NULL) {
1431			value = options_array_item_value(a)->string;
1432			if ((cp = strchr(value, '=')) == NULL)
1433				goto next;
1434			valuelen = cp - value;
1435			if (slen > valuelen || strncmp(value, s, slen) != 0)
1436				goto next;
1437
1438			list = xreallocarray(list, (*size) + 1, sizeof *list);
1439			list[(*size)++] = xstrndup(value, valuelen);
1440
1441		next:
1442			a = options_array_next(a);
1443		}
1444	}
1445	if (at_start)
1446		return (list);
1447
1448	for (oe = options_table; oe->name != NULL; oe++) {
1449		if (strncmp(oe->name, s, slen) == 0) {
1450			list = xreallocarray(list, (*size) + 1, sizeof *list);
1451			list[(*size)++] = xstrdup(oe->name);
1452		}
1453	}
1454	for (layout = layouts; *layout != NULL; layout++) {
1455		if (strncmp(*layout, s, slen) == 0) {
1456			list = xreallocarray(list, (*size) + 1, sizeof *list);
1457			list[(*size)++] = xstrdup(*layout);
1458		}
1459	}
1460	return (list);
1461}
1462
1463/* Find longest prefix. */
1464static char *
1465status_prompt_complete_prefix(char **list, u_int size)
1466{
1467	char	 *out;
1468	u_int	  i;
1469	size_t	  j;
1470
1471	if (list == NULL || size == 0)
1472		return (NULL);
1473	out = xstrdup(list[0]);
1474	for (i = 1; i < size; i++) {
1475		j = strlen(list[i]);
1476		if (j > strlen(out))
1477			j = strlen(out);
1478		for (; j > 0; j--) {
1479			if (out[j - 1] != list[i][j - 1])
1480				out[j - 1] = '\0';
1481		}
1482	}
1483	return (out);
1484}
1485
1486/* Complete word menu callback. */
1487static void
1488status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key,
1489    void *data)
1490{
1491	struct status_prompt_menu	*spm = data;
1492	struct client			*c = spm->c;
1493	u_int				 i;
1494	char				*s;
1495
1496	if (key != KEYC_NONE) {
1497		idx += spm->start;
1498		if (spm->flag == '\0')
1499			s = xstrdup(spm->list[idx]);
1500		else
1501			xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]);
1502		if (c->prompt_flags & PROMPT_WINDOW) {
1503			free(c->prompt_buffer);
1504			c->prompt_buffer = utf8_fromcstr(s);
1505			c->prompt_index = utf8_strlen(c->prompt_buffer);
1506			c->flags |= CLIENT_REDRAWSTATUS;
1507		} else if (status_prompt_replace_complete(c, s))
1508			c->flags |= CLIENT_REDRAWSTATUS;
1509		free(s);
1510	}
1511
1512	for (i = 0; i < spm->size; i++)
1513		free(spm->list[i]);
1514	free(spm->list);
1515}
1516
1517/* Show complete word menu. */
1518static int
1519status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
1520    u_int offset, char flag)
1521{
1522	struct menu			*menu;
1523	struct menu_item		 item;
1524	struct status_prompt_menu	*spm;
1525	u_int				 lines = status_line_size(c), height, i;
1526	u_int				 py;
1527
1528	if (size <= 1)
1529		return (0);
1530	if (c->tty.sy - lines < 3)
1531		return (0);
1532
1533	spm = xmalloc(sizeof *spm);
1534	spm->c = c;
1535	spm->size = size;
1536	spm->list = list;
1537	spm->flag = flag;
1538
1539	height = c->tty.sy - lines - 2;
1540	if (height > 10)
1541		height = 10;
1542	if (height > size)
1543		height = size;
1544	spm->start = size - height;
1545
1546	menu = menu_create("");
1547	for (i = spm->start; i < size; i++) {
1548		item.name = list[i];
1549		item.key = '0' + (i - spm->start);
1550		item.command = NULL;
1551		menu_add_item(menu, &item, NULL, NULL, NULL);
1552	}
1553
1554	if (options_get_number(c->session->options, "status-position") == 0)
1555		py = lines;
1556	else
1557		py = c->tty.sy - 3 - height;
1558	offset += utf8_cstrwidth(c->prompt_string);
1559	if (offset > 2)
1560		offset -= 2;
1561	else
1562		offset = 0;
1563
1564	if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
1565	    py, c, NULL, status_prompt_menu_callback, spm) != 0) {
1566		menu_free(menu);
1567		free(spm);
1568		return (0);
1569	}
1570	return (1);
1571}
1572
1573/* Show complete word menu. */
1574static char *
1575status_prompt_complete_window_menu(struct client *c, struct session *s,
1576    const char *word, u_int offset, char flag)
1577{
1578	struct menu			 *menu;
1579	struct menu_item		  item;
1580	struct status_prompt_menu	 *spm;
1581	struct winlink			 *wl;
1582	char				**list = NULL, *tmp;
1583	u_int				  lines = status_line_size(c), height;
1584	u_int				  py, size = 0;
1585
1586	if (c->tty.sy - lines < 3)
1587		return (NULL);
1588
1589	spm = xmalloc(sizeof *spm);
1590	spm->c = c;
1591	spm->flag = flag;
1592
1593	height = c->tty.sy - lines - 2;
1594	if (height > 10)
1595		height = 10;
1596	spm->start = 0;
1597
1598	menu = menu_create("");
1599	RB_FOREACH(wl, winlinks, &s->windows) {
1600		if (word != NULL && *word != '\0') {
1601			xasprintf(&tmp, "%d", wl->idx);
1602			if (strncmp(tmp, word, strlen(word)) != 0) {
1603				free(tmp);
1604				continue;
1605			}
1606			free(tmp);
1607		}
1608
1609		list = xreallocarray(list, size + 1, sizeof *list);
1610		if (c->prompt_flags & PROMPT_WINDOW) {
1611			xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name);
1612			xasprintf(&list[size++], "%d", wl->idx);
1613		} else {
1614			xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx,
1615			    wl->window->name);
1616			xasprintf(&list[size++], "%s:%d", s->name, wl->idx);
1617		}
1618		item.name = tmp;
1619		item.key = '0' + size - 1;
1620		item.command = NULL;
1621		menu_add_item(menu, &item, NULL, NULL, NULL);
1622		free(tmp);
1623
1624		if (size == height)
1625			break;
1626	}
1627	if (size == 0) {
1628		menu_free(menu);
1629		return (NULL);
1630	}
1631	if (size == 1) {
1632		menu_free(menu);
1633		if (flag != '\0') {
1634			xasprintf(&tmp, "-%c%s", flag, list[0]);
1635			free(list[0]);
1636		} else
1637			tmp = list[0];
1638		free(list);
1639		return (tmp);
1640	}
1641	if (height > size)
1642		height = size;
1643
1644	spm->size = size;
1645	spm->list = list;
1646
1647	if (options_get_number(c->session->options, "status-position") == 0)
1648		py = lines;
1649	else
1650		py = c->tty.sy - 3 - height;
1651	offset += utf8_cstrwidth(c->prompt_string);
1652	if (offset > 2)
1653		offset -= 2;
1654	else
1655		offset = 0;
1656
1657	if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
1658	    py, c, NULL, status_prompt_menu_callback, spm) != 0) {
1659		menu_free(menu);
1660		free(spm);
1661		return (NULL);
1662	}
1663	return (NULL);
1664}
1665
1666/* Sort complete list. */
1667static int
1668status_prompt_complete_sort(const void *a, const void *b)
1669{
1670	const char	**aa = (const char **)a, **bb = (const char **)b;
1671
1672	return (strcmp(*aa, *bb));
1673}
1674
1675/* Complete a session. */
1676static char *
1677status_prompt_complete_session(char ***list, u_int *size, const char *s,
1678    char flag)
1679{
1680	struct session	*loop;
1681	char		*out, *tmp, n[11];
1682
1683	RB_FOREACH(loop, sessions, &sessions) {
1684		if (*s == '\0' || strncmp(loop->name, s, strlen(s)) == 0) {
1685			*list = xreallocarray(*list, (*size) + 2,
1686			    sizeof **list);
1687			xasprintf(&(*list)[(*size)++], "%s:", loop->name);
1688		} else if (*s == '$') {
1689			xsnprintf(n, sizeof n, "%u", loop->id);
1690			if (s[1] == '\0' ||
1691			    strncmp(n, s + 1, strlen(s) - 1) == 0) {
1692				*list = xreallocarray(*list, (*size) + 2,
1693				    sizeof **list);
1694				xasprintf(&(*list)[(*size)++], "$%s:", n);
1695			}
1696		}
1697	}
1698	out = status_prompt_complete_prefix(*list, *size);
1699	if (out != NULL && flag != '\0') {
1700		xasprintf(&tmp, "-%c%s", flag, out);
1701		free(out);
1702		out = tmp;
1703	}
1704	return (out);
1705}
1706
1707/* Complete word. */
1708static char *
1709status_prompt_complete(struct client *c, const char *word, u_int offset)
1710{
1711	struct session	 *session;
1712	const char	 *s, *colon;
1713	char		**list = NULL, *copy = NULL, *out = NULL;
1714	char		  flag = '\0';
1715	u_int		  size = 0, i;
1716
1717	if (*word == '\0' &&
1718	    ((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0))
1719		return (NULL);
1720
1721	if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) &&
1722	    strncmp(word, "-t", 2) != 0 &&
1723	    strncmp(word, "-s", 2) != 0) {
1724		list = status_prompt_complete_list(&size, word, offset == 0);
1725		if (size == 0)
1726			out = NULL;
1727		else if (size == 1)
1728			xasprintf(&out, "%s ", list[0]);
1729		else
1730			out = status_prompt_complete_prefix(list, size);
1731		goto found;
1732	}
1733
1734	if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) {
1735		s = word;
1736		flag = '\0';
1737	} else {
1738		s = word + 2;
1739		flag = word[1];
1740		offset += 2;
1741	}
1742
1743	/* If this is a window completion, open the window menu. */
1744	if (c->prompt_flags & PROMPT_WINDOW) {
1745		out = status_prompt_complete_window_menu(c, c->session, s,
1746		    offset, '\0');
1747		goto found;
1748	}
1749	colon = strchr(s, ':');
1750
1751	/* If there is no colon, complete as a session. */
1752	if (colon == NULL) {
1753		out = status_prompt_complete_session(&list, &size, s, flag);
1754		goto found;
1755	}
1756
1757	/* If there is a colon but no period, find session and show a menu. */
1758	if (strchr(colon + 1, '.') == NULL) {
1759		if (*s == ':')
1760			session = c->session;
1761		else {
1762			copy = xstrdup(s);
1763			*strchr(copy, ':') = '\0';
1764			session = session_find(copy);
1765			free(copy);
1766			if (session == NULL)
1767				goto found;
1768		}
1769		out = status_prompt_complete_window_menu(c, session, colon + 1,
1770		    offset, flag);
1771		if (out == NULL)
1772			return (NULL);
1773	}
1774
1775found:
1776	if (size != 0) {
1777		qsort(list, size, sizeof *list, status_prompt_complete_sort);
1778		for (i = 0; i < size; i++)
1779			log_debug("complete %u: %s", i, list[i]);
1780	}
1781
1782	if (out != NULL && strcmp(word, out) == 0) {
1783		free(out);
1784		out = NULL;
1785	}
1786	if (out != NULL ||
1787	    !status_prompt_complete_list_menu(c, list, size, offset, flag)) {
1788		for (i = 0; i < size; i++)
1789			free(list[i]);
1790		free(list);
1791	}
1792	return (out);
1793}
1794