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