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