1 /* $Id: status.c,v 1.144 2010-03-10 13:41:13 nicm Exp $ */
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
32 char *status_redraw_get_left(
33 struct client
*, time_t, int, struct grid_cell
*, size_t *);
34 char *status_redraw_get_right(
35 struct client
*, time_t, int, struct grid_cell
*, size_t *);
36 char *status_job(struct client
*, char **);
37 void status_job_callback(struct job
*);
38 size_t status_width(struct client
*, struct winlink
*, time_t);
40 struct client
*, struct winlink
*, time_t, struct grid_cell
*);
41 void status_replace1(struct client
*,
42 struct winlink
*, char **, char **, char *, size_t, int);
43 void status_message_callback(int, short, void *);
45 void status_prompt_add_history(struct client
*);
46 char *status_prompt_complete(const char *);
48 /* Retrieve options for left string. */
50 status_redraw_get_left(struct client
*c
,
51 time_t t
, int utf8flag
, struct grid_cell
*gc
, size_t *size
)
53 struct session
*s
= c
->session
;
58 fg
= options_get_number(&s
->options
, "status-left-fg");
60 colour_set_fg(gc
, fg
);
61 bg
= options_get_number(&s
->options
, "status-left-bg");
63 colour_set_bg(gc
, bg
);
64 attr
= options_get_number(&s
->options
, "status-left-attr");
68 left
= status_replace(
69 c
, NULL
, options_get_string(&s
->options
, "status-left"), t
, 1);
71 *size
= options_get_number(&s
->options
, "status-left-length");
72 leftlen
= screen_write_cstrlen(utf8flag
, "%s", left
);
78 /* Retrieve options for right string. */
80 status_redraw_get_right(struct client
*c
,
81 time_t t
, int utf8flag
, struct grid_cell
*gc
, size_t *size
)
83 struct session
*s
= c
->session
;
88 fg
= options_get_number(&s
->options
, "status-right-fg");
90 colour_set_fg(gc
, fg
);
91 bg
= options_get_number(&s
->options
, "status-right-bg");
93 colour_set_bg(gc
, bg
);
94 attr
= options_get_number(&s
->options
, "status-right-attr");
98 right
= status_replace(
99 c
, NULL
, options_get_string(&s
->options
, "status-right"), t
, 1);
101 *size
= options_get_number(&s
->options
, "status-right-length");
102 rightlen
= screen_write_cstrlen(utf8flag
, "%s", right
);
103 if (rightlen
< *size
)
108 /* Draw status for client on the last lines of given context. */
110 status_redraw(struct client
*c
)
112 struct screen_write_ctx ctx
;
113 struct session
*s
= c
->session
;
115 struct screen old_status
, window_list
;
116 struct grid_cell stdgc
, lgc
, rgc
, gc
;
119 u_int offset
, needed
;
120 u_int wlstart
, wlwidth
, wlavailable
, wloffset
, wlsize
;
122 int larrow
, rarrow
, utf8flag
;
124 /* No status line? */
125 if (c
->tty
.sy
== 0 || !options_get_number(&s
->options
, "status"))
130 /* Update status timer. */
131 if (gettimeofday(&c
->status_timer
, NULL
) != 0)
132 fatal("gettimeofday failed");
133 t
= c
->status_timer
.tv_sec
;
135 /* Set up default colour. */
136 memcpy(&stdgc
, &grid_default_cell
, sizeof gc
);
137 colour_set_fg(&stdgc
, options_get_number(&s
->options
, "status-fg"));
138 colour_set_bg(&stdgc
, options_get_number(&s
->options
, "status-bg"));
139 stdgc
.attr
|= options_get_number(&s
->options
, "status-attr");
141 /* Create the target screen. */
142 memcpy(&old_status
, &c
->status
, sizeof old_status
);
143 screen_init(&c
->status
, c
->tty
.sx
, 1, 0);
144 screen_write_start(&ctx
, NULL
, &c
->status
);
145 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
146 screen_write_putc(&ctx
, &stdgc
, ' ');
147 screen_write_stop(&ctx
);
149 /* If the height is one line, blank status line. */
153 /* Get UTF-8 flag. */
154 utf8flag
= options_get_number(&s
->options
, "status-utf8");
156 /* Work out left and right strings. */
157 memcpy(&lgc
, &stdgc
, sizeof lgc
);
158 left
= status_redraw_get_left(c
, t
, utf8flag
, &lgc
, &llen
);
159 memcpy(&rgc
, &stdgc
, sizeof rgc
);
160 right
= status_redraw_get_right(c
, t
, utf8flag
, &rgc
, &rlen
);
163 * Figure out how much space we have for the window list. If there
164 * isn't enough space, just show a blank status line.
171 if (c
->tty
.sx
== 0 || c
->tty
.sx
<= needed
)
173 wlavailable
= c
->tty
.sx
- needed
;
175 /* Calculate the total size needed for the window list. */
176 wlstart
= wloffset
= wlwidth
= 0;
177 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
178 if (wl
->status_text
!= NULL
)
179 xfree(wl
->status_text
);
180 memcpy(&wl
->status_cell
, &stdgc
, sizeof wl
->status_cell
);
181 wl
->status_text
= status_print(c
, wl
, t
, &wl
->status_cell
);
183 screen_write_cstrlen(utf8flag
, "%s", wl
->status_text
);
187 wlwidth
+= wl
->status_width
+ 1;
190 /* Create a new screen for the window list. */
191 screen_init(&window_list
, wlwidth
, 1, 0);
193 /* And draw the window list into it. */
194 screen_write_start(&ctx
, NULL
, &window_list
);
195 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
196 screen_write_cnputs(&ctx
,
197 -1, &wl
->status_cell
, utf8flag
, "%s", wl
->status_text
);
198 screen_write_putc(&ctx
, &stdgc
, ' ');
200 screen_write_stop(&ctx
);
202 /* If there is enough space for the total width, skip to draw now. */
203 if (wlwidth
<= wlavailable
)
206 /* Find size of current window text. */
207 wlsize
= s
->curw
->status_width
;
210 * If the current window is already on screen, good to draw from the
211 * start and just leave off the end.
213 if (wloffset
+ wlsize
< wlavailable
) {
214 if (wlavailable
> 0) {
218 wlwidth
= wlavailable
;
221 * Work out how many characters we need to omit from the
222 * start. There are wlavailable characters to fill, and
223 * wloffset + wlsize must be the last. So, the start character
224 * is wloffset + wlsize - wlavailable.
226 if (wlavailable
> 0) {
231 wlstart
= wloffset
+ wlsize
- wlavailable
;
232 if (wlavailable
> 0 && wlwidth
> wlstart
+ wlavailable
+ 1) {
237 wlwidth
= wlavailable
;
240 /* Bail if anything is now too small too. */
241 if (wlwidth
== 0 || wlavailable
== 0) {
242 screen_free(&window_list
);
247 * Now the start position is known, work out the state of the left and
251 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
252 if (larrow
== 1 && offset
< wlstart
) {
253 if (session_alert_has(s
, wl
, WINDOW_ACTIVITY
))
255 else if (session_alert_has(s
, wl
, WINDOW_BELL
))
257 else if (session_alert_has(s
, wl
, WINDOW_CONTENT
))
261 offset
+= wl
->status_width
;
263 if (rarrow
== 1 && offset
> wlstart
+ wlwidth
) {
264 if (session_alert_has(s
, wl
, WINDOW_ACTIVITY
))
266 else if (session_alert_has(s
, wl
, WINDOW_BELL
))
268 else if (session_alert_has(s
, wl
, WINDOW_CONTENT
))
275 screen_write_start(&ctx
, NULL
, &c
->status
);
277 /* Draw the left string and arrow. */
278 screen_write_cursormove(&ctx
, 0, 0);
280 screen_write_cnputs(&ctx
, llen
, &lgc
, utf8flag
, "%s", left
);
281 screen_write_putc(&ctx
, &stdgc
, ' ');
284 memcpy(&gc
, &stdgc
, sizeof gc
);
286 gc
.attr
^= GRID_ATTR_REVERSE
;
287 screen_write_putc(&ctx
, &gc
, '<');
290 /* Draw the right string and arrow. */
292 screen_write_cursormove(&ctx
, c
->tty
.sx
- rlen
- 2, 0);
293 memcpy(&gc
, &stdgc
, sizeof gc
);
295 gc
.attr
^= GRID_ATTR_REVERSE
;
296 screen_write_putc(&ctx
, &gc
, '>');
298 screen_write_cursormove(&ctx
, c
->tty
.sx
- rlen
- 1, 0);
300 screen_write_putc(&ctx
, &stdgc
, ' ');
301 screen_write_cnputs(&ctx
, rlen
, &rgc
, utf8flag
, "%s", right
);
304 /* Figure out the offset for the window list. */
309 if (wlwidth
< wlavailable
) {
310 switch (options_get_number(&s
->options
, "status-justify")) {
311 case 1: /* centered */
312 wloffset
+= (wlavailable
- wlwidth
) / 2;
315 wloffset
+= (wlavailable
- wlwidth
);
322 /* Copy the window list. */
323 screen_write_cursormove(&ctx
, wloffset
, 0);
324 screen_write_copy(&ctx
, &window_list
, wlstart
, 0, wlwidth
, 1);
325 screen_free(&window_list
);
327 screen_write_stop(&ctx
);
335 if (grid_compare(c
->status
.grid
, old_status
.grid
) == 0) {
336 screen_free(&old_status
);
339 screen_free(&old_status
);
343 /* Replace a single special sequence (prefixed by #). */
345 status_replace1(struct client
*c
,struct winlink
*wl
,
346 char **iptr
, char **optr
, char *out
, size_t outsize
, int jobsflag
)
348 struct session
*s
= c
->session
;
349 char ch
, tmp
[256], *ptr
, *endptr
, *freeptr
;
357 limit
= strtol(*iptr
, &endptr
, 10);
358 if ((limit
== 0 && errno
!= EINVAL
) ||
359 (limit
== LONG_MIN
&& errno
!= ERANGE
) ||
360 (limit
== LONG_MAX
&& errno
!= ERANGE
) ||
368 switch (*(*iptr
)++) {
374 if ((ptr
= status_job(c
, iptr
)) == NULL
)
379 if (gethostname(tmp
, sizeof tmp
) != 0)
380 fatal("gethostname failed");
384 xsnprintf(tmp
, sizeof tmp
, "%d", wl
->idx
);
388 xsnprintf(tmp
, sizeof tmp
, "%u",
389 window_pane_index(wl
->window
, wl
->window
->active
));
396 ptr
= wl
->window
->active
->base
.title
;
399 ptr
= wl
->window
->name
;
403 if (session_alert_has(s
, wl
, WINDOW_CONTENT
))
405 else if (session_alert_has(s
, wl
, WINDOW_BELL
))
407 else if (session_alert_has(s
, wl
, WINDOW_ACTIVITY
))
409 else if (wl
== s
->curw
)
411 else if (wl
== TAILQ_FIRST(&s
->lastw
))
418 * Embedded style, handled at display time. Leave present and
419 * skip input until ].
431 ptrlen
= strlen(ptr
);
432 if ((size_t) limit
< ptrlen
)
435 if (*optr
+ ptrlen
>= out
+ outsize
- 1)
437 while (ptrlen
> 0 && *ptr
!= '\0') {
449 (*iptr
)--; /* include ch */
450 while (**iptr
!= ch
&& **iptr
!= '\0') {
451 if (*optr
>= out
+ outsize
- 1)
453 *(*optr
)++ = *(*iptr
)++;
457 /* Replace special sequences in fmt. */
459 status_replace(struct client
*c
,
460 struct winlink
*wl
, const char *fmt
, time_t t
, int jobsflag
)
462 static char out
[BUFSIZ
];
463 char in
[BUFSIZ
], ch
, *iptr
, *optr
;
465 strftime(in
, sizeof in
, fmt
, localtime(&t
));
466 in
[(sizeof in
) - 1] = '\0';
471 while (*iptr
!= '\0') {
472 if (optr
>= out
+ (sizeof out
) - 1)
480 status_replace1(c
, wl
, &iptr
, &optr
, out
, sizeof out
, jobsflag
);
484 return (xstrdup(out
));
487 /* Figure out job name and get its result, starting it off if necessary. */
489 status_job(struct client
*c
, char **iptr
)
498 if (**iptr
== ')') { /* no command given */
503 cmd
= xmalloc(strlen(*iptr
) + 1);
507 for (; **iptr
!= '\0'; (*iptr
)++) {
508 if (!lastesc
&& **iptr
== ')')
509 break; /* unescaped ) is the end */
510 if (!lastesc
&& **iptr
== '\\') {
512 continue; /* skip \ if not escaped */
517 if (**iptr
== '\0') /* no terminating ) */ {
521 (*iptr
)++; /* skip final ) */
524 job
= job_get(&c
->status_jobs
, cmd
);
526 job
= job_add(&c
->status_jobs
,
527 JOB_PERSIST
, c
, cmd
, status_job_callback
, xfree
, NULL
);
531 if (job
->data
== NULL
)
532 return (xstrdup(""));
533 return (xstrdup(job
->data
));
536 /* Job has finished: save its result. */
538 status_job_callback(struct job
*job
)
544 if ((line
= evbuffer_readline(job
->event
->input
)) == NULL
) {
545 len
= EVBUFFER_LENGTH(job
->event
->input
);
546 buf
= xmalloc(len
+ 1);
548 memcpy(buf
, EVBUFFER_DATA(job
->event
->input
), len
);
552 if (job
->data
!= NULL
)
555 server_redraw_client(job
->client
);
560 job
->data
= xstrdup(line
);
563 /* Calculate winlink status line entry width. */
565 status_width(struct client
*c
, struct winlink
*wl
, time_t t
)
567 struct options
*oo
= &wl
->window
->options
;
568 struct session
*s
= c
->session
;
574 utf8flag
= options_get_number(&s
->options
, "status-utf8");
576 fmt
= options_get_string(&wl
->window
->options
, "window-status-format");
578 fmt
= options_get_string(oo
, "window-status-current-format");
580 text
= status_replace(c
, wl
, fmt
, t
, 1);
581 size
= screen_write_cstrlen(utf8flag
, "%s", text
);
587 /* Return winlink status line entry and adjust gc as necessary. */
590 struct client
*c
, struct winlink
*wl
, time_t t
, struct grid_cell
*gc
)
592 struct options
*oo
= &wl
->window
->options
;
593 struct session
*s
= c
->session
;
598 fg
= options_get_number(oo
, "window-status-fg");
600 colour_set_fg(gc
, fg
);
601 bg
= options_get_number(oo
, "window-status-bg");
603 colour_set_bg(gc
, bg
);
604 attr
= options_get_number(oo
, "window-status-attr");
607 fmt
= options_get_string(oo
, "window-status-format");
609 fg
= options_get_number(oo
, "window-status-current-fg");
611 colour_set_fg(gc
, fg
);
612 bg
= options_get_number(oo
, "window-status-current-bg");
614 colour_set_bg(gc
, bg
);
615 attr
= options_get_number(oo
, "window-status-current-attr");
618 fmt
= options_get_string(oo
, "window-status-current-format");
621 if (session_alert_has(s
, wl
, WINDOW_ACTIVITY
) ||
622 session_alert_has(s
, wl
, WINDOW_BELL
) ||
623 session_alert_has(s
, wl
, WINDOW_CONTENT
))
624 gc
->attr
^= GRID_ATTR_REVERSE
;
626 text
= status_replace(c
, wl
, fmt
, t
, 1);
630 /* Set a status line message. */
632 status_message_set(struct client
*c
, const char *fmt
, ...)
635 struct session
*s
= c
->session
;
636 struct message_entry
*msg
;
641 status_prompt_clear(c
);
642 status_message_clear(c
);
645 xvasprintf(&c
->message_string
, fmt
, ap
);
648 ARRAY_EXPAND(&c
->message_log
, 1);
649 msg
= &ARRAY_LAST(&c
->message_log
);
650 msg
->msg_time
= time(NULL
);
651 msg
->msg
= xstrdup(c
->message_string
);
656 limit
= options_get_number(&s
->options
, "message-limit");
657 if (ARRAY_LENGTH(&c
->message_log
) > limit
) {
658 limit
= ARRAY_LENGTH(&c
->message_log
) - limit
;
659 for (i
= 0; i
< limit
; i
++) {
660 msg
= &ARRAY_FIRST(&c
->message_log
);
662 ARRAY_REMOVE(&c
->message_log
, 0);
666 delay
= options_get_number(&c
->session
->options
, "display-time");
667 tv
.tv_sec
= delay
/ 1000;
668 tv
.tv_usec
= (delay
% 1000) * 1000L;
670 evtimer_del(&c
->message_timer
);
671 evtimer_set(&c
->message_timer
, status_message_callback
, c
);
672 evtimer_add(&c
->message_timer
, &tv
);
674 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
675 c
->flags
|= CLIENT_STATUS
;
678 /* Clear status line message. */
680 status_message_clear(struct client
*c
)
682 if (c
->message_string
== NULL
)
685 xfree(c
->message_string
);
686 c
->message_string
= NULL
;
688 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
689 c
->flags
|= CLIENT_REDRAW
; /* screen was frozen and may have changed */
691 screen_reinit(&c
->status
);
694 /* Clear status line message after timer expires. */
697 status_message_callback(unused
int fd
, unused
short event
, void *data
)
699 struct client
*c
= data
;
701 status_message_clear(c
);
704 /* Draw client message on status line of present else on last line. */
706 status_message_redraw(struct client
*c
)
708 struct screen_write_ctx ctx
;
709 struct session
*s
= c
->session
;
710 struct screen old_status
;
715 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
717 memcpy(&old_status
, &c
->status
, sizeof old_status
);
718 screen_init(&c
->status
, c
->tty
.sx
, 1, 0);
720 utf8flag
= options_get_number(&s
->options
, "status-utf8");
722 len
= screen_write_strlen(utf8flag
, "%s", c
->message_string
);
726 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
727 colour_set_fg(&gc
, options_get_number(&s
->options
, "message-fg"));
728 colour_set_bg(&gc
, options_get_number(&s
->options
, "message-bg"));
729 gc
.attr
|= options_get_number(&s
->options
, "message-attr");
731 screen_write_start(&ctx
, NULL
, &c
->status
);
733 screen_write_cursormove(&ctx
, 0, 0);
734 screen_write_nputs(&ctx
, len
, &gc
, utf8flag
, "%s", c
->message_string
);
735 for (; len
< c
->tty
.sx
; len
++)
736 screen_write_putc(&ctx
, &gc
, ' ');
738 screen_write_stop(&ctx
);
740 if (grid_compare(c
->status
.grid
, old_status
.grid
) == 0) {
741 screen_free(&old_status
);
744 screen_free(&old_status
);
748 /* Enable status line prompt. */
750 status_prompt_set(struct client
*c
, const char *msg
,
751 int (*callbackfn
)(void *, const char *), void (*freefn
)(void *),
752 void *data
, int flags
)
756 status_message_clear(c
);
757 status_prompt_clear(c
);
759 c
->prompt_string
= xstrdup(msg
);
761 c
->prompt_buffer
= xstrdup("");
764 c
->prompt_callbackfn
= callbackfn
;
765 c
->prompt_freefn
= freefn
;
766 c
->prompt_data
= data
;
768 c
->prompt_hindex
= 0;
770 c
->prompt_flags
= flags
;
772 keys
= options_get_number(&c
->session
->options
, "status-keys");
773 if (keys
== MODEKEY_EMACS
)
774 mode_key_init(&c
->prompt_mdata
, &mode_key_tree_emacs_edit
);
776 mode_key_init(&c
->prompt_mdata
, &mode_key_tree_vi_edit
);
778 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
779 c
->flags
|= CLIENT_STATUS
;
782 /* Remove status line prompt. */
784 status_prompt_clear(struct client
*c
)
786 if (c
->prompt_string
== NULL
)
789 if (c
->prompt_freefn
!= NULL
&& c
->prompt_data
!= NULL
)
790 c
->prompt_freefn(c
->prompt_data
);
792 xfree(c
->prompt_string
);
793 c
->prompt_string
= NULL
;
795 xfree(c
->prompt_buffer
);
796 c
->prompt_buffer
= NULL
;
798 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
799 c
->flags
|= CLIENT_REDRAW
; /* screen was frozen and may have changed */
801 screen_reinit(&c
->status
);
804 /* Update status line prompt with a new prompt string. */
806 status_prompt_update(struct client
*c
, const char *msg
)
808 xfree(c
->prompt_string
);
809 c
->prompt_string
= xstrdup(msg
);
811 *c
->prompt_buffer
= '\0';
814 c
->prompt_hindex
= 0;
816 c
->flags
|= CLIENT_STATUS
;
819 /* Draw client prompt on status line of present else on last line. */
821 status_prompt_redraw(struct client
*c
)
823 struct screen_write_ctx ctx
;
824 struct session
*s
= c
->session
;
825 struct screen old_status
;
826 size_t i
, size
, left
, len
, off
;
827 struct grid_cell gc
, *gcp
;
830 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
832 memcpy(&old_status
, &c
->status
, sizeof old_status
);
833 screen_init(&c
->status
, c
->tty
.sx
, 1, 0);
835 utf8flag
= options_get_number(&s
->options
, "status-utf8");
837 len
= screen_write_strlen(utf8flag
, "%s", c
->prompt_string
);
842 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
843 colour_set_fg(&gc
, options_get_number(&s
->options
, "message-fg"));
844 colour_set_bg(&gc
, options_get_number(&s
->options
, "message-bg"));
845 gc
.attr
|= options_get_number(&s
->options
, "message-attr");
847 screen_write_start(&ctx
, NULL
, &c
->status
);
849 screen_write_cursormove(&ctx
, 0, 0);
850 screen_write_nputs(&ctx
, len
, &gc
, utf8flag
, "%s", c
->prompt_string
);
852 left
= c
->tty
.sx
- len
;
854 size
= screen_write_strlen(utf8flag
, "%s", c
->prompt_buffer
);
855 if (c
->prompt_index
>= left
) {
856 off
= c
->prompt_index
- left
+ 1;
857 if (c
->prompt_index
== size
)
862 &ctx
, left
, &gc
, utf8flag
, "%s", c
->prompt_buffer
+ off
);
864 for (i
= len
+ size
; i
< c
->tty
.sx
; i
++)
865 screen_write_putc(&ctx
, &gc
, ' ');
868 screen_write_stop(&ctx
);
870 /* Apply fake cursor. */
871 off
= len
+ c
->prompt_index
- off
;
872 gcp
= grid_view_get_cell(c
->status
.grid
, off
, 0);
873 gcp
->attr
^= GRID_ATTR_REVERSE
;
875 if (grid_compare(c
->status
.grid
, old_status
.grid
) == 0) {
876 screen_free(&old_status
);
879 screen_free(&old_status
);
883 /* Handle keys in prompt. */
885 status_prompt_key(struct client
*c
, int key
)
887 struct paste_buffer
*pb
;
888 char *s
, *first
, *last
, word
[64], swapc
;
890 size_t size
, n
, off
, idx
;
892 size
= strlen(c
->prompt_buffer
);
893 switch (mode_key_lookup(&c
->prompt_mdata
, key
)) {
894 case MODEKEYEDIT_CURSORLEFT
:
895 if (c
->prompt_index
> 0) {
897 c
->flags
|= CLIENT_STATUS
;
900 case MODEKEYEDIT_SWITCHMODEAPPEND
:
901 case MODEKEYEDIT_CURSORRIGHT
:
902 if (c
->prompt_index
< size
) {
904 c
->flags
|= CLIENT_STATUS
;
907 case MODEKEYEDIT_STARTOFLINE
:
908 if (c
->prompt_index
!= 0) {
910 c
->flags
|= CLIENT_STATUS
;
913 case MODEKEYEDIT_ENDOFLINE
:
914 if (c
->prompt_index
!= size
) {
915 c
->prompt_index
= size
;
916 c
->flags
|= CLIENT_STATUS
;
919 case MODEKEYEDIT_COMPLETE
:
920 if (*c
->prompt_buffer
== '\0')
923 idx
= c
->prompt_index
;
927 /* Find the word we are in. */
928 first
= c
->prompt_buffer
+ idx
;
929 while (first
> c
->prompt_buffer
&& *first
!= ' ')
931 while (*first
== ' ')
933 last
= c
->prompt_buffer
+ idx
;
934 while (*last
!= '\0' && *last
!= ' ')
941 ((size_t) (last
- first
)) > (sizeof word
) - 1)
943 memcpy(word
, first
, last
- first
);
944 word
[last
- first
] = '\0';
946 /* And try to complete it. */
947 if ((s
= status_prompt_complete(word
)) == NULL
)
951 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
952 memmove(first
, last
, n
);
953 size
-= last
- first
;
955 /* Insert the new word. */
957 off
= first
- c
->prompt_buffer
;
958 c
->prompt_buffer
= xrealloc(c
->prompt_buffer
, 1, size
+ 1);
959 first
= c
->prompt_buffer
+ off
;
960 memmove(first
+ strlen(s
), first
, n
);
961 memcpy(first
, s
, strlen(s
));
963 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
966 c
->flags
|= CLIENT_STATUS
;
968 case MODEKEYEDIT_BACKSPACE
:
969 if (c
->prompt_index
!= 0) {
970 if (c
->prompt_index
== size
)
971 c
->prompt_buffer
[--c
->prompt_index
] = '\0';
973 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
974 c
->prompt_buffer
+ c
->prompt_index
,
975 size
+ 1 - c
->prompt_index
);
978 c
->flags
|= CLIENT_STATUS
;
981 case MODEKEYEDIT_DELETE
:
982 if (c
->prompt_index
!= size
) {
983 memmove(c
->prompt_buffer
+ c
->prompt_index
,
984 c
->prompt_buffer
+ c
->prompt_index
+ 1,
985 size
+ 1 - c
->prompt_index
);
986 c
->flags
|= CLIENT_STATUS
;
989 case MODEKEYEDIT_DELETELINE
:
990 *c
->prompt_buffer
= '\0';
992 c
->flags
|= CLIENT_STATUS
;
994 case MODEKEYEDIT_DELETETOENDOFLINE
:
995 if (c
->prompt_index
< size
) {
996 c
->prompt_buffer
[c
->prompt_index
] = '\0';
997 c
->flags
|= CLIENT_STATUS
;
1000 case MODEKEYEDIT_HISTORYUP
:
1001 if (ARRAY_LENGTH(&c
->prompt_hdata
) == 0)
1003 xfree(c
->prompt_buffer
);
1005 c
->prompt_buffer
= xstrdup(ARRAY_ITEM(&c
->prompt_hdata
,
1006 ARRAY_LENGTH(&c
->prompt_hdata
) - 1 - c
->prompt_hindex
));
1007 if (c
->prompt_hindex
!= ARRAY_LENGTH(&c
->prompt_hdata
) - 1)
1010 c
->prompt_index
= strlen(c
->prompt_buffer
);
1011 c
->flags
|= CLIENT_STATUS
;
1013 case MODEKEYEDIT_HISTORYDOWN
:
1014 xfree(c
->prompt_buffer
);
1016 if (c
->prompt_hindex
!= 0) {
1018 c
->prompt_buffer
= xstrdup(ARRAY_ITEM(
1019 &c
->prompt_hdata
, ARRAY_LENGTH(
1020 &c
->prompt_hdata
) - 1 - c
->prompt_hindex
));
1022 c
->prompt_buffer
= xstrdup("");
1024 c
->prompt_index
= strlen(c
->prompt_buffer
);
1025 c
->flags
|= CLIENT_STATUS
;
1027 case MODEKEYEDIT_PASTE
:
1028 if ((pb
= paste_get_top(&c
->session
->buffers
)) == NULL
)
1030 for (n
= 0; n
< pb
->size
; n
++) {
1031 ch
= (u_char
) pb
->data
[n
];
1032 if (ch
< 32 || ch
== 127)
1036 c
->prompt_buffer
= xrealloc(c
->prompt_buffer
, 1, size
+ n
+ 1);
1037 if (c
->prompt_index
== size
) {
1038 memcpy(c
->prompt_buffer
+ c
->prompt_index
, pb
->data
, n
);
1039 c
->prompt_index
+= n
;
1040 c
->prompt_buffer
[c
->prompt_index
] = '\0';
1042 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
1043 c
->prompt_buffer
+ c
->prompt_index
,
1044 size
+ 1 - c
->prompt_index
);
1045 memcpy(c
->prompt_buffer
+ c
->prompt_index
, pb
->data
, n
);
1046 c
->prompt_index
+= n
;
1049 c
->flags
|= CLIENT_STATUS
;
1051 case MODEKEYEDIT_TRANSPOSECHARS
:
1052 idx
= c
->prompt_index
;
1056 swapc
= c
->prompt_buffer
[idx
- 2];
1057 c
->prompt_buffer
[idx
- 2] = c
->prompt_buffer
[idx
- 1];
1058 c
->prompt_buffer
[idx
- 1] = swapc
;
1059 c
->prompt_index
= idx
;
1060 c
->flags
|= CLIENT_STATUS
;
1063 case MODEKEYEDIT_ENTER
:
1064 if (*c
->prompt_buffer
!= '\0')
1065 status_prompt_add_history(c
);
1066 if (c
->prompt_callbackfn(c
->prompt_data
, c
->prompt_buffer
) == 0)
1067 status_prompt_clear(c
);
1069 case MODEKEYEDIT_CANCEL
:
1070 if (c
->prompt_callbackfn(c
->prompt_data
, NULL
) == 0)
1071 status_prompt_clear(c
);
1074 if (key
< 32 || key
== 127)
1076 c
->prompt_buffer
= xrealloc(c
->prompt_buffer
, 1, size
+ 2);
1078 if (c
->prompt_index
== size
) {
1079 c
->prompt_buffer
[c
->prompt_index
++] = key
;
1080 c
->prompt_buffer
[c
->prompt_index
] = '\0';
1082 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1083 c
->prompt_buffer
+ c
->prompt_index
,
1084 size
+ 1 - c
->prompt_index
);
1085 c
->prompt_buffer
[c
->prompt_index
++] = key
;
1088 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1089 if (c
->prompt_callbackfn(
1090 c
->prompt_data
, c
->prompt_buffer
) == 0)
1091 status_prompt_clear(c
);
1094 c
->flags
|= CLIENT_STATUS
;
1101 /* Add line to the history. */
1103 status_prompt_add_history(struct client
*c
)
1105 if (ARRAY_LENGTH(&c
->prompt_hdata
) > 0 &&
1106 strcmp(ARRAY_LAST(&c
->prompt_hdata
), c
->prompt_buffer
) == 0)
1109 if (ARRAY_LENGTH(&c
->prompt_hdata
) == PROMPT_HISTORY
) {
1110 xfree(ARRAY_FIRST(&c
->prompt_hdata
));
1111 ARRAY_REMOVE(&c
->prompt_hdata
, 0);
1114 ARRAY_ADD(&c
->prompt_hdata
, xstrdup(c
->prompt_buffer
));
1117 /* Complete word. */
1119 status_prompt_complete(const char *s
)
1121 const struct cmd_entry
**cmdent
;
1122 const struct set_option_entry
*entry
;
1123 ARRAY_DECL(, const char *) list
;
1131 /* First, build a list of all the possible matches. */
1133 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1134 if (strncmp((*cmdent
)->name
, s
, strlen(s
)) == 0)
1135 ARRAY_ADD(&list
, (*cmdent
)->name
);
1137 for (entry
= set_option_table
; entry
->name
!= NULL
; entry
++) {
1138 if (strncmp(entry
->name
, s
, strlen(s
)) == 0)
1139 ARRAY_ADD(&list
, entry
->name
);
1141 for (entry
= set_session_option_table
; entry
->name
!= NULL
; entry
++) {
1142 if (strncmp(entry
->name
, s
, strlen(s
)) == 0)
1143 ARRAY_ADD(&list
, entry
->name
);
1145 for (entry
= set_window_option_table
; entry
->name
!= NULL
; entry
++) {
1146 if (strncmp(entry
->name
, s
, strlen(s
)) == 0)
1147 ARRAY_ADD(&list
, entry
->name
);
1150 /* If none, bail now. */
1151 if (ARRAY_LENGTH(&list
) == 0) {
1156 /* If an exact match, return it, with a trailing space. */
1157 if (ARRAY_LENGTH(&list
) == 1) {
1158 xasprintf(&s2
, "%s ", ARRAY_FIRST(&list
));
1163 /* Now loop through the list and find the longest common prefix. */
1164 prefix
= xstrdup(ARRAY_FIRST(&list
));
1165 for (i
= 1; i
< ARRAY_LENGTH(&list
); i
++) {
1166 s
= ARRAY_ITEM(&list
, i
);
1169 if (j
> strlen(prefix
))
1171 for (; j
> 0; j
--) {
1172 if (prefix
[j
- 1] != s
[j
- 1])
1173 prefix
[j
- 1] = '\0';