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