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