status.c revision 1.199
1/* $OpenBSD: status.c,v 1.199 2019/05/23 12:47:52 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_list(u_int *, const char *);
41static char	*status_prompt_complete_prefix(char **, u_int);
42static char	*status_prompt_complete(struct session *, const char *);
43
44/* Status prompt history. */
45#define PROMPT_HISTORY 100
46static char	**status_prompt_hlist;
47static u_int	  status_prompt_hsize;
48
49/* Find the history file to load/save from/to. */
50static char *
51status_prompt_find_history_file(void)
52{
53	const char	*home, *history_file;
54	char		*path;
55
56	history_file = options_get_string(global_options, "history-file");
57	if (*history_file == '\0')
58		return (NULL);
59	if (*history_file == '/')
60		return (xstrdup(history_file));
61
62	if (history_file[0] != '~' || history_file[1] != '/')
63		return (NULL);
64	if ((home = find_home()) == NULL)
65		return (NULL);
66	xasprintf(&path, "%s%s", home, history_file + 1);
67	return (path);
68}
69
70/* Load status prompt history from file. */
71void
72status_prompt_load_history(void)
73{
74	FILE	*f;
75	char	*history_file, *line, *tmp;
76	size_t	 length;
77
78	if ((history_file = status_prompt_find_history_file()) == NULL)
79		return;
80	log_debug("loading history from %s", history_file);
81
82	f = fopen(history_file, "r");
83	if (f == NULL) {
84		log_debug("%s: %s", history_file, strerror(errno));
85		free(history_file);
86		return;
87	}
88	free(history_file);
89
90	for (;;) {
91		if ((line = fgetln(f, &length)) == NULL)
92			break;
93
94		if (length > 0) {
95			if (line[length - 1] == '\n') {
96				line[length - 1] = '\0';
97				status_prompt_add_history(line);
98			} else {
99				tmp = xmalloc(length + 1);
100				memcpy(tmp, line, length);
101				tmp[length] = '\0';
102				status_prompt_add_history(tmp);
103				free(tmp);
104			}
105		}
106	}
107	fclose(f);
108}
109
110/* Save status prompt history to file. */
111void
112status_prompt_save_history(void)
113{
114	FILE	*f;
115	u_int	 i;
116	char	*history_file;
117
118	if ((history_file = status_prompt_find_history_file()) == NULL)
119		return;
120	log_debug("saving history to %s", history_file);
121
122	f = fopen(history_file, "w");
123	if (f == NULL) {
124		log_debug("%s: %s", history_file, strerror(errno));
125		free(history_file);
126		return;
127	}
128	free(history_file);
129
130	for (i = 0; i < status_prompt_hsize; i++) {
131		fputs(status_prompt_hlist[i], f);
132		fputc('\n', f);
133	}
134	fclose(f);
135
136}
137
138/* Status timer callback. */
139static void
140status_timer_callback(__unused int fd, __unused short events, void *arg)
141{
142	struct client	*c = arg;
143	struct session	*s = c->session;
144	struct timeval	 tv;
145
146	evtimer_del(&c->status.timer);
147
148	if (s == NULL)
149		return;
150
151	if (c->message_string == NULL && c->prompt_string == NULL)
152		c->flags |= CLIENT_REDRAWSTATUS;
153
154	timerclear(&tv);
155	tv.tv_sec = options_get_number(s->options, "status-interval");
156
157	if (tv.tv_sec != 0)
158		evtimer_add(&c->status.timer, &tv);
159	log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
160}
161
162/* Start status timer for client. */
163void
164status_timer_start(struct client *c)
165{
166	struct session	*s = c->session;
167
168	if (event_initialized(&c->status.timer))
169		evtimer_del(&c->status.timer);
170	else
171		evtimer_set(&c->status.timer, status_timer_callback, c);
172
173	if (s != NULL && options_get_number(s->options, "status"))
174		status_timer_callback(-1, 0, c);
175}
176
177/* Start status timer for all clients. */
178void
179status_timer_start_all(void)
180{
181	struct client	*c;
182
183	TAILQ_FOREACH(c, &clients, entry)
184		status_timer_start(c);
185}
186
187/* Update status cache. */
188void
189status_update_cache(struct session *s)
190{
191	s->statuslines = options_get_number(s->options, "status");
192	if (s->statuslines == 0)
193		s->statusat = -1;
194	else if (options_get_number(s->options, "status-position") == 0)
195		s->statusat = 0;
196	else
197		s->statusat = 1;
198}
199
200/* Get screen line of status line. -1 means off. */
201int
202status_at_line(struct client *c)
203{
204	struct session	*s = c->session;
205
206	if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
207		return (-1);
208	if (s->statusat != 1)
209		return (s->statusat);
210	return (c->tty.sy - status_line_size(c));
211}
212
213/* Get size of status line for client's session. 0 means off. */
214u_int
215status_line_size(struct client *c)
216{
217	struct session	*s = c->session;
218
219	if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
220		return (0);
221	return (s->statuslines);
222}
223
224/* Get window at window list position. */
225struct style_range *
226status_get_range(struct client *c, u_int x, u_int y)
227{
228	struct status_line	*sl = &c->status;
229	struct style_range	*sr;
230
231	if (y >= nitems(sl->entries))
232		return (NULL);
233	TAILQ_FOREACH(sr, &sl->entries[y].ranges, entry) {
234		if (x >= sr->start && x < sr->end)
235			return (sr);
236	}
237	return (NULL);
238}
239
240/* Free all ranges. */
241static void
242status_free_ranges(struct style_ranges *srs)
243{
244	struct style_range	*sr, *sr1;
245
246	TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) {
247		TAILQ_REMOVE(srs, sr, entry);
248		free(sr);
249	}
250}
251
252/* Save old status line. */
253static void
254status_push_screen(struct client *c)
255{
256	struct status_line *sl = &c->status;
257
258	if (sl->active == &sl->screen) {
259		sl->active = xmalloc(sizeof *sl->active);
260		screen_init(sl->active, c->tty.sx, status_line_size(c), 0);
261	}
262	sl->references++;
263}
264
265/* Restore old status line. */
266static void
267status_pop_screen(struct client *c)
268{
269	struct status_line *sl = &c->status;
270
271	if (--sl->references == 0) {
272		screen_free(sl->active);
273		free(sl->active);
274		sl->active = &sl->screen;
275	}
276}
277
278/* Initialize status line. */
279void
280status_init(struct client *c)
281{
282	struct status_line	*sl = &c->status;
283	u_int			 i;
284
285	for (i = 0; i < nitems(sl->entries); i++)
286		TAILQ_INIT(&sl->entries[i].ranges);
287
288	screen_init(&sl->screen, c->tty.sx, 1, 0);
289	sl->active = &sl->screen;
290}
291
292/* Free status line. */
293void
294status_free(struct client *c)
295{
296	struct status_line	*sl = &c->status;
297	u_int			 i;
298
299	for (i = 0; i < nitems(sl->entries); i++) {
300		status_free_ranges(&sl->entries[i].ranges);
301		free((void *)sl->entries[i].expanded);
302	}
303
304	if (event_initialized(&sl->timer))
305		evtimer_del(&sl->timer);
306
307	if (sl->active != &sl->screen) {
308		screen_free(sl->active);
309		free(sl->active);
310	}
311	screen_free(&sl->screen);
312}
313
314/* Draw status line for client. */
315int
316status_redraw(struct client *c)
317{
318	struct status_line		*sl = &c->status;
319	struct status_line_entry	*sle;
320	struct session			*s = c->session;
321	struct screen_write_ctx		 ctx;
322	struct grid_cell		 gc;
323	u_int				 lines, i, n, width = c->tty.sx;
324	int				 flags, force = 0, changed = 0;
325	struct options_entry		*o;
326	union options_value		*ov;
327	struct format_tree		*ft;
328	char				*expanded;
329
330	log_debug("%s enter", __func__);
331
332	/* Shouldn't get here if not the active screen. */
333	if (sl->active != &sl->screen)
334		fatalx("not the active screen");
335
336	/* No status line? */
337	lines = status_line_size(c);
338	if (c->tty.sy == 0 || lines == 0)
339		return (1);
340
341	/* Set up default colour. */
342	style_apply(&gc, s->options, "status-style");
343	if (!grid_cells_equal(&gc, &sl->style)) {
344		force = 1;
345		memcpy(&sl->style, &gc, sizeof sl->style);
346	}
347
348	/* Resize the target screen. */
349	if (screen_size_x(&sl->screen) != width ||
350	    screen_size_y(&sl->screen) != lines) {
351		if (screen_size_x(&sl->screen) != width)
352			force = 1;
353		screen_resize(&sl->screen, width, lines, 0);
354		changed = 1;
355	}
356	screen_write_start(&ctx, NULL, &sl->screen);
357
358	/* Create format tree. */
359	flags = FORMAT_STATUS;
360	if (c->flags & CLIENT_STATUSFORCE)
361		flags |= FORMAT_FORCE;
362	ft = format_create(c, NULL, FORMAT_NONE, flags);
363	format_defaults(ft, c, NULL, NULL, NULL);
364
365	/* Write the status lines. */
366	o = options_get(s->options, "status-format");
367	if (o == NULL) {
368		for (n = 0; n < width * lines; n++)
369			screen_write_putc(&ctx, &gc, ' ');
370	} else {
371		for (i = 0; i < lines; i++) {
372			screen_write_cursormove(&ctx, 0, i, 0);
373
374			ov = options_array_get(o, i);
375			if (ov == NULL) {
376				for (n = 0; n < width; n++)
377					screen_write_putc(&ctx, &gc, ' ');
378				continue;
379			}
380			sle = &sl->entries[i];
381
382			expanded = format_expand_time(ft, ov->string);
383			if (!force &&
384			    sle->expanded != NULL &&
385			    strcmp(expanded, sle->expanded) == 0) {
386				free(expanded);
387				continue;
388			}
389			changed = 1;
390
391			for (n = 0; n < width; n++)
392				screen_write_putc(&ctx, &gc, ' ');
393			screen_write_cursormove(&ctx, 0, i, 0);
394
395			status_free_ranges(&sle->ranges);
396			format_draw(&ctx, &gc, width, expanded, &sle->ranges);
397
398			free(sle->expanded);
399			sle->expanded = expanded;
400		}
401	}
402	screen_write_stop(&ctx);
403
404	/* Free the format tree. */
405	format_free(ft);
406
407	/* Return if the status line has changed. */
408	log_debug("%s exit: force=%d, changed=%d", __func__, force, changed);
409	return (force || changed);
410}
411
412/* Set a status line message. */
413void
414status_message_set(struct client *c, const char *fmt, ...)
415{
416	struct timeval	tv;
417	va_list		ap;
418	int		delay;
419
420	status_message_clear(c);
421	status_push_screen(c);
422
423	va_start(ap, fmt);
424	xvasprintf(&c->message_string, fmt, ap);
425	va_end(ap);
426
427	server_client_add_message(c, "%s", c->message_string);
428
429	delay = options_get_number(c->session->options, "display-time");
430	if (delay > 0) {
431		tv.tv_sec = delay / 1000;
432		tv.tv_usec = (delay % 1000) * 1000L;
433
434		if (event_initialized(&c->message_timer))
435			evtimer_del(&c->message_timer);
436		evtimer_set(&c->message_timer, status_message_callback, c);
437		evtimer_add(&c->message_timer, &tv);
438	}
439
440	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
441	c->flags |= CLIENT_REDRAWSTATUS;
442}
443
444/* Clear status line message. */
445void
446status_message_clear(struct client *c)
447{
448	if (c->message_string == NULL)
449		return;
450
451	free(c->message_string);
452	c->message_string = NULL;
453
454	if (c->prompt_string == NULL)
455		c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
456	c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */
457
458	status_pop_screen(c);
459}
460
461/* Clear status line message after timer expires. */
462static void
463status_message_callback(__unused int fd, __unused short event, void *data)
464{
465	struct client	*c = data;
466
467	status_message_clear(c);
468}
469
470/* Draw client message on status line of present else on last line. */
471int
472status_message_redraw(struct client *c)
473{
474	struct status_line	*sl = &c->status;
475	struct screen_write_ctx	 ctx;
476	struct session		*s = c->session;
477	struct screen		 old_screen;
478	size_t			 len;
479	u_int			 lines, offset;
480	struct grid_cell	 gc;
481
482	if (c->tty.sx == 0 || c->tty.sy == 0)
483		return (0);
484	memcpy(&old_screen, sl->active, sizeof old_screen);
485
486	lines = status_line_size(c);
487	if (lines <= 1)
488		lines = 1;
489	screen_init(sl->active, c->tty.sx, lines, 0);
490
491	len = screen_write_strlen("%s", c->message_string);
492	if (len > c->tty.sx)
493		len = c->tty.sx;
494
495	style_apply(&gc, s->options, "message-style");
496
497	screen_write_start(&ctx, NULL, sl->active);
498	screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
499	screen_write_cursormove(&ctx, 0, lines - 1, 0);
500	for (offset = 0; offset < c->tty.sx; offset++)
501		screen_write_putc(&ctx, &gc, ' ');
502	screen_write_cursormove(&ctx, 0, lines - 1, 0);
503	screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
504	screen_write_stop(&ctx);
505
506	if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
507		screen_free(&old_screen);
508		return (0);
509	}
510	screen_free(&old_screen);
511	return (1);
512}
513
514/* Enable status line prompt. */
515void
516status_prompt_set(struct client *c, const char *msg, const char *input,
517    prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags)
518{
519	struct format_tree	*ft;
520	char			*tmp, *cp;
521
522	ft = format_create(c, NULL, FORMAT_NONE, 0);
523	format_defaults(ft, c, NULL, NULL, NULL);
524
525	if (input == NULL)
526		input = "";
527	if (flags & PROMPT_NOFORMAT)
528		tmp = xstrdup(input);
529	else
530		tmp = format_expand_time(ft, input);
531
532	status_message_clear(c);
533	status_prompt_clear(c);
534	status_push_screen(c);
535
536	c->prompt_string = format_expand_time(ft, msg);
537
538	c->prompt_buffer = utf8_fromcstr(tmp);
539	c->prompt_index = utf8_strlen(c->prompt_buffer);
540
541	c->prompt_inputcb = inputcb;
542	c->prompt_freecb = freecb;
543	c->prompt_data = data;
544
545	c->prompt_hindex = 0;
546
547	c->prompt_flags = flags;
548	c->prompt_mode = PROMPT_ENTRY;
549
550	if (~flags & PROMPT_INCREMENTAL)
551		c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
552	c->flags |= CLIENT_REDRAWSTATUS;
553
554	if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') {
555		xasprintf(&cp, "=%s", tmp);
556		c->prompt_inputcb(c, c->prompt_data, cp, 0);
557		free(cp);
558	}
559
560	free(tmp);
561	format_free(ft);
562}
563
564/* Remove status line prompt. */
565void
566status_prompt_clear(struct client *c)
567{
568	if (c->prompt_string == NULL)
569		return;
570
571	if (c->prompt_freecb != NULL && c->prompt_data != NULL)
572		c->prompt_freecb(c->prompt_data);
573
574	free(c->prompt_string);
575	c->prompt_string = NULL;
576
577	free(c->prompt_buffer);
578	c->prompt_buffer = NULL;
579
580	free(c->prompt_saved);
581	c->prompt_saved = NULL;
582
583	c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
584	c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */
585
586	status_pop_screen(c);
587}
588
589/* Update status line prompt with a new prompt string. */
590void
591status_prompt_update(struct client *c, const char *msg, const char *input)
592{
593	struct format_tree	*ft;
594	char			*tmp;
595
596	ft = format_create(c, NULL, FORMAT_NONE, 0);
597	format_defaults(ft, c, NULL, NULL, NULL);
598
599	tmp = format_expand_time(ft, input);
600
601	free(c->prompt_string);
602	c->prompt_string = format_expand_time(ft, msg);
603
604	free(c->prompt_buffer);
605	c->prompt_buffer = utf8_fromcstr(tmp);
606	c->prompt_index = utf8_strlen(c->prompt_buffer);
607
608	c->prompt_hindex = 0;
609
610	c->flags |= CLIENT_REDRAWSTATUS;
611
612	free(tmp);
613	format_free(ft);
614}
615
616/* Draw client prompt on status line of present else on last line. */
617int
618status_prompt_redraw(struct client *c)
619{
620	struct status_line	*sl = &c->status;
621	struct screen_write_ctx	 ctx;
622	struct session		*s = c->session;
623	struct screen		 old_screen;
624	u_int			 i, lines, offset, left, start, width;
625	u_int			 pcursor, pwidth;
626	struct grid_cell	 gc, cursorgc;
627
628	if (c->tty.sx == 0 || c->tty.sy == 0)
629		return (0);
630	memcpy(&old_screen, sl->active, sizeof old_screen);
631
632	lines = status_line_size(c);
633	if (lines <= 1)
634		lines = 1;
635	screen_init(sl->active, c->tty.sx, lines, 0);
636
637	if (c->prompt_mode == PROMPT_COMMAND)
638		style_apply(&gc, s->options, "message-command-style");
639	else
640		style_apply(&gc, s->options, "message-style");
641
642	memcpy(&cursorgc, &gc, sizeof cursorgc);
643	cursorgc.attr ^= GRID_ATTR_REVERSE;
644
645	start = screen_write_strlen("%s", c->prompt_string);
646	if (start > c->tty.sx)
647		start = c->tty.sx;
648
649	screen_write_start(&ctx, NULL, sl->active);
650	screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
651	screen_write_cursormove(&ctx, 0, lines - 1, 0);
652	for (offset = 0; offset < c->tty.sx; offset++)
653		screen_write_putc(&ctx, &gc, ' ');
654	screen_write_cursormove(&ctx, 0, lines - 1, 0);
655	screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
656	screen_write_cursormove(&ctx, start, lines - 1, 0);
657
658	left = c->tty.sx - start;
659	if (left == 0)
660		goto finished;
661
662	pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index);
663	pwidth = utf8_strwidth(c->prompt_buffer, -1);
664	if (pcursor >= left) {
665		/*
666		 * The cursor would be outside the screen so start drawing
667		 * with it on the right.
668		 */
669		offset = (pcursor - left) + 1;
670		pwidth = left;
671	} else
672		offset = 0;
673	if (pwidth > left)
674		pwidth = left;
675
676	width = 0;
677	for (i = 0; c->prompt_buffer[i].size != 0; i++) {
678		if (width < offset) {
679			width += c->prompt_buffer[i].width;
680			continue;
681		}
682		if (width >= offset + pwidth)
683			break;
684		width += c->prompt_buffer[i].width;
685		if (width > offset + pwidth)
686			break;
687
688		if (i != c->prompt_index) {
689			utf8_copy(&gc.data, &c->prompt_buffer[i]);
690			screen_write_cell(&ctx, &gc);
691		} else {
692			utf8_copy(&cursorgc.data, &c->prompt_buffer[i]);
693			screen_write_cell(&ctx, &cursorgc);
694		}
695	}
696	if (sl->active->cx < screen_size_x(sl->active) && c->prompt_index >= i)
697		screen_write_putc(&ctx, &cursorgc, ' ');
698
699finished:
700	screen_write_stop(&ctx);
701
702	if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
703		screen_free(&old_screen);
704		return (0);
705	}
706	screen_free(&old_screen);
707	return (1);
708}
709
710/* Is this a separator? */
711static int
712status_prompt_in_list(const char *ws, const struct utf8_data *ud)
713{
714	if (ud->size != 1 || ud->width != 1)
715		return (0);
716	return (strchr(ws, *ud->data) != NULL);
717}
718
719/* Is this a space? */
720static int
721status_prompt_space(const struct utf8_data *ud)
722{
723	if (ud->size != 1 || ud->width != 1)
724		return (0);
725	return (*ud->data == ' ');
726}
727
728/*
729 * Translate key from emacs to vi. Return 0 to drop key, 1 to process the key
730 * as an emacs key; return 2 to append to the buffer.
731 */
732static int
733status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
734{
735	if (c->prompt_mode == PROMPT_ENTRY) {
736		switch (key) {
737		case '\003': /* C-c */
738		case '\010': /* C-h */
739		case '\011': /* Tab */
740		case '\025': /* C-u */
741		case '\027': /* C-w */
742		case '\n':
743		case '\r':
744		case KEYC_BSPACE:
745		case KEYC_DC:
746		case KEYC_DOWN:
747		case KEYC_END:
748		case KEYC_HOME:
749		case KEYC_LEFT:
750		case KEYC_RIGHT:
751		case KEYC_UP:
752			*new_key = key;
753			return (1);
754		case '\033': /* Escape */
755			c->prompt_mode = PROMPT_COMMAND;
756			c->flags |= CLIENT_REDRAWSTATUS;
757			return (0);
758		}
759		*new_key = key;
760		return (2);
761	}
762
763	switch (key) {
764	case 'A':
765	case 'I':
766	case 'C':
767	case 's':
768	case 'a':
769		c->prompt_mode = PROMPT_ENTRY;
770		c->flags |= CLIENT_REDRAWSTATUS;
771		break; /* switch mode and... */
772	case 'S':
773		c->prompt_mode = PROMPT_ENTRY;
774		c->flags |= CLIENT_REDRAWSTATUS;
775		*new_key = '\025'; /* C-u */
776		return (1);
777	case 'i':
778	case '\033': /* Escape */
779		c->prompt_mode = PROMPT_ENTRY;
780		c->flags |= CLIENT_REDRAWSTATUS;
781		return (0);
782	}
783
784	switch (key) {
785	case 'A':
786	case '$':
787		*new_key = KEYC_END;
788		return (1);
789	case 'I':
790	case '0':
791	case '^':
792		*new_key = KEYC_HOME;
793		return (1);
794	case 'C':
795	case 'D':
796		*new_key = '\013'; /* C-k */
797		return (1);
798	case KEYC_BSPACE:
799	case 'X':
800		*new_key = KEYC_BSPACE;
801		return (1);
802	case 'b':
803	case 'B':
804		*new_key = 'b'|KEYC_ESCAPE;
805		return (1);
806	case 'd':
807		*new_key = '\025';
808		return (1);
809	case 'e':
810	case 'E':
811	case 'w':
812	case 'W':
813		*new_key = 'f'|KEYC_ESCAPE;
814		return (1);
815	case 'p':
816		*new_key = '\031'; /* C-y */
817		return (1);
818	case 's':
819	case KEYC_DC:
820	case 'x':
821		*new_key = KEYC_DC;
822		return (1);
823	case KEYC_DOWN:
824	case 'j':
825		*new_key = KEYC_DOWN;
826		return (1);
827	case KEYC_LEFT:
828	case 'h':
829		*new_key = KEYC_LEFT;
830		return (1);
831	case 'a':
832	case KEYC_RIGHT:
833	case 'l':
834		*new_key = KEYC_RIGHT;
835		return (1);
836	case KEYC_UP:
837	case 'k':
838		*new_key = KEYC_UP;
839		return (1);
840	case '\010' /* C-h */:
841	case '\003' /* C-c */:
842	case '\n':
843	case '\r':
844		return (1);
845	}
846	return (0);
847}
848
849/* Paste into prompt. */
850static int
851status_prompt_paste(struct client *c)
852{
853	struct paste_buffer	*pb;
854	const char		*bufdata;
855	size_t			 size, n, bufsize;
856	u_int			 i;
857	struct utf8_data	*ud, *udp;
858	enum utf8_state		 more;
859
860	size = utf8_strlen(c->prompt_buffer);
861	if (c->prompt_saved != NULL) {
862		ud = c->prompt_saved;
863		n = utf8_strlen(c->prompt_saved);
864	} else {
865		if ((pb = paste_get_top(NULL)) == NULL)
866			return (0);
867		bufdata = paste_buffer_data(pb, &bufsize);
868		ud = xreallocarray(NULL, bufsize, sizeof *ud);
869		udp = ud;
870		for (i = 0; i != bufsize; /* nothing */) {
871			more = utf8_open(udp, bufdata[i]);
872			if (more == UTF8_MORE) {
873				while (++i != bufsize && more == UTF8_MORE)
874					more = utf8_append(udp, bufdata[i]);
875				if (more == UTF8_DONE) {
876					udp++;
877					continue;
878				}
879				i -= udp->have;
880			}
881			if (bufdata[i] <= 31 || bufdata[i] >= 127)
882				break;
883			utf8_set(udp, bufdata[i]);
884			udp++;
885			i++;
886		}
887		udp->size = 0;
888		n = udp - ud;
889	}
890	if (n == 0)
891		return (0);
892
893	c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
894	    sizeof *c->prompt_buffer);
895	if (c->prompt_index == size) {
896		memcpy(c->prompt_buffer + c->prompt_index, ud,
897		    n * sizeof *c->prompt_buffer);
898		c->prompt_index += n;
899		c->prompt_buffer[c->prompt_index].size = 0;
900	} else {
901		memmove(c->prompt_buffer + c->prompt_index + n,
902		    c->prompt_buffer + c->prompt_index,
903		    (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer);
904		memcpy(c->prompt_buffer + c->prompt_index, ud,
905		    n * sizeof *c->prompt_buffer);
906		c->prompt_index += n;
907	}
908
909	if (ud != c->prompt_saved)
910		free(ud);
911	return (1);
912}
913
914/* Handle keys in prompt. */
915int
916status_prompt_key(struct client *c, key_code key)
917{
918	struct options		*oo = c->session->options;
919	char			*s, *cp, word[64], prefix = '=';
920	const char		*histstr, *ws = NULL;
921	size_t			 size, n, off, idx, used;
922	struct utf8_data	 tmp, *first, *last, *ud;
923	int			 keys;
924
925	size = utf8_strlen(c->prompt_buffer);
926
927	if (c->prompt_flags & PROMPT_NUMERIC) {
928		if (key >= '0' && key <= '9')
929			goto append_key;
930		s = utf8_tocstr(c->prompt_buffer);
931		c->prompt_inputcb(c, c->prompt_data, s, 1);
932		status_prompt_clear(c);
933		free(s);
934		return (1);
935	}
936	key &= ~KEYC_XTERM;
937
938	keys = options_get_number(c->session->options, "status-keys");
939	if (keys == MODEKEY_VI) {
940		switch (status_prompt_translate_key(c, key, &key)) {
941		case 1:
942			goto process_key;
943		case 2:
944			goto append_key;
945		default:
946			return (0);
947		}
948	}
949
950process_key:
951	switch (key) {
952	case KEYC_LEFT:
953	case '\002': /* C-b */
954		if (c->prompt_index > 0) {
955			c->prompt_index--;
956			break;
957		}
958		break;
959	case KEYC_RIGHT:
960	case '\006': /* C-f */
961		if (c->prompt_index < size) {
962			c->prompt_index++;
963			break;
964		}
965		break;
966	case KEYC_HOME:
967	case '\001': /* C-a */
968		if (c->prompt_index != 0) {
969			c->prompt_index = 0;
970			break;
971		}
972		break;
973	case KEYC_END:
974	case '\005': /* C-e */
975		if (c->prompt_index != size) {
976			c->prompt_index = size;
977			break;
978		}
979		break;
980	case '\011': /* Tab */
981		if (c->prompt_buffer[0].size == 0)
982			break;
983
984		idx = c->prompt_index;
985		if (idx != 0)
986			idx--;
987
988		/* Find the word we are in. */
989		first = &c->prompt_buffer[idx];
990		while (first > c->prompt_buffer && !status_prompt_space(first))
991			first--;
992		while (first->size != 0 && status_prompt_space(first))
993			first++;
994		last = &c->prompt_buffer[idx];
995		while (last->size != 0 && !status_prompt_space(last))
996			last++;
997		while (last > c->prompt_buffer && status_prompt_space(last))
998			last--;
999		if (last->size != 0)
1000			last++;
1001		if (last <= first)
1002			break;
1003
1004		used = 0;
1005		for (ud = first; ud < last; ud++) {
1006			if (used + ud->size >= sizeof word)
1007				break;
1008			memcpy(word + used, ud->data, ud->size);
1009			used += ud->size;
1010		}
1011		if (ud != last)
1012			break;
1013		word[used] = '\0';
1014
1015		/* And try to complete it. */
1016		if ((s = status_prompt_complete(c->session, word)) == NULL)
1017			break;
1018
1019		/* Trim out word. */
1020		n = size - (last - c->prompt_buffer) + 1; /* with \0 */
1021		memmove(first, last, n * sizeof *c->prompt_buffer);
1022		size -= last - first;
1023
1024		/* Insert the new word. */
1025		size += strlen(s);
1026		off = first - c->prompt_buffer;
1027		c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
1028		    sizeof *c->prompt_buffer);
1029		first = c->prompt_buffer + off;
1030		memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
1031		for (idx = 0; idx < strlen(s); idx++)
1032			utf8_set(&first[idx], s[idx]);
1033
1034		c->prompt_index = (first - c->prompt_buffer) + strlen(s);
1035		free(s);
1036
1037		goto changed;
1038	case KEYC_BSPACE:
1039	case '\010': /* C-h */
1040		if (c->prompt_index != 0) {
1041			if (c->prompt_index == size)
1042				c->prompt_buffer[--c->prompt_index].size = 0;
1043			else {
1044				memmove(c->prompt_buffer + c->prompt_index - 1,
1045				    c->prompt_buffer + c->prompt_index,
1046				    (size + 1 - c->prompt_index) *
1047				    sizeof *c->prompt_buffer);
1048				c->prompt_index--;
1049			}
1050			goto changed;
1051		}
1052		break;
1053	case KEYC_DC:
1054	case '\004': /* C-d */
1055		if (c->prompt_index != size) {
1056			memmove(c->prompt_buffer + c->prompt_index,
1057			    c->prompt_buffer + c->prompt_index + 1,
1058			    (size + 1 - c->prompt_index) *
1059			    sizeof *c->prompt_buffer);
1060			goto changed;
1061		}
1062		break;
1063	case '\025': /* C-u */
1064		c->prompt_buffer[0].size = 0;
1065		c->prompt_index = 0;
1066		goto changed;
1067	case '\013': /* C-k */
1068		if (c->prompt_index < size) {
1069			c->prompt_buffer[c->prompt_index].size = 0;
1070			goto changed;
1071		}
1072		break;
1073	case '\027': /* C-w */
1074		ws = options_get_string(oo, "word-separators");
1075		idx = c->prompt_index;
1076
1077		/* Find a non-separator. */
1078		while (idx != 0) {
1079			idx--;
1080			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1081				break;
1082		}
1083
1084		/* Find the separator at the beginning of the word. */
1085		while (idx != 0) {
1086			idx--;
1087			if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
1088				/* Go back to the word. */
1089				idx++;
1090				break;
1091			}
1092		}
1093
1094		free(c->prompt_saved);
1095		c->prompt_saved = xcalloc(sizeof *c->prompt_buffer,
1096		    (c->prompt_index - idx) + 1);
1097		memcpy(c->prompt_saved, c->prompt_buffer + idx,
1098		    (c->prompt_index - idx) * sizeof *c->prompt_buffer);
1099
1100		memmove(c->prompt_buffer + idx,
1101		    c->prompt_buffer + c->prompt_index,
1102		    (size + 1 - c->prompt_index) *
1103		    sizeof *c->prompt_buffer);
1104		memset(c->prompt_buffer + size - (c->prompt_index - idx),
1105		    '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer);
1106		c->prompt_index = idx;
1107
1108		goto changed;
1109	case 'f'|KEYC_ESCAPE:
1110	case KEYC_RIGHT|KEYC_CTRL:
1111		ws = options_get_string(oo, "word-separators");
1112
1113		/* Find a word. */
1114		while (c->prompt_index != size) {
1115			idx = ++c->prompt_index;
1116			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1117				break;
1118		}
1119
1120		/* Find the separator at the end of the word. */
1121		while (c->prompt_index != size) {
1122			idx = ++c->prompt_index;
1123			if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1124				break;
1125		}
1126
1127		/* Back up to the end-of-word like vi. */
1128		if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
1129		    c->prompt_index != 0)
1130			c->prompt_index--;
1131
1132		goto changed;
1133	case 'b'|KEYC_ESCAPE:
1134	case KEYC_LEFT|KEYC_CTRL:
1135		ws = options_get_string(oo, "word-separators");
1136
1137		/* Find a non-separator. */
1138		while (c->prompt_index != 0) {
1139			idx = --c->prompt_index;
1140			if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
1141				break;
1142		}
1143
1144		/* Find the separator at the beginning of the word. */
1145		while (c->prompt_index != 0) {
1146			idx = --c->prompt_index;
1147			if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
1148				/* Go back to the word. */
1149				c->prompt_index++;
1150				break;
1151			}
1152		}
1153		goto changed;
1154	case KEYC_UP:
1155	case '\020': /* C-p */
1156		histstr = status_prompt_up_history(&c->prompt_hindex);
1157		if (histstr == NULL)
1158			break;
1159		free(c->prompt_buffer);
1160		c->prompt_buffer = utf8_fromcstr(histstr);
1161		c->prompt_index = utf8_strlen(c->prompt_buffer);
1162		goto changed;
1163	case KEYC_DOWN:
1164	case '\016': /* C-n */
1165		histstr = status_prompt_down_history(&c->prompt_hindex);
1166		if (histstr == NULL)
1167			break;
1168		free(c->prompt_buffer);
1169		c->prompt_buffer = utf8_fromcstr(histstr);
1170		c->prompt_index = utf8_strlen(c->prompt_buffer);
1171		goto changed;
1172	case '\031': /* C-y */
1173		if (status_prompt_paste(c))
1174			goto changed;
1175		break;
1176	case '\024': /* C-t */
1177		idx = c->prompt_index;
1178		if (idx < size)
1179			idx++;
1180		if (idx >= 2) {
1181			utf8_copy(&tmp, &c->prompt_buffer[idx - 2]);
1182			utf8_copy(&c->prompt_buffer[idx - 2],
1183			    &c->prompt_buffer[idx - 1]);
1184			utf8_copy(&c->prompt_buffer[idx - 1], &tmp);
1185			c->prompt_index = idx;
1186			goto changed;
1187		}
1188		break;
1189	case '\r':
1190	case '\n':
1191		s = utf8_tocstr(c->prompt_buffer);
1192		if (*s != '\0')
1193			status_prompt_add_history(s);
1194		if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
1195			status_prompt_clear(c);
1196		free(s);
1197		break;
1198	case '\033': /* Escape */
1199	case '\003': /* C-c */
1200	case '\007': /* C-g */
1201		if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
1202			status_prompt_clear(c);
1203		break;
1204	case '\022': /* C-r */
1205		if (c->prompt_flags & PROMPT_INCREMENTAL) {
1206			prefix = '-';
1207			goto changed;
1208		}
1209		break;
1210	case '\023': /* C-s */
1211		if (c->prompt_flags & PROMPT_INCREMENTAL) {
1212			prefix = '+';
1213			goto changed;
1214		}
1215		break;
1216	default:
1217		goto append_key;
1218	}
1219
1220	c->flags |= CLIENT_REDRAWSTATUS;
1221	return (0);
1222
1223append_key:
1224	if (key <= 0x1f || key >= KEYC_BASE)
1225		return (0);
1226	if (utf8_split(key, &tmp) != UTF8_DONE)
1227		return (0);
1228
1229	c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2,
1230	    sizeof *c->prompt_buffer);
1231
1232	if (c->prompt_index == size) {
1233		utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
1234		c->prompt_index++;
1235		c->prompt_buffer[c->prompt_index].size = 0;
1236	} else {
1237		memmove(c->prompt_buffer + c->prompt_index + 1,
1238		    c->prompt_buffer + c->prompt_index,
1239		    (size + 1 - c->prompt_index) *
1240		    sizeof *c->prompt_buffer);
1241		utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
1242		c->prompt_index++;
1243	}
1244
1245	if (c->prompt_flags & PROMPT_SINGLE) {
1246		s = utf8_tocstr(c->prompt_buffer);
1247		if (strlen(s) != 1)
1248			status_prompt_clear(c);
1249		else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
1250			status_prompt_clear(c);
1251		free(s);
1252	}
1253
1254changed:
1255	c->flags |= CLIENT_REDRAWSTATUS;
1256	if (c->prompt_flags & PROMPT_INCREMENTAL) {
1257		s = utf8_tocstr(c->prompt_buffer);
1258		xasprintf(&cp, "%c%s", prefix, s);
1259		c->prompt_inputcb(c, c->prompt_data, cp, 0);
1260		free(cp);
1261		free(s);
1262	}
1263	return (0);
1264}
1265
1266/* Get previous line from the history. */
1267static const char *
1268status_prompt_up_history(u_int *idx)
1269{
1270	/*
1271	 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1272	 * empty.
1273	 */
1274
1275	if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
1276		return (NULL);
1277	(*idx)++;
1278	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1279}
1280
1281/* Get next line from the history. */
1282static const char *
1283status_prompt_down_history(u_int *idx)
1284{
1285	if (status_prompt_hsize == 0 || *idx == 0)
1286		return ("");
1287	(*idx)--;
1288	if (*idx == 0)
1289		return ("");
1290	return (status_prompt_hlist[status_prompt_hsize - *idx]);
1291}
1292
1293/* Add line to the history. */
1294static void
1295status_prompt_add_history(const char *line)
1296{
1297	size_t	size;
1298
1299	if (status_prompt_hsize > 0 &&
1300	    strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
1301		return;
1302
1303	if (status_prompt_hsize == PROMPT_HISTORY) {
1304		free(status_prompt_hlist[0]);
1305
1306		size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
1307		memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
1308
1309		status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
1310		return;
1311	}
1312
1313	status_prompt_hlist = xreallocarray(status_prompt_hlist,
1314	    status_prompt_hsize + 1, sizeof *status_prompt_hlist);
1315	status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
1316}
1317
1318/* Build completion list. */
1319char **
1320status_prompt_complete_list(u_int *size, const char *s)
1321{
1322	char					**list = NULL;
1323	const char				**layout, *value, *cp;
1324	const struct cmd_entry			**cmdent;
1325	const struct options_table_entry	 *oe;
1326	u_int					  idx;
1327	size_t					  slen = strlen(s), valuelen;
1328	struct options_entry			 *o;
1329	struct options_array_item		 *a;
1330	const char				 *layouts[] = {
1331		"even-horizontal", "even-vertical", "main-horizontal",
1332		"main-vertical", "tiled", NULL
1333	};
1334
1335	*size = 0;
1336	for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1337		if (strncmp((*cmdent)->name, s, slen) == 0) {
1338			list = xreallocarray(list, (*size) + 1, sizeof *list);
1339			list[(*size)++] = xstrdup((*cmdent)->name);
1340		}
1341	}
1342	for (oe = options_table; oe->name != NULL; oe++) {
1343		if (strncmp(oe->name, s, slen) == 0) {
1344			list = xreallocarray(list, (*size) + 1, sizeof *list);
1345			list[(*size)++] = xstrdup(oe->name);
1346		}
1347	}
1348	for (layout = layouts; *layout != NULL; layout++) {
1349		if (strncmp(*layout, s, slen) == 0) {
1350			list = xreallocarray(list, (*size) + 1, sizeof *list);
1351			list[(*size)++] = xstrdup(*layout);
1352		}
1353	}
1354	o = options_get_only(global_options, "command-alias");
1355	if (o != NULL) {
1356		a = options_array_first(o);
1357		while (a != NULL) {
1358			value = options_array_item_value(a)->string;
1359			if ((cp = strchr(value, '=')) == NULL)
1360				goto next;
1361			valuelen = cp - value;
1362			if (slen > valuelen || strncmp(value, s, slen) != 0)
1363				goto next;
1364
1365			list = xreallocarray(list, (*size) + 1, sizeof *list);
1366			list[(*size)++] = xstrndup(value, valuelen);
1367
1368		next:
1369			a = options_array_next(a);
1370		}
1371	}
1372	for (idx = 0; idx < (*size); idx++)
1373		log_debug("complete %u: %s", idx, list[idx]);
1374	return (list);
1375}
1376
1377/* Find longest prefix. */
1378static char *
1379status_prompt_complete_prefix(char **list, u_int size)
1380{
1381	char	 *out;
1382	u_int	  i;
1383	size_t	  j;
1384
1385	out = xstrdup(list[0]);
1386	for (i = 1; i < size; i++) {
1387		j = strlen(list[i]);
1388		if (j > strlen(out))
1389			j = strlen(out);
1390		for (; j > 0; j--) {
1391			if (out[j - 1] != list[i][j - 1])
1392				out[j - 1] = '\0';
1393		}
1394	}
1395	return (out);
1396}
1397
1398/* Complete word. */
1399static char *
1400status_prompt_complete(struct session *session, const char *s)
1401{
1402	char		**list = NULL;
1403	const char	 *colon;
1404	u_int		  size = 0, i;
1405	struct session	 *s_loop;
1406	struct winlink	 *wl;
1407	struct window	 *w;
1408	char		 *copy, *out, *tmp;
1409
1410	if (*s == '\0')
1411		return (NULL);
1412	out = NULL;
1413
1414	if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
1415		list = status_prompt_complete_list(&size, s);
1416		if (size == 0)
1417			out = NULL;
1418		else if (size == 1)
1419			xasprintf(&out, "%s ", list[0]);
1420		else
1421			out = status_prompt_complete_prefix(list, size);
1422		for (i = 0; i < size; i++)
1423			free(list[i]);
1424		free(list);
1425		return (out);
1426	}
1427	copy = xstrdup(s);
1428
1429	colon = ":";
1430	if (copy[strlen(copy) - 1] == ':')
1431		copy[strlen(copy) - 1] = '\0';
1432	else
1433		colon = "";
1434	s = copy + 2;
1435
1436	RB_FOREACH(s_loop, sessions, &sessions) {
1437		if (strncmp(s_loop->name, s, strlen(s)) == 0) {
1438			list = xreallocarray(list, size + 2, sizeof *list);
1439			list[size++] = s_loop->name;
1440		}
1441	}
1442	if (size == 1) {
1443		out = xstrdup(list[0]);
1444		if (session_find(list[0]) != NULL)
1445			colon = ":";
1446	} else if (size != 0)
1447		out = status_prompt_complete_prefix(list, size);
1448	if (out != NULL) {
1449		xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1450		free(out);
1451		out = tmp;
1452		goto found;
1453	}
1454
1455	colon = "";
1456	if (*s == ':') {
1457		RB_FOREACH(wl, winlinks, &session->windows) {
1458			xasprintf(&tmp, ":%s", wl->window->name);
1459			if (strncmp(tmp, s, strlen(s)) == 0){
1460				list = xreallocarray(list, size + 1,
1461				    sizeof *list);
1462				list[size++] = tmp;
1463				continue;
1464			}
1465			free(tmp);
1466
1467			xasprintf(&tmp, ":%d", wl->idx);
1468			if (strncmp(tmp, s, strlen(s)) == 0) {
1469				list = xreallocarray(list, size + 1,
1470				    sizeof *list);
1471				list[size++] = tmp;
1472				continue;
1473			}
1474			free(tmp);
1475		}
1476	} else {
1477		RB_FOREACH(s_loop, sessions, &sessions) {
1478			RB_FOREACH(wl, winlinks, &s_loop->windows) {
1479				w = wl->window;
1480
1481				xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
1482				if (strncmp(tmp, s, strlen(s)) == 0) {
1483					list = xreallocarray(list, size + 1,
1484					    sizeof *list);
1485					list[size++] = tmp;
1486					continue;
1487				}
1488				free(tmp);
1489
1490				xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
1491				if (strncmp(tmp, s, strlen(s)) == 0) {
1492					list = xreallocarray(list, size + 1,
1493					    sizeof *list);
1494					list[size++] = tmp;
1495					continue;
1496				}
1497				free(tmp);
1498			}
1499		}
1500	}
1501	if (size == 1) {
1502		out = xstrdup(list[0]);
1503		colon = " ";
1504	} else if (size != 0)
1505		out = status_prompt_complete_prefix(list, size);
1506	if (out != NULL) {
1507		xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1508		out = tmp;
1509	}
1510
1511	for (i = 0; i < size; i++)
1512		free((void *)list[i]);
1513
1514found:
1515	free(copy);
1516	free(list);
1517	return (out);
1518}
1519