Annotation of src/usr.bin/tmux/status.c, Revision 1.136
1.136 ! nicm 1: /* $OpenBSD: status.c,v 1.135 2015/09/14 10:25:52 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.42 nicm 39: void status_message_callback(int, short, void *);
1.133 nicm 40: void status_timer_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.130 nicm 50: char *status_prompt_find_history_file(void);
51:
1.65 nicm 52: /* Status prompt history. */
1.128 nicm 53: #define PROMPT_HISTORY 100
54: char **status_prompt_hlist;
55: u_int status_prompt_hsize;
1.130 nicm 56:
57: /* Find the history file to load/save from/to. */
58: 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:
1.87 nicm 144: }
145:
1.133 nicm 146: /* Status timer callback. */
147: 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);
1.136 ! nicm 167: log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
1.133 nicm 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:
1.87 nicm 195: /* Get screen line of status line. -1 means off. */
196: int
197: status_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);
1.71 nicm 207: }
208:
1.48 nicm 209: /* Retrieve options for left string. */
210: char *
1.120 nicm 211: status_redraw_get_left(struct client *c, time_t t, int utf8flag,
212: struct grid_cell *gc, size_t *size)
1.48 nicm 213: {
214: struct session *s = c->session;
1.120 nicm 215: const char *template;
1.48 nicm 216: char *left;
217: size_t leftlen;
218:
1.108 nicm 219: style_apply_update(gc, &s->options, "status-left-style");
1.48 nicm 220:
1.120 nicm 221: template = options_get_string(&s->options, "status-left");
1.123 nicm 222: left = status_replace(c, NULL, template, t);
1.48 nicm 223:
224: *size = options_get_number(&s->options, "status-left-length");
225: leftlen = screen_write_cstrlen(utf8flag, "%s", left);
226: if (leftlen < *size)
227: *size = leftlen;
228: return (left);
229: }
230:
231: /* Retrieve options for right string. */
232: char *
1.120 nicm 233: status_redraw_get_right(struct client *c, time_t t, int utf8flag,
234: struct grid_cell *gc, size_t *size)
1.48 nicm 235: {
236: struct session *s = c->session;
1.120 nicm 237: const char *template;
1.48 nicm 238: char *right;
239: size_t rightlen;
240:
1.108 nicm 241: style_apply_update(gc, &s->options, "status-right-style");
1.48 nicm 242:
1.120 nicm 243: template = options_get_string(&s->options, "status-right");
1.123 nicm 244: right = status_replace(c, NULL, template, t);
1.48 nicm 245:
246: *size = options_get_number(&s->options, "status-right-length");
247: rightlen = screen_write_cstrlen(utf8flag, "%s", right);
248: if (rightlen < *size)
249: *size = rightlen;
250: return (right);
251: }
252:
1.125 nicm 253: /* Get window at window list position. */
254: struct window *
255: status_get_window_at(struct client *c, u_int x)
1.73 nicm 256: {
257: struct session *s = c->session;
258: struct winlink *wl;
1.115 nicm 259: struct options *oo;
260: size_t len;
1.73 nicm 261:
1.88 nicm 262: x += c->wlmouse;
1.73 nicm 263: RB_FOREACH(wl, winlinks, &s->windows) {
1.115 nicm 264: oo = &wl->window->options;
1.125 nicm 265: len = strlen(options_get_string(oo, "window-status-separator"));
1.115 nicm 266:
1.125 nicm 267: if (x < wl->status_width)
268: return (wl->window);
1.115 nicm 269: x -= wl->status_width + len;
1.73 nicm 270: }
1.125 nicm 271: return (NULL);
1.73 nicm 272: }
273:
1.1 nicm 274: /* Draw status for client on the last lines of given context. */
275: int
276: status_redraw(struct client *c)
277: {
1.48 nicm 278: struct screen_write_ctx ctx;
279: struct session *s = c->session;
280: struct winlink *wl;
281: struct screen old_status, window_list;
282: struct grid_cell stdgc, lgc, rgc, gc;
1.91 nicm 283: struct options *oo;
1.48 nicm 284: time_t t;
1.91 nicm 285: char *left, *right, *sep;
1.48 nicm 286: u_int offset, needed;
287: u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
1.91 nicm 288: size_t llen, rlen, seplen;
1.48 nicm 289: int larrow, rarrow, utf8flag;
1.1 nicm 290:
1.49 nicm 291: /* No status line? */
1.7 nicm 292: if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
293: return (1);
1.48 nicm 294: left = right = NULL;
1.7 nicm 295: larrow = rarrow = 0;
1.1 nicm 296:
1.133 nicm 297: /* Store current time. */
298: t = time(NULL);
1.48 nicm 299:
300: /* Set up default colour. */
1.108 nicm 301: style_apply(&stdgc, &s->options, "status-style");
1.1 nicm 302:
1.48 nicm 303: /* Create the target screen. */
304: memcpy(&old_status, &c->status, sizeof old_status);
305: screen_init(&c->status, c->tty.sx, 1, 0);
306: screen_write_start(&ctx, NULL, &c->status);
307: for (offset = 0; offset < c->tty.sx; offset++)
308: screen_write_putc(&ctx, &stdgc, ' ');
309: screen_write_stop(&ctx);
310:
311: /* If the height is one line, blank status line. */
312: if (c->tty.sy <= 1)
313: goto out;
1.1 nicm 314:
1.48 nicm 315: /* Get UTF-8 flag. */
1.3 nicm 316: utf8flag = options_get_number(&s->options, "status-utf8");
317:
1.48 nicm 318: /* Work out left and right strings. */
319: memcpy(&lgc, &stdgc, sizeof lgc);
320: left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen);
321: memcpy(&rgc, &stdgc, sizeof rgc);
322: right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen);
1.1 nicm 323:
324: /*
1.48 nicm 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.
1.1 nicm 327: */
1.55 nicm 328: needed = 0;
1.1 nicm 329: if (llen != 0)
1.118 nicm 330: needed += llen;
1.1 nicm 331: if (rlen != 0)
1.118 nicm 332: needed += rlen;
1.48 nicm 333: if (c->tty.sx == 0 || c->tty.sx <= needed)
334: goto out;
335: wlavailable = c->tty.sx - needed;
1.1 nicm 336:
1.48 nicm 337: /* Calculate the total size needed for the window list. */
338: wlstart = wloffset = wlwidth = 0;
1.1 nicm 339: RB_FOREACH(wl, winlinks, &s->windows) {
1.94 nicm 340: free(wl->status_text);
1.48 nicm 341: memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
342: wl->status_text = status_print(c, wl, t, &wl->status_cell);
1.55 nicm 343: wl->status_width =
1.48 nicm 344: screen_write_cstrlen(utf8flag, "%s", wl->status_text);
345:
1.1 nicm 346: if (wl == s->curw)
1.48 nicm 347: wloffset = wlwidth;
1.91 nicm 348:
349: oo = &wl->window->options;
350: sep = options_get_string(oo, "window-status-separator");
351: seplen = screen_write_strlen(utf8flag, "%s", sep);
352: wlwidth += wl->status_width + seplen;
1.48 nicm 353: }
354:
355: /* Create a new screen for the window list. */
356: screen_init(&window_list, wlwidth, 1, 0);
357:
358: /* And draw the window list into it. */
359: screen_write_start(&ctx, NULL, &window_list);
360: RB_FOREACH(wl, winlinks, &s->windows) {
1.55 nicm 361: screen_write_cnputs(&ctx,
1.48 nicm 362: -1, &wl->status_cell, utf8flag, "%s", wl->status_text);
1.91 nicm 363:
364: oo = &wl->window->options;
365: sep = options_get_string(oo, "window-status-separator");
366: screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep);
1.1 nicm 367: }
1.48 nicm 368: screen_write_stop(&ctx);
1.1 nicm 369:
1.48 nicm 370: /* If there is enough space for the total width, skip to draw now. */
371: if (wlwidth <= wlavailable)
1.1 nicm 372: goto draw;
373:
374: /* Find size of current window text. */
1.48 nicm 375: wlsize = s->curw->status_width;
1.1 nicm 376:
377: /*
1.48 nicm 378: * If the current window is already on screen, good to draw from the
1.1 nicm 379: * start and just leave off the end.
380: */
1.48 nicm 381: if (wloffset + wlsize < wlavailable) {
382: if (wlavailable > 0) {
383: rarrow = 1;
384: wlavailable--;
385: }
386: wlwidth = wlavailable;
387: } else {
388: /*
389: * Work out how many characters we need to omit from the
390: * start. There are wlavailable characters to fill, and
391: * wloffset + wlsize must be the last. So, the start character
392: * is wloffset + wlsize - wlavailable.
393: */
394: if (wlavailable > 0) {
395: larrow = 1;
396: wlavailable--;
397: }
1.55 nicm 398:
1.48 nicm 399: wlstart = wloffset + wlsize - wlavailable;
400: if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
1.1 nicm 401: rarrow = 1;
1.48 nicm 402: wlstart++;
403: wlavailable--;
1.1 nicm 404: }
1.48 nicm 405: wlwidth = wlavailable;
406: }
1.1 nicm 407:
1.48 nicm 408: /* Bail if anything is now too small too. */
409: if (wlwidth == 0 || wlavailable == 0) {
410: screen_free(&window_list);
411: goto out;
1.1 nicm 412: }
413:
414: /*
1.48 nicm 415: * Now the start position is known, work out the state of the left and
416: * right arrows.
1.1 nicm 417: */
418: offset = 0;
419: RB_FOREACH(wl, winlinks, &s->windows) {
1.63 nicm 420: if (wl->flags & WINLINK_ALERTFLAGS &&
421: larrow == 1 && offset < wlstart)
422: larrow = -1;
1.1 nicm 423:
1.48 nicm 424: offset += wl->status_width;
1.1 nicm 425:
1.63 nicm 426: if (wl->flags & WINLINK_ALERTFLAGS &&
427: rarrow == 1 && offset > wlstart + wlwidth)
428: rarrow = -1;
1.1 nicm 429: }
430:
1.48 nicm 431: draw:
1.55 nicm 432: /* Begin drawing. */
1.48 nicm 433: screen_write_start(&ctx, NULL, &c->status);
1.1 nicm 434:
1.48 nicm 435: /* Draw the left string and arrow. */
436: screen_write_cursormove(&ctx, 0, 0);
1.118 nicm 437: if (llen != 0)
1.48 nicm 438: screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left);
1.1 nicm 439: if (larrow != 0) {
440: memcpy(&gc, &stdgc, sizeof gc);
441: if (larrow == -1)
442: gc.attr ^= GRID_ATTR_REVERSE;
443: screen_write_putc(&ctx, &gc, '<');
444: }
1.48 nicm 445:
446: /* Draw the right string and arrow. */
1.1 nicm 447: if (rarrow != 0) {
1.118 nicm 448: screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
1.1 nicm 449: memcpy(&gc, &stdgc, sizeof gc);
450: if (rarrow == -1)
451: gc.attr ^= GRID_ATTR_REVERSE;
452: screen_write_putc(&ctx, &gc, '>');
1.48 nicm 453: } else
1.118 nicm 454: screen_write_cursormove(&ctx, c->tty.sx - rlen, 0);
455: if (rlen != 0)
1.48 nicm 456: screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right);
1.1 nicm 457:
1.48 nicm 458: /* Figure out the offset for the window list. */
1.58 nicm 459: if (llen != 0)
1.118 nicm 460: wloffset = llen;
1.58 nicm 461: else
462: wloffset = 0;
1.48 nicm 463: if (wlwidth < wlavailable) {
464: switch (options_get_number(&s->options, "status-justify")) {
1.119 sthen 465: case 1: /* centred */
1.58 nicm 466: wloffset += (wlavailable - wlwidth) / 2;
1.48 nicm 467: break;
468: case 2: /* right */
1.58 nicm 469: wloffset += (wlavailable - wlwidth);
1.48 nicm 470: break;
471: }
472: }
473: if (larrow != 0)
474: wloffset++;
475:
476: /* Copy the window list. */
1.88 nicm 477: c->wlmouse = -wloffset + wlstart;
1.48 nicm 478: screen_write_cursormove(&ctx, wloffset, 0);
479: screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
1.55 nicm 480: screen_free(&window_list);
1.1 nicm 481:
1.48 nicm 482: screen_write_stop(&ctx);
1.1 nicm 483:
484: out:
1.94 nicm 485: free(left);
486: free(right);
1.1 nicm 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:
1.46 nicm 496: /* Replace special sequences in fmt. */
497: char *
1.123 nicm 498: status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
1.46 nicm 499: {
1.96 nicm 500: struct format_tree *ft;
1.129 nicm 501: char *expanded;
1.47 nicm 502:
1.93 nicm 503: if (fmt == NULL)
504: return (xstrdup(""));
505:
1.135 nicm 506: if (c->flags & CLIENT_STATUSFORCE)
507: ft = format_create_flags(FORMAT_STATUS|FORMAT_FORCE);
508: else
509: ft = format_create_flags(FORMAT_STATUS);
1.129 nicm 510: format_defaults(ft, c, NULL, wl, NULL);
1.1 nicm 511:
1.129 nicm 512: expanded = format_expand_time(ft, fmt, t);
1.46 nicm 513:
1.99 nicm 514: format_free(ft);
515: return (expanded);
1.1 nicm 516: }
517:
1.46 nicm 518: /* Return winlink status line entry and adjust gc as necessary. */
1.1 nicm 519: char *
1.120 nicm 520: status_print(struct client *c, struct winlink *wl, time_t t,
521: struct grid_cell *gc)
1.1 nicm 522: {
1.33 nicm 523: struct options *oo = &wl->window->options;
1.47 nicm 524: struct session *s = c->session;
525: const char *fmt;
526: char *text;
1.1 nicm 527:
1.108 nicm 528: style_apply_update(gc, oo, "window-status-style");
1.47 nicm 529: fmt = options_get_string(oo, "window-status-format");
1.14 nicm 530: if (wl == s->curw) {
1.108 nicm 531: style_apply_update(gc, oo, "window-status-current-style");
1.47 nicm 532: fmt = options_get_string(oo, "window-status-current-format");
1.95 nicm 533: }
1.108 nicm 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");
1.1 nicm 541:
1.123 nicm 542: text = status_replace(c, wl, fmt, t);
1.1 nicm 543: return (text);
544: }
545:
1.46 nicm 546: /* Set a status line message. */
1.117 nicm 547: void
1.10 nicm 548: status_message_set(struct client *c, const char *fmt, ...)
1.1 nicm 549: {
1.44 nicm 550: struct timeval tv;
1.127 nicm 551: struct message_entry *msg, *msg1;
1.44 nicm 552: va_list ap;
553: int delay;
1.127 nicm 554: u_int first, limit;
555:
556: limit = options_get_number(&global_options, "message-limit");
1.1 nicm 557:
1.12 nicm 558: status_prompt_clear(c);
559: status_message_clear(c);
560:
1.28 nicm 561: va_start(ap, fmt);
562: xvasprintf(&c->message_string, fmt, ap);
563: va_end(ap);
564:
1.127 nicm 565: msg = xcalloc(1, sizeof *msg);
1.44 nicm 566: msg->msg_time = time(NULL);
1.127 nicm 567: msg->msg_num = c->message_next++;
1.44 nicm 568: msg->msg = xstrdup(c->message_string);
1.127 nicm 569: TAILQ_INSERT_TAIL(&c->message_log, msg, entry);
1.44 nicm 570:
1.127 nicm 571: first = c->message_next - limit;
572: TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
573: if (msg->msg_num >= first)
574: continue;
575: free(msg->msg);
576: TAILQ_REMOVE(&c->message_log, msg, entry);
577: free(msg);
1.44 nicm 578: }
579:
1.1 nicm 580: delay = options_get_number(&c->session->options, "display-time");
581: tv.tv_sec = delay / 1000;
582: tv.tv_usec = (delay % 1000) * 1000L;
1.44 nicm 583:
1.110 nicm 584: if (event_initialized(&c->message_timer))
1.90 nicm 585: evtimer_del(&c->message_timer);
1.42 nicm 586: evtimer_set(&c->message_timer, status_message_callback, c);
587: evtimer_add(&c->message_timer, &tv);
1.1 nicm 588:
589: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
590: c->flags |= CLIENT_STATUS;
591: }
592:
1.46 nicm 593: /* Clear status line message. */
1.1 nicm 594: void
595: status_message_clear(struct client *c)
596: {
597: if (c->message_string == NULL)
598: return;
599:
1.94 nicm 600: free(c->message_string);
1.1 nicm 601: c->message_string = NULL;
602:
603: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.12 nicm 604: c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
1.7 nicm 605:
606: screen_reinit(&c->status);
1.42 nicm 607: }
608:
1.46 nicm 609: /* Clear status line message after timer expires. */
1.42 nicm 610: 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);
1.1 nicm 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;
1.51 nicm 627: int utf8flag;
1.1 nicm 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:
1.51 nicm 634: utf8flag = options_get_number(&s->options, "status-utf8");
635:
636: len = screen_write_strlen(utf8flag, "%s", c->message_string);
1.1 nicm 637: if (len > c->tty.sx)
638: len = c->tty.sx;
639:
1.108 nicm 640: style_apply(&gc, &s->options, "message-style");
1.1 nicm 641:
642: screen_write_start(&ctx, NULL, &c->status);
643:
644: screen_write_cursormove(&ctx, 0, 0);
1.51 nicm 645: screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string);
1.1 nicm 646: for (; len < c->tty.sx; len++)
647: screen_write_putc(&ctx, &gc, ' ');
648:
649: screen_write_stop(&ctx);
650:
651: if (grid_compare(c->status.grid, old_status.grid) == 0) {
652: screen_free(&old_status);
653: return (0);
654: }
655: screen_free(&old_status);
656: return (1);
657: }
658:
1.46 nicm 659: /* Enable status line prompt. */
1.1 nicm 660: void
1.76 nicm 661: status_prompt_set(struct client *c, const char *msg, const char *input,
1.12 nicm 662: int (*callbackfn)(void *, const char *), void (*freefn)(void *),
663: void *data, int flags)
1.1 nicm 664: {
1.122 nicm 665: struct format_tree *ft;
666: int keys;
667: time_t t;
668:
669: ft = format_create();
670: format_defaults(ft, c, NULL, NULL, NULL);
671: t = time(NULL);
1.20 nicm 672:
1.12 nicm 673: status_message_clear(c);
674: status_prompt_clear(c);
675:
1.124 nicm 676: c->prompt_string = format_expand_time(ft, msg, t);
1.1 nicm 677:
1.124 nicm 678: c->prompt_buffer = format_expand_time(ft, input, t);
1.76 nicm 679: c->prompt_index = strlen(c->prompt_buffer);
1.1 nicm 680:
1.12 nicm 681: c->prompt_callbackfn = callbackfn;
682: c->prompt_freefn = freefn;
1.1 nicm 683: c->prompt_data = data;
684:
685: c->prompt_hindex = 0;
686:
687: c->prompt_flags = flags;
688:
1.20 nicm 689: keys = options_get_number(&c->session->options, "status-keys");
690: if (keys == MODEKEY_EMACS)
1.21 nicm 691: mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit);
1.20 nicm 692: else
1.21 nicm 693: mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit);
1.1 nicm 694:
695: c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
696: c->flags |= CLIENT_STATUS;
1.122 nicm 697:
698: format_free(ft);
1.1 nicm 699: }
700:
1.46 nicm 701: /* Remove status line prompt. */
1.1 nicm 702: void
703: status_prompt_clear(struct client *c)
704: {
1.55 nicm 705: if (c->prompt_string == NULL)
1.1 nicm 706: return;
707:
1.12 nicm 708: if (c->prompt_freefn != NULL && c->prompt_data != NULL)
709: c->prompt_freefn(c->prompt_data);
1.1 nicm 710:
1.94 nicm 711: free(c->prompt_string);
1.1 nicm 712: c->prompt_string = NULL;
713:
1.94 nicm 714: free(c->prompt_buffer);
1.1 nicm 715: c->prompt_buffer = NULL;
716:
717: c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
1.12 nicm 718: c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
1.7 nicm 719:
720: screen_reinit(&c->status);
1.27 nicm 721: }
722:
1.46 nicm 723: /* Update status line prompt with a new prompt string. */
1.27 nicm 724: void
1.76 nicm 725: status_prompt_update(struct client *c, const char *msg, const char *input)
1.27 nicm 726: {
1.122 nicm 727: struct format_tree *ft;
728: time_t t;
729:
730: ft = format_create();
731: format_defaults(ft, c, NULL, NULL, NULL);
732: t = time(NULL);
733:
1.94 nicm 734: free(c->prompt_string);
1.124 nicm 735: c->prompt_string = format_expand_time(ft, msg, t);
1.27 nicm 736:
1.94 nicm 737: free(c->prompt_buffer);
1.124 nicm 738: c->prompt_buffer = format_expand_time(ft, input, t);
1.76 nicm 739: c->prompt_index = strlen(c->prompt_buffer);
1.27 nicm 740:
741: c->prompt_hindex = 0;
742:
743: c->flags |= CLIENT_STATUS;
1.122 nicm 744:
745: format_free(ft);
1.1 nicm 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: {
1.51 nicm 752: struct screen_write_ctx ctx;
1.1 nicm 753: struct session *s = c->session;
754: struct screen old_status;
1.29 nicm 755: size_t i, size, left, len, off;
1.51 nicm 756: struct grid_cell gc, *gcp;
757: int utf8flag;
1.1 nicm 758:
759: if (c->tty.sx == 0 || c->tty.sy == 0)
760: return (0);
761: memcpy(&old_status, &c->status, sizeof old_status);
762: screen_init(&c->status, c->tty.sx, 1, 0);
763:
1.51 nicm 764: utf8flag = options_get_number(&s->options, "status-utf8");
765:
766: len = screen_write_strlen(utf8flag, "%s", c->prompt_string);
1.1 nicm 767: if (len > c->tty.sx)
768: len = c->tty.sx;
1.51 nicm 769: off = 0;
1.1 nicm 770:
1.79 nicm 771: /* Change colours for command mode. */
1.108 nicm 772: if (c->prompt_mdata.mode == 1)
773: style_apply(&gc, &s->options, "message-command-style");
774: else
775: style_apply(&gc, &s->options, "message-style");
1.1 nicm 776:
777: screen_write_start(&ctx, NULL, &c->status);
778:
779: screen_write_cursormove(&ctx, 0, 0);
1.51 nicm 780: screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string);
1.1 nicm 781:
782: left = c->tty.sx - len;
783: if (left != 0) {
1.51 nicm 784: size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer);
785: if (c->prompt_index >= left) {
1.17 nicm 786: off = c->prompt_index - left + 1;
1.51 nicm 787: if (c->prompt_index == size)
1.1 nicm 788: left--;
789: size = left;
790: }
1.51 nicm 791: screen_write_nputs(
792: &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off);
1.1 nicm 793:
1.55 nicm 794: for (i = len + size; i < c->tty.sx; i++)
795: screen_write_putc(&ctx, &gc, ' ');
1.1 nicm 796: }
797:
798: screen_write_stop(&ctx);
1.51 nicm 799:
800: /* Apply fake cursor. */
801: off = len + c->prompt_index - off;
802: gcp = grid_view_get_cell(c->status.grid, off, 0);
803: gcp->attr ^= GRID_ATTR_REVERSE;
1.1 nicm 804:
805: if (grid_compare(c->status.grid, old_status.grid) == 0) {
806: screen_free(&old_status);
807: return (0);
808: }
809: screen_free(&old_status);
810: return (1);
811: }
812:
813: /* Handle keys in prompt. */
814: void
815: status_prompt_key(struct client *c, int key)
816: {
1.81 nicm 817: struct session *sess = c->session;
818: struct options *oo = &sess->options;
1.1 nicm 819: struct paste_buffer *pb;
1.81 nicm 820: char *s, *first, *last, word[64], swapc;
1.134 nicm 821: const char *histstr, *bufdata, *wsep = NULL;
1.53 nicm 822: u_char ch;
1.134 nicm 823: size_t size, n, off, idx, bufsize;
1.1 nicm 824:
825: size = strlen(c->prompt_buffer);
1.101 nicm 826: switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) {
1.20 nicm 827: case MODEKEYEDIT_CURSORLEFT:
1.1 nicm 828: if (c->prompt_index > 0) {
829: c->prompt_index--;
830: c->flags |= CLIENT_STATUS;
831: }
832: break;
1.79 nicm 833: case MODEKEYEDIT_SWITCHMODE:
834: c->flags |= CLIENT_STATUS;
835: break;
1.20 nicm 836: case MODEKEYEDIT_SWITCHMODEAPPEND:
1.79 nicm 837: c->flags |= CLIENT_STATUS;
838: /* FALLTHROUGH */
1.20 nicm 839: case MODEKEYEDIT_CURSORRIGHT:
1.1 nicm 840: if (c->prompt_index < size) {
841: c->prompt_index++;
842: c->flags |= CLIENT_STATUS;
843: }
844: break;
1.89 nicm 845: case MODEKEYEDIT_SWITCHMODEBEGINLINE:
846: c->flags |= CLIENT_STATUS;
847: /* FALLTHROUGH */
1.20 nicm 848: case MODEKEYEDIT_STARTOFLINE:
1.1 nicm 849: if (c->prompt_index != 0) {
850: c->prompt_index = 0;
851: c->flags |= CLIENT_STATUS;
852: }
853: break;
1.89 nicm 854: case MODEKEYEDIT_SWITCHMODEAPPENDLINE:
855: c->flags |= CLIENT_STATUS;
856: /* FALLTHROUGH */
1.20 nicm 857: case MODEKEYEDIT_ENDOFLINE:
1.1 nicm 858: if (c->prompt_index != size) {
859: c->prompt_index = size;
860: c->flags |= CLIENT_STATUS;
861: }
862: break;
1.20 nicm 863: case MODEKEYEDIT_COMPLETE:
1.1 nicm 864: if (*c->prompt_buffer == '\0')
865: break;
866:
867: idx = c->prompt_index;
868: if (idx != 0)
869: idx--;
870:
871: /* Find the word we are in. */
872: first = c->prompt_buffer + idx;
873: while (first > c->prompt_buffer && *first != ' ')
874: first--;
875: while (*first == ' ')
876: first++;
877: last = c->prompt_buffer + idx;
878: while (*last != '\0' && *last != ' ')
879: last++;
880: while (*last == ' ')
881: last--;
882: if (*last != '\0')
883: last++;
884: if (last <= first ||
885: ((size_t) (last - first)) > (sizeof word) - 1)
886: break;
887: memcpy(word, first, last - first);
888: word[last - first] = '\0';
889:
890: /* And try to complete it. */
1.128 nicm 891: if ((s = status_prompt_complete(sess, word)) == NULL)
1.1 nicm 892: break;
893:
894: /* Trim out word. */
895: n = size - (last - c->prompt_buffer) + 1; /* with \0 */
896: memmove(first, last, n);
897: size -= last - first;
898:
899: /* Insert the new word. */
1.55 nicm 900: size += strlen(s);
1.1 nicm 901: off = first - c->prompt_buffer;
1.116 nicm 902: c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1);
1.1 nicm 903: first = c->prompt_buffer + off;
1.55 nicm 904: memmove(first + strlen(s), first, n);
905: memcpy(first, s, strlen(s));
1.1 nicm 906:
907: c->prompt_index = (first - c->prompt_buffer) + strlen(s);
1.94 nicm 908: free(s);
1.1 nicm 909:
910: c->flags |= CLIENT_STATUS;
911: break;
1.20 nicm 912: case MODEKEYEDIT_BACKSPACE:
1.1 nicm 913: if (c->prompt_index != 0) {
914: if (c->prompt_index == size)
915: c->prompt_buffer[--c->prompt_index] = '\0';
916: else {
917: memmove(c->prompt_buffer + c->prompt_index - 1,
918: c->prompt_buffer + c->prompt_index,
919: size + 1 - c->prompt_index);
920: c->prompt_index--;
921: }
922: c->flags |= CLIENT_STATUS;
923: }
924: break;
1.55 nicm 925: case MODEKEYEDIT_DELETE:
1.105 nicm 926: case MODEKEYEDIT_SWITCHMODESUBSTITUTE:
1.1 nicm 927: if (c->prompt_index != size) {
928: memmove(c->prompt_buffer + c->prompt_index,
929: c->prompt_buffer + c->prompt_index + 1,
930: size + 1 - c->prompt_index);
1.18 nicm 931: c->flags |= CLIENT_STATUS;
932: }
1.26 nicm 933: break;
934: case MODEKEYEDIT_DELETELINE:
1.105 nicm 935: case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
1.26 nicm 936: *c->prompt_buffer = '\0';
937: c->prompt_index = 0;
938: c->flags |= CLIENT_STATUS;
1.18 nicm 939: break;
1.20 nicm 940: case MODEKEYEDIT_DELETETOENDOFLINE:
1.105 nicm 941: case MODEKEYEDIT_SWITCHMODECHANGELINE:
1.18 nicm 942: if (c->prompt_index < size) {
943: c->prompt_buffer[c->prompt_index] = '\0';
1.1 nicm 944: c->flags |= CLIENT_STATUS;
945: }
946: break;
1.81 nicm 947: case MODEKEYEDIT_DELETEWORD:
948: wsep = options_get_string(oo, "word-separators");
949: idx = c->prompt_index;
950:
951: /* Find a non-separator. */
952: while (idx != 0) {
953: idx--;
954: if (!strchr(wsep, c->prompt_buffer[idx]))
955: break;
956: }
957:
958: /* Find the separator at the beginning of the word. */
959: while (idx != 0) {
960: idx--;
961: if (strchr(wsep, c->prompt_buffer[idx])) {
962: /* Go back to the word. */
963: idx++;
964: break;
965: }
966: }
967:
968: memmove(c->prompt_buffer + idx,
969: c->prompt_buffer + c->prompt_index,
970: size + 1 - c->prompt_index);
971: memset(c->prompt_buffer + size - (c->prompt_index - idx),
972: '\0', c->prompt_index - idx);
973: c->prompt_index = idx;
974: c->flags |= CLIENT_STATUS;
975: break;
1.83 nicm 976: case MODEKEYEDIT_NEXTSPACE:
977: wsep = " ";
978: /* FALLTHROUGH */
1.81 nicm 979: case MODEKEYEDIT_NEXTWORD:
1.83 nicm 980: if (wsep == NULL)
981: wsep = options_get_string(oo, "word-separators");
1.81 nicm 982:
983: /* Find a separator. */
984: while (c->prompt_index != size) {
985: c->prompt_index++;
986: if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
987: break;
988: }
989:
990: /* Find the word right after the separation. */
991: while (c->prompt_index != size) {
992: c->prompt_index++;
993: if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
994: break;
995: }
996:
997: c->flags |= CLIENT_STATUS;
998: break;
1.83 nicm 999: case MODEKEYEDIT_NEXTSPACEEND:
1000: wsep = " ";
1001: /* FALLTHROUGH */
1.81 nicm 1002: case MODEKEYEDIT_NEXTWORDEND:
1.83 nicm 1003: if (wsep == NULL)
1004: wsep = options_get_string(oo, "word-separators");
1.81 nicm 1005:
1006: /* Find a word. */
1007: while (c->prompt_index != size) {
1008: c->prompt_index++;
1009: if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1010: break;
1011: }
1012:
1013: /* Find the separator at the end of the word. */
1014: while (c->prompt_index != size) {
1015: c->prompt_index++;
1.82 nicm 1016: if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
1.81 nicm 1017: break;
1018: }
1.106 nicm 1019:
1020: /* Back up to the end-of-word like vi. */
1021: if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
1022: c->prompt_index != 0)
1023: c->prompt_index--;
1.81 nicm 1024:
1025: c->flags |= CLIENT_STATUS;
1026: break;
1.83 nicm 1027: case MODEKEYEDIT_PREVIOUSSPACE:
1028: wsep = " ";
1029: /* FALLTHROUGH */
1.81 nicm 1030: case MODEKEYEDIT_PREVIOUSWORD:
1.83 nicm 1031: if (wsep == NULL)
1032: wsep = options_get_string(oo, "word-separators");
1.81 nicm 1033:
1034: /* Find a non-separator. */
1035: while (c->prompt_index != 0) {
1036: c->prompt_index--;
1037: if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1038: break;
1039: }
1040:
1041: /* Find the separator at the beginning of the word. */
1042: while (c->prompt_index != 0) {
1043: c->prompt_index--;
1044: if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
1045: /* Go back to the word. */
1046: c->prompt_index++;
1047: break;
1048: }
1049: }
1050:
1051: c->flags |= CLIENT_STATUS;
1052: break;
1.20 nicm 1053: case MODEKEYEDIT_HISTORYUP:
1.66 nicm 1054: histstr = status_prompt_up_history(&c->prompt_hindex);
1055: if (histstr == NULL)
1.1 nicm 1056: break;
1.94 nicm 1057: free(c->prompt_buffer);
1.66 nicm 1058: c->prompt_buffer = xstrdup(histstr);
1.1 nicm 1059: c->prompt_index = strlen(c->prompt_buffer);
1060: c->flags |= CLIENT_STATUS;
1061: break;
1.20 nicm 1062: case MODEKEYEDIT_HISTORYDOWN:
1.66 nicm 1063: histstr = status_prompt_down_history(&c->prompt_hindex);
1064: if (histstr == NULL)
1.65 nicm 1065: break;
1.94 nicm 1066: free(c->prompt_buffer);
1.66 nicm 1067: c->prompt_buffer = xstrdup(histstr);
1.1 nicm 1068: c->prompt_index = strlen(c->prompt_buffer);
1069: c->flags |= CLIENT_STATUS;
1070: break;
1.20 nicm 1071: case MODEKEYEDIT_PASTE:
1.134 nicm 1072: if ((pb = paste_get_top(NULL)) == NULL)
1.1 nicm 1073: break;
1.134 nicm 1074: bufdata = paste_buffer_data(pb, &bufsize);
1075: for (n = 0; n < bufsize; n++) {
1076: ch = (u_char)bufdata[n];
1.53 nicm 1077: if (ch < 32 || ch == 127)
1.32 nicm 1078: break;
1079: }
1.1 nicm 1080:
1.116 nicm 1081: c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1);
1.1 nicm 1082: if (c->prompt_index == size) {
1.134 nicm 1083: memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
1.1 nicm 1084: c->prompt_index += n;
1085: c->prompt_buffer[c->prompt_index] = '\0';
1086: } else {
1087: memmove(c->prompt_buffer + c->prompt_index + n,
1088: c->prompt_buffer + c->prompt_index,
1089: size + 1 - c->prompt_index);
1.134 nicm 1090: memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
1.1 nicm 1091: c->prompt_index += n;
1092: }
1093:
1094: c->flags |= CLIENT_STATUS;
1.30 nicm 1095: break;
1096: case MODEKEYEDIT_TRANSPOSECHARS:
1097: idx = c->prompt_index;
1098: if (idx < size)
1099: idx++;
1100: if (idx >= 2) {
1101: swapc = c->prompt_buffer[idx - 2];
1102: c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
1103: c->prompt_buffer[idx - 1] = swapc;
1104: c->prompt_index = idx;
1105: c->flags |= CLIENT_STATUS;
1106: }
1.1 nicm 1107: break;
1.55 nicm 1108: case MODEKEYEDIT_ENTER:
1.25 nicm 1109: if (*c->prompt_buffer != '\0')
1.65 nicm 1110: status_prompt_add_history(c->prompt_buffer);
1.25 nicm 1111: if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
1112: status_prompt_clear(c);
1113: break;
1.20 nicm 1114: case MODEKEYEDIT_CANCEL:
1.12 nicm 1115: if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
1.1 nicm 1116: status_prompt_clear(c);
1117: break;
1.20 nicm 1118: case MODEKEY_OTHER:
1.61 nicm 1119: if ((key & 0xff00) != 0 || key < 32 || key == 127)
1.1 nicm 1120: break;
1.116 nicm 1121: c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2);
1.1 nicm 1122:
1123: if (c->prompt_index == size) {
1124: c->prompt_buffer[c->prompt_index++] = key;
1125: c->prompt_buffer[c->prompt_index] = '\0';
1126: } else {
1127: memmove(c->prompt_buffer + c->prompt_index + 1,
1128: c->prompt_buffer + c->prompt_index,
1129: size + 1 - c->prompt_index);
1130: c->prompt_buffer[c->prompt_index++] = key;
1131: }
1132:
1133: if (c->prompt_flags & PROMPT_SINGLE) {
1.12 nicm 1134: if (c->prompt_callbackfn(
1.1 nicm 1135: c->prompt_data, c->prompt_buffer) == 0)
1136: status_prompt_clear(c);
1137: }
1138:
1139: c->flags |= CLIENT_STATUS;
1140: break;
1141: default:
1142: break;
1143: }
1144: }
1145:
1.65 nicm 1146: /* Get previous line from the history. */
1147: const char *
1148: status_prompt_up_history(u_int *idx)
1149: {
1150: /*
1.128 nicm 1151: * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1152: * empty.
1.65 nicm 1153: */
1154:
1.128 nicm 1155: if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
1.65 nicm 1156: return (NULL);
1157: (*idx)++;
1.128 nicm 1158: return (status_prompt_hlist[status_prompt_hsize - *idx]);
1.65 nicm 1159: }
1160:
1161: /* Get next line from the history. */
1162: const char *
1163: status_prompt_down_history(u_int *idx)
1164: {
1.128 nicm 1165: if (status_prompt_hsize == 0 || *idx == 0)
1.65 nicm 1166: return ("");
1167: (*idx)--;
1168: if (*idx == 0)
1169: return ("");
1.128 nicm 1170: return (status_prompt_hlist[status_prompt_hsize - *idx]);
1.65 nicm 1171: }
1172:
1.1 nicm 1173: /* Add line to the history. */
1174: void
1.65 nicm 1175: status_prompt_add_history(const char *line)
1.1 nicm 1176: {
1.128 nicm 1177: size_t size;
1.65 nicm 1178:
1.128 nicm 1179: if (status_prompt_hsize > 0 &&
1180: strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
1.1 nicm 1181: return;
1182:
1.128 nicm 1183: if (status_prompt_hsize == PROMPT_HISTORY) {
1184: free(status_prompt_hlist[0]);
1.1 nicm 1185:
1.128 nicm 1186: size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
1187: memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
1.1 nicm 1188:
1.128 nicm 1189: status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
1190: return;
1191: }
1.1 nicm 1192:
1.128 nicm 1193: status_prompt_hlist = xreallocarray(status_prompt_hlist,
1194: status_prompt_hsize + 1, sizeof *status_prompt_hlist);
1195: status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
1196: }
1197:
1198: /* Build completion list. */
1199: const char **
1200: status_prompt_complete_list(u_int *size, const char *s)
1201: {
1202: const char **list = NULL, **layout;
1203: const struct cmd_entry **cmdent;
1204: const struct options_table_entry *oe;
1205: const char *layouts[] = {
1206: "even-horizontal", "even-vertical", "main-horizontal",
1207: "main-vertical", "tiled", NULL
1208: };
1.1 nicm 1209:
1.128 nicm 1210: *size = 0;
1.1 nicm 1211: for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1.128 nicm 1212: if (strncmp((*cmdent)->name, s, strlen(s)) == 0) {
1213: list = xreallocarray(list, (*size) + 1, sizeof *list);
1214: list[(*size)++] = (*cmdent)->name;
1215: }
1.56 nicm 1216: }
1.69 nicm 1217: for (oe = server_options_table; oe->name != NULL; oe++) {
1.128 nicm 1218: if (strncmp(oe->name, s, strlen(s)) == 0) {
1219: list = xreallocarray(list, (*size) + 1, sizeof *list);
1220: list[(*size)++] = oe->name;
1221: }
1.69 nicm 1222: }
1223: for (oe = session_options_table; oe->name != NULL; oe++) {
1.128 nicm 1224: if (strncmp(oe->name, s, strlen(s)) == 0) {
1225: list = xreallocarray(list, (*size) + 1, sizeof *list);
1226: list[(*size)++] = oe->name;
1227: }
1.69 nicm 1228: }
1229: for (oe = window_options_table; oe->name != NULL; oe++) {
1.128 nicm 1230: if (strncmp(oe->name, s, strlen(s)) == 0) {
1231: list = xreallocarray(list, (*size) + 1, sizeof *list);
1232: list[(*size)++] = oe->name;
1233: }
1234: }
1235: for (layout = layouts; *layout != NULL; layout++) {
1236: if (strncmp(*layout, s, strlen(s)) == 0) {
1237: list = xreallocarray(list, (*size) + 1, sizeof *list);
1238: list[(*size)++] = *layout;
1239: }
1240: }
1241: return (list);
1242: }
1243:
1244: /* Find longest prefix. */
1245: char *
1246: status_prompt_complete_prefix(const char **list, u_int size)
1247: {
1248: char *out;
1249: u_int i;
1250: size_t j;
1251:
1252: out = xstrdup(list[0]);
1253: for (i = 1; i < size; i++) {
1254: j = strlen(list[i]);
1255: if (j > strlen(out))
1256: j = strlen(out);
1257: for (; j > 0; j--) {
1258: if (out[j - 1] != list[i][j - 1])
1259: out[j - 1] = '\0';
1260: }
1.1 nicm 1261: }
1.128 nicm 1262: return (out);
1263: }
1.1 nicm 1264:
1.128 nicm 1265: /* Complete word. */
1266: char *
1267: status_prompt_complete(struct session *sess, const char *s)
1268: {
1269: const char **list = NULL, *colon;
1270: u_int size = 0, i;
1271: struct session *s_loop;
1272: struct winlink *wl;
1273: struct window *w;
1274: char *copy, *out, *tmp;
1275:
1276: if (*s == '\0')
1.1 nicm 1277: return (NULL);
1.128 nicm 1278: out = NULL;
1279:
1280: if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
1281: list = status_prompt_complete_list(&size, s);
1282: if (size == 0)
1283: out = NULL;
1284: else if (size == 1)
1285: xasprintf(&out, "%s ", list[0]);
1286: else
1287: out = status_prompt_complete_prefix(list, size);
1288: free(list);
1289: return (out);
1290: }
1291: copy = xstrdup(s);
1292:
1293: colon = ":";
1294: if (copy[strlen(copy) - 1] == ':')
1295: copy[strlen(copy) - 1] = '\0';
1296: else
1297: colon = "";
1298: s = copy + 2;
1.1 nicm 1299:
1.128 nicm 1300: RB_FOREACH(s_loop, sessions, &sessions) {
1301: if (strncmp(s_loop->name, s, strlen(s)) == 0) {
1302: list = xreallocarray(list, size + 2, sizeof *list);
1303: list[size++] = s_loop->name;
1304: }
1305: }
1306: if (size == 1) {
1307: out = xstrdup(list[0]);
1308: if (session_find(list[0]) != NULL)
1309: colon = ":";
1310: } else if (size != 0)
1311: out = status_prompt_complete_prefix(list, size);
1312: if (out != NULL) {
1313: xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1314: out = tmp;
1315: goto found;
1316: }
1317:
1318: colon = "";
1319: if (*s == ':') {
1320: RB_FOREACH(wl, winlinks, &sess->windows) {
1321: xasprintf(&tmp, ":%s", wl->window->name);
1322: if (strncmp(tmp, s, strlen(s)) == 0){
1323: list = xreallocarray(list, size + 1,
1324: sizeof *list);
1325: list[size++] = tmp;
1326: continue;
1327: }
1328: free(tmp);
1.1 nicm 1329:
1.128 nicm 1330: xasprintf(&tmp, ":%d", wl->idx);
1331: if (strncmp(tmp, s, strlen(s)) == 0) {
1332: list = xreallocarray(list, size + 1,
1333: sizeof *list);
1334: list[size++] = tmp;
1335: continue;
1336: }
1337: free(tmp);
1338: }
1339: } else {
1340: RB_FOREACH(s_loop, sessions, &sessions) {
1341: RB_FOREACH(wl, winlinks, &s_loop->windows) {
1342: w = wl->window;
1343:
1344: xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
1345: if (strncmp(tmp, s, strlen(s)) == 0) {
1346: list = xreallocarray(list, size + 1,
1347: sizeof *list);
1348: list[size++] = tmp;
1349: continue;
1350: }
1351: free(tmp);
1352:
1353: xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
1354: if (strncmp(tmp, s, strlen(s)) == 0) {
1355: list = xreallocarray(list, size + 1,
1356: sizeof *list);
1357: list[size++] = tmp;
1358: continue;
1359: }
1360: free(tmp);
1361: }
1.1 nicm 1362: }
1363: }
1.128 nicm 1364: if (size == 1) {
1365: out = xstrdup(list[0]);
1366: colon = " ";
1367: } else if (size != 0)
1368: out = status_prompt_complete_prefix(list, size);
1369: if (out != NULL) {
1370: xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
1371: out = tmp;
1372: }
1373:
1374: for (i = 0; i < size; i++)
1375: free((void *)list[i]);
1376:
1377: found:
1378: free(copy);
1379: free(list);
1380: return (out);
1.1 nicm 1381: }