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