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