Annotation of src/usr.bin/tmux/status.c, Revision 1.12
1.12 ! nicm 1: /* $OpenBSD: status.c,v 1.11 2009/07/16 23:25:21 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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:
32: char *status_replace(struct session *, char *, time_t);
33: char *status_replace_popen(char **);
34: size_t status_width(struct winlink *);
35: char *status_print(struct session *, struct winlink *, struct grid_cell *);
36:
37: void status_prompt_add_history(struct client *);
38: char *status_prompt_complete(const char *);
39:
40: /* Draw status for client on the last lines of given context. */
41: int
42: status_redraw(struct client *c)
43: {
44: struct screen_write_ctx ctx;
45: struct session *s = c->session;
46: struct winlink *wl;
1.7 nicm 47: struct screen old_status;
1.1 nicm 48: char *left, *right, *text, *ptr;
1.2 nicm 49: size_t llen, llen2, rlen, rlen2, offset;
1.7 nicm 50: size_t xx, yy, size, start, width;
1.1 nicm 51: struct grid_cell stdgc, gc;
1.3 nicm 52: int larrow, rarrow, utf8flag;
1.1 nicm 53:
54: left = right = NULL;
55:
1.7 nicm 56: /* No status line?*/
57: if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
58: return (1);
59: larrow = rarrow = 0;
1.1 nicm 60:
1.7 nicm 61: /* Create the target screen. */
62: memcpy(&old_status, &c->status, sizeof old_status);
63: screen_init(&c->status, c->tty.sx, 1, 0);
1.1 nicm 64:
65: if (gettimeofday(&c->status_timer, NULL) != 0)
66: fatal("gettimeofday");
67: memcpy(&stdgc, &grid_default_cell, sizeof gc);
68: stdgc.bg = options_get_number(&s->options, "status-fg");
69: stdgc.fg = options_get_number(&s->options, "status-bg");
70: stdgc.attr |= options_get_number(&s->options, "status-attr");
71:
72: yy = c->tty.sy - 1;
73: if (yy == 0)
74: goto blank;
75:
1.3 nicm 76: /* Caring about UTF-8 in status line? */
77: utf8flag = options_get_number(&s->options, "status-utf8");
78:
1.1 nicm 79: /* Work out the left and right strings. */
80: left = status_replace(s, options_get_string(
81: &s->options, "status-left"), c->status_timer.tv_sec);
82: llen = options_get_number(&s->options, "status-left-length");
1.3 nicm 83: llen2 = screen_write_strlen(utf8flag, "%s", left);
1.2 nicm 84: if (llen2 < llen)
85: llen = llen2;
1.1 nicm 86:
87: right = status_replace(s, options_get_string(
88: &s->options, "status-right"), c->status_timer.tv_sec);
89: rlen = options_get_number(&s->options, "status-right-length");
1.3 nicm 90: rlen2 = screen_write_strlen(utf8flag, "%s", right);
1.2 nicm 91: if (rlen2 < rlen)
92: rlen = rlen2;
1.1 nicm 93:
94: /*
95: * Figure out how much space we have for the window list. If there isn't
96: * enough space, just wimp out.
97: */
98: xx = 0;
99: if (llen != 0)
100: xx += llen + 1;
101: if (rlen != 0)
102: xx += rlen + 1;
103: if (c->tty.sx == 0 || c->tty.sx <= xx)
104: goto blank;
105: xx = c->tty.sx - xx;
106:
107: /*
108: * Right. We have xx characters to fill. Find out how much is to go in
109: * them and the offset of the current window (it must be on screen).
110: */
111: width = offset = 0;
112: RB_FOREACH(wl, winlinks, &s->windows) {
113: size = status_width(wl) + 1;
114: if (wl == s->curw)
115: offset = width;
116: width += size;
117: }
118: start = 0;
119:
120: /* If there is enough space for the total width, all is gravy. */
121: if (width <= xx)
122: goto draw;
123:
124: /* Find size of current window text. */
125: size = status_width(s->curw);
126:
127: /*
128: * If the offset is already on screen, we're good to draw from the
129: * start and just leave off the end.
130: */
131: if (offset + size < xx) {
132: if (xx > 0) {
133: rarrow = 1;
134: xx--;
135: }
136:
137: width = xx;
138: goto draw;
139: }
140:
141: /*
142: * Work out how many characters we need to omit from the start. There
143: * are xx characters to fill, and offset + size must be the last. So,
144: * the start character is offset + size - xx.
145: */
146: if (xx > 0) {
147: larrow = 1;
148: xx--;
149: }
150:
151: start = offset + size - xx;
152: if (xx > 0 && width > start + xx + 1) { /* + 1, eh? */
153: rarrow = 1;
154: start++;
155: xx--;
156: }
157: width = xx;
158:
159: draw:
160: /* Bail here if anything is too small too. XXX. */
161: if (width == 0 || xx == 0)
162: goto blank;
163:
164: /* Begin drawing and move to the starting position. */
165: screen_write_start(&ctx, NULL, &c->status);
166: if (llen != 0) {
167: screen_write_cursormove(&ctx, 0, yy);
1.5 nicm 168: screen_write_nputs(&ctx, llen, &stdgc, utf8flag, "%s", left);
169: screen_write_putc(&ctx, &stdgc, ' ');
1.1 nicm 170: if (larrow)
171: screen_write_putc(&ctx, &stdgc, ' ');
172: } else {
173: if (larrow)
174: screen_write_cursormove(&ctx, 1, yy);
175: else
176: screen_write_cursormove(&ctx, 0, yy);
177: }
178:
179: /* Draw each character in succession. */
180: offset = 0;
181: RB_FOREACH(wl, winlinks, &s->windows) {
182: memcpy(&gc, &stdgc, sizeof gc);
183: text = status_print(s, wl, &gc);
184:
185: if (larrow == 1 && offset < start) {
186: if (session_alert_has(s, wl, WINDOW_ACTIVITY))
187: larrow = -1;
188: else if (session_alert_has(s, wl, WINDOW_BELL))
189: larrow = -1;
190: else if (session_alert_has(s, wl, WINDOW_CONTENT))
191: larrow = -1;
192: }
193:
194: for (ptr = text; *ptr != '\0'; ptr++) {
195: if (offset >= start && offset < start + width)
196: screen_write_putc(&ctx, &gc, *ptr);
197: offset++;
198: }
199:
200: if (rarrow == 1 && offset > start + width) {
201: if (session_alert_has(s, wl, WINDOW_ACTIVITY))
202: rarrow = -1;
203: else if (session_alert_has(s, wl, WINDOW_BELL))
204: rarrow = -1;
205: else if (session_alert_has(s, wl, WINDOW_CONTENT))
206: rarrow = -1;
207: }
208:
209: if (offset < start + width) {
210: if (offset >= start) {
211: screen_write_putc(&ctx, &stdgc, ' ');
212: }
213: offset++;
214: }
215:
216: xfree(text);
217: }
218:
219: /* Fill the remaining space if any. */
220: while (offset++ < xx)
221: screen_write_putc(&ctx, &stdgc, ' ');
222:
223: /* Draw the last item. */
224: if (rlen != 0) {
225: screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, yy);
1.5 nicm 226: screen_write_putc(&ctx, &stdgc, ' ');
227: screen_write_nputs(&ctx, rlen, &stdgc, utf8flag, "%s", right);
1.1 nicm 228: }
229:
230: /* Draw the arrows. */
231: if (larrow != 0) {
232: memcpy(&gc, &stdgc, sizeof gc);
233: if (larrow == -1)
234: gc.attr ^= GRID_ATTR_REVERSE;
235: if (llen != 0)
236: screen_write_cursormove(&ctx, llen + 1, yy);
237: else
238: screen_write_cursormove(&ctx, 0, yy);
239: screen_write_putc(&ctx, &gc, '<');
240: }
241: if (rarrow != 0) {
242: memcpy(&gc, &stdgc, sizeof gc);
243: if (rarrow == -1)
244: gc.attr ^= GRID_ATTR_REVERSE;
245: if (rlen != 0)
246: screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, yy);
247: else
248: screen_write_cursormove(&ctx, c->tty.sx - 1, yy);
249: screen_write_putc(&ctx, &gc, '>');
250: }
251:
252: goto out;
253:
254: blank:
255: /* Just draw the whole line as blank. */
256: screen_write_start(&ctx, NULL, &c->status);
257: screen_write_cursormove(&ctx, 0, yy);
258: for (offset = 0; offset < c->tty.sx; offset++)
259: screen_write_putc(&ctx, &stdgc, ' ');
260:
261: out:
262: screen_write_stop(&ctx);
263:
264: if (left != NULL)
265: xfree(left);
266: if (right != NULL)
267: xfree(right);
268:
269: if (grid_compare(c->status.grid, old_status.grid) == 0) {
270: screen_free(&old_status);
271: return (0);
272: }
273: screen_free(&old_status);
274: return (1);
275: }
276:
277: char *
278: status_replace(struct session *s, char *fmt, time_t t)
279: {
280: struct winlink *wl = s->curw;
281: static char out[BUFSIZ];
282: char in[BUFSIZ], tmp[256], ch, *iptr, *optr, *ptr, *endptr;
283: char *savedptr;
284: size_t len;
285: long n;
286:
287: strftime(in, sizeof in, fmt, localtime(&t));
288: in[(sizeof in) - 1] = '\0';
289:
290: iptr = in;
291: optr = out;
292: savedptr = NULL;
293:
294: while (*iptr != '\0') {
295: if (optr >= out + (sizeof out) - 1)
296: break;
297: switch (ch = *iptr++) {
298: case '#':
299: errno = 0;
300: n = strtol(iptr, &endptr, 10);
301: if ((n == 0 && errno != EINVAL) ||
302: (n == LONG_MIN && errno != ERANGE) ||
303: (n == LONG_MAX && errno != ERANGE) ||
304: n != 0)
305: iptr = endptr;
306: if (n <= 0)
307: n = LONG_MAX;
308:
309: ptr = NULL;
310: switch (*iptr++) {
311: case '(':
312: if (ptr == NULL) {
313: ptr = status_replace_popen(&iptr);
314: if (ptr == NULL)
315: break;
316: savedptr = ptr;
317: }
318: /* FALLTHROUGH */
319: case 'H':
320: if (ptr == NULL) {
321: if (gethostname(tmp, sizeof tmp) != 0)
322: fatal("gethostname");
323: ptr = tmp;
324: }
325: /* FALLTHROUGH */
326: case 'S':
327: if (ptr == NULL)
328: ptr = s->name;
329: /* FALLTHROUGH */
330: case 'T':
331: if (ptr == NULL)
332: ptr = wl->window->active->base.title;
333: len = strlen(ptr);
334: if ((size_t) n < len)
335: len = n;
336: if (optr + len >= out + (sizeof out) - 1)
337: break;
338: while (len > 0 && *ptr != '\0') {
339: *optr++ = *ptr++;
340: len--;
341: }
342: break;
343: case '#':
344: *optr++ = '#';
345: break;
346: }
347: if (savedptr != NULL) {
348: xfree(savedptr);
349: savedptr = NULL;
350: }
351: break;
352: default:
353: *optr++ = ch;
354: break;
355: }
356: }
357: *optr = '\0';
358:
359: return (xstrdup(out));
360: }
361:
362: char *
363: status_replace_popen(char **iptr)
364: {
365: FILE *f;
366: char *buf, *cmd, *ptr;
367: int lastesc;
368: size_t len;
369:
370: if (**iptr == '\0')
371: return (NULL);
372: if (**iptr == ')') { /* no command given */
373: (*iptr)++;
374: return (NULL);
375: }
376:
377: buf = NULL;
378:
379: cmd = xmalloc(strlen(*iptr) + 1);
380: len = 0;
381:
382: lastesc = 0;
383: for (; **iptr != '\0'; (*iptr)++) {
384: if (!lastesc && **iptr == ')')
385: break; /* unescaped ) is the end */
386: if (!lastesc && **iptr == '\\') {
387: lastesc = 1;
388: continue; /* skip \ if not escaped */
389: }
390: lastesc = 0;
391: cmd[len++] = **iptr;
392: }
393: if (**iptr == '\0') /* no terminating ) */
394: goto out;
395: (*iptr)++; /* skip final ) */
396: cmd[len] = '\0';
397:
398: if ((f = popen(cmd, "r")) == NULL)
399: goto out;
400:
401: if ((buf = fgetln(f, &len)) == NULL) {
402: pclose(f);
403: goto out;
404: }
405: if (buf[len - 1] == '\n') {
406: buf[len - 1] = '\0';
407: buf = xstrdup(buf);
408: } else {
409: ptr = xmalloc(len + 1);
410: memcpy(ptr, buf, len);
411: ptr[len] = '\0';
412: buf = ptr;
413: }
414: pclose(f);
415:
416: out:
417: xfree(cmd);
418: return (buf);
419: }
420:
421: size_t
422: status_width(struct winlink *wl)
423: {
424: return (xsnprintf(NULL, 0, "%d:%s ", wl->idx, wl->window->name));
425: }
426:
427: char *
428: status_print(struct session *s, struct winlink *wl, struct grid_cell *gc)
429: {
430: char *text, flag;
431: u_char fg, bg, attr;
432:
433: fg = options_get_number(&wl->window->options, "window-status-fg");
434: if (fg != 8)
435: gc->fg = fg;
436: bg = options_get_number(&wl->window->options, "window-status-bg");
437: if (bg != 8)
438: gc->bg = bg;
439: attr = options_get_number(&wl->window->options, "window-status-attr");
440: if (attr != 0)
441: gc->attr = attr;
442:
443: flag = ' ';
444: if (wl == SLIST_FIRST(&s->lastw))
445: flag = '-';
446: if (wl == s->curw)
447: flag = '*';
448:
449: if (session_alert_has(s, wl, WINDOW_ACTIVITY)) {
450: flag = '#';
451: gc->attr ^= GRID_ATTR_REVERSE;
452: } else if (session_alert_has(s, wl, WINDOW_BELL)) {
453: flag = '!';
454: gc->attr ^= GRID_ATTR_REVERSE;
455: } else if (session_alert_has(s, wl, WINDOW_CONTENT)) {
456: flag = '+';
457: gc->attr ^= GRID_ATTR_REVERSE;
458: }
459:
460: xasprintf(&text, "%d:%s%c", wl->idx, wl->window->name, flag);
461: return (text);
462: }
463:
1.10 nicm 464: void printflike2
465: status_message_set(struct client *c, const char *fmt, ...)
1.1 nicm 466: {
467: struct timeval tv;
1.10 nicm 468: va_list ap;
1.1 nicm 469: int delay;
470:
1.12 ! nicm 471: status_prompt_clear(c);
! 472: status_message_clear(c);
! 473:
1.1 nicm 474: delay = options_get_number(&c->session->options, "display-time");
475: tv.tv_sec = delay / 1000;
476: tv.tv_usec = (delay % 1000) * 1000L;
477:
1.10 nicm 478: va_start(ap, fmt);
479: xvasprintf(&c->message_string, fmt, ap);
480: va_end(ap);
1.1 nicm 481: if (gettimeofday(&c->message_timer, NULL) != 0)
482: fatal("gettimeofday");
483: timeradd(&c->message_timer, &tv, &c->message_timer);
484:
485: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
486: c->flags |= CLIENT_STATUS;
487: }
488:
489: void
490: status_message_clear(struct client *c)
491: {
492: if (c->message_string == NULL)
493: return;
494:
495: xfree(c->message_string);
496: c->message_string = NULL;
497:
498: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.12 ! nicm 499: c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
1.7 nicm 500:
501: screen_reinit(&c->status);
1.1 nicm 502: }
503:
504: /* Draw client message on status line of present else on last line. */
505: int
506: status_message_redraw(struct client *c)
507: {
508: struct screen_write_ctx ctx;
509: struct session *s = c->session;
510: struct screen old_status;
511: size_t len;
512: struct grid_cell gc;
513:
514: if (c->tty.sx == 0 || c->tty.sy == 0)
515: return (0);
516: memcpy(&old_status, &c->status, sizeof old_status);
517: screen_init(&c->status, c->tty.sx, 1, 0);
518:
519: len = strlen(c->message_string);
520: if (len > c->tty.sx)
521: len = c->tty.sx;
522:
523: memcpy(&gc, &grid_default_cell, sizeof gc);
524: gc.bg = options_get_number(&s->options, "message-fg");
525: gc.fg = options_get_number(&s->options, "message-bg");
526: gc.attr |= options_get_number(&s->options, "message-attr");
527:
528: screen_write_start(&ctx, NULL, &c->status);
529:
530: screen_write_cursormove(&ctx, 0, 0);
531: screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->message_string);
532: for (; len < c->tty.sx; len++)
533: screen_write_putc(&ctx, &gc, ' ');
534:
535: screen_write_stop(&ctx);
536:
537: if (grid_compare(c->status.grid, old_status.grid) == 0) {
538: screen_free(&old_status);
539: return (0);
540: }
541: screen_free(&old_status);
542: return (1);
543: }
544:
545: void
1.12 ! nicm 546: status_prompt_set(struct client *c, const char *msg,
! 547: int (*callbackfn)(void *, const char *), void (*freefn)(void *),
! 548: void *data, int flags)
1.1 nicm 549: {
1.12 ! nicm 550: status_message_clear(c);
! 551: status_prompt_clear(c);
! 552:
1.1 nicm 553: c->prompt_string = xstrdup(msg);
554:
555: c->prompt_buffer = xstrdup("");
556: c->prompt_index = 0;
557:
1.12 ! nicm 558: c->prompt_callbackfn = callbackfn;
! 559: c->prompt_freefn = freefn;
1.1 nicm 560: c->prompt_data = data;
561:
562: c->prompt_hindex = 0;
563:
564: c->prompt_flags = flags;
565:
566: mode_key_init(&c->prompt_mdata,
567: options_get_number(&c->session->options, "status-keys"),
568: MODEKEY_CANEDIT);
569:
570: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
571: c->flags |= CLIENT_STATUS;
572: }
573:
574: void
575: status_prompt_clear(struct client *c)
576: {
1.12 ! nicm 577: if (c->prompt_string == NULL)
1.1 nicm 578: return;
579:
1.12 ! nicm 580: if (c->prompt_freefn != NULL && c->prompt_data != NULL)
! 581: c->prompt_freefn(c->prompt_data);
! 582:
1.1 nicm 583: mode_key_free(&c->prompt_mdata);
584:
585: xfree(c->prompt_string);
586: c->prompt_string = NULL;
587:
1.4 nicm 588: if (c->prompt_flags & PROMPT_HIDDEN)
589: memset(c->prompt_buffer, 0, strlen(c->prompt_buffer));
1.1 nicm 590: xfree(c->prompt_buffer);
591: c->prompt_buffer = NULL;
592:
593: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.12 ! nicm 594: c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
1.7 nicm 595:
596: screen_reinit(&c->status);
1.1 nicm 597: }
598:
599: /* Draw client prompt on status line of present else on last line. */
600: int
601: status_prompt_redraw(struct client *c)
602: {
603: struct screen_write_ctx ctx;
604: struct session *s = c->session;
605: struct screen old_status;
606: size_t i, size, left, len, offset, n;
607: char ch;
608: struct grid_cell gc;
609:
610: if (c->tty.sx == 0 || c->tty.sy == 0)
611: return (0);
612: memcpy(&old_status, &c->status, sizeof old_status);
613: screen_init(&c->status, c->tty.sx, 1, 0);
614: offset = 0;
615:
616: len = strlen(c->prompt_string);
617: if (len > c->tty.sx)
618: len = c->tty.sx;
619:
620: memcpy(&gc, &grid_default_cell, sizeof gc);
621: gc.bg = options_get_number(&s->options, "message-fg");
622: gc.fg = options_get_number(&s->options, "message-bg");
623: gc.attr |= options_get_number(&s->options, "message-attr");
624:
625: screen_write_start(&ctx, NULL, &c->status);
626:
627: screen_write_cursormove(&ctx, 0, 0);
628: screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->prompt_string);
629:
630: left = c->tty.sx - len;
631: if (left != 0) {
632: if (c->prompt_index < left)
633: size = strlen(c->prompt_buffer);
634: else {
635: offset = c->prompt_index - left - 1;
636: if (c->prompt_index == strlen(c->prompt_buffer))
637: left--;
638: size = left;
639: }
640: if (c->prompt_flags & PROMPT_HIDDEN) {
641: n = strlen(c->prompt_buffer);
642: if (n > left)
643: n = left;
644: for (i = 0; i < n; i++)
645: screen_write_putc(&ctx, &gc, '*');
646: } else {
647: screen_write_puts(&ctx, &gc,
648: "%.*s", (int) left, c->prompt_buffer + offset);
649: }
650:
651: for (i = len + size; i < c->tty.sx; i++)
652: screen_write_putc(&ctx, &gc, ' ');
653: }
654:
655: /* Draw a fake cursor. */
656: screen_write_cursormove(&ctx, len + c->prompt_index - offset, 0);
657: if (c->prompt_index == strlen(c->prompt_buffer))
658: ch = ' ';
659: else {
660: if (c->prompt_flags & PROMPT_HIDDEN)
661: ch = '*';
662: else
663: ch = c->prompt_buffer[c->prompt_index];
664: }
665: if (ch == '\0')
666: ch = ' ';
667: gc.attr ^= GRID_ATTR_REVERSE;
668: screen_write_putc(&ctx, &gc, ch);
669:
670: screen_write_stop(&ctx);
671:
672: if (grid_compare(c->status.grid, old_status.grid) == 0) {
673: screen_free(&old_status);
674: return (0);
675: }
676: screen_free(&old_status);
677: return (1);
678: }
679:
680: /* Handle keys in prompt. */
681: void
682: status_prompt_key(struct client *c, int key)
683: {
684: struct paste_buffer *pb;
685: char *s, *first, *last, word[64];
686: size_t size, n, off, idx;
687:
688: size = strlen(c->prompt_buffer);
689: switch (mode_key_lookup(&c->prompt_mdata, key)) {
690: case MODEKEYCMD_LEFT:
691: if (c->prompt_index > 0) {
692: c->prompt_index--;
693: c->flags |= CLIENT_STATUS;
694: }
695: break;
696: case MODEKEYCMD_RIGHT:
697: if (c->prompt_index < size) {
698: c->prompt_index++;
699: c->flags |= CLIENT_STATUS;
700: }
701: break;
702: case MODEKEYCMD_STARTOFLINE:
1.6 nicm 703: case MODEKEYCMD_BACKTOINDENTATION:
1.1 nicm 704: if (c->prompt_index != 0) {
705: c->prompt_index = 0;
706: c->flags |= CLIENT_STATUS;
707: }
708: break;
709: case MODEKEYCMD_ENDOFLINE:
710: if (c->prompt_index != size) {
711: c->prompt_index = size;
712: c->flags |= CLIENT_STATUS;
713: }
714: break;
715: case MODEKEYCMD_COMPLETE:
716: if (*c->prompt_buffer == '\0')
717: break;
718:
719: idx = c->prompt_index;
720: if (idx != 0)
721: idx--;
722:
723: /* Find the word we are in. */
724: first = c->prompt_buffer + idx;
725: while (first > c->prompt_buffer && *first != ' ')
726: first--;
727: while (*first == ' ')
728: first++;
729: last = c->prompt_buffer + idx;
730: while (*last != '\0' && *last != ' ')
731: last++;
732: while (*last == ' ')
733: last--;
734: if (*last != '\0')
735: last++;
736: if (last <= first ||
737: ((size_t) (last - first)) > (sizeof word) - 1)
738: break;
739: memcpy(word, first, last - first);
740: word[last - first] = '\0';
741:
742: /* And try to complete it. */
743: if ((s = status_prompt_complete(word)) == NULL)
744: break;
745:
746: /* Trim out word. */
747: n = size - (last - c->prompt_buffer) + 1; /* with \0 */
748: memmove(first, last, n);
749: size -= last - first;
750:
751: /* Insert the new word. */
752: size += strlen(s);
753: off = first - c->prompt_buffer;
754: c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1);
755: first = c->prompt_buffer + off;
756: memmove(first + strlen(s), first, n);
757: memcpy(first, s, strlen(s));
758:
759: c->prompt_index = (first - c->prompt_buffer) + strlen(s);
760:
761: c->flags |= CLIENT_STATUS;
762: break;
763: case MODEKEYCMD_BACKSPACE:
764: if (c->prompt_index != 0) {
765: if (c->prompt_index == size)
766: c->prompt_buffer[--c->prompt_index] = '\0';
767: else {
768: memmove(c->prompt_buffer + c->prompt_index - 1,
769: c->prompt_buffer + c->prompt_index,
770: size + 1 - c->prompt_index);
771: c->prompt_index--;
772: }
773: c->flags |= CLIENT_STATUS;
774: }
775: break;
776: case MODEKEYCMD_DELETE:
777: if (c->prompt_index != size) {
778: memmove(c->prompt_buffer + c->prompt_index,
779: c->prompt_buffer + c->prompt_index + 1,
780: size + 1 - c->prompt_index);
781: c->flags |= CLIENT_STATUS;
782: }
783: break;
784: case MODEKEYCMD_UP:
785: if (server_locked)
786: break;
787:
788: if (ARRAY_LENGTH(&c->prompt_hdata) == 0)
789: break;
1.4 nicm 790: if (c->prompt_flags & PROMPT_HIDDEN)
791: memset(c->prompt_buffer, 0, strlen(c->prompt_buffer));
1.1 nicm 792: xfree(c->prompt_buffer);
793:
794: c->prompt_buffer = xstrdup(ARRAY_ITEM(&c->prompt_hdata,
795: ARRAY_LENGTH(&c->prompt_hdata) - 1 - c->prompt_hindex));
796: if (c->prompt_hindex != ARRAY_LENGTH(&c->prompt_hdata) - 1)
797: c->prompt_hindex++;
798:
799: c->prompt_index = strlen(c->prompt_buffer);
800: c->flags |= CLIENT_STATUS;
801: break;
802: case MODEKEYCMD_DOWN:
803: if (server_locked)
804: break;
805:
1.4 nicm 806: if (c->prompt_flags & PROMPT_HIDDEN)
807: memset(c->prompt_buffer, 0, strlen(c->prompt_buffer));
1.1 nicm 808: xfree(c->prompt_buffer);
809:
810: if (c->prompt_hindex != 0) {
811: c->prompt_hindex--;
812: c->prompt_buffer = xstrdup(ARRAY_ITEM(
813: &c->prompt_hdata, ARRAY_LENGTH(
814: &c->prompt_hdata) - 1 - c->prompt_hindex));
815: } else
816: c->prompt_buffer = xstrdup("");
817:
818: c->prompt_index = strlen(c->prompt_buffer);
819: c->flags |= CLIENT_STATUS;
820: break;
821: case MODEKEYCMD_PASTE:
822: if ((pb = paste_get_top(&c->session->buffers)) == NULL)
823: break;
824: if ((last = strchr(pb->data, '\n')) == NULL)
825: last = strchr(pb->data, '\0');
826: n = last - pb->data;
827:
828: c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1);
829: if (c->prompt_index == size) {
830: memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
831: c->prompt_index += n;
832: c->prompt_buffer[c->prompt_index] = '\0';
833: } else {
834: memmove(c->prompt_buffer + c->prompt_index + n,
835: c->prompt_buffer + c->prompt_index,
836: size + 1 - c->prompt_index);
837: memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
838: c->prompt_index += n;
839: }
840:
841: c->flags |= CLIENT_STATUS;
842: break;
843: case MODEKEYCMD_CHOOSE:
844: if (*c->prompt_buffer != '\0') {
845: status_prompt_add_history(c);
1.12 ! nicm 846: if (c->prompt_callbackfn(
1.1 nicm 847: c->prompt_data, c->prompt_buffer) == 0)
848: status_prompt_clear(c);
849: break;
850: }
851: /* FALLTHROUGH */
852: case MODEKEYCMD_QUIT:
1.12 ! nicm 853: if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
1.1 nicm 854: status_prompt_clear(c);
855: break;
856: case MODEKEYCMD_OTHERKEY:
857: if (key < 32 || key > 126)
858: break;
859: c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2);
860:
861: if (c->prompt_index == size) {
862: c->prompt_buffer[c->prompt_index++] = key;
863: c->prompt_buffer[c->prompt_index] = '\0';
864: } else {
865: memmove(c->prompt_buffer + c->prompt_index + 1,
866: c->prompt_buffer + c->prompt_index,
867: size + 1 - c->prompt_index);
868: c->prompt_buffer[c->prompt_index++] = key;
869: }
870:
871: if (c->prompt_flags & PROMPT_SINGLE) {
1.12 ! nicm 872: if (c->prompt_callbackfn(
1.1 nicm 873: c->prompt_data, c->prompt_buffer) == 0)
874: status_prompt_clear(c);
875: }
876:
877: c->flags |= CLIENT_STATUS;
878: break;
879: default:
880: break;
881: }
882: }
883:
884: /* Add line to the history. */
885: void
886: status_prompt_add_history(struct client *c)
887: {
888: if (server_locked)
889: return;
890:
891: if (ARRAY_LENGTH(&c->prompt_hdata) > 0 &&
892: strcmp(ARRAY_LAST(&c->prompt_hdata), c->prompt_buffer) == 0)
893: return;
894:
895: if (ARRAY_LENGTH(&c->prompt_hdata) == PROMPT_HISTORY) {
896: xfree(ARRAY_FIRST(&c->prompt_hdata));
897: ARRAY_REMOVE(&c->prompt_hdata, 0);
898: }
899:
900: ARRAY_ADD(&c->prompt_hdata, xstrdup(c->prompt_buffer));
901: }
902:
903: /* Complete word. */
904: char *
905: status_prompt_complete(const char *s)
906: {
907: const struct cmd_entry **cmdent;
908: const struct set_option_entry *optent;
909: ARRAY_DECL(, const char *) list;
910: char *prefix, *s2;
1.9 nicm 911: u_int i;
1.1 nicm 912: size_t j;
913:
914: if (*s == '\0')
915: return (NULL);
916:
917: /* First, build a list of all the possible matches. */
918: ARRAY_INIT(&list);
919: for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
920: if (strncmp((*cmdent)->name, s, strlen(s)) == 0)
921: ARRAY_ADD(&list, (*cmdent)->name);
922: }
1.9 nicm 923: for (optent = set_option_table; optent->name != NULL; optent++) {
1.1 nicm 924: if (strncmp(optent->name, s, strlen(s)) == 0)
925: ARRAY_ADD(&list, optent->name);
926: }
1.9 nicm 927: for (optent = set_window_option_table; optent->name != NULL; optent++) {
1.1 nicm 928: if (strncmp(optent->name, s, strlen(s)) == 0)
929: ARRAY_ADD(&list, optent->name);
930: }
931:
932: /* If none, bail now. */
933: if (ARRAY_LENGTH(&list) == 0) {
934: ARRAY_FREE(&list);
935: return (NULL);
936: }
937:
938: /* If an exact match, return it, with a trailing space. */
939: if (ARRAY_LENGTH(&list) == 1) {
940: xasprintf(&s2, "%s ", ARRAY_FIRST(&list));
941: ARRAY_FREE(&list);
942: return (s2);
943: }
944:
945: /* Now loop through the list and find the longest common prefix. */
946: prefix = xstrdup(ARRAY_FIRST(&list));
947: for (i = 1; i < ARRAY_LENGTH(&list); i++) {
948: s = ARRAY_ITEM(&list, i);
949:
950: j = strlen(s);
951: if (j > strlen(prefix))
952: j = strlen(prefix);
953: for (; j > 0; j--) {
954: if (prefix[j - 1] != s[j - 1])
955: prefix[j - 1] = '\0';
956: }
957: }
958:
959: ARRAY_FREE(&list);
960: return (prefix);
961: }