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