Annotation of src/usr.bin/tmux/status.c, Revision 1.10
1.10 ! nicm 1: /* $OpenBSD: status.c,v 1.9 2009/07/15 07:50:34 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:
1.10 ! nicm 468: void printflike2
! 469: status_message_set(struct client *c, const char *fmt, ...)
1.1 nicm 470: {
471: struct timeval tv;
1.10 ! nicm 472: va_list ap;
1.1 nicm 473: int delay;
474:
475: delay = options_get_number(&c->session->options, "display-time");
476: tv.tv_sec = delay / 1000;
477: tv.tv_usec = (delay % 1000) * 1000L;
478:
1.10 ! nicm 479: va_start(ap, fmt);
! 480: xvasprintf(&c->message_string, fmt, ap);
! 481: va_end(ap);
1.1 nicm 482: if (gettimeofday(&c->message_timer, NULL) != 0)
483: fatal("gettimeofday");
484: timeradd(&c->message_timer, &tv, &c->message_timer);
485:
486: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
487: c->flags |= CLIENT_STATUS;
488: }
489:
490: void
491: status_message_clear(struct client *c)
492: {
493: if (c->message_string == NULL)
494: return;
495:
496: xfree(c->message_string);
497: c->message_string = NULL;
498:
499: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.8 nicm 500: c->flags |= CLIENT_STATUS;
1.7 nicm 501:
502: screen_reinit(&c->status);
1.1 nicm 503: }
504:
505: /* Draw client message on status line of present else on last line. */
506: int
507: status_message_redraw(struct client *c)
508: {
509: struct screen_write_ctx ctx;
510: struct session *s = c->session;
511: struct screen old_status;
512: size_t len;
513: struct grid_cell gc;
514:
515: if (c->tty.sx == 0 || c->tty.sy == 0)
516: return (0);
517: memcpy(&old_status, &c->status, sizeof old_status);
518: screen_init(&c->status, c->tty.sx, 1, 0);
519:
520: len = strlen(c->message_string);
521: if (len > c->tty.sx)
522: len = c->tty.sx;
523:
524: memcpy(&gc, &grid_default_cell, sizeof gc);
525: gc.bg = options_get_number(&s->options, "message-fg");
526: gc.fg = options_get_number(&s->options, "message-bg");
527: gc.attr |= options_get_number(&s->options, "message-attr");
528:
529: screen_write_start(&ctx, NULL, &c->status);
530:
531: screen_write_cursormove(&ctx, 0, 0);
532: screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->message_string);
533: for (; len < c->tty.sx; len++)
534: screen_write_putc(&ctx, &gc, ' ');
535:
536: screen_write_stop(&ctx);
537:
538: if (grid_compare(c->status.grid, old_status.grid) == 0) {
539: screen_free(&old_status);
540: return (0);
541: }
542: screen_free(&old_status);
543: return (1);
544: }
545:
546: void
547: status_prompt_set(struct client *c,
548: const char *msg, int (*fn)(void *, const char *), void *data, int flags)
549: {
550: c->prompt_string = xstrdup(msg);
551:
552: c->prompt_buffer = xstrdup("");
553: c->prompt_index = 0;
554:
555: c->prompt_callback = fn;
556: c->prompt_data = data;
557:
558: c->prompt_hindex = 0;
559:
560: c->prompt_flags = flags;
561:
562: mode_key_init(&c->prompt_mdata,
563: options_get_number(&c->session->options, "status-keys"),
564: MODEKEY_CANEDIT);
565:
566: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
567: c->flags |= CLIENT_STATUS;
568: }
569:
570: void
571: status_prompt_clear(struct client *c)
572: {
573: if (c->prompt_string == NULL)
574: return;
575:
576: mode_key_free(&c->prompt_mdata);
577:
578: xfree(c->prompt_string);
579: c->prompt_string = NULL;
580:
1.4 nicm 581: if (c->prompt_flags & PROMPT_HIDDEN)
582: memset(c->prompt_buffer, 0, strlen(c->prompt_buffer));
1.1 nicm 583: xfree(c->prompt_buffer);
584: c->prompt_buffer = NULL;
585:
586: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.8 nicm 587: c->flags |= CLIENT_STATUS;
1.7 nicm 588:
589: screen_reinit(&c->status);
1.1 nicm 590: }
591:
592: /* Draw client prompt on status line of present else on last line. */
593: int
594: status_prompt_redraw(struct client *c)
595: {
596: struct screen_write_ctx ctx;
597: struct session *s = c->session;
598: struct screen old_status;
599: size_t i, size, left, len, offset, n;
600: char ch;
601: struct grid_cell gc;
602:
603: if (c->tty.sx == 0 || c->tty.sy == 0)
604: return (0);
605: memcpy(&old_status, &c->status, sizeof old_status);
606: screen_init(&c->status, c->tty.sx, 1, 0);
607: offset = 0;
608:
609: len = strlen(c->prompt_string);
610: if (len > c->tty.sx)
611: len = c->tty.sx;
612:
613: memcpy(&gc, &grid_default_cell, sizeof gc);
614: gc.bg = options_get_number(&s->options, "message-fg");
615: gc.fg = options_get_number(&s->options, "message-bg");
616: gc.attr |= options_get_number(&s->options, "message-attr");
617:
618: screen_write_start(&ctx, NULL, &c->status);
619:
620: screen_write_cursormove(&ctx, 0, 0);
621: screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->prompt_string);
622:
623: left = c->tty.sx - len;
624: if (left != 0) {
625: if (c->prompt_index < left)
626: size = strlen(c->prompt_buffer);
627: else {
628: offset = c->prompt_index - left - 1;
629: if (c->prompt_index == strlen(c->prompt_buffer))
630: left--;
631: size = left;
632: }
633: if (c->prompt_flags & PROMPT_HIDDEN) {
634: n = strlen(c->prompt_buffer);
635: if (n > left)
636: n = left;
637: for (i = 0; i < n; i++)
638: screen_write_putc(&ctx, &gc, '*');
639: } else {
640: screen_write_puts(&ctx, &gc,
641: "%.*s", (int) left, c->prompt_buffer + offset);
642: }
643:
644: for (i = len + size; i < c->tty.sx; i++)
645: screen_write_putc(&ctx, &gc, ' ');
646: }
647:
648: /* Draw a fake cursor. */
649: screen_write_cursormove(&ctx, len + c->prompt_index - offset, 0);
650: if (c->prompt_index == strlen(c->prompt_buffer))
651: ch = ' ';
652: else {
653: if (c->prompt_flags & PROMPT_HIDDEN)
654: ch = '*';
655: else
656: ch = c->prompt_buffer[c->prompt_index];
657: }
658: if (ch == '\0')
659: ch = ' ';
660: gc.attr ^= GRID_ATTR_REVERSE;
661: screen_write_putc(&ctx, &gc, ch);
662:
663: screen_write_stop(&ctx);
664:
665: if (grid_compare(c->status.grid, old_status.grid) == 0) {
666: screen_free(&old_status);
667: return (0);
668: }
669: screen_free(&old_status);
670: return (1);
671: }
672:
673: /* Handle keys in prompt. */
674: void
675: status_prompt_key(struct client *c, int key)
676: {
677: struct paste_buffer *pb;
678: char *s, *first, *last, word[64];
679: size_t size, n, off, idx;
680:
681: size = strlen(c->prompt_buffer);
682: switch (mode_key_lookup(&c->prompt_mdata, key)) {
683: case MODEKEYCMD_LEFT:
684: if (c->prompt_index > 0) {
685: c->prompt_index--;
686: c->flags |= CLIENT_STATUS;
687: }
688: break;
689: case MODEKEYCMD_RIGHT:
690: if (c->prompt_index < size) {
691: c->prompt_index++;
692: c->flags |= CLIENT_STATUS;
693: }
694: break;
695: case MODEKEYCMD_STARTOFLINE:
1.6 nicm 696: case MODEKEYCMD_BACKTOINDENTATION:
1.1 nicm 697: if (c->prompt_index != 0) {
698: c->prompt_index = 0;
699: c->flags |= CLIENT_STATUS;
700: }
701: break;
702: case MODEKEYCMD_ENDOFLINE:
703: if (c->prompt_index != size) {
704: c->prompt_index = size;
705: c->flags |= CLIENT_STATUS;
706: }
707: break;
708: case MODEKEYCMD_COMPLETE:
709: if (*c->prompt_buffer == '\0')
710: break;
711:
712: idx = c->prompt_index;
713: if (idx != 0)
714: idx--;
715:
716: /* Find the word we are in. */
717: first = c->prompt_buffer + idx;
718: while (first > c->prompt_buffer && *first != ' ')
719: first--;
720: while (*first == ' ')
721: first++;
722: last = c->prompt_buffer + idx;
723: while (*last != '\0' && *last != ' ')
724: last++;
725: while (*last == ' ')
726: last--;
727: if (*last != '\0')
728: last++;
729: if (last <= first ||
730: ((size_t) (last - first)) > (sizeof word) - 1)
731: break;
732: memcpy(word, first, last - first);
733: word[last - first] = '\0';
734:
735: /* And try to complete it. */
736: if ((s = status_prompt_complete(word)) == NULL)
737: break;
738:
739: /* Trim out word. */
740: n = size - (last - c->prompt_buffer) + 1; /* with \0 */
741: memmove(first, last, n);
742: size -= last - first;
743:
744: /* Insert the new word. */
745: size += strlen(s);
746: off = first - c->prompt_buffer;
747: c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1);
748: first = c->prompt_buffer + off;
749: memmove(first + strlen(s), first, n);
750: memcpy(first, s, strlen(s));
751:
752: c->prompt_index = (first - c->prompt_buffer) + strlen(s);
753:
754: c->flags |= CLIENT_STATUS;
755: break;
756: case MODEKEYCMD_BACKSPACE:
757: if (c->prompt_index != 0) {
758: if (c->prompt_index == size)
759: c->prompt_buffer[--c->prompt_index] = '\0';
760: else {
761: memmove(c->prompt_buffer + c->prompt_index - 1,
762: c->prompt_buffer + c->prompt_index,
763: size + 1 - c->prompt_index);
764: c->prompt_index--;
765: }
766: c->flags |= CLIENT_STATUS;
767: }
768: break;
769: case MODEKEYCMD_DELETE:
770: if (c->prompt_index != size) {
771: memmove(c->prompt_buffer + c->prompt_index,
772: c->prompt_buffer + c->prompt_index + 1,
773: size + 1 - c->prompt_index);
774: c->flags |= CLIENT_STATUS;
775: }
776: break;
777: case MODEKEYCMD_UP:
778: if (server_locked)
779: break;
780:
781: if (ARRAY_LENGTH(&c->prompt_hdata) == 0)
782: break;
1.4 nicm 783: if (c->prompt_flags & PROMPT_HIDDEN)
784: memset(c->prompt_buffer, 0, strlen(c->prompt_buffer));
1.1 nicm 785: xfree(c->prompt_buffer);
786:
787: c->prompt_buffer = xstrdup(ARRAY_ITEM(&c->prompt_hdata,
788: ARRAY_LENGTH(&c->prompt_hdata) - 1 - c->prompt_hindex));
789: if (c->prompt_hindex != ARRAY_LENGTH(&c->prompt_hdata) - 1)
790: c->prompt_hindex++;
791:
792: c->prompt_index = strlen(c->prompt_buffer);
793: c->flags |= CLIENT_STATUS;
794: break;
795: case MODEKEYCMD_DOWN:
796: if (server_locked)
797: break;
798:
1.4 nicm 799: if (c->prompt_flags & PROMPT_HIDDEN)
800: memset(c->prompt_buffer, 0, strlen(c->prompt_buffer));
1.1 nicm 801: xfree(c->prompt_buffer);
802:
803: if (c->prompt_hindex != 0) {
804: c->prompt_hindex--;
805: c->prompt_buffer = xstrdup(ARRAY_ITEM(
806: &c->prompt_hdata, ARRAY_LENGTH(
807: &c->prompt_hdata) - 1 - c->prompt_hindex));
808: } else
809: c->prompt_buffer = xstrdup("");
810:
811: c->prompt_index = strlen(c->prompt_buffer);
812: c->flags |= CLIENT_STATUS;
813: break;
814: case MODEKEYCMD_PASTE:
815: if ((pb = paste_get_top(&c->session->buffers)) == NULL)
816: break;
817: if ((last = strchr(pb->data, '\n')) == NULL)
818: last = strchr(pb->data, '\0');
819: n = last - pb->data;
820:
821: c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1);
822: if (c->prompt_index == size) {
823: memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
824: c->prompt_index += n;
825: c->prompt_buffer[c->prompt_index] = '\0';
826: } else {
827: memmove(c->prompt_buffer + c->prompt_index + n,
828: c->prompt_buffer + c->prompt_index,
829: size + 1 - c->prompt_index);
830: memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
831: c->prompt_index += n;
832: }
833:
834: c->flags |= CLIENT_STATUS;
835: break;
836: case MODEKEYCMD_CHOOSE:
837: if (*c->prompt_buffer != '\0') {
838: status_prompt_add_history(c);
839: if (c->prompt_callback(
840: c->prompt_data, c->prompt_buffer) == 0)
841: status_prompt_clear(c);
842: break;
843: }
844: /* FALLTHROUGH */
845: case MODEKEYCMD_QUIT:
846: if (c->prompt_callback(c->prompt_data, NULL) == 0)
847: status_prompt_clear(c);
848: break;
849: case MODEKEYCMD_OTHERKEY:
850: if (key < 32 || key > 126)
851: break;
852: c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2);
853:
854: if (c->prompt_index == size) {
855: c->prompt_buffer[c->prompt_index++] = key;
856: c->prompt_buffer[c->prompt_index] = '\0';
857: } else {
858: memmove(c->prompt_buffer + c->prompt_index + 1,
859: c->prompt_buffer + c->prompt_index,
860: size + 1 - c->prompt_index);
861: c->prompt_buffer[c->prompt_index++] = key;
862: }
863:
864: if (c->prompt_flags & PROMPT_SINGLE) {
865: if (c->prompt_callback(
866: c->prompt_data, c->prompt_buffer) == 0)
867: status_prompt_clear(c);
868: }
869:
870: c->flags |= CLIENT_STATUS;
871: break;
872: default:
873: break;
874: }
875: }
876:
877: /* Add line to the history. */
878: void
879: status_prompt_add_history(struct client *c)
880: {
881: if (server_locked)
882: return;
883:
884: if (ARRAY_LENGTH(&c->prompt_hdata) > 0 &&
885: strcmp(ARRAY_LAST(&c->prompt_hdata), c->prompt_buffer) == 0)
886: return;
887:
888: if (ARRAY_LENGTH(&c->prompt_hdata) == PROMPT_HISTORY) {
889: xfree(ARRAY_FIRST(&c->prompt_hdata));
890: ARRAY_REMOVE(&c->prompt_hdata, 0);
891: }
892:
893: ARRAY_ADD(&c->prompt_hdata, xstrdup(c->prompt_buffer));
894: }
895:
896: /* Complete word. */
897: char *
898: status_prompt_complete(const char *s)
899: {
900: const struct cmd_entry **cmdent;
901: const struct set_option_entry *optent;
902: ARRAY_DECL(, const char *) list;
903: char *prefix, *s2;
1.9 nicm 904: u_int i;
1.1 nicm 905: size_t j;
906:
907: if (*s == '\0')
908: return (NULL);
909:
910: /* First, build a list of all the possible matches. */
911: ARRAY_INIT(&list);
912: for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
913: if (strncmp((*cmdent)->name, s, strlen(s)) == 0)
914: ARRAY_ADD(&list, (*cmdent)->name);
915: }
1.9 nicm 916: for (optent = set_option_table; optent->name != NULL; optent++) {
1.1 nicm 917: if (strncmp(optent->name, s, strlen(s)) == 0)
918: ARRAY_ADD(&list, optent->name);
919: }
1.9 nicm 920: for (optent = set_window_option_table; optent->name != NULL; optent++) {
1.1 nicm 921: if (strncmp(optent->name, s, strlen(s)) == 0)
922: ARRAY_ADD(&list, optent->name);
923: }
924:
925: /* If none, bail now. */
926: if (ARRAY_LENGTH(&list) == 0) {
927: ARRAY_FREE(&list);
928: return (NULL);
929: }
930:
931: /* If an exact match, return it, with a trailing space. */
932: if (ARRAY_LENGTH(&list) == 1) {
933: xasprintf(&s2, "%s ", ARRAY_FIRST(&list));
934: ARRAY_FREE(&list);
935: return (s2);
936: }
937:
938: /* Now loop through the list and find the longest common prefix. */
939: prefix = xstrdup(ARRAY_FIRST(&list));
940: for (i = 1; i < ARRAY_LENGTH(&list); i++) {
941: s = ARRAY_ITEM(&list, i);
942:
943: j = strlen(s);
944: if (j > strlen(prefix))
945: j = strlen(prefix);
946: for (; j > 0; j--) {
947: if (prefix[j - 1] != s[j - 1])
948: prefix[j - 1] = '\0';
949: }
950: }
951:
952: ARRAY_FREE(&list);
953: return (prefix);
954: }