status.c revision 1.157
1/* $OpenBSD: status.c,v 1.157 2017/01/05 09:07:16 nicm Exp $ */ 2 3/* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 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 32static char *status_redraw_get_left(struct client *, time_t, 33 struct grid_cell *, size_t *); 34static char *status_redraw_get_right(struct client *, time_t, 35 struct grid_cell *, size_t *); 36static char *status_print(struct client *, struct winlink *, time_t, 37 struct grid_cell *); 38static char *status_replace(struct client *, struct winlink *, const char *, 39 time_t); 40static void status_message_callback(int, short, void *); 41static void status_timer_callback(int, short, void *); 42 43static char *status_prompt_find_history_file(void); 44static const char *status_prompt_up_history(u_int *); 45static const char *status_prompt_down_history(u_int *); 46static void status_prompt_add_history(const char *); 47 48static const char **status_prompt_complete_list(u_int *, const char *); 49static char *status_prompt_complete_prefix(const char **, u_int); 50static char *status_prompt_complete(struct session *, const char *); 51 52/* Status prompt history. */ 53#define PROMPT_HISTORY 100 54static char **status_prompt_hlist; 55static u_int status_prompt_hsize; 56 57/* Find the history file to load/save from/to. */ 58static char * 59status_prompt_find_history_file(void) 60{ 61 const char *home, *history_file; 62 char *path; 63 64 history_file = options_get_string(global_options, "history-file"); 65 if (*history_file == '\0') 66 return (NULL); 67 if (*history_file == '/') 68 return (xstrdup(history_file)); 69 70 if (history_file[0] != '~' || history_file[1] != '/') 71 return (NULL); 72 if ((home = find_home()) == NULL) 73 return (NULL); 74 xasprintf(&path, "%s%s", home, history_file + 1); 75 return (path); 76} 77 78/* Load status prompt history from file. */ 79void 80status_prompt_load_history(void) 81{ 82 FILE *f; 83 char *history_file, *line, *tmp; 84 size_t length; 85 86 if ((history_file = status_prompt_find_history_file()) == NULL) 87 return; 88 log_debug("loading history from %s", history_file); 89 90 f = fopen(history_file, "r"); 91 if (f == NULL) { 92 log_debug("%s: %s", history_file, strerror(errno)); 93 free(history_file); 94 return; 95 } 96 free(history_file); 97 98 for (;;) { 99 if ((line = fgetln(f, &length)) == NULL) 100 break; 101 102 if (length > 0) { 103 if (line[length - 1] == '\n') { 104 line[length - 1] = '\0'; 105 status_prompt_add_history(line); 106 } else { 107 tmp = xmalloc(length + 1); 108 memcpy(tmp, line, length); 109 tmp[length] = '\0'; 110 status_prompt_add_history(tmp); 111 free(tmp); 112 } 113 } 114 } 115 fclose(f); 116} 117 118/* Save status prompt history to file. */ 119void 120status_prompt_save_history(void) 121{ 122 FILE *f; 123 u_int i; 124 char *history_file; 125 126 if ((history_file = status_prompt_find_history_file()) == NULL) 127 return; 128 log_debug("saving history to %s", history_file); 129 130 f = fopen(history_file, "w"); 131 if (f == NULL) { 132 log_debug("%s: %s", history_file, strerror(errno)); 133 free(history_file); 134 return; 135 } 136 free(history_file); 137 138 for (i = 0; i < status_prompt_hsize; i++) { 139 fputs(status_prompt_hlist[i], f); 140 fputc('\n', f); 141 } 142 fclose(f); 143 144} 145 146/* Status timer callback. */ 147static void 148status_timer_callback(__unused int fd, __unused short events, void *arg) 149{ 150 struct client *c = arg; 151 struct session *s = c->session; 152 struct timeval tv; 153 154 evtimer_del(&c->status_timer); 155 156 if (s == NULL) 157 return; 158 159 if (c->message_string == NULL && c->prompt_string == NULL) 160 c->flags |= CLIENT_STATUS; 161 162 timerclear(&tv); 163 tv.tv_sec = options_get_number(s->options, "status-interval"); 164 165 if (tv.tv_sec != 0) 166 evtimer_add(&c->status_timer, &tv); 167 log_debug("client %p, status interval %d", c, (int)tv.tv_sec); 168} 169 170/* Start status timer for client. */ 171void 172status_timer_start(struct client *c) 173{ 174 struct session *s = c->session; 175 176 if (event_initialized(&c->status_timer)) 177 evtimer_del(&c->status_timer); 178 else 179 evtimer_set(&c->status_timer, status_timer_callback, c); 180 181 if (s != NULL && options_get_number(s->options, "status")) 182 status_timer_callback(-1, 0, c); 183} 184 185/* Start status timer for all clients. */ 186void 187status_timer_start_all(void) 188{ 189 struct client *c; 190 191 TAILQ_FOREACH(c, &clients, entry) 192 status_timer_start(c); 193} 194 195/* Get screen line of status line. -1 means off. */ 196int 197status_at_line(struct client *c) 198{ 199 struct session *s = c->session; 200 201 if (!options_get_number(s->options, "status")) 202 return (-1); 203 204 if (options_get_number(s->options, "status-position") == 0) 205 return (0); 206 return (c->tty.sy - 1); 207} 208 209/* Retrieve options for left string. */ 210static char * 211status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc, 212 size_t *size) 213{ 214 struct session *s = c->session; 215 const char *template; 216 char *left; 217 size_t leftlen; 218 219 style_apply_update(gc, s->options, "status-left-style"); 220 221 template = options_get_string(s->options, "status-left"); 222 left = status_replace(c, NULL, template, t); 223 224 *size = options_get_number(s->options, "status-left-length"); 225 leftlen = screen_write_cstrlen("%s", left); 226 if (leftlen < *size) 227 *size = leftlen; 228 return (left); 229} 230 231/* Retrieve options for right string. */ 232static char * 233status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc, 234 size_t *size) 235{ 236 struct session *s = c->session; 237 const char *template; 238 char *right; 239 size_t rightlen; 240 241 style_apply_update(gc, s->options, "status-right-style"); 242 243 template = options_get_string(s->options, "status-right"); 244 right = status_replace(c, NULL, template, t); 245 246 *size = options_get_number(s->options, "status-right-length"); 247 rightlen = screen_write_cstrlen("%s", right); 248 if (rightlen < *size) 249 *size = rightlen; 250 return (right); 251} 252 253/* Get window at window list position. */ 254struct window * 255status_get_window_at(struct client *c, u_int x) 256{ 257 struct session *s = c->session; 258 struct winlink *wl; 259 struct options *oo; 260 const char *sep; 261 size_t seplen; 262 263 x += c->wlmouse; 264 RB_FOREACH(wl, winlinks, &s->windows) { 265 oo = wl->window->options; 266 267 sep = options_get_string(oo, "window-status-separator"); 268 seplen = screen_write_cstrlen("%s", sep); 269 270 if (x < wl->status_width) 271 return (wl->window); 272 x -= wl->status_width + seplen; 273 } 274 return (NULL); 275} 276 277/* Draw status for client on the last lines of given context. */ 278int 279status_redraw(struct client *c) 280{ 281 struct screen_write_ctx ctx; 282 struct session *s = c->session; 283 struct winlink *wl; 284 struct screen old_status, window_list; 285 struct grid_cell stdgc, lgc, rgc, gc; 286 struct options *oo; 287 time_t t; 288 char *left, *right, *sep; 289 u_int offset, needed; 290 u_int wlstart, wlwidth, wlavailable, wloffset, wlsize; 291 size_t llen, rlen, seplen; 292 int larrow, rarrow; 293 294 /* No status line? */ 295 if (c->tty.sy == 0 || !options_get_number(s->options, "status")) 296 return (1); 297 left = right = NULL; 298 larrow = rarrow = 0; 299 300 /* Store current time. */ 301 t = time(NULL); 302 303 /* Set up default colour. */ 304 style_apply(&stdgc, s->options, "status-style"); 305 306 /* Create the target screen. */ 307 memcpy(&old_status, &c->status, sizeof old_status); 308 screen_init(&c->status, c->tty.sx, 1, 0); 309 screen_write_start(&ctx, NULL, &c->status); 310 for (offset = 0; offset < c->tty.sx; offset++) 311 screen_write_putc(&ctx, &stdgc, ' '); 312 screen_write_stop(&ctx); 313 314 /* If the height is one line, blank status line. */ 315 if (c->tty.sy <= 1) 316 goto out; 317 318 /* Work out left and right strings. */ 319 memcpy(&lgc, &stdgc, sizeof lgc); 320 left = status_redraw_get_left(c, t, &lgc, &llen); 321 memcpy(&rgc, &stdgc, sizeof rgc); 322 right = status_redraw_get_right(c, t, &rgc, &rlen); 323 324 /* 325 * Figure out how much space we have for the window list. If there 326 * isn't enough space, just show a blank status line. 327 */ 328 needed = 0; 329 if (llen != 0) 330 needed += llen; 331 if (rlen != 0) 332 needed += rlen; 333 if (c->tty.sx == 0 || c->tty.sx <= needed) 334 goto out; 335 wlavailable = c->tty.sx - needed; 336 337 /* Calculate the total size needed for the window list. */ 338 wlstart = wloffset = wlwidth = 0; 339 RB_FOREACH(wl, winlinks, &s->windows) { 340 free(wl->status_text); 341 memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell); 342 wl->status_text = status_print(c, wl, t, &wl->status_cell); 343 wl->status_width = screen_write_cstrlen("%s", wl->status_text); 344 345 if (wl == s->curw) 346 wloffset = wlwidth; 347 348 oo = wl->window->options; 349 sep = options_get_string(oo, "window-status-separator"); 350 seplen = screen_write_cstrlen("%s", sep); 351 wlwidth += wl->status_width + seplen; 352 } 353 354 /* Create a new screen for the window list. */ 355 screen_init(&window_list, wlwidth, 1, 0); 356 357 /* And draw the window list into it. */ 358 screen_write_start(&ctx, NULL, &window_list); 359 RB_FOREACH(wl, winlinks, &s->windows) { 360 screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s", 361 wl->status_text); 362 363 oo = wl->window->options; 364 sep = options_get_string(oo, "window-status-separator"); 365 screen_write_cnputs(&ctx, -1, &stdgc, "%s", sep); 366 } 367 screen_write_stop(&ctx); 368 369 /* If there is enough space for the total width, skip to draw now. */ 370 if (wlwidth <= wlavailable) 371 goto draw; 372 373 /* Find size of current window text. */ 374 wlsize = s->curw->status_width; 375 376 /* 377 * If the current window is already on screen, good to draw from the 378 * start and just leave off the end. 379 */ 380 if (wloffset + wlsize < wlavailable) { 381 if (wlavailable > 0) { 382 rarrow = 1; 383 wlavailable--; 384 } 385 wlwidth = wlavailable; 386 } else { 387 /* 388 * Work out how many characters we need to omit from the 389 * start. There are wlavailable characters to fill, and 390 * wloffset + wlsize must be the last. So, the start character 391 * is wloffset + wlsize - wlavailable. 392 */ 393 if (wlavailable > 0) { 394 larrow = 1; 395 wlavailable--; 396 } 397 398 wlstart = wloffset + wlsize - wlavailable; 399 if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) { 400 rarrow = 1; 401 wlstart++; 402 wlavailable--; 403 } 404 wlwidth = wlavailable; 405 } 406 407 /* Bail if anything is now too small too. */ 408 if (wlwidth == 0 || wlavailable == 0) { 409 screen_free(&window_list); 410 goto out; 411 } 412 413 /* 414 * Now the start position is known, work out the state of the left and 415 * right arrows. 416 */ 417 offset = 0; 418 RB_FOREACH(wl, winlinks, &s->windows) { 419 if (wl->flags & WINLINK_ALERTFLAGS && 420 larrow == 1 && offset < wlstart) 421 larrow = -1; 422 423 offset += wl->status_width; 424 425 if (wl->flags & WINLINK_ALERTFLAGS && 426 rarrow == 1 && offset > wlstart + wlwidth) 427 rarrow = -1; 428 } 429 430draw: 431 /* Begin drawing. */ 432 screen_write_start(&ctx, NULL, &c->status); 433 434 /* Draw the left string and arrow. */ 435 screen_write_cursormove(&ctx, 0, 0); 436 if (llen != 0) 437 screen_write_cnputs(&ctx, llen, &lgc, "%s", left); 438 if (larrow != 0) { 439 memcpy(&gc, &stdgc, sizeof gc); 440 if (larrow == -1) 441 gc.attr ^= GRID_ATTR_REVERSE; 442 screen_write_putc(&ctx, &gc, '<'); 443 } 444 445 /* Draw the right string and arrow. */ 446 if (rarrow != 0) { 447 screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0); 448 memcpy(&gc, &stdgc, sizeof gc); 449 if (rarrow == -1) 450 gc.attr ^= GRID_ATTR_REVERSE; 451 screen_write_putc(&ctx, &gc, '>'); 452 } else 453 screen_write_cursormove(&ctx, c->tty.sx - rlen, 0); 454 if (rlen != 0) 455 screen_write_cnputs(&ctx, rlen, &rgc, "%s", right); 456 457 /* Figure out the offset for the window list. */ 458 if (llen != 0) 459 wloffset = llen; 460 else 461 wloffset = 0; 462 if (wlwidth < wlavailable) { 463 switch (options_get_number(s->options, "status-justify")) { 464 case 1: /* centred */ 465 wloffset += (wlavailable - wlwidth) / 2; 466 break; 467 case 2: /* right */ 468 wloffset += (wlavailable - wlwidth); 469 break; 470 } 471 } 472 if (larrow != 0) 473 wloffset++; 474 475 /* Copy the window list. */ 476 c->wlmouse = -wloffset + wlstart; 477 screen_write_cursormove(&ctx, wloffset, 0); 478 screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1, NULL, 479 NULL); 480 screen_free(&window_list); 481 482 screen_write_stop(&ctx); 483 484out: 485 free(left); 486 free(right); 487 488 if (grid_compare(c->status.grid, old_status.grid) == 0) { 489 screen_free(&old_status); 490 return (0); 491 } 492 screen_free(&old_status); 493 return (1); 494} 495 496/* Replace special sequences in fmt. */ 497static char * 498status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) 499{ 500 struct format_tree *ft; 501 char *expanded; 502 503 if (fmt == NULL) 504 return (xstrdup("")); 505 506 if (c->flags & CLIENT_STATUSFORCE) 507 ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE); 508 else 509 ft = format_create(NULL, FORMAT_STATUS); 510 format_defaults(ft, c, NULL, wl, NULL); 511 512 expanded = format_expand_time(ft, fmt, t); 513 514 format_free(ft); 515 return (expanded); 516} 517 518/* Return winlink status line entry and adjust gc as necessary. */ 519static char * 520status_print(struct client *c, struct winlink *wl, time_t t, 521 struct grid_cell *gc) 522{ 523 struct options *oo = wl->window->options; 524 struct session *s = c->session; 525 const char *fmt; 526 char *text; 527 528 style_apply_update(gc, oo, "window-status-style"); 529 fmt = options_get_string(oo, "window-status-format"); 530 if (wl == s->curw) { 531 style_apply_update(gc, oo, "window-status-current-style"); 532 fmt = options_get_string(oo, "window-status-current-format"); 533 } 534 if (wl == TAILQ_FIRST(&s->lastw)) 535 style_apply_update(gc, oo, "window-status-last-style"); 536 537 if (wl->flags & WINLINK_BELL) 538 style_apply_update(gc, oo, "window-status-bell-style"); 539 else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) 540 style_apply_update(gc, oo, "window-status-activity-style"); 541 542 text = status_replace(c, wl, fmt, t); 543 return (text); 544} 545 546/* Set a status line message. */ 547void 548status_message_set(struct client *c, const char *fmt, ...) 549{ 550 struct timeval tv; 551 struct message_entry *msg, *msg1; 552 va_list ap; 553 int delay; 554 u_int limit; 555 556 limit = options_get_number(global_options, "message-limit"); 557 558 status_message_clear(c); 559 560 va_start(ap, fmt); 561 xvasprintf(&c->message_string, fmt, ap); 562 va_end(ap); 563 564 msg = xcalloc(1, sizeof *msg); 565 msg->msg_time = time(NULL); 566 msg->msg_num = c->message_next++; 567 msg->msg = xstrdup(c->message_string); 568 TAILQ_INSERT_TAIL(&c->message_log, msg, entry); 569 570 TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { 571 if (msg->msg_num + limit >= c->message_next) 572 break; 573 free(msg->msg); 574 TAILQ_REMOVE(&c->message_log, msg, entry); 575 free(msg); 576 } 577 578 delay = options_get_number(c->session->options, "display-time"); 579 if (delay > 0) { 580 tv.tv_sec = delay / 1000; 581 tv.tv_usec = (delay % 1000) * 1000L; 582 583 if (event_initialized(&c->message_timer)) 584 evtimer_del(&c->message_timer); 585 evtimer_set(&c->message_timer, status_message_callback, c); 586 evtimer_add(&c->message_timer, &tv); 587 } 588 589 c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); 590 c->flags |= CLIENT_STATUS; 591} 592 593/* Clear status line message. */ 594void 595status_message_clear(struct client *c) 596{ 597 if (c->message_string == NULL) 598 return; 599 600 free(c->message_string); 601 c->message_string = NULL; 602 603 if (c->prompt_string == NULL) 604 c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); 605 c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ 606 607 screen_reinit(&c->status); 608} 609 610/* Clear status line message after timer expires. */ 611static void 612status_message_callback(__unused int fd, __unused short event, void *data) 613{ 614 struct client *c = data; 615 616 status_message_clear(c); 617} 618 619/* Draw client message on status line of present else on last line. */ 620int 621status_message_redraw(struct client *c) 622{ 623 struct screen_write_ctx ctx; 624 struct session *s = c->session; 625 struct screen old_status; 626 size_t len; 627 struct grid_cell gc; 628 629 if (c->tty.sx == 0 || c->tty.sy == 0) 630 return (0); 631 memcpy(&old_status, &c->status, sizeof old_status); 632 screen_init(&c->status, c->tty.sx, 1, 0); 633 634 len = screen_write_strlen("%s", c->message_string); 635 if (len > c->tty.sx) 636 len = c->tty.sx; 637 638 style_apply(&gc, s->options, "message-style"); 639 640 screen_write_start(&ctx, NULL, &c->status); 641 642 screen_write_cursormove(&ctx, 0, 0); 643 screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); 644 for (; len < c->tty.sx; len++) 645 screen_write_putc(&ctx, &gc, ' '); 646 647 screen_write_stop(&ctx); 648 649 if (grid_compare(c->status.grid, old_status.grid) == 0) { 650 screen_free(&old_status); 651 return (0); 652 } 653 screen_free(&old_status); 654 return (1); 655} 656 657/* Enable status line prompt. */ 658void 659status_prompt_set(struct client *c, const char *msg, const char *input, 660 int (*callbackfn)(void *, const char *), void (*freefn)(void *), 661 void *data, int flags) 662{ 663 struct format_tree *ft; 664 time_t t; 665 char *tmp; 666 667 ft = format_create(NULL, 0); 668 format_defaults(ft, c, NULL, NULL, NULL); 669 670 t = time(NULL); 671 tmp = format_expand_time(ft, input, t); 672 673 status_message_clear(c); 674 status_prompt_clear(c); 675 676 c->prompt_string = format_expand_time(ft, msg, t); 677 678 c->prompt_buffer = utf8_fromcstr(tmp); 679 c->prompt_index = utf8_strlen(c->prompt_buffer); 680 681 c->prompt_callbackfn = callbackfn; 682 c->prompt_freefn = freefn; 683 c->prompt_data = data; 684 685 c->prompt_hindex = 0; 686 687 c->prompt_flags = flags; 688 c->prompt_mode = PROMPT_ENTRY; 689 690 c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); 691 c->flags |= CLIENT_STATUS; 692 693 free(tmp); 694 format_free(ft); 695} 696 697/* Remove status line prompt. */ 698void 699status_prompt_clear(struct client *c) 700{ 701 if (c->prompt_string == NULL) 702 return; 703 704 if (c->prompt_freefn != NULL && c->prompt_data != NULL) 705 c->prompt_freefn(c->prompt_data); 706 707 free(c->prompt_string); 708 c->prompt_string = NULL; 709 710 free(c->prompt_buffer); 711 c->prompt_buffer = NULL; 712 713 c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); 714 c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ 715 716 screen_reinit(&c->status); 717} 718 719/* Update status line prompt with a new prompt string. */ 720void 721status_prompt_update(struct client *c, const char *msg, const char *input) 722{ 723 struct format_tree *ft; 724 time_t t; 725 char *tmp; 726 727 ft = format_create(NULL, 0); 728 format_defaults(ft, c, NULL, NULL, NULL); 729 730 t = time(NULL); 731 tmp = format_expand_time(ft, input, t); 732 733 free(c->prompt_string); 734 c->prompt_string = format_expand_time(ft, msg, t); 735 736 free(c->prompt_buffer); 737 c->prompt_buffer = utf8_fromcstr(tmp); 738 c->prompt_index = utf8_strlen(c->prompt_buffer); 739 740 c->prompt_hindex = 0; 741 742 c->flags |= CLIENT_STATUS; 743 744 free(tmp); 745 format_free(ft); 746} 747 748/* Draw client prompt on status line of present else on last line. */ 749int 750status_prompt_redraw(struct client *c) 751{ 752 struct screen_write_ctx ctx; 753 struct session *s = c->session; 754 struct screen old_status; 755 u_int i, offset, left, start, pcursor, pwidth, width; 756 struct grid_cell gc, cursorgc; 757 758 if (c->tty.sx == 0 || c->tty.sy == 0) 759 return (0); 760 memcpy(&old_status, &c->status, sizeof old_status); 761 screen_init(&c->status, c->tty.sx, 1, 0); 762 763 if (c->prompt_mode == PROMPT_COMMAND) 764 style_apply(&gc, s->options, "message-command-style"); 765 else 766 style_apply(&gc, s->options, "message-style"); 767 768 memcpy(&cursorgc, &gc, sizeof cursorgc); 769 cursorgc.attr ^= GRID_ATTR_REVERSE; 770 771 start = screen_write_strlen("%s", c->prompt_string); 772 if (start > c->tty.sx) 773 start = c->tty.sx; 774 775 screen_write_start(&ctx, NULL, &c->status); 776 screen_write_cursormove(&ctx, 0, 0); 777 screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string); 778 while (c->status.cx < screen_size_x(&c->status)) 779 screen_write_putc(&ctx, &gc, ' '); 780 screen_write_cursormove(&ctx, start, 0); 781 782 left = c->tty.sx - start; 783 if (left == 0) 784 goto finished; 785 786 pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index); 787 pwidth = utf8_strwidth(c->prompt_buffer, -1); 788 if (pcursor >= left) { 789 /* 790 * The cursor would be outside the screen so start drawing 791 * with it on the right. 792 */ 793 offset = (pcursor - left) + 1; 794 pwidth = left; 795 } else 796 offset = 0; 797 if (pwidth > left) 798 pwidth = left; 799 800 width = 0; 801 for (i = 0; c->prompt_buffer[i].size != 0; i++) { 802 if (width < offset) { 803 width += c->prompt_buffer[i].width; 804 continue; 805 } 806 if (width >= offset + pwidth) 807 break; 808 width += c->prompt_buffer[i].width; 809 if (width > offset + pwidth) 810 break; 811 812 if (i != c->prompt_index) { 813 utf8_copy(&gc.data, &c->prompt_buffer[i]); 814 screen_write_cell(&ctx, &gc); 815 } else { 816 utf8_copy(&cursorgc.data, &c->prompt_buffer[i]); 817 screen_write_cell(&ctx, &cursorgc); 818 } 819 } 820 if (c->status.cx < screen_size_x(&c->status) && c->prompt_index >= i) 821 screen_write_putc(&ctx, &cursorgc, ' '); 822 823finished: 824 screen_write_stop(&ctx); 825 826 if (grid_compare(c->status.grid, old_status.grid) == 0) { 827 screen_free(&old_status); 828 return (0); 829 } 830 screen_free(&old_status); 831 return (1); 832} 833 834/* Is this a separator? */ 835static int 836status_prompt_in_list(const char *ws, const struct utf8_data *ud) 837{ 838 if (ud->size != 1 || ud->width != 1) 839 return (0); 840 return (strchr(ws, *ud->data) != NULL); 841} 842 843/* Is this a space? */ 844static int 845status_prompt_space(const struct utf8_data *ud) 846{ 847 if (ud->size != 1 || ud->width != 1) 848 return (0); 849 return (*ud->data == ' '); 850} 851 852/* 853 * Translate key from emacs to vi. Return 0 to drop key, 1 to process the key 854 * as an emacs key; return 2 to append to the buffer. 855 */ 856static int 857status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) 858{ 859 if (c->prompt_mode == PROMPT_ENTRY) { 860 switch (key) { 861 case '\003': /* C-c */ 862 case '\010': /* C-h */ 863 case '\011': /* Tab */ 864 case '\025': /* C-u */ 865 case '\027': /* C-w */ 866 case '\n': 867 case '\r': 868 case KEYC_BSPACE: 869 case KEYC_DC: 870 case KEYC_DOWN: 871 case KEYC_END: 872 case KEYC_HOME: 873 case KEYC_LEFT: 874 case KEYC_RIGHT: 875 case KEYC_UP: 876 *new_key = key; 877 return (1); 878 case '\033': /* Escape */ 879 c->prompt_mode = PROMPT_COMMAND; 880 c->flags |= CLIENT_STATUS; 881 return (0); 882 } 883 *new_key = key; 884 return (2); 885 } 886 887 switch (key) { 888 case 'A': 889 case 'I': 890 case 'C': 891 case 's': 892 case 'a': 893 c->prompt_mode = PROMPT_ENTRY; 894 c->flags |= CLIENT_STATUS; 895 break; /* switch mode and... */ 896 case 'S': 897 c->prompt_mode = PROMPT_ENTRY; 898 c->flags |= CLIENT_STATUS; 899 *new_key = '\025'; /* C-u */ 900 return (1); 901 case 'i': 902 case '\033': /* Escape */ 903 c->prompt_mode = PROMPT_ENTRY; 904 c->flags |= CLIENT_STATUS; 905 return (0); 906 } 907 908 switch (key) { 909 case 'A': 910 case '$': 911 *new_key = KEYC_END; 912 return (1); 913 case 'I': 914 case '0': 915 case '^': 916 *new_key = KEYC_HOME; 917 return (1); 918 case 'C': 919 case 'D': 920 *new_key = '\013'; /* C-k */ 921 return (1); 922 case KEYC_BSPACE: 923 case 'X': 924 *new_key = KEYC_BSPACE; 925 return (1); 926 case 'b': 927 case 'B': 928 *new_key = 'b'|KEYC_ESCAPE; 929 return (1); 930 case 'd': 931 *new_key = '\025'; 932 return (1); 933 case 'e': 934 case 'E': 935 case 'w': 936 case 'W': 937 *new_key = 'f'|KEYC_ESCAPE; 938 return (1); 939 case 'p': 940 *new_key = '\031'; /* C-y */ 941 return (1); 942 case 's': 943 case KEYC_DC: 944 case 'x': 945 *new_key = KEYC_DC; 946 return (1); 947 case KEYC_DOWN: 948 case 'j': 949 *new_key = KEYC_DOWN; 950 return (1); 951 case KEYC_LEFT: 952 case 'h': 953 *new_key = KEYC_LEFT; 954 return (1); 955 case 'a': 956 case KEYC_RIGHT: 957 case 'l': 958 *new_key = KEYC_RIGHT; 959 return (1); 960 case KEYC_UP: 961 case 'k': 962 *new_key = KEYC_UP; 963 return (1); 964 case '\010' /* C-h */: 965 case '\003' /* C-c */: 966 case '\n': 967 case '\r': 968 return (1); 969 } 970 return (0); 971} 972 973/* Handle keys in prompt. */ 974int 975status_prompt_key(struct client *c, key_code key) 976{ 977 struct options *oo = c->session->options; 978 struct paste_buffer *pb; 979 char *s, word[64]; 980 const char *histstr, *bufdata, *ws = NULL; 981 u_char ch; 982 size_t size, n, off, idx, bufsize, used; 983 struct utf8_data tmp, *first, *last, *ud; 984 int keys; 985 986 size = utf8_strlen(c->prompt_buffer); 987 988 if (c->prompt_flags & PROMPT_NUMERIC) { 989 if (key >= '0' && key <= '9') 990 goto append_key; 991 s = utf8_tocstr(c->prompt_buffer); 992 c->prompt_callbackfn(c->prompt_data, s); 993 status_prompt_clear(c); 994 free(s); 995 return (1); 996 } 997 998 keys = options_get_number(c->session->options, "status-keys"); 999 if (keys == MODEKEY_VI) { 1000 switch (status_prompt_translate_key(c, key, &key)) { 1001 case 1: 1002 goto process_key; 1003 case 2: 1004 goto append_key; 1005 default: 1006 return (0); 1007 } 1008 } 1009 1010process_key: 1011 switch (key) { 1012 case KEYC_LEFT: 1013 case '\002': /* C-b */ 1014 if (c->prompt_index > 0) { 1015 c->prompt_index--; 1016 c->flags |= CLIENT_STATUS; 1017 } 1018 break; 1019 case KEYC_RIGHT: 1020 case '\006': /* C-f */ 1021 if (c->prompt_index < size) { 1022 c->prompt_index++; 1023 c->flags |= CLIENT_STATUS; 1024 } 1025 break; 1026 case KEYC_HOME: 1027 case '\001': /* C-a */ 1028 if (c->prompt_index != 0) { 1029 c->prompt_index = 0; 1030 c->flags |= CLIENT_STATUS; 1031 } 1032 break; 1033 case KEYC_END: 1034 case '\005': /* C-e */ 1035 if (c->prompt_index != size) { 1036 c->prompt_index = size; 1037 c->flags |= CLIENT_STATUS; 1038 } 1039 break; 1040 case '\011': /* Tab */ 1041 if (c->prompt_buffer[0].size == 0) 1042 break; 1043 1044 idx = c->prompt_index; 1045 if (idx != 0) 1046 idx--; 1047 1048 /* Find the word we are in. */ 1049 first = &c->prompt_buffer[idx]; 1050 while (first > c->prompt_buffer && !status_prompt_space(first)) 1051 first--; 1052 while (first->size != 0 && status_prompt_space(first)) 1053 first++; 1054 last = &c->prompt_buffer[idx]; 1055 while (last->size != 0 && !status_prompt_space(last)) 1056 last++; 1057 while (last > c->prompt_buffer && status_prompt_space(last)) 1058 last--; 1059 if (last->size != 0) 1060 last++; 1061 if (last <= first) 1062 break; 1063 1064 used = 0; 1065 for (ud = first; ud < last; ud++) { 1066 if (used + ud->size >= sizeof word) 1067 break; 1068 memcpy(word + used, ud->data, ud->size); 1069 used += ud->size; 1070 } 1071 if (ud != last) 1072 break; 1073 word[used] = '\0'; 1074 1075 /* And try to complete it. */ 1076 if ((s = status_prompt_complete(c->session, word)) == NULL) 1077 break; 1078 1079 /* Trim out word. */ 1080 n = size - (last - c->prompt_buffer) + 1; /* with \0 */ 1081 memmove(first, last, n * sizeof *c->prompt_buffer); 1082 size -= last - first; 1083 1084 /* Insert the new word. */ 1085 size += strlen(s); 1086 off = first - c->prompt_buffer; 1087 c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, 1088 sizeof *c->prompt_buffer); 1089 first = c->prompt_buffer + off; 1090 memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); 1091 for (idx = 0; idx < strlen(s); idx++) 1092 utf8_set(&first[idx], s[idx]); 1093 1094 c->prompt_index = (first - c->prompt_buffer) + strlen(s); 1095 free(s); 1096 1097 c->flags |= CLIENT_STATUS; 1098 break; 1099 case KEYC_BSPACE: 1100 case '\010': /* C-h */ 1101 if (c->prompt_index != 0) { 1102 if (c->prompt_index == size) 1103 c->prompt_buffer[--c->prompt_index].size = 0; 1104 else { 1105 memmove(c->prompt_buffer + c->prompt_index - 1, 1106 c->prompt_buffer + c->prompt_index, 1107 (size + 1 - c->prompt_index) * 1108 sizeof *c->prompt_buffer); 1109 c->prompt_index--; 1110 } 1111 c->flags |= CLIENT_STATUS; 1112 } 1113 break; 1114 case KEYC_DC: 1115 case '\004': /* C-d */ 1116 if (c->prompt_index != size) { 1117 memmove(c->prompt_buffer + c->prompt_index, 1118 c->prompt_buffer + c->prompt_index + 1, 1119 (size + 1 - c->prompt_index) * 1120 sizeof *c->prompt_buffer); 1121 c->flags |= CLIENT_STATUS; 1122 } 1123 break; 1124 case '\025': /* C-u */ 1125 c->prompt_buffer[0].size = 0; 1126 c->prompt_index = 0; 1127 c->flags |= CLIENT_STATUS; 1128 break; 1129 case '\013': /* C-k */ 1130 if (c->prompt_index < size) { 1131 c->prompt_buffer[c->prompt_index].size = 0; 1132 c->flags |= CLIENT_STATUS; 1133 } 1134 break; 1135 case '\027': /* C-w */ 1136 ws = options_get_string(oo, "word-separators"); 1137 idx = c->prompt_index; 1138 1139 /* Find a non-separator. */ 1140 while (idx != 0) { 1141 idx--; 1142 if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) 1143 break; 1144 } 1145 1146 /* Find the separator at the beginning of the word. */ 1147 while (idx != 0) { 1148 idx--; 1149 if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { 1150 /* Go back to the word. */ 1151 idx++; 1152 break; 1153 } 1154 } 1155 1156 memmove(c->prompt_buffer + idx, 1157 c->prompt_buffer + c->prompt_index, 1158 (size + 1 - c->prompt_index) * 1159 sizeof *c->prompt_buffer); 1160 memset(c->prompt_buffer + size - (c->prompt_index - idx), 1161 '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer); 1162 c->prompt_index = idx; 1163 1164 c->flags |= CLIENT_STATUS; 1165 break; 1166 case 'f'|KEYC_ESCAPE: 1167 ws = options_get_string(oo, "word-separators"); 1168 1169 /* Find a word. */ 1170 while (c->prompt_index != size) { 1171 idx = ++c->prompt_index; 1172 if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) 1173 break; 1174 } 1175 1176 /* Find the separator at the end of the word. */ 1177 while (c->prompt_index != size) { 1178 idx = ++c->prompt_index; 1179 if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) 1180 break; 1181 } 1182 1183 /* Back up to the end-of-word like vi. */ 1184 if (options_get_number(oo, "status-keys") == MODEKEY_VI && 1185 c->prompt_index != 0) 1186 c->prompt_index--; 1187 1188 c->flags |= CLIENT_STATUS; 1189 break; 1190 case 'b'|KEYC_ESCAPE: 1191 ws = options_get_string(oo, "word-separators"); 1192 1193 /* Find a non-separator. */ 1194 while (c->prompt_index != 0) { 1195 idx = --c->prompt_index; 1196 if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) 1197 break; 1198 } 1199 1200 /* Find the separator at the beginning of the word. */ 1201 while (c->prompt_index != 0) { 1202 idx = --c->prompt_index; 1203 if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { 1204 /* Go back to the word. */ 1205 c->prompt_index++; 1206 break; 1207 } 1208 } 1209 1210 c->flags |= CLIENT_STATUS; 1211 break; 1212 case KEYC_UP: 1213 case '\020': /* C-p */ 1214 histstr = status_prompt_up_history(&c->prompt_hindex); 1215 if (histstr == NULL) 1216 break; 1217 free(c->prompt_buffer); 1218 c->prompt_buffer = utf8_fromcstr(histstr); 1219 c->prompt_index = utf8_strlen(c->prompt_buffer); 1220 c->flags |= CLIENT_STATUS; 1221 break; 1222 case KEYC_DOWN: 1223 case '\016': /* C-n */ 1224 histstr = status_prompt_down_history(&c->prompt_hindex); 1225 if (histstr == NULL) 1226 break; 1227 free(c->prompt_buffer); 1228 c->prompt_buffer = utf8_fromcstr(histstr); 1229 c->prompt_index = utf8_strlen(c->prompt_buffer); 1230 c->flags |= CLIENT_STATUS; 1231 break; 1232 case '\031': /* C-y */ 1233 if ((pb = paste_get_top(NULL)) == NULL) 1234 break; 1235 bufdata = paste_buffer_data(pb, &bufsize); 1236 for (n = 0; n < bufsize; n++) { 1237 ch = (u_char)bufdata[n]; 1238 if (ch < 32 || ch >= 127) 1239 break; 1240 } 1241 1242 c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1, 1243 sizeof *c->prompt_buffer); 1244 if (c->prompt_index == size) { 1245 for (idx = 0; idx < n; idx++) { 1246 ud = &c->prompt_buffer[c->prompt_index + idx]; 1247 utf8_set(ud, bufdata[idx]); 1248 } 1249 c->prompt_index += n; 1250 c->prompt_buffer[c->prompt_index].size = 0; 1251 } else { 1252 memmove(c->prompt_buffer + c->prompt_index + n, 1253 c->prompt_buffer + c->prompt_index, 1254 (size + 1 - c->prompt_index) * 1255 sizeof *c->prompt_buffer); 1256 for (idx = 0; idx < n; idx++) { 1257 ud = &c->prompt_buffer[c->prompt_index + idx]; 1258 utf8_set(ud, bufdata[idx]); 1259 } 1260 c->prompt_index += n; 1261 } 1262 1263 c->flags |= CLIENT_STATUS; 1264 break; 1265 case '\024': /* C-t */ 1266 idx = c->prompt_index; 1267 if (idx < size) 1268 idx++; 1269 if (idx >= 2) { 1270 utf8_copy(&tmp, &c->prompt_buffer[idx - 2]); 1271 utf8_copy(&c->prompt_buffer[idx - 2], 1272 &c->prompt_buffer[idx - 1]); 1273 utf8_copy(&c->prompt_buffer[idx - 1], &tmp); 1274 c->prompt_index = idx; 1275 c->flags |= CLIENT_STATUS; 1276 } 1277 break; 1278 case '\r': 1279 case '\n': 1280 s = utf8_tocstr(c->prompt_buffer); 1281 if (*s != '\0') 1282 status_prompt_add_history(s); 1283 if (c->prompt_callbackfn(c->prompt_data, s) == 0) 1284 status_prompt_clear(c); 1285 free(s); 1286 break; 1287 case '\033': /* Escape */ 1288 case '\003': /* C-c */ 1289 if (c->prompt_callbackfn(c->prompt_data, NULL) == 0) 1290 status_prompt_clear(c); 1291 break; 1292 } 1293 1294append_key: 1295 if (key <= 0x1f || key >= KEYC_BASE) 1296 return (0); 1297 if (utf8_split(key, &tmp) != UTF8_DONE) 1298 return (0); 1299 1300 c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, 1301 sizeof *c->prompt_buffer); 1302 1303 if (c->prompt_index == size) { 1304 utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp); 1305 c->prompt_index++; 1306 c->prompt_buffer[c->prompt_index].size = 0; 1307 } else { 1308 memmove(c->prompt_buffer + c->prompt_index + 1, 1309 c->prompt_buffer + c->prompt_index, 1310 (size + 1 - c->prompt_index) * 1311 sizeof *c->prompt_buffer); 1312 utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp); 1313 c->prompt_index++; 1314 } 1315 1316 if (c->prompt_flags & PROMPT_SINGLE) { 1317 s = utf8_tocstr(c->prompt_buffer); 1318 if (strlen(s) != 1) 1319 status_prompt_clear(c); 1320 else if (c->prompt_callbackfn(c->prompt_data, s) == 0) 1321 status_prompt_clear(c); 1322 free(s); 1323 } 1324 1325 c->flags |= CLIENT_STATUS; 1326 return (0); 1327} 1328 1329/* Get previous line from the history. */ 1330static const char * 1331status_prompt_up_history(u_int *idx) 1332{ 1333 /* 1334 * History runs from 0 to size - 1. Index is from 0 to size. Zero is 1335 * empty. 1336 */ 1337 1338 if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) 1339 return (NULL); 1340 (*idx)++; 1341 return (status_prompt_hlist[status_prompt_hsize - *idx]); 1342} 1343 1344/* Get next line from the history. */ 1345static const char * 1346status_prompt_down_history(u_int *idx) 1347{ 1348 if (status_prompt_hsize == 0 || *idx == 0) 1349 return (""); 1350 (*idx)--; 1351 if (*idx == 0) 1352 return (""); 1353 return (status_prompt_hlist[status_prompt_hsize - *idx]); 1354} 1355 1356/* Add line to the history. */ 1357static void 1358status_prompt_add_history(const char *line) 1359{ 1360 size_t size; 1361 1362 if (status_prompt_hsize > 0 && 1363 strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) 1364 return; 1365 1366 if (status_prompt_hsize == PROMPT_HISTORY) { 1367 free(status_prompt_hlist[0]); 1368 1369 size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; 1370 memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); 1371 1372 status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); 1373 return; 1374 } 1375 1376 status_prompt_hlist = xreallocarray(status_prompt_hlist, 1377 status_prompt_hsize + 1, sizeof *status_prompt_hlist); 1378 status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); 1379} 1380 1381/* Build completion list. */ 1382static const char ** 1383status_prompt_complete_list(u_int *size, const char *s) 1384{ 1385 const char **list = NULL, **layout; 1386 const struct cmd_entry **cmdent; 1387 const struct options_table_entry *oe; 1388 const char *layouts[] = { 1389 "even-horizontal", "even-vertical", "main-horizontal", 1390 "main-vertical", "tiled", NULL 1391 }; 1392 1393 *size = 0; 1394 for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { 1395 if (strncmp((*cmdent)->name, s, strlen(s)) == 0) { 1396 list = xreallocarray(list, (*size) + 1, sizeof *list); 1397 list[(*size)++] = (*cmdent)->name; 1398 } 1399 } 1400 for (oe = options_table; oe->name != NULL; oe++) { 1401 if (strncmp(oe->name, s, strlen(s)) == 0) { 1402 list = xreallocarray(list, (*size) + 1, sizeof *list); 1403 list[(*size)++] = oe->name; 1404 } 1405 } 1406 for (layout = layouts; *layout != NULL; layout++) { 1407 if (strncmp(*layout, s, strlen(s)) == 0) { 1408 list = xreallocarray(list, (*size) + 1, sizeof *list); 1409 list[(*size)++] = *layout; 1410 } 1411 } 1412 return (list); 1413} 1414 1415/* Find longest prefix. */ 1416static char * 1417status_prompt_complete_prefix(const char **list, u_int size) 1418{ 1419 char *out; 1420 u_int i; 1421 size_t j; 1422 1423 out = xstrdup(list[0]); 1424 for (i = 1; i < size; i++) { 1425 j = strlen(list[i]); 1426 if (j > strlen(out)) 1427 j = strlen(out); 1428 for (; j > 0; j--) { 1429 if (out[j - 1] != list[i][j - 1]) 1430 out[j - 1] = '\0'; 1431 } 1432 } 1433 return (out); 1434} 1435 1436/* Complete word. */ 1437static char * 1438status_prompt_complete(struct session *session, const char *s) 1439{ 1440 const char **list = NULL, *colon; 1441 u_int size = 0, i; 1442 struct session *s_loop; 1443 struct winlink *wl; 1444 struct window *w; 1445 char *copy, *out, *tmp; 1446 1447 if (*s == '\0') 1448 return (NULL); 1449 out = NULL; 1450 1451 if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { 1452 list = status_prompt_complete_list(&size, s); 1453 if (size == 0) 1454 out = NULL; 1455 else if (size == 1) 1456 xasprintf(&out, "%s ", list[0]); 1457 else 1458 out = status_prompt_complete_prefix(list, size); 1459 free(list); 1460 return (out); 1461 } 1462 copy = xstrdup(s); 1463 1464 colon = ":"; 1465 if (copy[strlen(copy) - 1] == ':') 1466 copy[strlen(copy) - 1] = '\0'; 1467 else 1468 colon = ""; 1469 s = copy + 2; 1470 1471 RB_FOREACH(s_loop, sessions, &sessions) { 1472 if (strncmp(s_loop->name, s, strlen(s)) == 0) { 1473 list = xreallocarray(list, size + 2, sizeof *list); 1474 list[size++] = s_loop->name; 1475 } 1476 } 1477 if (size == 1) { 1478 out = xstrdup(list[0]); 1479 if (session_find(list[0]) != NULL) 1480 colon = ":"; 1481 } else if (size != 0) 1482 out = status_prompt_complete_prefix(list, size); 1483 if (out != NULL) { 1484 xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); 1485 out = tmp; 1486 goto found; 1487 } 1488 1489 colon = ""; 1490 if (*s == ':') { 1491 RB_FOREACH(wl, winlinks, &session->windows) { 1492 xasprintf(&tmp, ":%s", wl->window->name); 1493 if (strncmp(tmp, s, strlen(s)) == 0){ 1494 list = xreallocarray(list, size + 1, 1495 sizeof *list); 1496 list[size++] = tmp; 1497 continue; 1498 } 1499 free(tmp); 1500 1501 xasprintf(&tmp, ":%d", wl->idx); 1502 if (strncmp(tmp, s, strlen(s)) == 0) { 1503 list = xreallocarray(list, size + 1, 1504 sizeof *list); 1505 list[size++] = tmp; 1506 continue; 1507 } 1508 free(tmp); 1509 } 1510 } else { 1511 RB_FOREACH(s_loop, sessions, &sessions) { 1512 RB_FOREACH(wl, winlinks, &s_loop->windows) { 1513 w = wl->window; 1514 1515 xasprintf(&tmp, "%s:%s", s_loop->name, w->name); 1516 if (strncmp(tmp, s, strlen(s)) == 0) { 1517 list = xreallocarray(list, size + 1, 1518 sizeof *list); 1519 list[size++] = tmp; 1520 continue; 1521 } 1522 free(tmp); 1523 1524 xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); 1525 if (strncmp(tmp, s, strlen(s)) == 0) { 1526 list = xreallocarray(list, size + 1, 1527 sizeof *list); 1528 list[size++] = tmp; 1529 continue; 1530 } 1531 free(tmp); 1532 } 1533 } 1534 } 1535 if (size == 1) { 1536 out = xstrdup(list[0]); 1537 colon = " "; 1538 } else if (size != 0) 1539 out = status_prompt_complete_prefix(list, size); 1540 if (out != NULL) { 1541 xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); 1542 out = tmp; 1543 } 1544 1545 for (i = 0; i < size; i++) 1546 free((void *)list[i]); 1547 1548found: 1549 free(copy); 1550 free(list); 1551 return (out); 1552} 1553