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