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
*);
39 struct client
*, struct winlink
*, time_t, struct grid_cell
*);
40 void status_replace1(struct client
*,
41 struct winlink
*, char **, char **, char *, size_t, int);
42 void status_message_callback(int, short, void *);
44 const char *status_prompt_up_history(u_int
*);
45 const char *status_prompt_down_history(u_int
*);
46 void status_prompt_add_history(const char *);
47 char *status_prompt_complete(const char *);
49 /* Status prompt history. */
50 ARRAY_DECL(, char *) status_prompt_history
= ARRAY_INITIALIZER
;
52 /* Retrieve options for left string. */
54 status_redraw_get_left(struct client
*c
,
55 time_t t
, int utf8flag
, struct grid_cell
*gc
, size_t *size
)
57 struct session
*s
= c
->session
;
62 fg
= options_get_number(&s
->options
, "status-left-fg");
64 colour_set_fg(gc
, fg
);
65 bg
= options_get_number(&s
->options
, "status-left-bg");
67 colour_set_bg(gc
, bg
);
68 attr
= options_get_number(&s
->options
, "status-left-attr");
72 left
= status_replace(
73 c
, NULL
, options_get_string(&s
->options
, "status-left"), t
, 1);
75 *size
= options_get_number(&s
->options
, "status-left-length");
76 leftlen
= screen_write_cstrlen(utf8flag
, "%s", left
);
82 /* Retrieve options for right string. */
84 status_redraw_get_right(struct client
*c
,
85 time_t t
, int utf8flag
, struct grid_cell
*gc
, size_t *size
)
87 struct session
*s
= c
->session
;
92 fg
= options_get_number(&s
->options
, "status-right-fg");
94 colour_set_fg(gc
, fg
);
95 bg
= options_get_number(&s
->options
, "status-right-bg");
97 colour_set_bg(gc
, bg
);
98 attr
= options_get_number(&s
->options
, "status-right-attr");
102 right
= status_replace(
103 c
, NULL
, options_get_string(&s
->options
, "status-right"), t
, 1);
105 *size
= options_get_number(&s
->options
, "status-right-length");
106 rightlen
= screen_write_cstrlen(utf8flag
, "%s", right
);
107 if (rightlen
< *size
)
112 /* Draw status for client on the last lines of given context. */
114 status_redraw(struct client
*c
)
116 struct screen_write_ctx ctx
;
117 struct session
*s
= c
->session
;
119 struct screen old_status
, window_list
;
120 struct grid_cell stdgc
, lgc
, rgc
, gc
;
123 u_int offset
, needed
;
124 u_int wlstart
, wlwidth
, wlavailable
, wloffset
, wlsize
;
126 int larrow
, rarrow
, utf8flag
;
128 /* No status line? */
129 if (c
->tty
.sy
== 0 || !options_get_number(&s
->options
, "status"))
134 /* Update status timer. */
135 if (gettimeofday(&c
->status_timer
, NULL
) != 0)
136 fatal("gettimeofday failed");
137 t
= c
->status_timer
.tv_sec
;
139 /* Set up default colour. */
140 memcpy(&stdgc
, &grid_default_cell
, sizeof gc
);
141 colour_set_fg(&stdgc
, options_get_number(&s
->options
, "status-fg"));
142 colour_set_bg(&stdgc
, options_get_number(&s
->options
, "status-bg"));
143 stdgc
.attr
|= options_get_number(&s
->options
, "status-attr");
145 /* Create the target screen. */
146 memcpy(&old_status
, &c
->status
, sizeof old_status
);
147 screen_init(&c
->status
, c
->tty
.sx
, 1, 0);
148 screen_write_start(&ctx
, NULL
, &c
->status
);
149 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
150 screen_write_putc(&ctx
, &stdgc
, ' ');
151 screen_write_stop(&ctx
);
153 /* If the height is one line, blank status line. */
157 /* Get UTF-8 flag. */
158 utf8flag
= options_get_number(&s
->options
, "status-utf8");
160 /* Work out left and right strings. */
161 memcpy(&lgc
, &stdgc
, sizeof lgc
);
162 left
= status_redraw_get_left(c
, t
, utf8flag
, &lgc
, &llen
);
163 memcpy(&rgc
, &stdgc
, sizeof rgc
);
164 right
= status_redraw_get_right(c
, t
, utf8flag
, &rgc
, &rlen
);
167 * Figure out how much space we have for the window list. If there
168 * isn't enough space, just show a blank status line.
175 if (c
->tty
.sx
== 0 || c
->tty
.sx
<= needed
)
177 wlavailable
= c
->tty
.sx
- needed
;
179 /* Calculate the total size needed for the window list. */
180 wlstart
= wloffset
= wlwidth
= 0;
181 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
182 if (wl
->status_text
!= NULL
)
183 xfree(wl
->status_text
);
184 memcpy(&wl
->status_cell
, &stdgc
, sizeof wl
->status_cell
);
185 wl
->status_text
= status_print(c
, wl
, t
, &wl
->status_cell
);
187 screen_write_cstrlen(utf8flag
, "%s", wl
->status_text
);
191 wlwidth
+= wl
->status_width
+ 1;
194 /* Create a new screen for the window list. */
195 screen_init(&window_list
, wlwidth
, 1, 0);
197 /* And draw the window list into it. */
198 screen_write_start(&ctx
, NULL
, &window_list
);
199 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
200 screen_write_cnputs(&ctx
,
201 -1, &wl
->status_cell
, utf8flag
, "%s", wl
->status_text
);
202 screen_write_putc(&ctx
, &stdgc
, ' ');
204 screen_write_stop(&ctx
);
206 /* If there is enough space for the total width, skip to draw now. */
207 if (wlwidth
<= wlavailable
)
210 /* Find size of current window text. */
211 wlsize
= s
->curw
->status_width
;
214 * If the current window is already on screen, good to draw from the
215 * start and just leave off the end.
217 if (wloffset
+ wlsize
< wlavailable
) {
218 if (wlavailable
> 0) {
222 wlwidth
= wlavailable
;
225 * Work out how many characters we need to omit from the
226 * start. There are wlavailable characters to fill, and
227 * wloffset + wlsize must be the last. So, the start character
228 * is wloffset + wlsize - wlavailable.
230 if (wlavailable
> 0) {
235 wlstart
= wloffset
+ wlsize
- wlavailable
;
236 if (wlavailable
> 0 && wlwidth
> wlstart
+ wlavailable
+ 1) {
241 wlwidth
= wlavailable
;
244 /* Bail if anything is now too small too. */
245 if (wlwidth
== 0 || wlavailable
== 0) {
246 screen_free(&window_list
);
251 * Now the start position is known, work out the state of the left and
255 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
256 if (wl
->flags
& WINLINK_ALERTFLAGS
&&
257 larrow
== 1 && offset
< wlstart
)
260 offset
+= wl
->status_width
;
262 if (wl
->flags
& WINLINK_ALERTFLAGS
&&
263 rarrow
== 1 && offset
> wlstart
+ wlwidth
)
269 screen_write_start(&ctx
, NULL
, &c
->status
);
271 /* Draw the left string and arrow. */
272 screen_write_cursormove(&ctx
, 0, 0);
274 screen_write_cnputs(&ctx
, llen
, &lgc
, utf8flag
, "%s", left
);
275 screen_write_putc(&ctx
, &stdgc
, ' ');
278 memcpy(&gc
, &stdgc
, sizeof gc
);
280 gc
.attr
^= GRID_ATTR_REVERSE
;
281 screen_write_putc(&ctx
, &gc
, '<');
284 /* Draw the right string and arrow. */
286 screen_write_cursormove(&ctx
, c
->tty
.sx
- rlen
- 2, 0);
287 memcpy(&gc
, &stdgc
, sizeof gc
);
289 gc
.attr
^= GRID_ATTR_REVERSE
;
290 screen_write_putc(&ctx
, &gc
, '>');
292 screen_write_cursormove(&ctx
, c
->tty
.sx
- rlen
- 1, 0);
294 screen_write_putc(&ctx
, &stdgc
, ' ');
295 screen_write_cnputs(&ctx
, rlen
, &rgc
, utf8flag
, "%s", right
);
298 /* Figure out the offset for the window list. */
303 if (wlwidth
< wlavailable
) {
304 switch (options_get_number(&s
->options
, "status-justify")) {
305 case 1: /* centered */
306 wloffset
+= (wlavailable
- wlwidth
) / 2;
309 wloffset
+= (wlavailable
- wlwidth
);
316 /* Copy the window list. */
317 screen_write_cursormove(&ctx
, wloffset
, 0);
318 screen_write_copy(&ctx
, &window_list
, wlstart
, 0, wlwidth
, 1);
319 screen_free(&window_list
);
321 screen_write_stop(&ctx
);
329 if (grid_compare(c
->status
.grid
, old_status
.grid
) == 0) {
330 screen_free(&old_status
);
333 screen_free(&old_status
);
337 /* Replace a single special sequence (prefixed by #). */
339 status_replace1(struct client
*c
,struct winlink
*wl
,
340 char **iptr
, char **optr
, char *out
, size_t outsize
, int jobsflag
)
342 struct session
*s
= c
->session
;
343 char ch
, tmp
[256], *ptr
, *endptr
, *freeptr
;
351 limit
= strtol(*iptr
, &endptr
, 10);
352 if ((limit
== 0 && errno
!= EINVAL
) ||
353 (limit
== LONG_MIN
&& errno
!= ERANGE
) ||
354 (limit
== LONG_MAX
&& errno
!= ERANGE
) ||
362 switch (*(*iptr
)++) {
368 if ((ptr
= status_job(c
, iptr
)) == NULL
)
373 if (gethostname(tmp
, sizeof tmp
) != 0)
374 fatal("gethostname failed");
378 xsnprintf(tmp
, sizeof tmp
, "%d", wl
->idx
);
382 xsnprintf(tmp
, sizeof tmp
, "%u",
383 window_pane_index(wl
->window
, wl
->window
->active
));
390 ptr
= wl
->window
->active
->base
.title
;
393 ptr
= wl
->window
->name
;
396 ptr
= window_printable_flags(s
, wl
);
401 * Embedded style, handled at display time. Leave present and
402 * skip input until ].
414 ptrlen
= strlen(ptr
);
415 if ((size_t) limit
< ptrlen
)
418 if (*optr
+ ptrlen
>= out
+ outsize
- 1)
420 while (ptrlen
> 0 && *ptr
!= '\0') {
432 (*iptr
)--; /* include ch */
433 while (**iptr
!= ch
&& **iptr
!= '\0') {
434 if (*optr
>= out
+ outsize
- 1)
436 *(*optr
)++ = *(*iptr
)++;
440 /* Replace special sequences in fmt. */
442 status_replace(struct client
*c
,
443 struct winlink
*wl
, const char *fmt
, time_t t
, int jobsflag
)
445 static char out
[BUFSIZ
];
446 char in
[BUFSIZ
], ch
, *iptr
, *optr
;
448 strftime(in
, sizeof in
, fmt
, localtime(&t
));
449 in
[(sizeof in
) - 1] = '\0';
454 while (*iptr
!= '\0') {
455 if (optr
>= out
+ (sizeof out
) - 1)
459 if (ch
!= '#' || *iptr
== '\0') {
463 status_replace1(c
, wl
, &iptr
, &optr
, out
, sizeof out
, jobsflag
);
467 return (xstrdup(out
));
470 /* Figure out job name and get its result, starting it off if necessary. */
472 status_job(struct client
*c
, char **iptr
)
481 if (**iptr
== ')') { /* no command given */
486 cmd
= xmalloc(strlen(*iptr
) + 1);
490 for (; **iptr
!= '\0'; (*iptr
)++) {
491 if (!lastesc
&& **iptr
== ')')
492 break; /* unescaped ) is the end */
493 if (!lastesc
&& **iptr
== '\\') {
495 continue; /* skip \ if not escaped */
500 if (**iptr
== '\0') /* no terminating ) */ {
504 (*iptr
)++; /* skip final ) */
507 job
= job_get(&c
->status_jobs
, cmd
);
509 job
= job_add(&c
->status_jobs
,
510 JOB_PERSIST
, c
, cmd
, status_job_callback
, xfree
, NULL
);
514 if (job
->data
== NULL
)
515 return (xstrdup(""));
516 return (xstrdup(job
->data
));
519 /* Job has finished: save its result. */
521 status_job_callback(struct job
*job
)
527 if ((line
= evbuffer_readline(job
->event
->input
)) == NULL
) {
528 len
= EVBUFFER_LENGTH(job
->event
->input
);
529 buf
= xmalloc(len
+ 1);
531 memcpy(buf
, EVBUFFER_DATA(job
->event
->input
), len
);
535 if (job
->data
!= NULL
)
538 server_redraw_client(job
->client
);
543 job
->data
= xstrdup(line
);
546 /* Return winlink status line entry and adjust gc as necessary. */
549 struct client
*c
, struct winlink
*wl
, time_t t
, struct grid_cell
*gc
)
551 struct options
*oo
= &wl
->window
->options
;
552 struct session
*s
= c
->session
;
557 fg
= options_get_number(oo
, "window-status-fg");
559 colour_set_fg(gc
, fg
);
560 bg
= options_get_number(oo
, "window-status-bg");
562 colour_set_bg(gc
, bg
);
563 attr
= options_get_number(oo
, "window-status-attr");
566 fmt
= options_get_string(oo
, "window-status-format");
568 fg
= options_get_number(oo
, "window-status-current-fg");
570 colour_set_fg(gc
, fg
);
571 bg
= options_get_number(oo
, "window-status-current-bg");
573 colour_set_bg(gc
, bg
);
574 attr
= options_get_number(oo
, "window-status-current-attr");
577 fmt
= options_get_string(oo
, "window-status-current-format");
580 if (wl
->flags
& WINLINK_ALERTFLAGS
) {
581 fg
= options_get_number(oo
, "window-status-alert-fg");
583 colour_set_fg(gc
, fg
);
584 bg
= options_get_number(oo
, "window-status-alert-bg");
586 colour_set_bg(gc
, bg
);
587 attr
= options_get_number(oo
, "window-status-alert-attr");
592 text
= status_replace(c
, wl
, fmt
, t
, 1);
596 /* Set a status line message. */
598 status_message_set(struct client
*c
, const char *fmt
, ...)
601 struct session
*s
= c
->session
;
602 struct message_entry
*msg
;
607 status_prompt_clear(c
);
608 status_message_clear(c
);
611 xvasprintf(&c
->message_string
, fmt
, ap
);
614 ARRAY_EXPAND(&c
->message_log
, 1);
615 msg
= &ARRAY_LAST(&c
->message_log
);
616 msg
->msg_time
= time(NULL
);
617 msg
->msg
= xstrdup(c
->message_string
);
622 limit
= options_get_number(&s
->options
, "message-limit");
623 if (ARRAY_LENGTH(&c
->message_log
) > limit
) {
624 limit
= ARRAY_LENGTH(&c
->message_log
) - limit
;
625 for (i
= 0; i
< limit
; i
++) {
626 msg
= &ARRAY_FIRST(&c
->message_log
);
628 ARRAY_REMOVE(&c
->message_log
, 0);
632 delay
= options_get_number(&c
->session
->options
, "display-time");
633 tv
.tv_sec
= delay
/ 1000;
634 tv
.tv_usec
= (delay
% 1000) * 1000L;
636 evtimer_del(&c
->message_timer
);
637 evtimer_set(&c
->message_timer
, status_message_callback
, c
);
638 evtimer_add(&c
->message_timer
, &tv
);
640 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
641 c
->flags
|= CLIENT_STATUS
;
644 /* Clear status line message. */
646 status_message_clear(struct client
*c
)
648 if (c
->message_string
== NULL
)
651 xfree(c
->message_string
);
652 c
->message_string
= NULL
;
654 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
655 c
->flags
|= CLIENT_REDRAW
; /* screen was frozen and may have changed */
657 screen_reinit(&c
->status
);
660 /* Clear status line message after timer expires. */
663 status_message_callback(unused
int fd
, unused
short event
, void *data
)
665 struct client
*c
= data
;
667 status_message_clear(c
);
670 /* Draw client message on status line of present else on last line. */
672 status_message_redraw(struct client
*c
)
674 struct screen_write_ctx ctx
;
675 struct session
*s
= c
->session
;
676 struct screen old_status
;
681 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
683 memcpy(&old_status
, &c
->status
, sizeof old_status
);
684 screen_init(&c
->status
, c
->tty
.sx
, 1, 0);
686 utf8flag
= options_get_number(&s
->options
, "status-utf8");
688 len
= screen_write_strlen(utf8flag
, "%s", c
->message_string
);
692 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
693 colour_set_fg(&gc
, options_get_number(&s
->options
, "message-fg"));
694 colour_set_bg(&gc
, options_get_number(&s
->options
, "message-bg"));
695 gc
.attr
|= options_get_number(&s
->options
, "message-attr");
697 screen_write_start(&ctx
, NULL
, &c
->status
);
699 screen_write_cursormove(&ctx
, 0, 0);
700 screen_write_nputs(&ctx
, len
, &gc
, utf8flag
, "%s", c
->message_string
);
701 for (; len
< c
->tty
.sx
; len
++)
702 screen_write_putc(&ctx
, &gc
, ' ');
704 screen_write_stop(&ctx
);
706 if (grid_compare(c
->status
.grid
, old_status
.grid
) == 0) {
707 screen_free(&old_status
);
710 screen_free(&old_status
);
714 /* Enable status line prompt. */
716 status_prompt_set(struct client
*c
, const char *msg
,
717 int (*callbackfn
)(void *, const char *), void (*freefn
)(void *),
718 void *data
, int flags
)
722 status_message_clear(c
);
723 status_prompt_clear(c
);
725 c
->prompt_string
= xstrdup(msg
);
727 c
->prompt_buffer
= xstrdup("");
730 c
->prompt_callbackfn
= callbackfn
;
731 c
->prompt_freefn
= freefn
;
732 c
->prompt_data
= data
;
734 c
->prompt_hindex
= 0;
736 c
->prompt_flags
= flags
;
738 keys
= options_get_number(&c
->session
->options
, "status-keys");
739 if (keys
== MODEKEY_EMACS
)
740 mode_key_init(&c
->prompt_mdata
, &mode_key_tree_emacs_edit
);
742 mode_key_init(&c
->prompt_mdata
, &mode_key_tree_vi_edit
);
744 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
745 c
->flags
|= CLIENT_STATUS
;
748 /* Remove status line prompt. */
750 status_prompt_clear(struct client
*c
)
752 if (c
->prompt_string
== NULL
)
755 if (c
->prompt_freefn
!= NULL
&& c
->prompt_data
!= NULL
)
756 c
->prompt_freefn(c
->prompt_data
);
758 xfree(c
->prompt_string
);
759 c
->prompt_string
= NULL
;
761 xfree(c
->prompt_buffer
);
762 c
->prompt_buffer
= NULL
;
764 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
765 c
->flags
|= CLIENT_REDRAW
; /* screen was frozen and may have changed */
767 screen_reinit(&c
->status
);
770 /* Update status line prompt with a new prompt string. */
772 status_prompt_update(struct client
*c
, const char *msg
)
774 xfree(c
->prompt_string
);
775 c
->prompt_string
= xstrdup(msg
);
777 *c
->prompt_buffer
= '\0';
780 c
->prompt_hindex
= 0;
782 c
->flags
|= CLIENT_STATUS
;
785 /* Draw client prompt on status line of present else on last line. */
787 status_prompt_redraw(struct client
*c
)
789 struct screen_write_ctx ctx
;
790 struct session
*s
= c
->session
;
791 struct screen old_status
;
792 size_t i
, size
, left
, len
, off
;
793 struct grid_cell gc
, *gcp
;
796 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
798 memcpy(&old_status
, &c
->status
, sizeof old_status
);
799 screen_init(&c
->status
, c
->tty
.sx
, 1, 0);
801 utf8flag
= options_get_number(&s
->options
, "status-utf8");
803 len
= screen_write_strlen(utf8flag
, "%s", c
->prompt_string
);
808 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
809 colour_set_fg(&gc
, options_get_number(&s
->options
, "message-fg"));
810 colour_set_bg(&gc
, options_get_number(&s
->options
, "message-bg"));
811 gc
.attr
|= options_get_number(&s
->options
, "message-attr");
813 screen_write_start(&ctx
, NULL
, &c
->status
);
815 screen_write_cursormove(&ctx
, 0, 0);
816 screen_write_nputs(&ctx
, len
, &gc
, utf8flag
, "%s", c
->prompt_string
);
818 left
= c
->tty
.sx
- len
;
820 size
= screen_write_strlen(utf8flag
, "%s", c
->prompt_buffer
);
821 if (c
->prompt_index
>= left
) {
822 off
= c
->prompt_index
- left
+ 1;
823 if (c
->prompt_index
== size
)
828 &ctx
, left
, &gc
, utf8flag
, "%s", c
->prompt_buffer
+ off
);
830 for (i
= len
+ size
; i
< c
->tty
.sx
; i
++)
831 screen_write_putc(&ctx
, &gc
, ' ');
834 screen_write_stop(&ctx
);
836 /* Apply fake cursor. */
837 off
= len
+ c
->prompt_index
- off
;
838 gcp
= grid_view_get_cell(c
->status
.grid
, off
, 0);
839 gcp
->attr
^= GRID_ATTR_REVERSE
;
841 if (grid_compare(c
->status
.grid
, old_status
.grid
) == 0) {
842 screen_free(&old_status
);
845 screen_free(&old_status
);
849 /* Handle keys in prompt. */
851 status_prompt_key(struct client
*c
, int key
)
853 struct paste_buffer
*pb
;
854 char *s
, *first
, *last
, word
[64], swapc
;
857 size_t size
, n
, off
, idx
;
859 size
= strlen(c
->prompt_buffer
);
860 switch (mode_key_lookup(&c
->prompt_mdata
, key
)) {
861 case MODEKEYEDIT_CURSORLEFT
:
862 if (c
->prompt_index
> 0) {
864 c
->flags
|= CLIENT_STATUS
;
867 case MODEKEYEDIT_SWITCHMODEAPPEND
:
868 case MODEKEYEDIT_CURSORRIGHT
:
869 if (c
->prompt_index
< size
) {
871 c
->flags
|= CLIENT_STATUS
;
874 case MODEKEYEDIT_STARTOFLINE
:
875 if (c
->prompt_index
!= 0) {
877 c
->flags
|= CLIENT_STATUS
;
880 case MODEKEYEDIT_ENDOFLINE
:
881 if (c
->prompt_index
!= size
) {
882 c
->prompt_index
= size
;
883 c
->flags
|= CLIENT_STATUS
;
886 case MODEKEYEDIT_COMPLETE
:
887 if (*c
->prompt_buffer
== '\0')
890 idx
= c
->prompt_index
;
894 /* Find the word we are in. */
895 first
= c
->prompt_buffer
+ idx
;
896 while (first
> c
->prompt_buffer
&& *first
!= ' ')
898 while (*first
== ' ')
900 last
= c
->prompt_buffer
+ idx
;
901 while (*last
!= '\0' && *last
!= ' ')
908 ((size_t) (last
- first
)) > (sizeof word
) - 1)
910 memcpy(word
, first
, last
- first
);
911 word
[last
- first
] = '\0';
913 /* And try to complete it. */
914 if ((s
= status_prompt_complete(word
)) == NULL
)
918 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
919 memmove(first
, last
, n
);
920 size
-= last
- first
;
922 /* Insert the new word. */
924 off
= first
- c
->prompt_buffer
;
925 c
->prompt_buffer
= xrealloc(c
->prompt_buffer
, 1, size
+ 1);
926 first
= c
->prompt_buffer
+ off
;
927 memmove(first
+ strlen(s
), first
, n
);
928 memcpy(first
, s
, strlen(s
));
930 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
933 c
->flags
|= CLIENT_STATUS
;
935 case MODEKEYEDIT_BACKSPACE
:
936 if (c
->prompt_index
!= 0) {
937 if (c
->prompt_index
== size
)
938 c
->prompt_buffer
[--c
->prompt_index
] = '\0';
940 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
941 c
->prompt_buffer
+ c
->prompt_index
,
942 size
+ 1 - c
->prompt_index
);
945 c
->flags
|= CLIENT_STATUS
;
948 case MODEKEYEDIT_DELETE
:
949 if (c
->prompt_index
!= size
) {
950 memmove(c
->prompt_buffer
+ c
->prompt_index
,
951 c
->prompt_buffer
+ c
->prompt_index
+ 1,
952 size
+ 1 - c
->prompt_index
);
953 c
->flags
|= CLIENT_STATUS
;
956 case MODEKEYEDIT_DELETELINE
:
957 *c
->prompt_buffer
= '\0';
959 c
->flags
|= CLIENT_STATUS
;
961 case MODEKEYEDIT_DELETETOENDOFLINE
:
962 if (c
->prompt_index
< size
) {
963 c
->prompt_buffer
[c
->prompt_index
] = '\0';
964 c
->flags
|= CLIENT_STATUS
;
967 case MODEKEYEDIT_HISTORYUP
:
968 histstr
= status_prompt_up_history(&c
->prompt_hindex
);
971 xfree(c
->prompt_buffer
);
972 c
->prompt_buffer
= xstrdup(histstr
);
973 c
->prompt_index
= strlen(c
->prompt_buffer
);
974 c
->flags
|= CLIENT_STATUS
;
976 case MODEKEYEDIT_HISTORYDOWN
:
977 histstr
= status_prompt_down_history(&c
->prompt_hindex
);
980 xfree(c
->prompt_buffer
);
981 c
->prompt_buffer
= xstrdup(histstr
);
982 c
->prompt_index
= strlen(c
->prompt_buffer
);
983 c
->flags
|= CLIENT_STATUS
;
985 case MODEKEYEDIT_PASTE
:
986 if ((pb
= paste_get_top(&global_buffers
)) == NULL
)
988 for (n
= 0; n
< pb
->size
; n
++) {
989 ch
= (u_char
) pb
->data
[n
];
990 if (ch
< 32 || ch
== 127)
994 c
->prompt_buffer
= xrealloc(c
->prompt_buffer
, 1, size
+ n
+ 1);
995 if (c
->prompt_index
== size
) {
996 memcpy(c
->prompt_buffer
+ c
->prompt_index
, pb
->data
, n
);
997 c
->prompt_index
+= n
;
998 c
->prompt_buffer
[c
->prompt_index
] = '\0';
1000 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
1001 c
->prompt_buffer
+ c
->prompt_index
,
1002 size
+ 1 - c
->prompt_index
);
1003 memcpy(c
->prompt_buffer
+ c
->prompt_index
, pb
->data
, n
);
1004 c
->prompt_index
+= n
;
1007 c
->flags
|= CLIENT_STATUS
;
1009 case MODEKEYEDIT_TRANSPOSECHARS
:
1010 idx
= c
->prompt_index
;
1014 swapc
= c
->prompt_buffer
[idx
- 2];
1015 c
->prompt_buffer
[idx
- 2] = c
->prompt_buffer
[idx
- 1];
1016 c
->prompt_buffer
[idx
- 1] = swapc
;
1017 c
->prompt_index
= idx
;
1018 c
->flags
|= CLIENT_STATUS
;
1021 case MODEKEYEDIT_ENTER
:
1022 if (*c
->prompt_buffer
!= '\0')
1023 status_prompt_add_history(c
->prompt_buffer
);
1024 if (c
->prompt_callbackfn(c
->prompt_data
, c
->prompt_buffer
) == 0)
1025 status_prompt_clear(c
);
1027 case MODEKEYEDIT_CANCEL
:
1028 if (c
->prompt_callbackfn(c
->prompt_data
, NULL
) == 0)
1029 status_prompt_clear(c
);
1032 if ((key
& 0xff00) != 0 || key
< 32 || key
== 127)
1034 c
->prompt_buffer
= xrealloc(c
->prompt_buffer
, 1, size
+ 2);
1036 if (c
->prompt_index
== size
) {
1037 c
->prompt_buffer
[c
->prompt_index
++] = key
;
1038 c
->prompt_buffer
[c
->prompt_index
] = '\0';
1040 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1041 c
->prompt_buffer
+ c
->prompt_index
,
1042 size
+ 1 - c
->prompt_index
);
1043 c
->prompt_buffer
[c
->prompt_index
++] = key
;
1046 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1047 if (c
->prompt_callbackfn(
1048 c
->prompt_data
, c
->prompt_buffer
) == 0)
1049 status_prompt_clear(c
);
1052 c
->flags
|= CLIENT_STATUS
;
1059 /* Get previous line from the history. */
1061 status_prompt_up_history(u_int
*idx
)
1066 * History runs from 0 to size - 1.
1068 * Index is from 0 to size. Zero is empty.
1071 size
= ARRAY_LENGTH(&status_prompt_history
);
1072 if (size
== 0 || *idx
== size
)
1075 return (ARRAY_ITEM(&status_prompt_history
, size
- *idx
));
1078 /* Get next line from the history. */
1080 status_prompt_down_history(u_int
*idx
)
1084 size
= ARRAY_LENGTH(&status_prompt_history
);
1085 if (size
== 0 || *idx
== 0)
1090 return (ARRAY_ITEM(&status_prompt_history
, size
- *idx
));
1093 /* Add line to the history. */
1095 status_prompt_add_history(const char *line
)
1099 size
= ARRAY_LENGTH(&status_prompt_history
);
1100 if (size
> 0 && strcmp(ARRAY_LAST(&status_prompt_history
), line
) == 0)
1103 if (size
== PROMPT_HISTORY
) {
1104 xfree(ARRAY_FIRST(&status_prompt_history
));
1105 ARRAY_REMOVE(&status_prompt_history
, 0);
1108 ARRAY_ADD(&status_prompt_history
, xstrdup(line
));
1111 /* Complete word. */
1113 status_prompt_complete(const char *s
)
1115 const struct cmd_entry
**cmdent
;
1116 const struct options_table_entry
*oe
;
1117 ARRAY_DECL(, const char *) list
;
1125 /* First, build a list of all the possible matches. */
1127 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1128 if (strncmp((*cmdent
)->name
, s
, strlen(s
)) == 0)
1129 ARRAY_ADD(&list
, (*cmdent
)->name
);
1131 for (oe
= server_options_table
; oe
->name
!= NULL
; oe
++) {
1132 if (strncmp(oe
->name
, s
, strlen(s
)) == 0)
1133 ARRAY_ADD(&list
, oe
->name
);
1135 for (oe
= session_options_table
; oe
->name
!= NULL
; oe
++) {
1136 if (strncmp(oe
->name
, s
, strlen(s
)) == 0)
1137 ARRAY_ADD(&list
, oe
->name
);
1139 for (oe
= window_options_table
; oe
->name
!= NULL
; oe
++) {
1140 if (strncmp(oe
->name
, s
, strlen(s
)) == 0)
1141 ARRAY_ADD(&list
, oe
->name
);
1144 /* If none, bail now. */
1145 if (ARRAY_LENGTH(&list
) == 0) {
1150 /* If an exact match, return it, with a trailing space. */
1151 if (ARRAY_LENGTH(&list
) == 1) {
1152 xasprintf(&s2
, "%s ", ARRAY_FIRST(&list
));
1157 /* Now loop through the list and find the longest common prefix. */
1158 prefix
= xstrdup(ARRAY_FIRST(&list
));
1159 for (i
= 1; i
< ARRAY_LENGTH(&list
); i
++) {
1160 s
= ARRAY_ITEM(&list
, i
);
1163 if (j
> strlen(prefix
))
1165 for (; j
> 0; j
--) {
1166 if (prefix
[j
- 1] != s
[j
- 1])
1167 prefix
[j
- 1] = '\0';