Annotation of src/usr.bin/tmux/status.c, Revision 1.129
1.129 ! nicm 1: /* $OpenBSD: status.c,v 1.128 2015/05/06 23:56:46 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20: #include <sys/time.h>
21:
22: #include <errno.h>
23: #include <limits.h>
24: #include <stdarg.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <time.h>
28: #include <unistd.h>
29:
30: #include "tmux.h"
31:
1.123 nicm 32: char *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *,
33: size_t *);
34: char *status_redraw_get_right(struct client *, time_t, int,
35: struct grid_cell *, size_t *);
36: char *status_print(struct client *, struct winlink *, time_t,
37: struct grid_cell *);
38: char *status_replace(struct client *, struct winlink *, const char *, time_t);
1.129 ! nicm 39: void status_replace1(char **, char **, char *, size_t);
1.42 nicm 40: void status_message_callback(int, short, void *);
1.1 nicm 41:
1.65 nicm 42: const char *status_prompt_up_history(u_int *);
43: const char *status_prompt_down_history(u_int *);
44: void status_prompt_add_history(const char *);
1.128 nicm 45:
46: const char **status_prompt_complete_list(u_int *, const char *);
47: char *status_prompt_complete_prefix(const char **, u_int);
48: char *status_prompt_complete(struct session *, const char *);
1.1 nicm 49:
1.65 nicm 50: /* Status prompt history. */
1.128 nicm 51: #define PROMPT_HISTORY 100
52: char **status_prompt_hlist;
53: u_int status_prompt_hsize;
1.65 nicm 54:
1.71 nicm 55: /* Status output tree. */
56: RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp);
57:
58: /* Output tree comparison function. */
59: int
60: status_out_cmp(struct status_out *so1, struct status_out *so2)
61: {
62: return (strcmp(so1->cmd, so2->cmd));
1.87 nicm 63: }
64:
65: /* Get screen line of status line. -1 means off. */
66: int
67: status_at_line(struct client *c)
68: {
69: struct session *s = c->session;
70:
71: if (!options_get_number(&s->options, "status"))
72: return (-1);
73:
74: if (options_get_number(&s->options, "status-position") == 0)
75: return (0);
76: return (c->tty.sy - 1);
1.71 nicm 77: }
78:
1.48 nicm 79: /* Retrieve options for left string. */
80: char *
1.120 nicm 81: status_redraw_get_left(struct client *c, time_t t, int utf8flag,
82: struct grid_cell *gc, size_t *size)
1.48 nicm 83: {
84: struct session *s = c->session;
1.120 nicm 85: const char *template;
1.48 nicm 86: char *left;
87: size_t leftlen;
88:
1.108 nicm 89: style_apply_update(gc, &s->options, "status-left-style");
1.48 nicm 90:
1.120 nicm 91: template = options_get_string(&s->options, "status-left");
1.123 nicm 92: left = status_replace(c, NULL, template, t);
1.48 nicm 93:
94: *size = options_get_number(&s->options, "status-left-length");
95: leftlen = screen_write_cstrlen(utf8flag, "%s", left);
96: if (leftlen < *size)
97: *size = leftlen;
98: return (left);
99: }
100:
101: /* Retrieve options for right string. */
102: char *
1.120 nicm 103: status_redraw_get_right(struct client *c, time_t t, int utf8flag,
104: struct grid_cell *gc, size_t *size)
1.48 nicm 105: {
106: struct session *s = c->session;
1.120 nicm 107: const char *template;
1.48 nicm 108: char *right;
109: size_t rightlen;
110:
1.108 nicm 111: style_apply_update(gc, &s->options, "status-right-style");
1.48 nicm 112:
1.120 nicm 113: template = options_get_string(&s->options, "status-right");
1.123 nicm 114: right = status_replace(c, NULL, template, t);
1.48 nicm 115:
116: *size = options_get_number(&s->options, "status-right-length");
117: rightlen = screen_write_cstrlen(utf8flag, "%s", right);
118: if (rightlen < *size)
119: *size = rightlen;
120: return (right);
121: }
122:
1.125 nicm 123: /* Get window at window list position. */
124: struct window *
125: status_get_window_at(struct client *c, u_int x)
1.73 nicm 126: {
127: struct session *s = c->session;
128: struct winlink *wl;
1.115 nicm 129: struct options *oo;
130: size_t len;
1.73 nicm 131:
1.88 nicm 132: x += c->wlmouse;
1.73 nicm 133: RB_FOREACH(wl, winlinks, &s->windows) {
1.115 nicm 134: oo = &wl->window->options;
1.125 nicm 135: len = strlen(options_get_string(oo, "window-status-separator"));
1.115 nicm 136:
1.125 nicm 137: if (x < wl->status_width)
138: return (wl->window);
1.115 nicm 139: x -= wl->status_width + len;
1.73 nicm 140: }
1.125 nicm 141: return (NULL);
1.73 nicm 142: }
143:
1.1 nicm 144: /* Draw status for client on the last lines of given context. */
145: int
146: status_redraw(struct client *c)
147: {
1.48 nicm 148: struct screen_write_ctx ctx;
149: struct session *s = c->session;
150: struct winlink *wl;
151: struct screen old_status, window_list;
152: struct grid_cell stdgc, lgc, rgc, gc;
1.91 nicm 153: struct options *oo;
1.48 nicm 154: time_t t;
1.91 nicm 155: char *left, *right, *sep;
1.48 nicm 156: u_int offset, needed;
157: u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
1.91 nicm 158: size_t llen, rlen, seplen;
1.48 nicm 159: int larrow, rarrow, utf8flag;
1.1 nicm 160:
1.49 nicm 161: /* No status line? */
1.7 nicm 162: if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
163: return (1);
1.48 nicm 164: left = right = NULL;
1.7 nicm 165: larrow = rarrow = 0;
1.1 nicm 166:
1.48 nicm 167: /* Update status timer. */
1.1 nicm 168: if (gettimeofday(&c->status_timer, NULL) != 0)
1.34 nicm 169: fatal("gettimeofday failed");
1.48 nicm 170: t = c->status_timer.tv_sec;
171:
172: /* Set up default colour. */
1.108 nicm 173: style_apply(&stdgc, &s->options, "status-style");
1.1 nicm 174:
1.48 nicm 175: /* Create the target screen. */
176: memcpy(&old_status, &c->status, sizeof old_status);
177: screen_init(&c->status, c->tty.sx, 1, 0);
178: screen_write_start(&ctx, NULL, &c->status);
179: for (offset = 0; offset < c->tty.sx; offset++)
180: screen_write_putc(&ctx, &stdgc, ' ');
181: screen_write_stop(&ctx);
182:
183: /* If the height is one line, blank status line. */
184: if (c->tty.sy <= 1)
185: goto out;
1.1 nicm 186:
1.48 nicm 187: /* Get UTF-8 flag. */
1.3 nicm 188: utf8flag = options_get_number(&s->options, "status-utf8");
189:
1.48 nicm 190: /* Work out left and right strings. */
191: memcpy(&lgc, &stdgc, sizeof lgc);
192: left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen);
193: memcpy(&rgc, &stdgc, sizeof rgc);
194: right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen);
1.1 nicm 195:
196: /*
1.48 nicm 197: * Figure out how much space we have for the window list. If there
198: * isn't enough space, just show a blank status line.
1.1 nicm 199: */
1.55 nicm 200: needed = 0;
1.1 nicm 201: if (llen != 0)
1.118 nicm 202: needed += llen;
1.1 nicm 203: if (rlen != 0)
1.118 nicm 204: needed += rlen;
1.48 nicm 205: if (c->tty.sx == 0 || c->tty.sx <= needed)
206: goto out;
207: wlavailable = c->tty.sx - needed;
1.1 nicm 208:
1.48 nicm 209: /* Calculate the total size needed for the window list. */
210: wlstart = wloffset = wlwidth = 0;
1.1 nicm 211: RB_FOREACH(wl, winlinks, &s->windows) {
1.94 nicm 212: free(wl->status_text);
1.48 nicm 213: memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
214: wl->status_text = status_print(c, wl, t, &wl->status_cell);
1.55 nicm 215: wl->status_width =
1.48 nicm 216: screen_write_cstrlen(utf8flag, "%s", wl->status_text);
217:
1.1 nicm 218: if (wl == s->curw)
1.48 nicm 219: wloffset = wlwidth;
1.91 nicm 220:
221: oo = &wl->window->options;
222: sep = options_get_string(oo, "window-status-separator");
223: seplen = screen_write_strlen(utf8flag, "%s", sep);
224: wlwidth += wl->status_width + seplen;
1.48 nicm 225: }
226:
227: /* Create a new screen for the window list. */
228: screen_init(&window_list, wlwidth, 1, 0);
229:
230: /* And draw the window list into it. */
231: screen_write_start(&ctx, NULL, &window_list);
232: RB_FOREACH(wl, winlinks, &s->windows) {
1.55 nicm 233: screen_write_cnputs(&ctx,
1.48 nicm 234: -1, &wl->status_cell, utf8flag, "%s", wl->status_text);
1.91 nicm 235:
236: oo = &wl->window->options;
237: sep = options_get_string(oo, "window-status-separator");
238: screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep);
1.1 nicm 239: }
1.48 nicm 240: screen_write_stop(&ctx);
1.1 nicm 241:
1.48 nicm 242: /* If there is enough space for the total width, skip to draw now. */
243: if (wlwidth <= wlavailable)
1.1 nicm 244: goto draw;
245:
246: /* Find size of current window text. */
1.48 nicm 247: wlsize = s->curw->status_width;
1.1 nicm 248:
249: /*
1.48 nicm 250: * If the current window is already on screen, good to draw from the
1.1 nicm 251: * start and just leave off the end.
252: */
1.48 nicm 253: if (wloffset + wlsize < wlavailable) {
254: if (wlavailable > 0) {
255: rarrow = 1;
256: wlavailable--;
257: }
258: wlwidth = wlavailable;
259: } else {
260: /*
261: * Work out how many characters we need to omit from the
262: * start. There are wlavailable characters to fill, and
263: * wloffset + wlsize must be the last. So, the start character
264: * is wloffset + wlsize - wlavailable.
265: */
266: if (wlavailable > 0) {
267: larrow = 1;
268: wlavailable--;
269: }
1.55 nicm 270:
1.48 nicm 271: wlstart = wloffset + wlsize - wlavailable;
272: if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
1.1 nicm 273: rarrow = 1;
1.48 nicm 274: wlstart++;
275: wlavailable--;
1.1 nicm 276: }
1.48 nicm 277: wlwidth = wlavailable;
278: }
1.1 nicm 279:
1.48 nicm 280: /* Bail if anything is now too small too. */
281: if (wlwidth == 0 || wlavailable == 0) {
282: screen_free(&window_list);
283: goto out;
1.1 nicm 284: }
285:
286: /*
1.48 nicm 287: * Now the start position is known, work out the state of the left and
288: * right arrows.
1.1 nicm 289: */
290: offset = 0;
291: RB_FOREACH(wl, winlinks, &s->windows) {
1.63 nicm 292: if (wl->flags & WINLINK_ALERTFLAGS &&
293: larrow == 1 && offset < wlstart)
294: larrow = -1;
1.1 nicm 295:
1.48 nicm 296: offset += wl->status_width;
1.1 nicm 297:
1.63 nicm 298: if (wl->flags & WINLINK_ALERTFLAGS &&
299: rarrow == 1 && offset > wlstart + wlwidth)
300: rarrow = -1;
1.1 nicm 301: }
302:
1.48 nicm 303: draw:
1.55 nicm 304: /* Begin drawing. */
1.48 nicm 305: screen_write_start(&ctx, NULL, &c->status);
1.1 nicm 306:
1.48 nicm 307: /* Draw the left string and arrow. */
308: screen_write_cursormove(&ctx, 0, 0);
1.118 nicm 309: if (llen != 0)
1.48 nicm 310: screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left);
1.1 nicm 311: if (larrow != 0) {
312: memcpy(&gc, &stdgc, sizeof gc);
313: if (larrow == -1)
314: gc.attr ^= GRID_ATTR_REVERSE;
315: screen_write_putc(&ctx, &gc, '<');
316: }
1.48 nicm 317:
318: /* Draw the right string and arrow. */
1.1 nicm 319: if (rarrow != 0) {
1.118 nicm 320: screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
1.1 nicm 321: memcpy(&gc, &stdgc, sizeof gc);
322: if (rarrow == -1)
323: gc.attr ^= GRID_ATTR_REVERSE;
324: screen_write_putc(&ctx, &gc, '>');
1.48 nicm 325: } else
1.118 nicm 326: screen_write_cursormove(&ctx, c->tty.sx - rlen, 0);
327: if (rlen != 0)
1.48 nicm 328: screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right);
1.1 nicm 329:
1.48 nicm 330: /* Figure out the offset for the window list. */
1.58 nicm 331: if (llen != 0)
1.118 nicm 332: wloffset = llen;
1.58 nicm 333: else
334: wloffset = 0;
1.48 nicm 335: if (wlwidth < wlavailable) {
336: switch (options_get_number(&s->options, "status-justify")) {
1.119 sthen 337: case 1: /* centred */
1.58 nicm 338: wloffset += (wlavailable - wlwidth) / 2;
1.48 nicm 339: break;
340: case 2: /* right */
1.58 nicm 341: wloffset += (wlavailable - wlwidth);
1.48 nicm 342: break;
343: }
344: }
345: if (larrow != 0)
346: wloffset++;
347:
348: /* Copy the window list. */
1.88 nicm 349: c->wlmouse = -wloffset + wlstart;
1.48 nicm 350: screen_write_cursormove(&ctx, wloffset, 0);
351: screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
1.55 nicm 352: screen_free(&window_list);
1.1 nicm 353:
1.48 nicm 354: screen_write_stop(&ctx);
1.1 nicm 355:
356: out:
1.94 nicm 357: free(left);
358: free(right);
1.1 nicm 359:
360: if (grid_compare(c->status.grid, old_status.grid) == 0) {
361: screen_free(&old_status);
362: return (0);
363: }
364: screen_free(&old_status);
365: return (1);
366: }
367:
1.46 nicm 368: /* Replace special sequences in fmt. */
369: char *
1.123 nicm 370: status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
1.46 nicm 371: {
1.96 nicm 372: struct format_tree *ft;
1.129 ! nicm 373: char *expanded;
1.47 nicm 374:
1.93 nicm 375: if (fmt == NULL)
376: return (xstrdup(""));
377:
1.129 ! nicm 378: ft = format_create_status(1);
! 379: format_defaults(ft, c, NULL, wl, NULL);
1.1 nicm 380:
1.129 ! nicm 381: expanded = format_expand_time(ft, fmt, t);
1.46 nicm 382:
1.99 nicm 383: format_free(ft);
384: return (expanded);
1.1 nicm 385: }
386:
1.46 nicm 387: /* Return winlink status line entry and adjust gc as necessary. */
1.1 nicm 388: char *
1.120 nicm 389: status_print(struct client *c, struct winlink *wl, time_t t,
390: struct grid_cell *gc)
1.1 nicm 391: {
1.33 nicm 392: struct options *oo = &wl->window->options;
1.47 nicm 393: struct session *s = c->session;
394: const char *fmt;
395: char *text;
1.1 nicm 396:
1.108 nicm 397: style_apply_update(gc, oo, "window-status-style");
1.47 nicm 398: fmt = options_get_string(oo, "window-status-format");
1.14 nicm 399: if (wl == s->curw) {
1.108 nicm 400: style_apply_update(gc, oo, "window-status-current-style");
1.47 nicm 401: fmt = options_get_string(oo, "window-status-current-format");
1.95 nicm 402: }
1.108 nicm 403: if (wl == TAILQ_FIRST(&s->lastw))
404: style_apply_update(gc, oo, "window-status-last-style");
405:
406: if (wl->flags & WINLINK_BELL)
407: style_apply_update(gc, oo, "window-status-bell-style");
408: else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
409: style_apply_update(gc, oo, "window-status-activity-style");
1.1 nicm 410:
1.123 nicm 411: text = status_replace(c, wl, fmt, t);
1.1 nicm 412: return (text);
413: }
414:
1.46 nicm 415: /* Set a status line message. */
1.117 nicm 416: void
1.10 nicm 417: status_message_set(struct client *c, const char *fmt, ...)
1.1 nicm 418: {
1.44 nicm 419: struct timeval tv;
1.127 nicm 420: struct message_entry *msg, *msg1;
1.44 nicm 421: va_list ap;
422: int delay;
1.127 nicm 423: u_int first, limit;
424:
425: limit = options_get_number(&global_options, "message-limit");
1.1 nicm 426:
1.12 nicm 427: status_prompt_clear(c);
428: status_message_clear(c);
429:
1.28 nicm 430: va_start(ap, fmt);
431: xvasprintf(&c->message_string, fmt, ap);
432: va_end(ap);
433:
1.127 nicm 434: msg = xcalloc(1, sizeof *msg);
1.44 nicm 435: msg->msg_time = time(NULL);
1.127 nicm 436: msg->msg_num = c->message_next++;
1.44 nicm 437: msg->msg = xstrdup(c->message_string);
1.127 nicm 438: TAILQ_INSERT_TAIL(&c->message_log, msg, entry);
1.44 nicm 439:
1.127 nicm 440: first = c->message_next - limit;
441: TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
442: if (msg->msg_num >= first)
443: continue;
444: free(msg->msg);
445: TAILQ_REMOVE(&c->message_log, msg, entry);
446: free(msg);
1.44 nicm 447: }
448:
1.1 nicm 449: delay = options_get_number(&c->session->options, "display-time");
450: tv.tv_sec = delay / 1000;
451: tv.tv_usec = (delay % 1000) * 1000L;
1.44 nicm 452:
1.110 nicm 453: if (event_initialized(&c->message_timer))
1.90 nicm 454: evtimer_del(&c->message_timer);
1.42 nicm 455: evtimer_set(&c->message_timer, status_message_callback, c);
456: evtimer_add(&c->message_timer, &tv);
1.1 nicm 457:
458: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
459: c->flags |= CLIENT_STATUS;
460: }
461:
1.46 nicm 462: /* Clear status line message. */
1.1 nicm 463: void
464: status_message_clear(struct client *c)
465: {
466: if (c->message_string == NULL)
467: return;
468:
1.94 nicm 469: free(c->message_string);
1.1 nicm 470: c->message_string = NULL;
471:
472: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.12 nicm 473: c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
1.7 nicm 474:
475: screen_reinit(&c->status);
1.42 nicm 476: }
477:
1.46 nicm 478: /* Clear status line message after timer expires. */
1.42 nicm 479: void
480: status_message_callback(unused int fd, unused short event, void *data)
481: {
482: struct client *c = data;
483:
484: status_message_clear(c);
1.1 nicm 485: }
486:
487: /* Draw client message on status line of present else on last line. */
488: int
489: status_message_redraw(struct client *c)
490: {
491: struct screen_write_ctx ctx;
492: struct session *s = c->session;
493: struct screen old_status;
494: size_t len;
495: struct grid_cell gc;
1.51 nicm 496: int utf8flag;
1.1 nicm 497:
498: if (c->tty.sx == 0 || c->tty.sy == 0)
499: return (0);
500: memcpy(&old_status, &c->status, sizeof old_status);
501: screen_init(&c->status, c->tty.sx, 1, 0);
502:
1.51 nicm 503: utf8flag = options_get_number(&s->options, "status-utf8");
504:
505: len = screen_write_strlen(utf8flag, "%s", c->message_string);
1.1 nicm 506: if (len > c->tty.sx)
507: len = c->tty.sx;
508:
1.108 nicm 509: style_apply(&gc, &s->options, "message-style");
1.1 nicm 510:
511: screen_write_start(&ctx, NULL, &c->status);
512:
513: screen_write_cursormove(&ctx, 0, 0);
1.51 nicm 514: screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string);
1.1 nicm 515: for (; len < c->tty.sx; len++)
516: screen_write_putc(&ctx, &gc, ' ');
517:
518: screen_write_stop(&ctx);
519:
520: if (grid_compare(c->status.grid, old_status.grid) == 0) {
521: screen_free(&old_status);
522: return (0);
523: }
524: screen_free(&old_status);
525: return (1);
526: }
527:
1.46 nicm 528: /* Enable status line prompt. */
1.1 nicm 529: void
1.76 nicm 530: status_prompt_set(struct client *c, const char *msg, const char *input,
1.12 nicm 531: int (*callbackfn)(void *, const char *), void (*freefn)(void *),
532: void *data, int flags)
1.1 nicm 533: {
1.122 nicm 534: struct format_tree *ft;
535: int keys;
536: time_t t;
537:
538: ft = format_create();
539: format_defaults(ft, c, NULL, NULL, NULL);
540: t = time(NULL);
1.20 nicm 541:
1.12 nicm 542: status_message_clear(c);
543: status_prompt_clear(c);
544:
1.124 nicm 545: c->prompt_string = format_expand_time(ft, msg, t);
1.1 nicm 546:
1.124 nicm 547: c->prompt_buffer = format_expand_time(ft, input, t);
1.76 nicm 548: c->prompt_index = strlen(c->prompt_buffer);
1.1 nicm 549:
1.12 nicm 550: c->prompt_callbackfn = callbackfn;
551: c->prompt_freefn = freefn;
1.1 nicm 552: c->prompt_data = data;
553:
554: c->prompt_hindex = 0;
555:
556: c->prompt_flags = flags;
557:
1.20 nicm 558: keys = options_get_number(&c->session->options, "status-keys");
559: if (keys == MODEKEY_EMACS)
1.21 nicm 560: mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit);
1.20 nicm 561: else
1.21 nicm 562: mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit);
1.1 nicm 563:
564: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
565: c->flags |= CLIENT_STATUS;
1.122 nicm 566:
567: format_free(ft);
1.1 nicm 568: }
569:
1.46 nicm 570: /* Remove status line prompt. */
1.1 nicm 571: void
572: status_prompt_clear(struct client *c)
573: {
1.55 nicm 574: if (c->prompt_string == NULL)
1.1 nicm 575: return;
576:
1.12 nicm 577: if (c->prompt_freefn != NULL && c->prompt_data != NULL)
578: c->prompt_freefn(c->prompt_data);
1.1 nicm 579:
1.94 nicm 580: free(c->prompt_string);
1.1 nicm 581: c->prompt_string = NULL;
582:
1.94 nicm 583: free(c->prompt_buffer);
1.1 nicm 584: c->prompt_buffer = NULL;
585:
586: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.12 nicm 587: c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
1.7 nicm 588:
589: screen_reinit(&c->status);
1.27 nicm 590: }
591:
1.46 nicm 592: /* Update status line prompt with a new prompt string. */
1.27 nicm 593: void
1.76 nicm 594: status_prompt_update(struct client *c, const char *msg, const char *input)
1.27 nicm 595: {
1.122 nicm 596: struct format_tree *ft;
597: time_t t;
598:
599: ft = format_create();
600: format_defaults(ft, c, NULL, NULL, NULL);
601: t = time(NULL);
602:
1.94 nicm 603: free(c->prompt_string);
1.124 nicm 604: c->prompt_string = format_expand_time(ft, msg, t);
1.27 nicm 605:
1.94 nicm 606: free(c->prompt_buffer);
1.124 nicm 607: c->prompt_buffer = format_expand_time(ft, input, t);
1.76 nicm 608: c->prompt_index = strlen(c->prompt_buffer);
1.27 nicm 609:
610: c->prompt_hindex = 0;
611:
612: c->flags |= CLIENT_STATUS;
1.122 nicm 613:
614: format_free(ft);
1.1 nicm 615: }
616:
617: /* Draw client prompt on status line of present else on last line. */
618: int
619: status_prompt_redraw(struct client *c)
620: {
1.51 nicm 621: struct screen_write_ctx ctx;
1.1 nicm 622: struct session *s = c->session;
623: struct screen old_status;
1.29 nicm 624: size_t i, size, left, len, off;
1.51 nicm 625: struct grid_cell gc, *gcp;
626: int utf8flag;
1.1 nicm 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:
1.51 nicm 633: utf8flag = options_get_number(&s->options, "status-utf8");
634:
635: len = screen_write_strlen(utf8flag, "%s", c->prompt_string);
1.1 nicm 636: if (len > c->tty.sx)
637: len = c->tty.sx;
1.51 nicm 638: off = 0;
1.1 nicm 639:
1.79 nicm 640: /* Change colours for command mode. */
1.108 nicm 641: if (c->prompt_mdata.mode == 1)
642: style_apply(&gc, &s->options, "message-command-style");
643: else
644: style_apply(&gc, &s->options, "message-style");
1.1 nicm 645:
646: screen_write_start(&ctx, NULL, &c->status);
647:
648: screen_write_cursormove(&ctx, 0, 0);
1.51 nicm 649: screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string);
1.1 nicm 650:
651: left = c->tty.sx - len;
652: if (left != 0) {
1.51 nicm 653: size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer);
654: if (c->prompt_index >= left) {
1.17 nicm 655: off = c->prompt_index - left + 1;
1.51 nicm 656: if (c->prompt_index == size)
1.1 nicm 657: left--;
658: size = left;
659: }
1.51 nicm 660: screen_write_nputs(
661: &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off);
1.1 nicm 662:
1.55 nicm 663: for (i = len + size; i < c->tty.sx; i++)
664: screen_write_putc(&ctx, &gc, ' ');
1.1 nicm 665: }
666:
667: screen_write_stop(&ctx);
1.51 nicm 668:
669: /* Apply fake cursor. */
670: off = len + c->prompt_index - off;
671: gcp = grid_view_get_cell(c->status.grid, off, 0);
672: gcp->attr ^= GRID_ATTR_REVERSE;
1.1 nicm 673:
674: if (grid_compare(c->status.grid, old_status.grid) == 0) {
675: screen_free(&old_status);
676: return (0);
677: }
678: screen_free(&old_status);
679: return (1);
680: }
681:
682: /* Handle keys in prompt. */
683: void
684: status_prompt_key(struct client *c, int key)
685: {
1.81 nicm 686: struct session *sess = c->session;
687: struct options *oo = &sess->options;
1.1 nicm 688: struct paste_buffer *pb;
1.81 nicm 689: char *s, *first, *last, word[64], swapc;
690: const char *histstr;
1.83 nicm 691: const char *wsep = NULL;
1.53 nicm 692: u_char ch;
1.1 nicm 693: size_t size, n, off, idx;
694:
695: size = strlen(c->prompt_buffer);
1.101 nicm 696: switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) {
1.20 nicm 697: case MODEKEYEDIT_CURSORLEFT:
1.1 nicm 698: if (c->prompt_index > 0) {
699: c->prompt_index--;
700: c->flags |= CLIENT_STATUS;
701: }
702: break;
1.79 nicm 703: case MODEKEYEDIT_SWITCHMODE:
704: c->flags |= CLIENT_STATUS;
705: break;
1.20 nicm 706: case MODEKEYEDIT_SWITCHMODEAPPEND:
1.79 nicm 707: c->flags |= CLIENT_STATUS;
708: /* FALLTHROUGH */
1.20 nicm 709: case MODEKEYEDIT_CURSORRIGHT:
1.1 nicm 710: if (c->prompt_index < size) {
711: c->prompt_index++;
712: c->flags |= CLIENT_STATUS;
713: }
714: break;
1.89 nicm 715: case MODEKEYEDIT_SWITCHMODEBEGINLINE:
716: c->flags |= CLIENT_STATUS;
717: /* FALLTHROUGH */
1.20 nicm 718: case MODEKEYEDIT_STARTOFLINE:
1.1 nicm 719: if (c->prompt_index != 0) {
720: c->prompt_index = 0;
721: c->flags |= CLIENT_STATUS;
722: }
723: break;
1.89 nicm 724: case MODEKEYEDIT_SWITCHMODEAPPENDLINE:
725: c->flags |= CLIENT_STATUS;
726: /* FALLTHROUGH */
1.20 nicm 727: case MODEKEYEDIT_ENDOFLINE:
1.1 nicm 728: if (c->prompt_index != size) {
729: c->prompt_index = size;
730: c->flags |= CLIENT_STATUS;
731: }
732: break;
1.20 nicm 733: case MODEKEYEDIT_COMPLETE:
1.1 nicm 734: if (*c->prompt_buffer == '\0')
735: break;
736:
737: idx = c->prompt_index;
738: if (idx != 0)
739: idx--;
740:
741: /* Find the word we are in. */
742: first = c->prompt_buffer + idx;
743: while (first > c->prompt_buffer && *first != ' ')
744: first--;
745: while (*first == ' ')
746: first++;
747: last = c->prompt_buffer + idx;
748: while (*last != '\0' && *last != ' ')
749: last++;
750: while (*last == ' ')
751: last--;
752: if (*last != '\0')
753: last++;
754: if (last <= first ||
755: ((size_t) (last - first)) > (sizeof word) - 1)
756: break;
757: memcpy(word, first, last - first);
758: word[last - first] = '\0';
759:
760: /* And try to complete it. */
1.128 nicm 761: if ((s = status_prompt_complete(sess, word)) == NULL)
1.1 nicm 762: break;
763:
764: /* Trim out word. */
765: n = size - (last - c->prompt_buffer) + 1; /* with \0 */
766: memmove(first, last, n);
767: size -= last - first;
768:
769: /* Insert the new word. */
1.55 nicm 770: size += strlen(s);
1.1 nicm 771: off = first - c->prompt_buffer;
1.116 nicm 772: c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1);
1.1 nicm 773: first = c->prompt_buffer + off;
1.55 nicm 774: memmove(first + strlen(s), first, n);
775: memcpy(first, s, strlen(s));
1.1 nicm 776:
777: c->prompt_index = (first - c->prompt_buffer) + strlen(s);
1.94 nicm 778: free(s);
1.1 nicm 779:
780: c->flags |= CLIENT_STATUS;
781: break;
1.20 nicm 782: case MODEKEYEDIT_BACKSPACE:
1.1 nicm 783: if (c->prompt_index != 0) {
784: if (c->prompt_index == size)
785: c->prompt_buffer[--c->prompt_index] = '\0';
786: else {
787: memmove(c->prompt_buffer + c->prompt_index - 1,
788: c->prompt_buffer + c->prompt_index,
789: size + 1 - c->prompt_index);
790: c->prompt_index--;
791: }
792: c->flags |= CLIENT_STATUS;
793: }
794: break;
1.55 nicm 795: case MODEKEYEDIT_DELETE:
1.105 nicm 796: case MODEKEYEDIT_SWITCHMODESUBSTITUTE:
1.1 nicm 797: if (c->prompt_index != size) {
798: memmove(c->prompt_buffer + c->prompt_index,
799: c->prompt_buffer + c->prompt_index + 1,
800: size + 1 - c->prompt_index);
1.18 nicm 801: c->flags |= CLIENT_STATUS;
802: }
1.26 nicm 803: break;
804: case MODEKEYEDIT_DELETELINE:
1.105 nicm 805: case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
1.26 nicm 806: *c->prompt_buffer = '\0';
807: c->prompt_index = 0;
808: c->flags |= CLIENT_STATUS;
1.18 nicm 809: break;
1.20 nicm 810: case MODEKEYEDIT_DELETETOENDOFLINE:
1.105 nicm 811: case MODEKEYEDIT_SWITCHMODECHANGELINE:
1.18 nicm 812: if (c->prompt_index < size) {
813: c->prompt_buffer[c->prompt_index] = '\0';
1.1 nicm 814: c->flags |= CLIENT_STATUS;
815: }
816: break;
1.81 nicm 817: case MODEKEYEDIT_DELETEWORD:
818: wsep = options_get_string(oo, "word-separators");
819: idx = c->prompt_index;
820:
821: /* Find a non-separator. */
822: while (idx != 0) {
823: idx--;
824: if (!strchr(wsep, c->prompt_buffer[idx]))
825: break;
826: }
827:
828: /* Find the separator at the beginning of the word. */
829: while (idx != 0) {
830: idx--;
831: if (strchr(wsep, c->prompt_buffer[idx])) {
832: /* Go back to the word. */
833: idx++;
834: break;
835: }
836: }
837:
838: memmove(c->prompt_buffer + idx,
839: c->prompt_buffer + c->prompt_index,
840: size + 1 - c->prompt_index);
841: memset(c->prompt_buffer + size - (c->prompt_index - idx),
842: '\0', c->prompt_index - idx);
843: c->prompt_index = idx;
844: c->flags |= CLIENT_STATUS;
845: break;
1.83 nicm 846: case MODEKEYEDIT_NEXTSPACE:
847: wsep = " ";
848: /* FALLTHROUGH */
1.81 nicm 849: case MODEKEYEDIT_NEXTWORD:
1.83 nicm 850: if (wsep == NULL)
851: wsep = options_get_string(oo, "word-separators");
1.81 nicm 852:
853: /* Find a separator. */
854: while (c->prompt_index != size) {
855: c->prompt_index++;
856: if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
857: break;
858: }
859:
860: /* Find the word right after the separation. */
861: while (c->prompt_index != size) {
862: c->prompt_index++;
863: if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
864: break;
865: }
866:
867: c->flags |= CLIENT_STATUS;
868: break;
1.83 nicm 869: case MODEKEYEDIT_NEXTSPACEEND:
870: wsep = " ";
871: /* FALLTHROUGH */
1.81 nicm 872: case MODEKEYEDIT_NEXTWORDEND:
1.83 nicm 873: if (wsep == NULL)
874: wsep = options_get_string(oo, "word-separators");
1.81 nicm 875:
876: /* Find a word. */
877: while (c->prompt_index != size) {
878: c->prompt_index++;
879: if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
880: break;
881: }
882:
883: /* Find the separator at the end of the word. */
884: while (c->prompt_index != size) {
885: c->prompt_index++;
1.82 nicm 886: if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
1.81 nicm 887: break;
888: }
1.106 nicm 889:
890: /* Back up to the end-of-word like vi. */
891: if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
892: c->prompt_index != 0)
893: c->prompt_index--;
1.81 nicm 894:
895: c->flags |= CLIENT_STATUS;
896: break;
1.83 nicm 897: case MODEKEYEDIT_PREVIOUSSPACE:
898: wsep = " ";
899: /* FALLTHROUGH */
1.81 nicm 900: case MODEKEYEDIT_PREVIOUSWORD:
1.83 nicm 901: if (wsep == NULL)
902: wsep = options_get_string(oo, "word-separators");
1.81 nicm 903:
904: /* Find a non-separator. */
905: while (c->prompt_index != 0) {
906: c->prompt_index--;
907: if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
908: break;
909: }
910:
911: /* Find the separator at the beginning of the word. */
912: while (c->prompt_index != 0) {
913: c->prompt_index--;
914: if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
915: /* Go back to the word. */
916: c->prompt_index++;
917: break;
918: }
919: }
920:
921: c->flags |= CLIENT_STATUS;
922: break;
1.20 nicm 923: case MODEKEYEDIT_HISTORYUP:
1.66 nicm 924: histstr = status_prompt_up_history(&c->prompt_hindex);
925: if (histstr == NULL)
1.1 nicm 926: break;
1.94 nicm 927: free(c->prompt_buffer);
1.66 nicm 928: c->prompt_buffer = xstrdup(histstr);
1.1 nicm 929: c->prompt_index = strlen(c->prompt_buffer);
930: c->flags |= CLIENT_STATUS;
931: break;
1.20 nicm 932: case MODEKEYEDIT_HISTORYDOWN:
1.66 nicm 933: histstr = status_prompt_down_history(&c->prompt_hindex);
934: if (histstr == NULL)
1.65 nicm 935: break;
1.94 nicm 936: free(c->prompt_buffer);
1.66 nicm 937: c->prompt_buffer = xstrdup(histstr);
1.1 nicm 938: c->prompt_index = strlen(c->prompt_buffer);
939: c->flags |= CLIENT_STATUS;
940: break;
1.20 nicm 941: case MODEKEYEDIT_PASTE:
1.114 nicm 942: if ((pb = paste_get_top()) == NULL)
1.1 nicm 943: break;
1.32 nicm 944: for (n = 0; n < pb->size; n++) {
1.53 nicm 945: ch = (u_char) pb->data[n];
946: if (ch < 32 || ch == 127)
1.32 nicm 947: break;
948: }
1.1 nicm 949:
1.116 nicm 950: c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1);
1.1 nicm 951: if (c->prompt_index == size) {
952: memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
953: c->prompt_index += n;
954: c->prompt_buffer[c->prompt_index] = '\0';
955: } else {
956: memmove(c->prompt_buffer + c->prompt_index + n,
957: c->prompt_buffer + c->prompt_index,
958: size + 1 - c->prompt_index);
959: memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
960: c->prompt_index += n;
961: }
962:
963: c->flags |= CLIENT_STATUS;
1.30 nicm 964: break;
965: case MODEKEYEDIT_TRANSPOSECHARS:
966: idx = c->prompt_index;
967: if (idx < size)
968: idx++;
969: if (idx >= 2) {
970: swapc = c->prompt_buffer[idx - 2];
971: c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
972: c->prompt_buffer[idx - 1] = swapc;
973: c->prompt_index = idx;
974: c->flags |= CLIENT_STATUS;
975: }
1.1 nicm 976: break;
1.55 nicm 977: case MODEKEYEDIT_ENTER:
1.25 nicm 978: if (*c->prompt_buffer != '\0')
1.65 nicm 979: status_prompt_add_history(c->prompt_buffer);
1.25 nicm 980: if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
981: status_prompt_clear(c);
982: break;
1.20 nicm 983: case MODEKEYEDIT_CANCEL:
1.12 nicm 984: if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
1.1 nicm 985: status_prompt_clear(c);
986: break;
1.20 nicm 987: case MODEKEY_OTHER:
1.61 nicm 988: if ((key & 0xff00) != 0 || key < 32 || key == 127)
1.1 nicm 989: break;
1.116 nicm 990: c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2);
1.1 nicm 991:
992: if (c->prompt_index == size) {
993: c->prompt_buffer[c->prompt_index++] = key;
994: c->prompt_buffer[c->prompt_index] = '\0';
995: } else {
996: memmove(c->prompt_buffer + c->prompt_index + 1,
997: c->prompt_buffer + c->prompt_index,
998: size + 1 - c->prompt_index);
999: c->prompt_buffer[c->prompt_index++] = key;
1000: }
1001:
1002: if (c->prompt_flags & PROMPT_SINGLE) {
1.12 nicm 1003: if (c->prompt_callbackfn(
1.1 nicm 1004: c->prompt_data, c->prompt_buffer) == 0)
1005: status_prompt_clear(c);
1006: }
1007:
1008: c->flags |= CLIENT_STATUS;
1009: break;
1010: default:
1011: break;
1012: }
1013: }
1014:
1.65 nicm 1015: /* Get previous line from the history. */
1016: const char *
1017: status_prompt_up_history(u_int *idx)
1018: {
1019: /*
1.128 nicm 1020: * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1021: * empty.
1.65 nicm 1022: */
1023:
1.128 nicm 1024: if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
1.65 nicm 1025: return (NULL);
1026: (*idx)++;
1.128 nicm 1027: return (status_prompt_hlist[status_prompt_hsize - *idx]);
1.65 nicm 1028: }
1029:
1030: /* Get next line from the history. */
1031: const char *
1032: status_prompt_down_history(u_int *idx)
1033: {
1.128 nicm 1034: if (status_prompt_hsize == 0 || *idx == 0)
1.65 nicm 1035: return ("");
1036: (*idx)--;
1037: if (*idx == 0)
1038: return ("");
1.128 nicm 1039: return (status_prompt_hlist[status_prompt_hsize - *idx]);
1.65 nicm 1040: }
1041:
1.1 nicm 1042: /* Add line to the history. */
1043: void
1.65 nicm 1044: status_prompt_add_history(const char *line)
1.1 nicm 1045: {
1.128 nicm 1046: size_t size;
1.65 nicm 1047:
1.128 nicm 1048: if (status_prompt_hsize > 0 &&
1049: strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
1.1 nicm 1050: return;
1051:
1.128 nicm 1052: if (status_prompt_hsize == PROMPT_HISTORY) {
1053: free(status_prompt_hlist[0]);
1.1 nicm 1054:
1.128 nicm 1055: size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
1056: memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
1.1 nicm 1057:
1.128 nicm 1058: status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
1059: return;
1060: }
1.1 nicm 1061:
1.128 nicm 1062: status_prompt_hlist = xreallocarray(status_prompt_hlist,
1063: status_prompt_hsize + 1, sizeof *status_prompt_hlist);
1064: status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
1065: }
1066:
1067: /* Build completion list. */
1068: const char **
1069: status_prompt_complete_list(u_int *size, const char *s)
1070: {
1071: const char **list = NULL, **layout;
1072: const struct cmd_entry **cmdent;
1073: const struct options_table_entry *oe;
1074: const char *layouts[] = {
1075: "even-horizontal", "even-vertical", "main-horizontal",
1076: "main-vertical", "tiled", NULL
1077: };
1.1 nicm 1078:
1.128 nicm 1079: *size = 0;
1.1 nicm 1080: for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1.128 nicm 1081: if (strncmp((*cmdent)->name, s, strlen(s)) == 0) {
1082: list = xreallocarray(list, (*size) + 1, sizeof *list);
1083: list[(*size)++] = (*cmdent)->name;
1084: }
1.56 nicm 1085: }
1.69 nicm 1086: for (oe = server_options_table; oe->name != NULL; oe++) {
1.128 nicm 1087: if (strncmp(oe->name, s, strlen(s)) == 0) {
1088: list = xreallocarray(list, (*size) + 1, sizeof *list);
1089: list[(*size)++] = oe->name;
1090: }
1.69 nicm 1091: }
1092: for (oe = session_options_table; oe->name != NULL; oe++) {
1.128 nicm 1093: if (strncmp(oe->name, s, strlen(s)) == 0) {
1094: list = xreallocarray(list, (*size) + 1, sizeof *list);
1095: list[(*size)++] = oe->name;
1096: }
1.69 nicm 1097: }
1098: for (oe = window_options_table; oe->name != NULL; oe++) {
1.128 nicm 1099: if (strncmp(oe->name, s, strlen(s)) == 0) {
1100: list = xreallocarray(list, (*size) + 1, sizeof *list);
1101: list[(*size)++] = oe->name;
1102: }
1103: }
1104: for (layout = layouts; *layout != NULL; layout++) {
1105: if (strncmp(*layout, s, strlen(s)) == 0) {
1106: list = xreallocarray(list, (*size) + 1, sizeof *list);
1107: list[(*size)++] = *layout;
1108: }
1109: }
1110: return (list);
1111: }
1112:
1113: /* Find longest prefix. */
1114: char *
1115: status_prompt_complete_prefix(const char **list, u_int size)
1116: {
1117: char *out;
1118: u_int i;
1119: size_t j;
1120:
1121: out = xstrdup(list[0]);
1122: for (i = 1; i < size; i++) {
1123: j = strlen(list[i]);
1124: if (j > strlen(out))
1125: j = strlen(out);
1126: for (; j > 0; j--) {
1127: if (out[j - 1] != list[i][j - 1])
1128: out[j - 1] = '\0';
1129: }
1.1 nicm 1130: }
1.128 nicm 1131: return (out);
1132: }
1.1 nicm 1133:
1.128 nicm 1134: /* Complete word. */
1135: char *
1136: status_prompt_complete(struct session *sess, const char *s)
1137: {
1138: const char **list = NULL, *colon;
1139: u_int size = 0, i;
1140: struct session *s_loop;
1141: struct winlink *wl;
1142: struct window *w;
1143: char *copy, *out, *tmp;
1144:
1145: if (*s == '\0')
1.1 nicm 1146: return (NULL);
1.128 nicm 1147: out = NULL;
1148:
1149: if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
1150: list = status_prompt_complete_list(&size, s);
1151: if (size == 0)
1152: out = NULL;
1153: else if (size == 1)
1154: xasprintf(&out, "%s ", list[0]);
1155: else
1156: out = status_prompt_complete_prefix(list, size);
1157: free(list);
1158: return (out);
1159: }
1160: copy = xstrdup(s);
1161:
1162: colon = ":";
1163: if (copy[strlen(copy) - 1] == ':')
1164: copy[strlen(copy) - 1] = '\0';
1165: else
1166: colon = "";
1167: s = copy + 2;
1.1 nicm 1168:
1.128 nicm 1169: RB_FOREACH(s_loop, sessions, &sessions) {
1170: if (strncmp(s_loop->name, s, strlen(s)) == 0) {
1171: list = xreallocarray(list, size + 2, sizeof *list);
1172: list[size++] = s_loop->name;
1173: }
1174: }
1175: if (size == 1) {
1176: out = xstrdup(list[0]);
1177: if (session_find(list[0]) != NULL)
1178: colon = ":";
1179: } else if (size != 0)
1180: out = status_prompt_complete_prefix(list, size);
1181: if (out != NULL) {
1182: xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1183: out = tmp;
1184: goto found;
1185: }
1186:
1187: colon = "";
1188: if (*s == ':') {
1189: RB_FOREACH(wl, winlinks, &sess->windows) {
1190: xasprintf(&tmp, ":%s", wl->window->name);
1191: if (strncmp(tmp, s, strlen(s)) == 0){
1192: list = xreallocarray(list, size + 1,
1193: sizeof *list);
1194: list[size++] = tmp;
1195: continue;
1196: }
1197: free(tmp);
1.1 nicm 1198:
1.128 nicm 1199: xasprintf(&tmp, ":%d", wl->idx);
1200: if (strncmp(tmp, s, strlen(s)) == 0) {
1201: list = xreallocarray(list, size + 1,
1202: sizeof *list);
1203: list[size++] = tmp;
1204: continue;
1205: }
1206: free(tmp);
1207: }
1208: } else {
1209: RB_FOREACH(s_loop, sessions, &sessions) {
1210: RB_FOREACH(wl, winlinks, &s_loop->windows) {
1211: w = wl->window;
1212:
1213: xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
1214: if (strncmp(tmp, s, strlen(s)) == 0) {
1215: list = xreallocarray(list, size + 1,
1216: sizeof *list);
1217: list[size++] = tmp;
1218: continue;
1219: }
1220: free(tmp);
1221:
1222: xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
1223: if (strncmp(tmp, s, strlen(s)) == 0) {
1224: list = xreallocarray(list, size + 1,
1225: sizeof *list);
1226: list[size++] = tmp;
1227: continue;
1228: }
1229: free(tmp);
1230: }
1.1 nicm 1231: }
1232: }
1.128 nicm 1233: if (size == 1) {
1234: out = xstrdup(list[0]);
1235: colon = " ";
1236: } else if (size != 0)
1237: out = status_prompt_complete_prefix(list, size);
1238: if (out != NULL) {
1239: xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1240: out = tmp;
1241: }
1242:
1243: for (i = 0; i < size; i++)
1244: free((void *)list[i]);
1245:
1246: found:
1247: free(copy);
1248: free(list);
1249: return (out);
1.1 nicm 1250: }