Annotation of embedaddon/tmux/status.c, revision 1.1
1.1 ! misho 1: /* $OpenBSD$ */
! 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:
! 32: static char *status_redraw_get_left(struct client *, time_t,
! 33: struct grid_cell *, size_t *);
! 34: static char *status_redraw_get_right(struct client *, time_t,
! 35: struct grid_cell *, size_t *);
! 36: static char *status_print(struct client *, struct winlink *, time_t,
! 37: struct grid_cell *);
! 38: static char *status_replace(struct client *, struct winlink *, const char *,
! 39: time_t);
! 40: static void status_message_callback(int, short, void *);
! 41: static void status_timer_callback(int, short, void *);
! 42:
! 43: static char *status_prompt_find_history_file(void);
! 44: static const char *status_prompt_up_history(u_int *);
! 45: static const char *status_prompt_down_history(u_int *);
! 46: static void status_prompt_add_history(const char *);
! 47:
! 48: static const char **status_prompt_complete_list(u_int *, const char *);
! 49: static char *status_prompt_complete_prefix(const char **, u_int);
! 50: static char *status_prompt_complete(struct session *, const char *);
! 51:
! 52: /* Status prompt history. */
! 53: #define PROMPT_HISTORY 100
! 54: static char **status_prompt_hlist;
! 55: static u_int status_prompt_hsize;
! 56:
! 57: /* Find the history file to load/save from/to. */
! 58: static char *
! 59: status_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. */
! 79: void
! 80: status_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. */
! 119: void
! 120: status_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. */
! 147: static void
! 148: status_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. */
! 171: void
! 172: status_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. */
! 186: void
! 187: status_timer_start_all(void)
! 188: {
! 189: struct client *c;
! 190:
! 191: TAILQ_FOREACH(c, &clients, entry)
! 192: status_timer_start(c);
! 193: }
! 194:
! 195: /* Update status cache. */
! 196: void
! 197: status_update_saved(struct session *s)
! 198: {
! 199: if (!options_get_number(s->options, "status"))
! 200: s->statusat = -1;
! 201: else if (options_get_number(s->options, "status-position") == 0)
! 202: s->statusat = 0;
! 203: else
! 204: s->statusat = 1;
! 205: }
! 206:
! 207: /* Get screen line of status line. -1 means off. */
! 208: int
! 209: status_at_line(struct client *c)
! 210: {
! 211: struct session *s = c->session;
! 212:
! 213: if (s->statusat != 1)
! 214: return (s->statusat);
! 215: return (c->tty.sy - 1);
! 216: }
! 217:
! 218: /* Retrieve options for left string. */
! 219: static char *
! 220: status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc,
! 221: size_t *size)
! 222: {
! 223: struct session *s = c->session;
! 224: const char *template;
! 225: char *left;
! 226: size_t leftlen;
! 227:
! 228: style_apply_update(gc, s->options, "status-left-style");
! 229:
! 230: template = options_get_string(s->options, "status-left");
! 231: left = status_replace(c, NULL, template, t);
! 232:
! 233: *size = options_get_number(s->options, "status-left-length");
! 234: leftlen = screen_write_cstrlen("%s", left);
! 235: if (leftlen < *size)
! 236: *size = leftlen;
! 237: return (left);
! 238: }
! 239:
! 240: /* Retrieve options for right string. */
! 241: static char *
! 242: status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc,
! 243: size_t *size)
! 244: {
! 245: struct session *s = c->session;
! 246: const char *template;
! 247: char *right;
! 248: size_t rightlen;
! 249:
! 250: style_apply_update(gc, s->options, "status-right-style");
! 251:
! 252: template = options_get_string(s->options, "status-right");
! 253: right = status_replace(c, NULL, template, t);
! 254:
! 255: *size = options_get_number(s->options, "status-right-length");
! 256: rightlen = screen_write_cstrlen("%s", right);
! 257: if (rightlen < *size)
! 258: *size = rightlen;
! 259: return (right);
! 260: }
! 261:
! 262: /* Get window at window list position. */
! 263: struct window *
! 264: status_get_window_at(struct client *c, u_int x)
! 265: {
! 266: struct session *s = c->session;
! 267: struct winlink *wl;
! 268: struct options *oo;
! 269: const char *sep;
! 270: size_t seplen;
! 271:
! 272: x += c->wlmouse;
! 273: RB_FOREACH(wl, winlinks, &s->windows) {
! 274: oo = wl->window->options;
! 275:
! 276: sep = options_get_string(oo, "window-status-separator");
! 277: seplen = screen_write_cstrlen("%s", sep);
! 278:
! 279: if (x < wl->status_width)
! 280: return (wl->window);
! 281: x -= wl->status_width + seplen;
! 282: }
! 283: return (NULL);
! 284: }
! 285:
! 286: /* Draw status for client on the last lines of given context. */
! 287: int
! 288: status_redraw(struct client *c)
! 289: {
! 290: struct screen_write_ctx ctx;
! 291: struct session *s = c->session;
! 292: struct winlink *wl;
! 293: struct screen old_status, window_list;
! 294: struct grid_cell stdgc, lgc, rgc, gc;
! 295: struct options *oo;
! 296: time_t t;
! 297: char *left, *right;
! 298: const char *sep;
! 299: u_int offset, needed;
! 300: u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
! 301: size_t llen, rlen, seplen;
! 302: int larrow, rarrow;
! 303:
! 304: /* No status line? */
! 305: if (c->tty.sy == 0 || !options_get_number(s->options, "status"))
! 306: return (1);
! 307: left = right = NULL;
! 308: larrow = rarrow = 0;
! 309:
! 310: /* Store current time. */
! 311: t = time(NULL);
! 312:
! 313: /* Set up default colour. */
! 314: style_apply(&stdgc, s->options, "status-style");
! 315:
! 316: /* Create the target screen. */
! 317: memcpy(&old_status, &c->status, sizeof old_status);
! 318: screen_init(&c->status, c->tty.sx, 1, 0);
! 319: screen_write_start(&ctx, NULL, &c->status);
! 320: for (offset = 0; offset < c->tty.sx; offset++)
! 321: screen_write_putc(&ctx, &stdgc, ' ');
! 322: screen_write_stop(&ctx);
! 323:
! 324: /* If the height is one line, blank status line. */
! 325: if (c->tty.sy <= 1)
! 326: goto out;
! 327:
! 328: /* Work out left and right strings. */
! 329: memcpy(&lgc, &stdgc, sizeof lgc);
! 330: left = status_redraw_get_left(c, t, &lgc, &llen);
! 331: memcpy(&rgc, &stdgc, sizeof rgc);
! 332: right = status_redraw_get_right(c, t, &rgc, &rlen);
! 333:
! 334: /*
! 335: * Figure out how much space we have for the window list. If there
! 336: * isn't enough space, just show a blank status line.
! 337: */
! 338: needed = 0;
! 339: if (llen != 0)
! 340: needed += llen;
! 341: if (rlen != 0)
! 342: needed += rlen;
! 343: if (c->tty.sx == 0 || c->tty.sx <= needed)
! 344: goto out;
! 345: wlavailable = c->tty.sx - needed;
! 346:
! 347: /* Calculate the total size needed for the window list. */
! 348: wlstart = wloffset = wlwidth = 0;
! 349: RB_FOREACH(wl, winlinks, &s->windows) {
! 350: free(wl->status_text);
! 351: memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
! 352: wl->status_text = status_print(c, wl, t, &wl->status_cell);
! 353: wl->status_width = screen_write_cstrlen("%s", wl->status_text);
! 354:
! 355: if (wl == s->curw)
! 356: wloffset = wlwidth;
! 357:
! 358: oo = wl->window->options;
! 359: sep = options_get_string(oo, "window-status-separator");
! 360: seplen = screen_write_cstrlen("%s", sep);
! 361: wlwidth += wl->status_width + seplen;
! 362: }
! 363:
! 364: /* Create a new screen for the window list. */
! 365: screen_init(&window_list, wlwidth, 1, 0);
! 366:
! 367: /* And draw the window list into it. */
! 368: screen_write_start(&ctx, NULL, &window_list);
! 369: RB_FOREACH(wl, winlinks, &s->windows) {
! 370: screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s",
! 371: wl->status_text);
! 372:
! 373: oo = wl->window->options;
! 374: sep = options_get_string(oo, "window-status-separator");
! 375: screen_write_cnputs(&ctx, -1, &stdgc, "%s", sep);
! 376: }
! 377: screen_write_stop(&ctx);
! 378:
! 379: /* If there is enough space for the total width, skip to draw now. */
! 380: if (wlwidth <= wlavailable)
! 381: goto draw;
! 382:
! 383: /* Find size of current window text. */
! 384: wlsize = s->curw->status_width;
! 385:
! 386: /*
! 387: * If the current window is already on screen, good to draw from the
! 388: * start and just leave off the end.
! 389: */
! 390: if (wloffset + wlsize < wlavailable) {
! 391: if (wlavailable > 0) {
! 392: rarrow = 1;
! 393: wlavailable--;
! 394: }
! 395: wlwidth = wlavailable;
! 396: } else {
! 397: /*
! 398: * Work out how many characters we need to omit from the
! 399: * start. There are wlavailable characters to fill, and
! 400: * wloffset + wlsize must be the last. So, the start character
! 401: * is wloffset + wlsize - wlavailable.
! 402: */
! 403: if (wlavailable > 0) {
! 404: larrow = 1;
! 405: wlavailable--;
! 406: }
! 407:
! 408: wlstart = wloffset + wlsize - wlavailable;
! 409: if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
! 410: rarrow = 1;
! 411: wlstart++;
! 412: wlavailable--;
! 413: }
! 414: wlwidth = wlavailable;
! 415: }
! 416:
! 417: /* Bail if anything is now too small too. */
! 418: if (wlwidth == 0 || wlavailable == 0) {
! 419: screen_free(&window_list);
! 420: goto out;
! 421: }
! 422:
! 423: /*
! 424: * Now the start position is known, work out the state of the left and
! 425: * right arrows.
! 426: */
! 427: offset = 0;
! 428: RB_FOREACH(wl, winlinks, &s->windows) {
! 429: if (wl->flags & WINLINK_ALERTFLAGS &&
! 430: larrow == 1 && offset < wlstart)
! 431: larrow = -1;
! 432:
! 433: offset += wl->status_width;
! 434:
! 435: if (wl->flags & WINLINK_ALERTFLAGS &&
! 436: rarrow == 1 && offset > wlstart + wlwidth)
! 437: rarrow = -1;
! 438: }
! 439:
! 440: draw:
! 441: /* Begin drawing. */
! 442: screen_write_start(&ctx, NULL, &c->status);
! 443:
! 444: /* Draw the left string and arrow. */
! 445: screen_write_cursormove(&ctx, 0, 0);
! 446: if (llen != 0)
! 447: screen_write_cnputs(&ctx, llen, &lgc, "%s", left);
! 448: if (larrow != 0) {
! 449: memcpy(&gc, &stdgc, sizeof gc);
! 450: if (larrow == -1)
! 451: gc.attr ^= GRID_ATTR_REVERSE;
! 452: screen_write_putc(&ctx, &gc, '<');
! 453: }
! 454:
! 455: /* Draw the right string and arrow. */
! 456: if (rarrow != 0) {
! 457: screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
! 458: memcpy(&gc, &stdgc, sizeof gc);
! 459: if (rarrow == -1)
! 460: gc.attr ^= GRID_ATTR_REVERSE;
! 461: screen_write_putc(&ctx, &gc, '>');
! 462: } else
! 463: screen_write_cursormove(&ctx, c->tty.sx - rlen, 0);
! 464: if (rlen != 0)
! 465: screen_write_cnputs(&ctx, rlen, &rgc, "%s", right);
! 466:
! 467: /* Figure out the offset for the window list. */
! 468: if (llen != 0)
! 469: wloffset = llen;
! 470: else
! 471: wloffset = 0;
! 472: if (wlwidth < wlavailable) {
! 473: switch (options_get_number(s->options, "status-justify")) {
! 474: case 1: /* centred */
! 475: wloffset += (wlavailable - wlwidth) / 2;
! 476: break;
! 477: case 2: /* right */
! 478: wloffset += (wlavailable - wlwidth);
! 479: break;
! 480: }
! 481: }
! 482: if (larrow != 0)
! 483: wloffset++;
! 484:
! 485: /* Copy the window list. */
! 486: c->wlmouse = -wloffset + wlstart;
! 487: screen_write_cursormove(&ctx, wloffset, 0);
! 488: screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1, NULL,
! 489: NULL);
! 490: screen_free(&window_list);
! 491:
! 492: screen_write_stop(&ctx);
! 493:
! 494: out:
! 495: free(left);
! 496: free(right);
! 497:
! 498: if (grid_compare(c->status.grid, old_status.grid) == 0) {
! 499: screen_free(&old_status);
! 500: return (0);
! 501: }
! 502: screen_free(&old_status);
! 503: return (1);
! 504: }
! 505:
! 506: /* Replace special sequences in fmt. */
! 507: static char *
! 508: status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
! 509: {
! 510: struct format_tree *ft;
! 511: char *expanded;
! 512: u_int tag;
! 513:
! 514: if (fmt == NULL)
! 515: return (xstrdup(""));
! 516:
! 517: if (wl != NULL)
! 518: tag = FORMAT_WINDOW|wl->window->id;
! 519: else
! 520: tag = FORMAT_NONE;
! 521: if (c->flags & CLIENT_STATUSFORCE)
! 522: ft = format_create(NULL, tag, FORMAT_STATUS|FORMAT_FORCE);
! 523: else
! 524: ft = format_create(NULL, tag, FORMAT_STATUS);
! 525: format_defaults(ft, c, NULL, wl, NULL);
! 526:
! 527: expanded = format_expand_time(ft, fmt, t);
! 528:
! 529: format_free(ft);
! 530: return (expanded);
! 531: }
! 532:
! 533: /* Return winlink status line entry and adjust gc as necessary. */
! 534: static char *
! 535: status_print(struct client *c, struct winlink *wl, time_t t,
! 536: struct grid_cell *gc)
! 537: {
! 538: struct options *oo = wl->window->options;
! 539: struct session *s = c->session;
! 540: const char *fmt;
! 541: char *text;
! 542:
! 543: style_apply_update(gc, oo, "window-status-style");
! 544: fmt = options_get_string(oo, "window-status-format");
! 545: if (wl == s->curw) {
! 546: style_apply_update(gc, oo, "window-status-current-style");
! 547: fmt = options_get_string(oo, "window-status-current-format");
! 548: }
! 549: if (wl == TAILQ_FIRST(&s->lastw))
! 550: style_apply_update(gc, oo, "window-status-last-style");
! 551:
! 552: if (wl->flags & WINLINK_BELL)
! 553: style_apply_update(gc, oo, "window-status-bell-style");
! 554: else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
! 555: style_apply_update(gc, oo, "window-status-activity-style");
! 556:
! 557: text = status_replace(c, wl, fmt, t);
! 558: return (text);
! 559: }
! 560:
! 561: /* Set a status line message. */
! 562: void
! 563: status_message_set(struct client *c, const char *fmt, ...)
! 564: {
! 565: struct timeval tv;
! 566: va_list ap;
! 567: int delay;
! 568:
! 569: status_message_clear(c);
! 570:
! 571: va_start(ap, fmt);
! 572: xvasprintf(&c->message_string, fmt, ap);
! 573: va_end(ap);
! 574:
! 575: server_client_add_message(c, "%s", c->message_string);
! 576:
! 577: delay = options_get_number(c->session->options, "display-time");
! 578: if (delay > 0) {
! 579: tv.tv_sec = delay / 1000;
! 580: tv.tv_usec = (delay % 1000) * 1000L;
! 581:
! 582: if (event_initialized(&c->message_timer))
! 583: evtimer_del(&c->message_timer);
! 584: evtimer_set(&c->message_timer, status_message_callback, c);
! 585: evtimer_add(&c->message_timer, &tv);
! 586: }
! 587:
! 588: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
! 589: c->flags |= CLIENT_STATUS;
! 590: }
! 591:
! 592: /* Clear status line message. */
! 593: void
! 594: status_message_clear(struct client *c)
! 595: {
! 596: if (c->message_string == NULL)
! 597: return;
! 598:
! 599: free(c->message_string);
! 600: c->message_string = NULL;
! 601:
! 602: if (c->prompt_string == NULL)
! 603: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
! 604: c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
! 605:
! 606: screen_reinit(&c->status);
! 607: }
! 608:
! 609: /* Clear status line message after timer expires. */
! 610: static void
! 611: status_message_callback(__unused int fd, __unused short event, void *data)
! 612: {
! 613: struct client *c = data;
! 614:
! 615: status_message_clear(c);
! 616: }
! 617:
! 618: /* Draw client message on status line of present else on last line. */
! 619: int
! 620: status_message_redraw(struct client *c)
! 621: {
! 622: struct screen_write_ctx ctx;
! 623: struct session *s = c->session;
! 624: struct screen old_status;
! 625: size_t len;
! 626: struct grid_cell gc;
! 627:
! 628: if (c->tty.sx == 0 || c->tty.sy == 0)
! 629: return (0);
! 630: memcpy(&old_status, &c->status, sizeof old_status);
! 631: screen_init(&c->status, c->tty.sx, 1, 0);
! 632:
! 633: len = screen_write_strlen("%s", c->message_string);
! 634: if (len > c->tty.sx)
! 635: len = c->tty.sx;
! 636:
! 637: style_apply(&gc, s->options, "message-style");
! 638:
! 639: screen_write_start(&ctx, NULL, &c->status);
! 640:
! 641: screen_write_cursormove(&ctx, 0, 0);
! 642: screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
! 643: for (; len < c->tty.sx; len++)
! 644: screen_write_putc(&ctx, &gc, ' ');
! 645:
! 646: screen_write_stop(&ctx);
! 647:
! 648: if (grid_compare(c->status.grid, old_status.grid) == 0) {
! 649: screen_free(&old_status);
! 650: return (0);
! 651: }
! 652: screen_free(&old_status);
! 653: return (1);
! 654: }
! 655:
! 656: /* Enable status line prompt. */
! 657: void
! 658: status_prompt_set(struct client *c, const char *msg, const char *input,
! 659: int (*callbackfn)(void *, const char *, int), void (*freefn)(void *),
! 660: void *data, int flags)
! 661: {
! 662: struct format_tree *ft;
! 663: time_t t;
! 664: char *tmp;
! 665:
! 666: ft = format_create(NULL, FORMAT_NONE, 0);
! 667: format_defaults(ft, c, NULL, NULL, NULL);
! 668:
! 669: t = time(NULL);
! 670: tmp = format_expand_time(ft, input, t);
! 671:
! 672: status_message_clear(c);
! 673: status_prompt_clear(c);
! 674:
! 675: c->prompt_string = format_expand_time(ft, msg, t);
! 676:
! 677: c->prompt_buffer = utf8_fromcstr(tmp);
! 678: c->prompt_index = utf8_strlen(c->prompt_buffer);
! 679:
! 680: c->prompt_callbackfn = callbackfn;
! 681: c->prompt_freefn = freefn;
! 682: c->prompt_data = data;
! 683:
! 684: c->prompt_hindex = 0;
! 685:
! 686: c->prompt_flags = flags;
! 687: c->prompt_mode = PROMPT_ENTRY;
! 688:
! 689: if (~flags & PROMPT_INCREMENTAL)
! 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. */
! 698: void
! 699: status_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. */
! 720: void
! 721: status_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, FORMAT_NONE, 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. */
! 749: int
! 750: status_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:
! 823: finished:
! 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? */
! 835: static int
! 836: status_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? */
! 844: static int
! 845: status_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: */
! 856: static int
! 857: status_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. */
! 974: int
! 975: status_prompt_key(struct client *c, key_code key)
! 976: {
! 977: struct options *oo = c->session->options;
! 978: struct paste_buffer *pb;
! 979: char *s, *cp, word[64], prefix = '=';
! 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, 1);
! 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:
! 1010: process_key:
! 1011: switch (key) {
! 1012: case KEYC_LEFT:
! 1013: case '\002': /* C-b */
! 1014: if (c->prompt_index > 0) {
! 1015: c->prompt_index--;
! 1016: break;
! 1017: }
! 1018: break;
! 1019: case KEYC_RIGHT:
! 1020: case '\006': /* C-f */
! 1021: if (c->prompt_index < size) {
! 1022: c->prompt_index++;
! 1023: break;
! 1024: }
! 1025: break;
! 1026: case KEYC_HOME:
! 1027: case '\001': /* C-a */
! 1028: if (c->prompt_index != 0) {
! 1029: c->prompt_index = 0;
! 1030: break;
! 1031: }
! 1032: break;
! 1033: case KEYC_END:
! 1034: case '\005': /* C-e */
! 1035: if (c->prompt_index != size) {
! 1036: c->prompt_index = size;
! 1037: break;
! 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: goto changed;
! 1098: case KEYC_BSPACE:
! 1099: case '\010': /* C-h */
! 1100: if (c->prompt_index != 0) {
! 1101: if (c->prompt_index == size)
! 1102: c->prompt_buffer[--c->prompt_index].size = 0;
! 1103: else {
! 1104: memmove(c->prompt_buffer + c->prompt_index - 1,
! 1105: c->prompt_buffer + c->prompt_index,
! 1106: (size + 1 - c->prompt_index) *
! 1107: sizeof *c->prompt_buffer);
! 1108: c->prompt_index--;
! 1109: }
! 1110: goto changed;
! 1111: }
! 1112: break;
! 1113: case KEYC_DC:
! 1114: case '\004': /* C-d */
! 1115: if (c->prompt_index != size) {
! 1116: memmove(c->prompt_buffer + c->prompt_index,
! 1117: c->prompt_buffer + c->prompt_index + 1,
! 1118: (size + 1 - c->prompt_index) *
! 1119: sizeof *c->prompt_buffer);
! 1120: goto changed;
! 1121: }
! 1122: break;
! 1123: case '\025': /* C-u */
! 1124: c->prompt_buffer[0].size = 0;
! 1125: c->prompt_index = 0;
! 1126: goto changed;
! 1127: case '\013': /* C-k */
! 1128: if (c->prompt_index < size) {
! 1129: c->prompt_buffer[c->prompt_index].size = 0;
! 1130: goto changed;
! 1131: }
! 1132: break;
! 1133: case '\027': /* C-w */
! 1134: ws = options_get_string(oo, "word-separators");
! 1135: idx = c->prompt_index;
! 1136:
! 1137: /* Find a non-separator. */
! 1138: while (idx != 0) {
! 1139: idx--;
! 1140: if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
! 1141: break;
! 1142: }
! 1143:
! 1144: /* Find the separator at the beginning of the word. */
! 1145: while (idx != 0) {
! 1146: idx--;
! 1147: if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
! 1148: /* Go back to the word. */
! 1149: idx++;
! 1150: break;
! 1151: }
! 1152: }
! 1153:
! 1154: memmove(c->prompt_buffer + idx,
! 1155: c->prompt_buffer + c->prompt_index,
! 1156: (size + 1 - c->prompt_index) *
! 1157: sizeof *c->prompt_buffer);
! 1158: memset(c->prompt_buffer + size - (c->prompt_index - idx),
! 1159: '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer);
! 1160: c->prompt_index = idx;
! 1161:
! 1162: goto changed;
! 1163: case 'f'|KEYC_ESCAPE:
! 1164: ws = options_get_string(oo, "word-separators");
! 1165:
! 1166: /* Find a word. */
! 1167: while (c->prompt_index != size) {
! 1168: idx = ++c->prompt_index;
! 1169: if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
! 1170: break;
! 1171: }
! 1172:
! 1173: /* Find the separator at the end of the word. */
! 1174: while (c->prompt_index != size) {
! 1175: idx = ++c->prompt_index;
! 1176: if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
! 1177: break;
! 1178: }
! 1179:
! 1180: /* Back up to the end-of-word like vi. */
! 1181: if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
! 1182: c->prompt_index != 0)
! 1183: c->prompt_index--;
! 1184:
! 1185: goto changed;
! 1186: case 'b'|KEYC_ESCAPE:
! 1187: ws = options_get_string(oo, "word-separators");
! 1188:
! 1189: /* Find a non-separator. */
! 1190: while (c->prompt_index != 0) {
! 1191: idx = --c->prompt_index;
! 1192: if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
! 1193: break;
! 1194: }
! 1195:
! 1196: /* Find the separator at the beginning of the word. */
! 1197: while (c->prompt_index != 0) {
! 1198: idx = --c->prompt_index;
! 1199: if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
! 1200: /* Go back to the word. */
! 1201: c->prompt_index++;
! 1202: break;
! 1203: }
! 1204: }
! 1205: goto changed;
! 1206: case KEYC_UP:
! 1207: case '\020': /* C-p */
! 1208: histstr = status_prompt_up_history(&c->prompt_hindex);
! 1209: if (histstr == NULL)
! 1210: break;
! 1211: free(c->prompt_buffer);
! 1212: c->prompt_buffer = utf8_fromcstr(histstr);
! 1213: c->prompt_index = utf8_strlen(c->prompt_buffer);
! 1214: goto changed;
! 1215: case KEYC_DOWN:
! 1216: case '\016': /* C-n */
! 1217: histstr = status_prompt_down_history(&c->prompt_hindex);
! 1218: if (histstr == NULL)
! 1219: break;
! 1220: free(c->prompt_buffer);
! 1221: c->prompt_buffer = utf8_fromcstr(histstr);
! 1222: c->prompt_index = utf8_strlen(c->prompt_buffer);
! 1223: goto changed;
! 1224: case '\031': /* C-y */
! 1225: if ((pb = paste_get_top(NULL)) == NULL)
! 1226: break;
! 1227: bufdata = paste_buffer_data(pb, &bufsize);
! 1228: for (n = 0; n < bufsize; n++) {
! 1229: ch = (u_char)bufdata[n];
! 1230: if (ch < 32 || ch >= 127)
! 1231: break;
! 1232: }
! 1233:
! 1234: c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
! 1235: sizeof *c->prompt_buffer);
! 1236: if (c->prompt_index == size) {
! 1237: for (idx = 0; idx < n; idx++) {
! 1238: ud = &c->prompt_buffer[c->prompt_index + idx];
! 1239: utf8_set(ud, bufdata[idx]);
! 1240: }
! 1241: c->prompt_index += n;
! 1242: c->prompt_buffer[c->prompt_index].size = 0;
! 1243: } else {
! 1244: memmove(c->prompt_buffer + c->prompt_index + n,
! 1245: c->prompt_buffer + c->prompt_index,
! 1246: (size + 1 - c->prompt_index) *
! 1247: sizeof *c->prompt_buffer);
! 1248: for (idx = 0; idx < n; idx++) {
! 1249: ud = &c->prompt_buffer[c->prompt_index + idx];
! 1250: utf8_set(ud, bufdata[idx]);
! 1251: }
! 1252: c->prompt_index += n;
! 1253: }
! 1254: goto changed;
! 1255: case '\024': /* C-t */
! 1256: idx = c->prompt_index;
! 1257: if (idx < size)
! 1258: idx++;
! 1259: if (idx >= 2) {
! 1260: utf8_copy(&tmp, &c->prompt_buffer[idx - 2]);
! 1261: utf8_copy(&c->prompt_buffer[idx - 2],
! 1262: &c->prompt_buffer[idx - 1]);
! 1263: utf8_copy(&c->prompt_buffer[idx - 1], &tmp);
! 1264: c->prompt_index = idx;
! 1265: goto changed;
! 1266: }
! 1267: break;
! 1268: case '\r':
! 1269: case '\n':
! 1270: s = utf8_tocstr(c->prompt_buffer);
! 1271: if (*s != '\0')
! 1272: status_prompt_add_history(s);
! 1273: if (c->prompt_callbackfn(c->prompt_data, s, 1) == 0)
! 1274: status_prompt_clear(c);
! 1275: free(s);
! 1276: break;
! 1277: case '\033': /* Escape */
! 1278: case '\003': /* C-c */
! 1279: if (c->prompt_callbackfn(c->prompt_data, NULL, 1) == 0)
! 1280: status_prompt_clear(c);
! 1281: break;
! 1282: case '\022': /* C-r */
! 1283: if (c->prompt_flags & PROMPT_INCREMENTAL) {
! 1284: prefix = '-';
! 1285: goto changed;
! 1286: }
! 1287: break;
! 1288: case '\023': /* C-s */
! 1289: if (c->prompt_flags & PROMPT_INCREMENTAL) {
! 1290: prefix = '+';
! 1291: goto changed;
! 1292: }
! 1293: break;
! 1294: default:
! 1295: goto append_key;
! 1296: }
! 1297:
! 1298: c->flags |= CLIENT_STATUS;
! 1299: return (0);
! 1300:
! 1301: append_key:
! 1302: if (key <= 0x1f || key >= KEYC_BASE)
! 1303: return (0);
! 1304: if (utf8_split(key, &tmp) != UTF8_DONE)
! 1305: return (0);
! 1306:
! 1307: c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2,
! 1308: sizeof *c->prompt_buffer);
! 1309:
! 1310: if (c->prompt_index == size) {
! 1311: utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
! 1312: c->prompt_index++;
! 1313: c->prompt_buffer[c->prompt_index].size = 0;
! 1314: } else {
! 1315: memmove(c->prompt_buffer + c->prompt_index + 1,
! 1316: c->prompt_buffer + c->prompt_index,
! 1317: (size + 1 - c->prompt_index) *
! 1318: sizeof *c->prompt_buffer);
! 1319: utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
! 1320: c->prompt_index++;
! 1321: }
! 1322:
! 1323: if (c->prompt_flags & PROMPT_SINGLE) {
! 1324: s = utf8_tocstr(c->prompt_buffer);
! 1325: if (strlen(s) != 1)
! 1326: status_prompt_clear(c);
! 1327: else if (c->prompt_callbackfn(c->prompt_data, s, 1) == 0)
! 1328: status_prompt_clear(c);
! 1329: free(s);
! 1330: }
! 1331:
! 1332: changed:
! 1333: c->flags |= CLIENT_STATUS;
! 1334: if (c->prompt_flags & PROMPT_INCREMENTAL) {
! 1335: s = utf8_tocstr(c->prompt_buffer);
! 1336: xasprintf(&cp, "%c%s", prefix, s);
! 1337: c->prompt_callbackfn(c->prompt_data, cp, 0);
! 1338: free(cp);
! 1339: free(s);
! 1340: }
! 1341: return (0);
! 1342: }
! 1343:
! 1344: /* Get previous line from the history. */
! 1345: static const char *
! 1346: status_prompt_up_history(u_int *idx)
! 1347: {
! 1348: /*
! 1349: * History runs from 0 to size - 1. Index is from 0 to size. Zero is
! 1350: * empty.
! 1351: */
! 1352:
! 1353: if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
! 1354: return (NULL);
! 1355: (*idx)++;
! 1356: return (status_prompt_hlist[status_prompt_hsize - *idx]);
! 1357: }
! 1358:
! 1359: /* Get next line from the history. */
! 1360: static const char *
! 1361: status_prompt_down_history(u_int *idx)
! 1362: {
! 1363: if (status_prompt_hsize == 0 || *idx == 0)
! 1364: return ("");
! 1365: (*idx)--;
! 1366: if (*idx == 0)
! 1367: return ("");
! 1368: return (status_prompt_hlist[status_prompt_hsize - *idx]);
! 1369: }
! 1370:
! 1371: /* Add line to the history. */
! 1372: static void
! 1373: status_prompt_add_history(const char *line)
! 1374: {
! 1375: size_t size;
! 1376:
! 1377: if (status_prompt_hsize > 0 &&
! 1378: strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
! 1379: return;
! 1380:
! 1381: if (status_prompt_hsize == PROMPT_HISTORY) {
! 1382: free(status_prompt_hlist[0]);
! 1383:
! 1384: size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
! 1385: memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
! 1386:
! 1387: status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
! 1388: return;
! 1389: }
! 1390:
! 1391: status_prompt_hlist = xreallocarray(status_prompt_hlist,
! 1392: status_prompt_hsize + 1, sizeof *status_prompt_hlist);
! 1393: status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
! 1394: }
! 1395:
! 1396: /* Build completion list. */
! 1397: static const char **
! 1398: status_prompt_complete_list(u_int *size, const char *s)
! 1399: {
! 1400: const char **list = NULL, **layout;
! 1401: const struct cmd_entry **cmdent;
! 1402: const struct options_table_entry *oe;
! 1403: const char *layouts[] = {
! 1404: "even-horizontal", "even-vertical", "main-horizontal",
! 1405: "main-vertical", "tiled", NULL
! 1406: };
! 1407:
! 1408: *size = 0;
! 1409: for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
! 1410: if (strncmp((*cmdent)->name, s, strlen(s)) == 0) {
! 1411: list = xreallocarray(list, (*size) + 1, sizeof *list);
! 1412: list[(*size)++] = (*cmdent)->name;
! 1413: }
! 1414: }
! 1415: for (oe = options_table; oe->name != NULL; oe++) {
! 1416: if (strncmp(oe->name, s, strlen(s)) == 0) {
! 1417: list = xreallocarray(list, (*size) + 1, sizeof *list);
! 1418: list[(*size)++] = oe->name;
! 1419: }
! 1420: }
! 1421: for (layout = layouts; *layout != NULL; layout++) {
! 1422: if (strncmp(*layout, s, strlen(s)) == 0) {
! 1423: list = xreallocarray(list, (*size) + 1, sizeof *list);
! 1424: list[(*size)++] = *layout;
! 1425: }
! 1426: }
! 1427: return (list);
! 1428: }
! 1429:
! 1430: /* Find longest prefix. */
! 1431: static char *
! 1432: status_prompt_complete_prefix(const char **list, u_int size)
! 1433: {
! 1434: char *out;
! 1435: u_int i;
! 1436: size_t j;
! 1437:
! 1438: out = xstrdup(list[0]);
! 1439: for (i = 1; i < size; i++) {
! 1440: j = strlen(list[i]);
! 1441: if (j > strlen(out))
! 1442: j = strlen(out);
! 1443: for (; j > 0; j--) {
! 1444: if (out[j - 1] != list[i][j - 1])
! 1445: out[j - 1] = '\0';
! 1446: }
! 1447: }
! 1448: return (out);
! 1449: }
! 1450:
! 1451: /* Complete word. */
! 1452: static char *
! 1453: status_prompt_complete(struct session *session, const char *s)
! 1454: {
! 1455: const char **list = NULL, *colon;
! 1456: u_int size = 0, i;
! 1457: struct session *s_loop;
! 1458: struct winlink *wl;
! 1459: struct window *w;
! 1460: char *copy, *out, *tmp;
! 1461:
! 1462: if (*s == '\0')
! 1463: return (NULL);
! 1464: out = NULL;
! 1465:
! 1466: if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
! 1467: list = status_prompt_complete_list(&size, s);
! 1468: if (size == 0)
! 1469: out = NULL;
! 1470: else if (size == 1)
! 1471: xasprintf(&out, "%s ", list[0]);
! 1472: else
! 1473: out = status_prompt_complete_prefix(list, size);
! 1474: free(list);
! 1475: return (out);
! 1476: }
! 1477: copy = xstrdup(s);
! 1478:
! 1479: colon = ":";
! 1480: if (copy[strlen(copy) - 1] == ':')
! 1481: copy[strlen(copy) - 1] = '\0';
! 1482: else
! 1483: colon = "";
! 1484: s = copy + 2;
! 1485:
! 1486: RB_FOREACH(s_loop, sessions, &sessions) {
! 1487: if (strncmp(s_loop->name, s, strlen(s)) == 0) {
! 1488: list = xreallocarray(list, size + 2, sizeof *list);
! 1489: list[size++] = s_loop->name;
! 1490: }
! 1491: }
! 1492: if (size == 1) {
! 1493: out = xstrdup(list[0]);
! 1494: if (session_find(list[0]) != NULL)
! 1495: colon = ":";
! 1496: } else if (size != 0)
! 1497: out = status_prompt_complete_prefix(list, size);
! 1498: if (out != NULL) {
! 1499: xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
! 1500: out = tmp;
! 1501: goto found;
! 1502: }
! 1503:
! 1504: colon = "";
! 1505: if (*s == ':') {
! 1506: RB_FOREACH(wl, winlinks, &session->windows) {
! 1507: xasprintf(&tmp, ":%s", wl->window->name);
! 1508: if (strncmp(tmp, s, strlen(s)) == 0){
! 1509: list = xreallocarray(list, size + 1,
! 1510: sizeof *list);
! 1511: list[size++] = tmp;
! 1512: continue;
! 1513: }
! 1514: free(tmp);
! 1515:
! 1516: xasprintf(&tmp, ":%d", wl->idx);
! 1517: if (strncmp(tmp, s, strlen(s)) == 0) {
! 1518: list = xreallocarray(list, size + 1,
! 1519: sizeof *list);
! 1520: list[size++] = tmp;
! 1521: continue;
! 1522: }
! 1523: free(tmp);
! 1524: }
! 1525: } else {
! 1526: RB_FOREACH(s_loop, sessions, &sessions) {
! 1527: RB_FOREACH(wl, winlinks, &s_loop->windows) {
! 1528: w = wl->window;
! 1529:
! 1530: xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
! 1531: if (strncmp(tmp, s, strlen(s)) == 0) {
! 1532: list = xreallocarray(list, size + 1,
! 1533: sizeof *list);
! 1534: list[size++] = tmp;
! 1535: continue;
! 1536: }
! 1537: free(tmp);
! 1538:
! 1539: xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
! 1540: if (strncmp(tmp, s, strlen(s)) == 0) {
! 1541: list = xreallocarray(list, size + 1,
! 1542: sizeof *list);
! 1543: list[size++] = tmp;
! 1544: continue;
! 1545: }
! 1546: free(tmp);
! 1547: }
! 1548: }
! 1549: }
! 1550: if (size == 1) {
! 1551: out = xstrdup(list[0]);
! 1552: colon = " ";
! 1553: } else if (size != 0)
! 1554: out = status_prompt_complete_prefix(list, size);
! 1555: if (out != NULL) {
! 1556: xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
! 1557: out = tmp;
! 1558: }
! 1559:
! 1560: for (i = 0; i < size; i++)
! 1561: free((void *)list[i]);
! 1562:
! 1563: found:
! 1564: free(copy);
! 1565: free(list);
! 1566: return (out);
! 1567: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>