[BACK]Return to status.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/status.c, Revision 1.1

1.1     ! nicm        1: /* $OpenBSD$ */
        !             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:
        !            32: char   *status_replace(struct session *, char *, time_t);
        !            33: char   *status_replace_popen(char **);
        !            34: size_t status_width(struct winlink *);
        !            35: char   *status_print(struct session *, struct winlink *, struct grid_cell *);
        !            36:
        !            37: void   status_prompt_add_history(struct client *);
        !            38: char   *status_prompt_complete(const char *);
        !            39:
        !            40: /* Draw status for client on the last lines of given context. */
        !            41: int
        !            42: status_redraw(struct client *c)
        !            43: {
        !            44:        struct screen_write_ctx         ctx;
        !            45:        struct session                 *s = c->session;
        !            46:        struct winlink                 *wl;
        !            47:        struct window_pane             *wp;
        !            48:        struct screen                  *sc = NULL, old_status;
        !            49:        char                           *left, *right, *text, *ptr;
        !            50:        size_t                          llen, rlen, offset, xx, yy, sy;
        !            51:        size_t                          size, start, width;
        !            52:        struct grid_cell                stdgc, gc;
        !            53:        int                             larrow, rarrow;
        !            54:
        !            55:        left = right = NULL;
        !            56:
        !            57:        /* Create the target screen. */
        !            58:        memcpy(&old_status, &c->status, sizeof old_status);
        !            59:        screen_init(&c->status, c->tty.sx, 1, 0);
        !            60:
        !            61:        /* No status line? */
        !            62:        if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
        !            63:                goto off;
        !            64:        larrow = rarrow = 0;
        !            65:
        !            66:        if (gettimeofday(&c->status_timer, NULL) != 0)
        !            67:                fatal("gettimeofday");
        !            68:        memcpy(&stdgc, &grid_default_cell, sizeof gc);
        !            69:        stdgc.bg = options_get_number(&s->options, "status-fg");
        !            70:        stdgc.fg = options_get_number(&s->options, "status-bg");
        !            71:        stdgc.attr |= options_get_number(&s->options, "status-attr");
        !            72:
        !            73:        yy = c->tty.sy - 1;
        !            74:        if (yy == 0)
        !            75:                goto blank;
        !            76:
        !            77:        /* Work out the left and right strings. */
        !            78:        left = status_replace(s, options_get_string(
        !            79:            &s->options, "status-left"), c->status_timer.tv_sec);
        !            80:        llen = options_get_number(&s->options, "status-left-length");
        !            81:        if (strlen(left) < llen)
        !            82:                llen = strlen(left);
        !            83:        left[llen] = '\0';
        !            84:
        !            85:        right = status_replace(s, options_get_string(
        !            86:            &s->options, "status-right"), c->status_timer.tv_sec);
        !            87:        rlen = options_get_number(&s->options, "status-right-length");
        !            88:        if (strlen(right) < rlen)
        !            89:                rlen = strlen(right);
        !            90:        right[rlen] = '\0';
        !            91:
        !            92:        /*
        !            93:         * Figure out how much space we have for the window list. If there isn't
        !            94:         * enough space, just wimp out.
        !            95:         */
        !            96:        xx = 0;
        !            97:        if (llen != 0)
        !            98:                xx += llen + 1;
        !            99:        if (rlen != 0)
        !           100:                xx += rlen + 1;
        !           101:        if (c->tty.sx == 0 || c->tty.sx <= xx)
        !           102:                goto blank;
        !           103:        xx = c->tty.sx - xx;
        !           104:
        !           105:        /*
        !           106:         * Right. We have xx characters to fill. Find out how much is to go in
        !           107:         * them and the offset of the current window (it must be on screen).
        !           108:         */
        !           109:        width = offset = 0;
        !           110:        RB_FOREACH(wl, winlinks, &s->windows) {
        !           111:                size = status_width(wl) + 1;
        !           112:                if (wl == s->curw)
        !           113:                        offset = width;
        !           114:                width += size;
        !           115:        }
        !           116:        start = 0;
        !           117:
        !           118:        /* If there is enough space for the total width, all is gravy. */
        !           119:        if (width <= xx)
        !           120:                goto draw;
        !           121:
        !           122:        /* Find size of current window text. */
        !           123:        size = status_width(s->curw);
        !           124:
        !           125:        /*
        !           126:         * If the offset is already on screen, we're good to draw from the
        !           127:         * start and just leave off the end.
        !           128:         */
        !           129:        if (offset + size < xx) {
        !           130:                if (xx > 0) {
        !           131:                        rarrow = 1;
        !           132:                        xx--;
        !           133:                }
        !           134:
        !           135:                width = xx;
        !           136:                goto draw;
        !           137:        }
        !           138:
        !           139:        /*
        !           140:         * Work out how many characters we need to omit from the start. There
        !           141:         * are xx characters to fill, and offset + size must be the last. So,
        !           142:         * the start character is offset + size - xx.
        !           143:         */
        !           144:        if (xx > 0) {
        !           145:                larrow = 1;
        !           146:                xx--;
        !           147:        }
        !           148:
        !           149:        start = offset + size - xx;
        !           150:        if (xx > 0 && width > start + xx + 1) { /* + 1, eh? */
        !           151:                rarrow = 1;
        !           152:                start++;
        !           153:                xx--;
        !           154:        }
        !           155:        width = xx;
        !           156:
        !           157: draw:
        !           158:        /* Bail here if anything is too small too. XXX. */
        !           159:        if (width == 0 || xx == 0)
        !           160:                goto blank;
        !           161:
        !           162:        /* Begin drawing and move to the starting position. */
        !           163:        screen_write_start(&ctx, NULL, &c->status);
        !           164:        if (llen != 0) {
        !           165:                screen_write_cursormove(&ctx, 0, yy);
        !           166:                screen_write_puts(&ctx, &stdgc, "%s ", left);
        !           167:                if (larrow)
        !           168:                        screen_write_putc(&ctx, &stdgc, ' ');
        !           169:        } else {
        !           170:                if (larrow)
        !           171:                        screen_write_cursormove(&ctx, 1, yy);
        !           172:                else
        !           173:                        screen_write_cursormove(&ctx, 0, yy);
        !           174:        }
        !           175:
        !           176:        /* Draw each character in succession. */
        !           177:        offset = 0;
        !           178:        RB_FOREACH(wl, winlinks, &s->windows) {
        !           179:                memcpy(&gc, &stdgc, sizeof gc);
        !           180:                text = status_print(s, wl, &gc);
        !           181:
        !           182:                if (larrow == 1 && offset < start) {
        !           183:                        if (session_alert_has(s, wl, WINDOW_ACTIVITY))
        !           184:                                larrow = -1;
        !           185:                        else if (session_alert_has(s, wl, WINDOW_BELL))
        !           186:                                larrow = -1;
        !           187:                        else if (session_alert_has(s, wl, WINDOW_CONTENT))
        !           188:                                larrow = -1;
        !           189:                }
        !           190:
        !           191:                for (ptr = text; *ptr != '\0'; ptr++) {
        !           192:                        if (offset >= start && offset < start + width)
        !           193:                                screen_write_putc(&ctx, &gc, *ptr);
        !           194:                        offset++;
        !           195:                }
        !           196:
        !           197:                if (rarrow == 1 && offset > start + width) {
        !           198:                        if (session_alert_has(s, wl, WINDOW_ACTIVITY))
        !           199:                                rarrow = -1;
        !           200:                        else if (session_alert_has(s, wl, WINDOW_BELL))
        !           201:                                rarrow = -1;
        !           202:                        else if (session_alert_has(s, wl, WINDOW_CONTENT))
        !           203:                                rarrow = -1;
        !           204:                }
        !           205:
        !           206:                if (offset < start + width) {
        !           207:                        if (offset >= start) {
        !           208:                                screen_write_putc(&ctx, &stdgc, ' ');
        !           209:                        }
        !           210:                        offset++;
        !           211:                }
        !           212:
        !           213:                xfree(text);
        !           214:        }
        !           215:
        !           216:        /* Fill the remaining space if any. */
        !           217:        while (offset++ < xx)
        !           218:                screen_write_putc(&ctx, &stdgc, ' ');
        !           219:
        !           220:        /* Draw the last item. */
        !           221:        if (rlen != 0) {
        !           222:                screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, yy);
        !           223:                screen_write_puts(&ctx, &stdgc, " %s", right);
        !           224:        }
        !           225:
        !           226:        /* Draw the arrows. */
        !           227:        if (larrow != 0) {
        !           228:                memcpy(&gc, &stdgc, sizeof gc);
        !           229:                if (larrow == -1)
        !           230:                        gc.attr ^= GRID_ATTR_REVERSE;
        !           231:                if (llen != 0)
        !           232:                        screen_write_cursormove(&ctx, llen + 1, yy);
        !           233:                else
        !           234:                        screen_write_cursormove(&ctx, 0, yy);
        !           235:                screen_write_putc(&ctx, &gc, '<');
        !           236:        }
        !           237:        if (rarrow != 0) {
        !           238:                memcpy(&gc, &stdgc, sizeof gc);
        !           239:                if (rarrow == -1)
        !           240:                        gc.attr ^= GRID_ATTR_REVERSE;
        !           241:                if (rlen != 0)
        !           242:                        screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, yy);
        !           243:                else
        !           244:                        screen_write_cursormove(&ctx, c->tty.sx - 1, yy);
        !           245:                screen_write_putc(&ctx, &gc, '>');
        !           246:        }
        !           247:
        !           248:        goto out;
        !           249:
        !           250: blank:
        !           251:        /* Just draw the whole line as blank. */
        !           252:        screen_write_start(&ctx, NULL, &c->status);
        !           253:        screen_write_cursormove(&ctx, 0, yy);
        !           254:        for (offset = 0; offset < c->tty.sx; offset++)
        !           255:                screen_write_putc(&ctx, &stdgc, ' ');
        !           256:
        !           257:        goto out;
        !           258:
        !           259: off:
        !           260:        /*
        !           261:         * Draw the real window last line. Necessary to wipe over message if
        !           262:         * status is off. Not sure this is the right place for this.
        !           263:         */
        !           264:        memcpy(&stdgc, &grid_default_cell, sizeof stdgc);
        !           265:        screen_write_start(&ctx, NULL, &c->status);
        !           266:
        !           267:        sy = 0;
        !           268:        TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
        !           269:                sy += wp->sy + 1;
        !           270:                sc = wp->screen;
        !           271:        }
        !           272:
        !           273:        screen_write_cursormove(&ctx, 0, 0);
        !           274:        if (sy < c->tty.sy) {
        !           275:                /* If the screen is too small, use blank. */
        !           276:                for (offset = 0; offset < c->tty.sx; offset++)
        !           277:                        screen_write_putc(&ctx, &stdgc, ' ');
        !           278:        } else {
        !           279:                screen_write_copy(&ctx,
        !           280:                    sc, 0, sc->grid->hsize + screen_size_y(sc) - 1, c->tty.sx, 1);
        !           281:        }
        !           282:
        !           283: out:
        !           284:        screen_write_stop(&ctx);
        !           285:
        !           286:        if (left != NULL)
        !           287:                xfree(left);
        !           288:        if (right != NULL)
        !           289:                xfree(right);
        !           290:
        !           291:        if (grid_compare(c->status.grid, old_status.grid) == 0) {
        !           292:                screen_free(&old_status);
        !           293:                return (0);
        !           294:        }
        !           295:        screen_free(&old_status);
        !           296:        return (1);
        !           297: }
        !           298:
        !           299: char *
        !           300: status_replace(struct session *s, char *fmt, time_t t)
        !           301: {
        !           302:        struct winlink *wl = s->curw;
        !           303:        static char     out[BUFSIZ];
        !           304:        char            in[BUFSIZ], tmp[256], ch, *iptr, *optr, *ptr, *endptr;
        !           305:        char           *savedptr;
        !           306:        size_t          len;
        !           307:        long            n;
        !           308:
        !           309:        strftime(in, sizeof in, fmt, localtime(&t));
        !           310:        in[(sizeof in) - 1] = '\0';
        !           311:
        !           312:        iptr = in;
        !           313:        optr = out;
        !           314:        savedptr = NULL;
        !           315:
        !           316:        while (*iptr != '\0') {
        !           317:                if (optr >= out + (sizeof out) - 1)
        !           318:                        break;
        !           319:                switch (ch = *iptr++) {
        !           320:                case '#':
        !           321:                        errno = 0;
        !           322:                        n = strtol(iptr, &endptr, 10);
        !           323:                        if ((n == 0 && errno != EINVAL) ||
        !           324:                            (n == LONG_MIN && errno != ERANGE) ||
        !           325:                            (n == LONG_MAX && errno != ERANGE) ||
        !           326:                            n != 0)
        !           327:                                iptr = endptr;
        !           328:                        if (n <= 0)
        !           329:                                n = LONG_MAX;
        !           330:
        !           331:                        ptr = NULL;
        !           332:                        switch (*iptr++) {
        !           333:                        case '(':
        !           334:                                if (ptr == NULL) {
        !           335:                                        ptr = status_replace_popen(&iptr);
        !           336:                                        if (ptr == NULL)
        !           337:                                                break;
        !           338:                                        savedptr = ptr;
        !           339:                                }
        !           340:                                /* FALLTHROUGH */
        !           341:                        case 'H':
        !           342:                                if (ptr == NULL) {
        !           343:                                        if (gethostname(tmp, sizeof tmp) != 0)
        !           344:                                                fatal("gethostname");
        !           345:                                        ptr = tmp;
        !           346:                                }
        !           347:                                /* FALLTHROUGH */
        !           348:                        case 'S':
        !           349:                                if (ptr == NULL)
        !           350:                                        ptr = s->name;
        !           351:                                /* FALLTHROUGH */
        !           352:                        case 'T':
        !           353:                                if (ptr == NULL)
        !           354:                                        ptr = wl->window->active->base.title;
        !           355:                                len = strlen(ptr);
        !           356:                                if ((size_t) n < len)
        !           357:                                        len = n;
        !           358:                                if (optr + len >= out + (sizeof out) - 1)
        !           359:                                        break;
        !           360:                                while (len > 0 && *ptr != '\0') {
        !           361:                                        *optr++ = *ptr++;
        !           362:                                        len--;
        !           363:                                }
        !           364:                                break;
        !           365:                        case '#':
        !           366:                                *optr++ = '#';
        !           367:                                break;
        !           368:                        }
        !           369:                        if (savedptr != NULL) {
        !           370:                                xfree(savedptr);
        !           371:                                savedptr = NULL;
        !           372:                        }
        !           373:                        break;
        !           374:                default:
        !           375:                        *optr++ = ch;
        !           376:                        break;
        !           377:                }
        !           378:        }
        !           379:        *optr = '\0';
        !           380:
        !           381:        return (xstrdup(out));
        !           382: }
        !           383:
        !           384: char *
        !           385: status_replace_popen(char **iptr)
        !           386: {
        !           387:        FILE    *f;
        !           388:        char    *buf, *cmd, *ptr;
        !           389:        int     lastesc;
        !           390:        size_t  len;
        !           391:
        !           392:        if (**iptr == '\0')
        !           393:                return (NULL);
        !           394:        if (**iptr == ')') {            /* no command given */
        !           395:                (*iptr)++;
        !           396:                return (NULL);
        !           397:        }
        !           398:
        !           399:        buf = NULL;
        !           400:
        !           401:        cmd = xmalloc(strlen(*iptr) + 1);
        !           402:        len = 0;
        !           403:
        !           404:        lastesc = 0;
        !           405:        for (; **iptr != '\0'; (*iptr)++) {
        !           406:                if (!lastesc && **iptr == ')')
        !           407:                        break;          /* unescaped ) is the end */
        !           408:                if (!lastesc && **iptr == '\\') {
        !           409:                        lastesc = 1;
        !           410:                        continue;       /* skip \ if not escaped */
        !           411:                }
        !           412:                lastesc = 0;
        !           413:                cmd[len++] = **iptr;
        !           414:        }
        !           415:        if (**iptr == '\0')             /* no terminating ) */
        !           416:                goto out;
        !           417:        (*iptr)++;                      /* skip final ) */
        !           418:        cmd[len] = '\0';
        !           419:
        !           420:        if ((f = popen(cmd, "r")) == NULL)
        !           421:                goto out;
        !           422:
        !           423:        if ((buf = fgetln(f, &len)) == NULL) {
        !           424:                pclose(f);
        !           425:                goto out;
        !           426:        }
        !           427:        if (buf[len - 1] == '\n') {
        !           428:                buf[len - 1] = '\0';
        !           429:                buf = xstrdup(buf);
        !           430:        } else {
        !           431:                ptr = xmalloc(len + 1);
        !           432:                memcpy(ptr, buf, len);
        !           433:                ptr[len] = '\0';
        !           434:                buf = ptr;
        !           435:        }
        !           436:        pclose(f);
        !           437:
        !           438: out:
        !           439:        xfree(cmd);
        !           440:        return (buf);
        !           441: }
        !           442:
        !           443: size_t
        !           444: status_width(struct winlink *wl)
        !           445: {
        !           446:        return (xsnprintf(NULL, 0, "%d:%s ", wl->idx, wl->window->name));
        !           447: }
        !           448:
        !           449: char *
        !           450: status_print(struct session *s, struct winlink *wl, struct grid_cell *gc)
        !           451: {
        !           452:        char   *text, flag;
        !           453:        u_char  fg, bg, attr;
        !           454:
        !           455:        fg = options_get_number(&wl->window->options, "window-status-fg");
        !           456:        if (fg != 8)
        !           457:                gc->fg = fg;
        !           458:        bg = options_get_number(&wl->window->options, "window-status-bg");
        !           459:        if (bg != 8)
        !           460:                gc->bg = bg;
        !           461:        attr = options_get_number(&wl->window->options, "window-status-attr");
        !           462:        if (attr != 0)
        !           463:                gc->attr = attr;
        !           464:
        !           465:        flag = ' ';
        !           466:        if (wl == SLIST_FIRST(&s->lastw))
        !           467:                flag = '-';
        !           468:        if (wl == s->curw)
        !           469:                flag = '*';
        !           470:
        !           471:        if (session_alert_has(s, wl, WINDOW_ACTIVITY)) {
        !           472:                flag = '#';
        !           473:                gc->attr ^= GRID_ATTR_REVERSE;
        !           474:        } else if (session_alert_has(s, wl, WINDOW_BELL)) {
        !           475:                flag = '!';
        !           476:                gc->attr ^= GRID_ATTR_REVERSE;
        !           477:        } else if (session_alert_has(s, wl, WINDOW_CONTENT)) {
        !           478:                flag = '+';
        !           479:                gc->attr ^= GRID_ATTR_REVERSE;
        !           480:        }
        !           481:
        !           482:        xasprintf(&text, "%d:%s%c", wl->idx, wl->window->name, flag);
        !           483:        return (text);
        !           484: }
        !           485:
        !           486: void
        !           487: status_message_set(struct client *c, const char *msg)
        !           488: {
        !           489:        struct timeval  tv;
        !           490:        int             delay;
        !           491:
        !           492:        delay = options_get_number(&c->session->options, "display-time");
        !           493:        tv.tv_sec = delay / 1000;
        !           494:        tv.tv_usec = (delay % 1000) * 1000L;
        !           495:
        !           496:        c->message_string = xstrdup(msg);
        !           497:        if (gettimeofday(&c->message_timer, NULL) != 0)
        !           498:                fatal("gettimeofday");
        !           499:        timeradd(&c->message_timer, &tv, &c->message_timer);
        !           500:
        !           501:        c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
        !           502:        c->flags |= CLIENT_STATUS;
        !           503: }
        !           504:
        !           505: void
        !           506: status_message_clear(struct client *c)
        !           507: {
        !           508:        if (c->message_string == NULL)
        !           509:                return;
        !           510:
        !           511:        xfree(c->message_string);
        !           512:        c->message_string = NULL;
        !           513:
        !           514:        c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
        !           515:        c->flags |= CLIENT_REDRAW;
        !           516: }
        !           517:
        !           518: /* Draw client message on status line of present else on last line. */
        !           519: int
        !           520: status_message_redraw(struct client *c)
        !           521: {
        !           522:        struct screen_write_ctx         ctx;
        !           523:        struct session                 *s = c->session;
        !           524:        struct screen                   old_status;
        !           525:        size_t                          len;
        !           526:        struct grid_cell                gc;
        !           527:
        !           528:        if (c->tty.sx == 0 || c->tty.sy == 0)
        !           529:                return (0);
        !           530:        memcpy(&old_status, &c->status, sizeof old_status);
        !           531:        screen_init(&c->status, c->tty.sx, 1, 0);
        !           532:
        !           533:        len = strlen(c->message_string);
        !           534:        if (len > c->tty.sx)
        !           535:                len = c->tty.sx;
        !           536:
        !           537:        memcpy(&gc, &grid_default_cell, sizeof gc);
        !           538:        gc.bg = options_get_number(&s->options, "message-fg");
        !           539:        gc.fg = options_get_number(&s->options, "message-bg");
        !           540:        gc.attr |= options_get_number(&s->options, "message-attr");
        !           541:
        !           542:        screen_write_start(&ctx, NULL, &c->status);
        !           543:
        !           544:        screen_write_cursormove(&ctx, 0, 0);
        !           545:        screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->message_string);
        !           546:        for (; len < c->tty.sx; len++)
        !           547:                screen_write_putc(&ctx, &gc, ' ');
        !           548:
        !           549:        screen_write_stop(&ctx);
        !           550:
        !           551:        if (grid_compare(c->status.grid, old_status.grid) == 0) {
        !           552:                screen_free(&old_status);
        !           553:                return (0);
        !           554:        }
        !           555:        screen_free(&old_status);
        !           556:        return (1);
        !           557: }
        !           558:
        !           559: void
        !           560: status_prompt_set(struct client *c,
        !           561:     const char *msg, int (*fn)(void *, const char *), void *data, int flags)
        !           562: {
        !           563:        c->prompt_string = xstrdup(msg);
        !           564:
        !           565:        c->prompt_buffer = xstrdup("");
        !           566:        c->prompt_index = 0;
        !           567:
        !           568:        c->prompt_callback = fn;
        !           569:        c->prompt_data = data;
        !           570:
        !           571:        c->prompt_hindex = 0;
        !           572:
        !           573:        c->prompt_flags = flags;
        !           574:
        !           575:        mode_key_init(&c->prompt_mdata,
        !           576:            options_get_number(&c->session->options, "status-keys"),
        !           577:            MODEKEY_CANEDIT);
        !           578:
        !           579:        c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
        !           580:        c->flags |= CLIENT_STATUS;
        !           581: }
        !           582:
        !           583: void
        !           584: status_prompt_clear(struct client *c)
        !           585: {
        !           586:        if (c->prompt_string == NULL)
        !           587:                return;
        !           588:
        !           589:        mode_key_free(&c->prompt_mdata);
        !           590:
        !           591:        xfree(c->prompt_string);
        !           592:        c->prompt_string = NULL;
        !           593:
        !           594:        xfree(c->prompt_buffer);
        !           595:        c->prompt_buffer = NULL;
        !           596:
        !           597:        c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
        !           598:        c->flags |= CLIENT_REDRAW;
        !           599: }
        !           600:
        !           601: /* Draw client prompt on status line of present else on last line. */
        !           602: int
        !           603: status_prompt_redraw(struct client *c)
        !           604: {
        !           605:        struct screen_write_ctx ctx;
        !           606:        struct session                 *s = c->session;
        !           607:        struct screen                   old_status;
        !           608:        size_t                          i, size, left, len, offset, n;
        !           609:        char                            ch;
        !           610:        struct grid_cell                gc;
        !           611:
        !           612:        if (c->tty.sx == 0 || c->tty.sy == 0)
        !           613:                return (0);
        !           614:        memcpy(&old_status, &c->status, sizeof old_status);
        !           615:        screen_init(&c->status, c->tty.sx, 1, 0);
        !           616:        offset = 0;
        !           617:
        !           618:        len = strlen(c->prompt_string);
        !           619:        if (len > c->tty.sx)
        !           620:                len = c->tty.sx;
        !           621:
        !           622:        memcpy(&gc, &grid_default_cell, sizeof gc);
        !           623:        gc.bg = options_get_number(&s->options, "message-fg");
        !           624:        gc.fg = options_get_number(&s->options, "message-bg");
        !           625:        gc.attr |= options_get_number(&s->options, "message-attr");
        !           626:
        !           627:        screen_write_start(&ctx, NULL, &c->status);
        !           628:
        !           629:        screen_write_cursormove(&ctx, 0, 0);
        !           630:        screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->prompt_string);
        !           631:
        !           632:        left = c->tty.sx - len;
        !           633:        if (left != 0) {
        !           634:                if (c->prompt_index < left)
        !           635:                        size = strlen(c->prompt_buffer);
        !           636:                else {
        !           637:                        offset = c->prompt_index - left - 1;
        !           638:                        if (c->prompt_index == strlen(c->prompt_buffer))
        !           639:                                left--;
        !           640:                        size = left;
        !           641:                }
        !           642:                if (c->prompt_flags & PROMPT_HIDDEN) {
        !           643:                        n = strlen(c->prompt_buffer);
        !           644:                        if (n > left)
        !           645:                                n = left;
        !           646:                        for (i = 0; i < n; i++)
        !           647:                                screen_write_putc(&ctx, &gc, '*');
        !           648:                } else {
        !           649:                        screen_write_puts(&ctx, &gc,
        !           650:                            "%.*s", (int) left, c->prompt_buffer + offset);
        !           651:                }
        !           652:
        !           653:                for (i = len + size; i < c->tty.sx; i++)
        !           654:                        screen_write_putc(&ctx, &gc, ' ');
        !           655:        }
        !           656:
        !           657:        /* Draw a fake cursor. */
        !           658:        screen_write_cursormove(&ctx, len + c->prompt_index - offset, 0);
        !           659:        if (c->prompt_index == strlen(c->prompt_buffer))
        !           660:                ch = ' ';
        !           661:        else {
        !           662:                if (c->prompt_flags & PROMPT_HIDDEN)
        !           663:                        ch = '*';
        !           664:                else
        !           665:                        ch = c->prompt_buffer[c->prompt_index];
        !           666:        }
        !           667:        if (ch == '\0')
        !           668:                ch = ' ';
        !           669:        gc.attr ^= GRID_ATTR_REVERSE;
        !           670:        screen_write_putc(&ctx, &gc, ch);
        !           671:
        !           672:        screen_write_stop(&ctx);
        !           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: {
        !           686:        struct paste_buffer     *pb;
        !           687:        char                    *s, *first, *last, word[64];
        !           688:        size_t                   size, n, off, idx;
        !           689:
        !           690:        size = strlen(c->prompt_buffer);
        !           691:        switch (mode_key_lookup(&c->prompt_mdata, key)) {
        !           692:        case MODEKEYCMD_LEFT:
        !           693:                if (c->prompt_index > 0) {
        !           694:                        c->prompt_index--;
        !           695:                        c->flags |= CLIENT_STATUS;
        !           696:                }
        !           697:                break;
        !           698:        case MODEKEYCMD_RIGHT:
        !           699:                if (c->prompt_index < size) {
        !           700:                        c->prompt_index++;
        !           701:                        c->flags |= CLIENT_STATUS;
        !           702:                }
        !           703:                break;
        !           704:        case MODEKEYCMD_STARTOFLINE:
        !           705:                if (c->prompt_index != 0) {
        !           706:                        c->prompt_index = 0;
        !           707:                        c->flags |= CLIENT_STATUS;
        !           708:                }
        !           709:                break;
        !           710:        case MODEKEYCMD_ENDOFLINE:
        !           711:                if (c->prompt_index != size) {
        !           712:                        c->prompt_index = size;
        !           713:                        c->flags |= CLIENT_STATUS;
        !           714:                }
        !           715:                break;
        !           716:        case MODEKEYCMD_COMPLETE:
        !           717:                if (*c->prompt_buffer == '\0')
        !           718:                        break;
        !           719:
        !           720:                idx = c->prompt_index;
        !           721:                if (idx != 0)
        !           722:                        idx--;
        !           723:
        !           724:                /* Find the word we are in. */
        !           725:                first = c->prompt_buffer + idx;
        !           726:                while (first > c->prompt_buffer && *first != ' ')
        !           727:                        first--;
        !           728:                while (*first == ' ')
        !           729:                        first++;
        !           730:                last = c->prompt_buffer + idx;
        !           731:                while (*last != '\0' && *last != ' ')
        !           732:                        last++;
        !           733:                while (*last == ' ')
        !           734:                        last--;
        !           735:                if (*last != '\0')
        !           736:                        last++;
        !           737:                if (last <= first ||
        !           738:                    ((size_t) (last - first)) > (sizeof word) - 1)
        !           739:                        break;
        !           740:                memcpy(word, first, last - first);
        !           741:                word[last - first] = '\0';
        !           742:
        !           743:                /* And try to complete it. */
        !           744:                if ((s = status_prompt_complete(word)) == NULL)
        !           745:                        break;
        !           746:
        !           747:                /* Trim out word. */
        !           748:                n = size - (last - c->prompt_buffer) + 1; /* with \0 */
        !           749:                memmove(first, last, n);
        !           750:                size -= last - first;
        !           751:
        !           752:                /* Insert the new word. */
        !           753:                size += strlen(s);
        !           754:                off = first - c->prompt_buffer;
        !           755:                c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1);
        !           756:                first = c->prompt_buffer + off;
        !           757:                memmove(first + strlen(s), first, n);
        !           758:                memcpy(first, s, strlen(s));
        !           759:
        !           760:                c->prompt_index = (first - c->prompt_buffer) + strlen(s);
        !           761:
        !           762:                c->flags |= CLIENT_STATUS;
        !           763:                break;
        !           764:        case MODEKEYCMD_BACKSPACE:
        !           765:                if (c->prompt_index != 0) {
        !           766:                        if (c->prompt_index == size)
        !           767:                                c->prompt_buffer[--c->prompt_index] = '\0';
        !           768:                        else {
        !           769:                                memmove(c->prompt_buffer + c->prompt_index - 1,
        !           770:                                    c->prompt_buffer + c->prompt_index,
        !           771:                                    size + 1 - c->prompt_index);
        !           772:                                c->prompt_index--;
        !           773:                        }
        !           774:                        c->flags |= CLIENT_STATUS;
        !           775:                }
        !           776:                break;
        !           777:        case MODEKEYCMD_DELETE:
        !           778:                if (c->prompt_index != size) {
        !           779:                        memmove(c->prompt_buffer + c->prompt_index,
        !           780:                            c->prompt_buffer + c->prompt_index + 1,
        !           781:                            size + 1 - c->prompt_index);
        !           782:                        c->flags |= CLIENT_STATUS;
        !           783:                }
        !           784:                break;
        !           785:        case MODEKEYCMD_UP:
        !           786:                if (server_locked)
        !           787:                        break;
        !           788:
        !           789:                if (ARRAY_LENGTH(&c->prompt_hdata) == 0)
        !           790:                        break;
        !           791:                xfree(c->prompt_buffer);
        !           792:
        !           793:                c->prompt_buffer = xstrdup(ARRAY_ITEM(&c->prompt_hdata,
        !           794:                    ARRAY_LENGTH(&c->prompt_hdata) - 1 - c->prompt_hindex));
        !           795:                if (c->prompt_hindex != ARRAY_LENGTH(&c->prompt_hdata) - 1)
        !           796:                        c->prompt_hindex++;
        !           797:
        !           798:                c->prompt_index = strlen(c->prompt_buffer);
        !           799:                c->flags |= CLIENT_STATUS;
        !           800:                break;
        !           801:        case MODEKEYCMD_DOWN:
        !           802:                if (server_locked)
        !           803:                        break;
        !           804:
        !           805:                xfree(c->prompt_buffer);
        !           806:
        !           807:                if (c->prompt_hindex != 0) {
        !           808:                        c->prompt_hindex--;
        !           809:                        c->prompt_buffer = xstrdup(ARRAY_ITEM(
        !           810:                            &c->prompt_hdata, ARRAY_LENGTH(
        !           811:                            &c->prompt_hdata) - 1 - c->prompt_hindex));
        !           812:                } else
        !           813:                        c->prompt_buffer = xstrdup("");
        !           814:
        !           815:                c->prompt_index = strlen(c->prompt_buffer);
        !           816:                c->flags |= CLIENT_STATUS;
        !           817:                break;
        !           818:        case MODEKEYCMD_PASTE:
        !           819:                if ((pb = paste_get_top(&c->session->buffers)) == NULL)
        !           820:                        break;
        !           821:                if ((last = strchr(pb->data, '\n')) == NULL)
        !           822:                        last = strchr(pb->data, '\0');
        !           823:                n = last - pb->data;
        !           824:
        !           825:                c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1);
        !           826:                if (c->prompt_index == size) {
        !           827:                        memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
        !           828:                        c->prompt_index += n;
        !           829:                        c->prompt_buffer[c->prompt_index] = '\0';
        !           830:                } else {
        !           831:                        memmove(c->prompt_buffer + c->prompt_index + n,
        !           832:                            c->prompt_buffer + c->prompt_index,
        !           833:                            size + 1 - c->prompt_index);
        !           834:                        memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
        !           835:                        c->prompt_index += n;
        !           836:                }
        !           837:
        !           838:                c->flags |= CLIENT_STATUS;
        !           839:                break;
        !           840:        case MODEKEYCMD_CHOOSE:
        !           841:                if (*c->prompt_buffer != '\0') {
        !           842:                        status_prompt_add_history(c);
        !           843:                        if (c->prompt_callback(
        !           844:                            c->prompt_data, c->prompt_buffer) == 0)
        !           845:                                status_prompt_clear(c);
        !           846:                        break;
        !           847:                }
        !           848:                /* FALLTHROUGH */
        !           849:        case MODEKEYCMD_QUIT:
        !           850:                if (c->prompt_callback(c->prompt_data, NULL) == 0)
        !           851:                        status_prompt_clear(c);
        !           852:                break;
        !           853:        case MODEKEYCMD_OTHERKEY:
        !           854:                if (key < 32 || key > 126)
        !           855:                        break;
        !           856:                c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2);
        !           857:
        !           858:                if (c->prompt_index == size) {
        !           859:                        c->prompt_buffer[c->prompt_index++] = key;
        !           860:                        c->prompt_buffer[c->prompt_index] = '\0';
        !           861:                } else {
        !           862:                        memmove(c->prompt_buffer + c->prompt_index + 1,
        !           863:                            c->prompt_buffer + c->prompt_index,
        !           864:                            size + 1 - c->prompt_index);
        !           865:                        c->prompt_buffer[c->prompt_index++] = key;
        !           866:                }
        !           867:
        !           868:                if (c->prompt_flags & PROMPT_SINGLE) {
        !           869:                        if (c->prompt_callback(
        !           870:                            c->prompt_data, c->prompt_buffer) == 0)
        !           871:                                status_prompt_clear(c);
        !           872:                }
        !           873:
        !           874:                c->flags |= CLIENT_STATUS;
        !           875:                break;
        !           876:        default:
        !           877:                break;
        !           878:        }
        !           879: }
        !           880:
        !           881: /* Add line to the history. */
        !           882: void
        !           883: status_prompt_add_history(struct client *c)
        !           884: {
        !           885:        if (server_locked)
        !           886:                return;
        !           887:
        !           888:        if (ARRAY_LENGTH(&c->prompt_hdata) > 0 &&
        !           889:            strcmp(ARRAY_LAST(&c->prompt_hdata), c->prompt_buffer) == 0)
        !           890:                return;
        !           891:
        !           892:        if (ARRAY_LENGTH(&c->prompt_hdata) == PROMPT_HISTORY) {
        !           893:                xfree(ARRAY_FIRST(&c->prompt_hdata));
        !           894:                ARRAY_REMOVE(&c->prompt_hdata, 0);
        !           895:        }
        !           896:
        !           897:        ARRAY_ADD(&c->prompt_hdata, xstrdup(c->prompt_buffer));
        !           898: }
        !           899:
        !           900: /* Complete word. */
        !           901: char *
        !           902: status_prompt_complete(const char *s)
        !           903: {
        !           904:        const struct cmd_entry        **cmdent;
        !           905:        const struct set_option_entry  *optent;
        !           906:        ARRAY_DECL(, const char *)      list;
        !           907:        char                           *prefix, *s2;
        !           908:        u_int                           i;
        !           909:        size_t                          j;
        !           910:
        !           911:        if (*s == '\0')
        !           912:                return (NULL);
        !           913:
        !           914:        /* First, build a list of all the possible matches. */
        !           915:        ARRAY_INIT(&list);
        !           916:        for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
        !           917:                if (strncmp((*cmdent)->name, s, strlen(s)) == 0)
        !           918:                        ARRAY_ADD(&list, (*cmdent)->name);
        !           919:        }
        !           920:        for (i = 0; i < NSETOPTION; i++) {
        !           921:                optent = &set_option_table[i];
        !           922:                if (strncmp(optent->name, s, strlen(s)) == 0)
        !           923:                        ARRAY_ADD(&list, optent->name);
        !           924:        }
        !           925:        for (i = 0; i < NSETWINDOWOPTION; i++) {
        !           926:                optent = &set_window_option_table[i];
        !           927:                if (strncmp(optent->name, s, strlen(s)) == 0)
        !           928:                        ARRAY_ADD(&list, optent->name);
        !           929:        }
        !           930:
        !           931:        /* If none, bail now. */
        !           932:        if (ARRAY_LENGTH(&list) == 0) {
        !           933:                ARRAY_FREE(&list);
        !           934:                return (NULL);
        !           935:        }
        !           936:
        !           937:        /* If an exact match, return it, with a trailing space. */
        !           938:        if (ARRAY_LENGTH(&list) == 1) {
        !           939:                xasprintf(&s2, "%s ", ARRAY_FIRST(&list));
        !           940:                ARRAY_FREE(&list);
        !           941:                return (s2);
        !           942:        }
        !           943:
        !           944:        /* Now loop through the list and find the longest common prefix. */
        !           945:        prefix = xstrdup(ARRAY_FIRST(&list));
        !           946:        for (i = 1; i < ARRAY_LENGTH(&list); i++) {
        !           947:                s = ARRAY_ITEM(&list, i);
        !           948:
        !           949:                j = strlen(s);
        !           950:                if (j > strlen(prefix))
        !           951:                        j = strlen(prefix);
        !           952:                for (; j > 0; j--) {
        !           953:                        if (prefix[j - 1] != s[j - 1])
        !           954:                                prefix[j - 1] = '\0';
        !           955:                }
        !           956:        }
        !           957:
        !           958:        ARRAY_FREE(&list);
        !           959:        return (prefix);
        !           960: }