Annotation of embedaddon/tmux/server-client.c, revision 1.1

1.1     ! misho       1: /* $OpenBSD$ */
        !             2: 
        !             3: /*
        !             4:  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
        !             5:  *
        !             6:  * Permission to use, copy, modify, and distribute this software for any
        !             7:  * purpose with or without fee is hereby granted, provided that the above
        !             8:  * copyright notice and this permission notice appear in all copies.
        !             9:  *
        !            10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
        !            15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
        !            16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            17:  */
        !            18: 
        !            19: #include <sys/types.h>
        !            20: #include <sys/ioctl.h>
        !            21: #include <sys/uio.h>
        !            22: 
        !            23: #include <errno.h>
        !            24: #include <event.h>
        !            25: #include <fcntl.h>
        !            26: #include <stdlib.h>
        !            27: #include <string.h>
        !            28: #include <time.h>
        !            29: #include <unistd.h>
        !            30: 
        !            31: #include "tmux.h"
        !            32: 
        !            33: static void    server_client_free(int, short, void *);
        !            34: static void    server_client_check_focus(struct window_pane *);
        !            35: static void    server_client_check_resize(struct window_pane *);
        !            36: static key_code        server_client_check_mouse(struct client *);
        !            37: static void    server_client_repeat_timer(int, short, void *);
        !            38: static void    server_client_click_timer(int, short, void *);
        !            39: static void    server_client_check_exit(struct client *);
        !            40: static void    server_client_check_redraw(struct client *);
        !            41: static void    server_client_set_title(struct client *);
        !            42: static void    server_client_reset_state(struct client *);
        !            43: static int     server_client_assume_paste(struct session *);
        !            44: 
        !            45: static void    server_client_dispatch(struct imsg *, void *);
        !            46: static void    server_client_dispatch_command(struct client *, struct imsg *);
        !            47: static void    server_client_dispatch_identify(struct client *, struct imsg *);
        !            48: static void    server_client_dispatch_shell(struct client *);
        !            49: 
        !            50: /* Idenfity mode callback. */
        !            51: static void
        !            52: server_client_callback_identify(__unused int fd, __unused short events, void *data)
        !            53: {
        !            54:        server_client_clear_identify(data, NULL);
        !            55: }
        !            56: 
        !            57: /* Set identify mode on client. */
        !            58: void
        !            59: server_client_set_identify(struct client *c)
        !            60: {
        !            61:        struct timeval  tv;
        !            62:        int             delay;
        !            63: 
        !            64:        delay = options_get_number(c->session->options, "display-panes-time");
        !            65:        tv.tv_sec = delay / 1000;
        !            66:        tv.tv_usec = (delay % 1000) * 1000L;
        !            67: 
        !            68:        if (event_initialized(&c->identify_timer))
        !            69:                evtimer_del(&c->identify_timer);
        !            70:        evtimer_set(&c->identify_timer, server_client_callback_identify, c);
        !            71:        evtimer_add(&c->identify_timer, &tv);
        !            72: 
        !            73:        c->flags |= CLIENT_IDENTIFY;
        !            74:        c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
        !            75:        server_redraw_client(c);
        !            76: }
        !            77: 
        !            78: /* Clear identify mode on client. */
        !            79: void
        !            80: server_client_clear_identify(struct client *c, struct window_pane *wp)
        !            81: {
        !            82:        if (~c->flags & CLIENT_IDENTIFY)
        !            83:                return;
        !            84:        c->flags &= ~CLIENT_IDENTIFY;
        !            85: 
        !            86:        if (c->identify_callback != NULL)
        !            87:                c->identify_callback(c, wp);
        !            88: 
        !            89:        c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
        !            90:        server_redraw_client(c);
        !            91: }
        !            92: 
        !            93: /* Check if this client is inside this server. */
        !            94: int
        !            95: server_client_check_nested(struct client *c)
        !            96: {
        !            97:        struct environ_entry    *envent;
        !            98:        struct window_pane      *wp;
        !            99: 
        !           100:        envent = environ_find(c->environ, "TMUX");
        !           101:        if (envent == NULL || *envent->value == '\0')
        !           102:                return (0);
        !           103: 
        !           104:        RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
        !           105:                if (strcmp(wp->tty, c->ttyname) == 0)
        !           106:                        return (1);
        !           107:        }
        !           108:        return (0);
        !           109: }
        !           110: 
        !           111: /* Set client key table. */
        !           112: void
        !           113: server_client_set_key_table(struct client *c, const char *name)
        !           114: {
        !           115:        if (name == NULL)
        !           116:                name = server_client_get_key_table(c);
        !           117: 
        !           118:        key_bindings_unref_table(c->keytable);
        !           119:        c->keytable = key_bindings_get_table(name, 1);
        !           120:        c->keytable->references++;
        !           121: }
        !           122: 
        !           123: /* Get default key table. */
        !           124: const char *
        !           125: server_client_get_key_table(struct client *c)
        !           126: {
        !           127:        struct session  *s = c->session;
        !           128:        const char      *name;
        !           129: 
        !           130:        if (s == NULL)
        !           131:                return ("root");
        !           132: 
        !           133:        name = options_get_string(s->options, "key-table");
        !           134:        if (*name == '\0')
        !           135:                return ("root");
        !           136:        return (name);
        !           137: }
        !           138: 
        !           139: /* Is this client using the default key table? */
        !           140: int
        !           141: server_client_is_default_key_table(struct client *c)
        !           142: {
        !           143:        return (strcmp(c->keytable->name, server_client_get_key_table(c)) == 0);
        !           144: }
        !           145: 
        !           146: /* Create a new client. */
        !           147: void
        !           148: server_client_create(int fd)
        !           149: {
        !           150:        struct client   *c;
        !           151: 
        !           152:        setblocking(fd, 0);
        !           153: 
        !           154:        c = xcalloc(1, sizeof *c);
        !           155:        c->references = 1;
        !           156:        c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c);
        !           157: 
        !           158:        if (gettimeofday(&c->creation_time, NULL) != 0)
        !           159:                fatal("gettimeofday failed");
        !           160:        memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
        !           161: 
        !           162:        c->environ = environ_create();
        !           163: 
        !           164:        c->fd = -1;
        !           165:        c->cwd = NULL;
        !           166: 
        !           167:        TAILQ_INIT(&c->queue);
        !           168: 
        !           169:        c->stdin_data = evbuffer_new();
        !           170:        c->stdout_data = evbuffer_new();
        !           171:        c->stderr_data = evbuffer_new();
        !           172: 
        !           173:        c->tty.fd = -1;
        !           174:        c->title = NULL;
        !           175: 
        !           176:        c->session = NULL;
        !           177:        c->last_session = NULL;
        !           178:        c->tty.sx = 80;
        !           179:        c->tty.sy = 24;
        !           180: 
        !           181:        screen_init(&c->status, c->tty.sx, 1, 0);
        !           182: 
        !           183:        c->message_string = NULL;
        !           184:        TAILQ_INIT(&c->message_log);
        !           185: 
        !           186:        c->prompt_string = NULL;
        !           187:        c->prompt_buffer = NULL;
        !           188:        c->prompt_index = 0;
        !           189: 
        !           190:        c->flags |= CLIENT_FOCUSED;
        !           191: 
        !           192:        c->keytable = key_bindings_get_table("root", 1);
        !           193:        c->keytable->references++;
        !           194: 
        !           195:        evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
        !           196:        evtimer_set(&c->click_timer, server_client_click_timer, c);
        !           197: 
        !           198:        TAILQ_INSERT_TAIL(&clients, c, entry);
        !           199:        log_debug("new client %p", c);
        !           200: }
        !           201: 
        !           202: /* Open client terminal if needed. */
        !           203: int
        !           204: server_client_open(struct client *c, char **cause)
        !           205: {
        !           206:        if (c->flags & CLIENT_CONTROL)
        !           207:                return (0);
        !           208: 
        !           209:        if (strcmp(c->ttyname, "/dev/tty") == 0) {
        !           210:                *cause = xstrdup("can't use /dev/tty");
        !           211:                return (-1);
        !           212:        }
        !           213: 
        !           214:        if (!(c->flags & CLIENT_TERMINAL)) {
        !           215:                *cause = xstrdup("not a terminal");
        !           216:                return (-1);
        !           217:        }
        !           218: 
        !           219:        if (tty_open(&c->tty, cause) != 0)
        !           220:                return (-1);
        !           221: 
        !           222:        return (0);
        !           223: }
        !           224: 
        !           225: /* Lost a client. */
        !           226: void
        !           227: server_client_lost(struct client *c)
        !           228: {
        !           229:        struct message_entry    *msg, *msg1;
        !           230: 
        !           231:        c->flags |= CLIENT_DEAD;
        !           232: 
        !           233:        server_client_clear_identify(c, NULL);
        !           234:        status_prompt_clear(c);
        !           235:        status_message_clear(c);
        !           236: 
        !           237:        if (c->stdin_callback != NULL)
        !           238:                c->stdin_callback(c, 1, c->stdin_callback_data);
        !           239: 
        !           240:        TAILQ_REMOVE(&clients, c, entry);
        !           241:        log_debug("lost client %p", c);
        !           242: 
        !           243:        /*
        !           244:         * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
        !           245:         * and tty_free might close an unrelated fd.
        !           246:         */
        !           247:        if (c->flags & CLIENT_TERMINAL)
        !           248:                tty_free(&c->tty);
        !           249:        free(c->ttyname);
        !           250:        free(c->term);
        !           251: 
        !           252:        evbuffer_free(c->stdin_data);
        !           253:        evbuffer_free(c->stdout_data);
        !           254:        if (c->stderr_data != c->stdout_data)
        !           255:                evbuffer_free(c->stderr_data);
        !           256: 
        !           257:        if (event_initialized(&c->status_timer))
        !           258:                evtimer_del(&c->status_timer);
        !           259:        screen_free(&c->status);
        !           260: 
        !           261:        free(c->title);
        !           262:        free((void *)c->cwd);
        !           263: 
        !           264:        evtimer_del(&c->repeat_timer);
        !           265:        evtimer_del(&c->click_timer);
        !           266: 
        !           267:        key_bindings_unref_table(c->keytable);
        !           268: 
        !           269:        if (event_initialized(&c->identify_timer))
        !           270:                evtimer_del(&c->identify_timer);
        !           271: 
        !           272:        free(c->message_string);
        !           273:        if (event_initialized(&c->message_timer))
        !           274:                evtimer_del(&c->message_timer);
        !           275:        TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
        !           276:                free(msg->msg);
        !           277:                TAILQ_REMOVE(&c->message_log, msg, entry);
        !           278:                free(msg);
        !           279:        }
        !           280: 
        !           281:        free(c->prompt_string);
        !           282:        free(c->prompt_buffer);
        !           283: 
        !           284:        environ_free(c->environ);
        !           285: 
        !           286:        proc_remove_peer(c->peer);
        !           287:        c->peer = NULL;
        !           288: 
        !           289:        server_client_unref(c);
        !           290: 
        !           291:        server_add_accept(0); /* may be more file descriptors now */
        !           292: 
        !           293:        recalculate_sizes();
        !           294:        server_check_unattached();
        !           295:        server_update_socket();
        !           296: }
        !           297: 
        !           298: /* Remove reference from a client. */
        !           299: void
        !           300: server_client_unref(struct client *c)
        !           301: {
        !           302:        log_debug("unref client %p (%d references)", c, c->references);
        !           303: 
        !           304:        c->references--;
        !           305:        if (c->references == 0)
        !           306:                event_once(-1, EV_TIMEOUT, server_client_free, c, NULL);
        !           307: }
        !           308: 
        !           309: /* Free dead client. */
        !           310: static void
        !           311: server_client_free(__unused int fd, __unused short events, void *arg)
        !           312: {
        !           313:        struct client   *c = arg;
        !           314: 
        !           315:        log_debug("free client %p (%d references)", c, c->references);
        !           316: 
        !           317:        if (!TAILQ_EMPTY(&c->queue))
        !           318:                fatalx("queue not empty");
        !           319: 
        !           320:        if (c->references == 0) {
        !           321:                free((void *)c->name);
        !           322:                free(c);
        !           323:        }
        !           324: }
        !           325: 
        !           326: /* Detach a client. */
        !           327: void
        !           328: server_client_detach(struct client *c, enum msgtype msgtype)
        !           329: {
        !           330:        struct session  *s = c->session;
        !           331: 
        !           332:        if (s == NULL)
        !           333:                return;
        !           334: 
        !           335:        notify_client("client-detached", c);
        !           336:        proc_send_s(c->peer, msgtype, s->name);
        !           337: }
        !           338: 
        !           339: /* Execute command to replace a client. */
        !           340: void
        !           341: server_client_exec(struct client *c, const char *cmd)
        !           342: {
        !           343:        struct session  *s = c->session;
        !           344:        char            *msg;
        !           345:        const char      *shell;
        !           346:        size_t           cmdsize, shellsize;
        !           347: 
        !           348:        if (*cmd == '\0')
        !           349:                return;
        !           350:        cmdsize = strlen(cmd) + 1;
        !           351: 
        !           352:        if (s != NULL)
        !           353:                shell = options_get_string(s->options, "default-shell");
        !           354:        else
        !           355:                shell = options_get_string(global_s_options, "default-shell");
        !           356:        shellsize = strlen(shell) + 1;
        !           357: 
        !           358:        msg = xmalloc(cmdsize + shellsize);
        !           359:        memcpy(msg, cmd, cmdsize);
        !           360:        memcpy(msg + cmdsize, shell, shellsize);
        !           361: 
        !           362:        proc_send(c->peer, MSG_EXEC, -1, msg, cmdsize + shellsize);
        !           363:        free(msg);
        !           364: }
        !           365: 
        !           366: /* Check for mouse keys. */
        !           367: static key_code
        !           368: server_client_check_mouse(struct client *c)
        !           369: {
        !           370:        struct session          *s = c->session;
        !           371:        struct mouse_event      *m = &c->tty.mouse;
        !           372:        struct window           *w;
        !           373:        struct window_pane      *wp;
        !           374:        u_int                    x, y, b;
        !           375:        int                      flag;
        !           376:        key_code                 key;
        !           377:        struct timeval           tv;
        !           378:        enum { NOTYPE, MOVE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type;
        !           379:        enum { NOWHERE, PANE, STATUS, BORDER } where;
        !           380: 
        !           381:        type = NOTYPE;
        !           382:        where = NOWHERE;
        !           383: 
        !           384:        log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y,
        !           385:            m->lx, m->ly, c->tty.mouse_drag_flag);
        !           386: 
        !           387:        /* What type of event is this? */
        !           388:        if ((m->sgr_type != ' ' &&
        !           389:            MOUSE_DRAG(m->sgr_b) &&
        !           390:            MOUSE_BUTTONS(m->sgr_b) == 3) ||
        !           391:            (m->sgr_type == ' ' &&
        !           392:            MOUSE_DRAG(m->b) &&
        !           393:            MOUSE_BUTTONS(m->b) == 3 &&
        !           394:            MOUSE_BUTTONS(m->lb) == 3)) {
        !           395:                type = MOVE;
        !           396:                x = m->x, y = m->y, b = 0;
        !           397:                log_debug("move at %u,%u", x, y);
        !           398:        } else if (MOUSE_DRAG(m->b)) {
        !           399:                type = DRAG;
        !           400:                if (c->tty.mouse_drag_flag) {
        !           401:                        x = m->x, y = m->y, b = m->b;
        !           402:                        log_debug("drag update at %u,%u", x, y);
        !           403:                } else {
        !           404:                        x = m->lx, y = m->ly, b = m->lb;
        !           405:                        log_debug("drag start at %u,%u", x, y);
        !           406:                }
        !           407:        } else if (MOUSE_WHEEL(m->b)) {
        !           408:                type = WHEEL;
        !           409:                x = m->x, y = m->y, b = m->b;
        !           410:                log_debug("wheel at %u,%u", x, y);
        !           411:        } else if (MOUSE_RELEASE(m->b)) {
        !           412:                type = UP;
        !           413:                x = m->x, y = m->y, b = m->lb;
        !           414:                log_debug("up at %u,%u", x, y);
        !           415:        } else {
        !           416:                if (c->flags & CLIENT_DOUBLECLICK) {
        !           417:                        evtimer_del(&c->click_timer);
        !           418:                        c->flags &= ~CLIENT_DOUBLECLICK;
        !           419:                        if (m->b == c->click_button) {
        !           420:                                type = DOUBLE;
        !           421:                                x = m->x, y = m->y, b = m->b;
        !           422:                                log_debug("double-click at %u,%u", x, y);
        !           423:                                flag = CLIENT_TRIPLECLICK;
        !           424:                                goto add_timer;
        !           425:                        }
        !           426:                } else if (c->flags & CLIENT_TRIPLECLICK) {
        !           427:                        evtimer_del(&c->click_timer);
        !           428:                        c->flags &= ~CLIENT_TRIPLECLICK;
        !           429:                        if (m->b == c->click_button) {
        !           430:                                type = TRIPLE;
        !           431:                                x = m->x, y = m->y, b = m->b;
        !           432:                                log_debug("triple-click at %u,%u", x, y);
        !           433:                                goto have_event;
        !           434:                        }
        !           435:                }
        !           436: 
        !           437:                type = DOWN;
        !           438:                x = m->x, y = m->y, b = m->b;
        !           439:                log_debug("down at %u,%u", x, y);
        !           440:                flag = CLIENT_DOUBLECLICK;
        !           441: 
        !           442:        add_timer:
        !           443:                if (KEYC_CLICK_TIMEOUT != 0) {
        !           444:                        c->flags |= flag;
        !           445:                        c->click_button = m->b;
        !           446: 
        !           447:                        tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000;
        !           448:                        tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L;
        !           449:                        evtimer_del(&c->click_timer);
        !           450:                        evtimer_add(&c->click_timer, &tv);
        !           451:                }
        !           452:        }
        !           453: 
        !           454: have_event:
        !           455:        if (type == NOTYPE)
        !           456:                return (KEYC_UNKNOWN);
        !           457: 
        !           458:        /* Always save the session. */
        !           459:        m->s = s->id;
        !           460: 
        !           461:        /* Is this on the status line? */
        !           462:        m->statusat = status_at_line(c);
        !           463:        if (m->statusat != -1 && y == (u_int)m->statusat) {
        !           464:                w = status_get_window_at(c, x);
        !           465:                if (w == NULL)
        !           466:                        return (KEYC_UNKNOWN);
        !           467:                m->w = w->id;
        !           468:                where = STATUS;
        !           469:        } else
        !           470:                m->w = -1;
        !           471: 
        !           472:        /* Not on status line. Adjust position and check for border or pane. */
        !           473:        if (where == NOWHERE) {
        !           474:                if (m->statusat == 0 && y > 0)
        !           475:                        y--;
        !           476:                else if (m->statusat > 0 && y >= (u_int)m->statusat)
        !           477:                        y = m->statusat - 1;
        !           478: 
        !           479:                TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
        !           480:                        if ((wp->xoff + wp->sx == x &&
        !           481:                            wp->yoff <= 1 + y &&
        !           482:                            wp->yoff + wp->sy >= y) ||
        !           483:                            (wp->yoff + wp->sy == y &&
        !           484:                            wp->xoff <= 1 + x &&
        !           485:                            wp->xoff + wp->sx >= x))
        !           486:                                break;
        !           487:                }
        !           488:                if (wp != NULL)
        !           489:                        where = BORDER;
        !           490:                else {
        !           491:                        wp = window_get_active_at(s->curw->window, x, y);
        !           492:                        if (wp != NULL) {
        !           493:                                where = PANE;
        !           494:                                log_debug("mouse at %u,%u is on pane %%%u",
        !           495:                                    x, y, wp->id);
        !           496:                        }
        !           497:                }
        !           498:                if (where == NOWHERE)
        !           499:                        return (KEYC_UNKNOWN);
        !           500:                m->wp = wp->id;
        !           501:                m->w = wp->window->id;
        !           502:        } else
        !           503:                m->wp = -1;
        !           504: 
        !           505:        /* Stop dragging if needed. */
        !           506:        if (type != DRAG && type != WHEEL && c->tty.mouse_drag_flag) {
        !           507:                if (c->tty.mouse_drag_release != NULL)
        !           508:                        c->tty.mouse_drag_release(c, m);
        !           509: 
        !           510:                c->tty.mouse_drag_update = NULL;
        !           511:                c->tty.mouse_drag_release = NULL;
        !           512: 
        !           513:                /*
        !           514:                 * End a mouse drag by passing a MouseDragEnd key corresponding
        !           515:                 * to the button that started the drag.
        !           516:                 */
        !           517:                switch (c->tty.mouse_drag_flag) {
        !           518:                case 1:
        !           519:                        if (where == PANE)
        !           520:                                key = KEYC_MOUSEDRAGEND1_PANE;
        !           521:                        if (where == STATUS)
        !           522:                                key = KEYC_MOUSEDRAGEND1_STATUS;
        !           523:                        if (where == BORDER)
        !           524:                                key = KEYC_MOUSEDRAGEND1_BORDER;
        !           525:                        break;
        !           526:                case 2:
        !           527:                        if (where == PANE)
        !           528:                                key = KEYC_MOUSEDRAGEND2_PANE;
        !           529:                        if (where == STATUS)
        !           530:                                key = KEYC_MOUSEDRAGEND2_STATUS;
        !           531:                        if (where == BORDER)
        !           532:                                key = KEYC_MOUSEDRAGEND2_BORDER;
        !           533:                        break;
        !           534:                case 3:
        !           535:                        if (where == PANE)
        !           536:                                key = KEYC_MOUSEDRAGEND3_PANE;
        !           537:                        if (where == STATUS)
        !           538:                                key = KEYC_MOUSEDRAGEND3_STATUS;
        !           539:                        if (where == BORDER)
        !           540:                                key = KEYC_MOUSEDRAGEND3_BORDER;
        !           541:                        break;
        !           542:                default:
        !           543:                        key = KEYC_MOUSE;
        !           544:                        break;
        !           545:                }
        !           546:                c->tty.mouse_drag_flag = 0;
        !           547: 
        !           548:                return (key);
        !           549:        }
        !           550: 
        !           551:        /* Convert to a key binding. */
        !           552:        key = KEYC_UNKNOWN;
        !           553:        switch (type) {
        !           554:        case NOTYPE:
        !           555:                break;
        !           556:        case MOVE:
        !           557:                if (where == PANE)
        !           558:                        key = KEYC_MOUSEMOVE_PANE;
        !           559:                if (where == STATUS)
        !           560:                        key = KEYC_MOUSEMOVE_STATUS;
        !           561:                if (where == BORDER)
        !           562:                        key = KEYC_MOUSEMOVE_BORDER;
        !           563:                break;
        !           564:        case DRAG:
        !           565:                if (c->tty.mouse_drag_update != NULL)
        !           566:                        key = KEYC_DRAGGING;
        !           567:                else {
        !           568:                        switch (MOUSE_BUTTONS(b)) {
        !           569:                        case 0:
        !           570:                                if (where == PANE)
        !           571:                                        key = KEYC_MOUSEDRAG1_PANE;
        !           572:                                if (where == STATUS)
        !           573:                                        key = KEYC_MOUSEDRAG1_STATUS;
        !           574:                                if (where == BORDER)
        !           575:                                        key = KEYC_MOUSEDRAG1_BORDER;
        !           576:                                break;
        !           577:                        case 1:
        !           578:                                if (where == PANE)
        !           579:                                        key = KEYC_MOUSEDRAG2_PANE;
        !           580:                                if (where == STATUS)
        !           581:                                        key = KEYC_MOUSEDRAG2_STATUS;
        !           582:                                if (where == BORDER)
        !           583:                                        key = KEYC_MOUSEDRAG2_BORDER;
        !           584:                                break;
        !           585:                        case 2:
        !           586:                                if (where == PANE)
        !           587:                                        key = KEYC_MOUSEDRAG3_PANE;
        !           588:                                if (where == STATUS)
        !           589:                                        key = KEYC_MOUSEDRAG3_STATUS;
        !           590:                                if (where == BORDER)
        !           591:                                        key = KEYC_MOUSEDRAG3_BORDER;
        !           592:                                break;
        !           593:                        }
        !           594:                }
        !           595: 
        !           596:                /*
        !           597:                 * Begin a drag by setting the flag to a non-zero value that
        !           598:                 * corresponds to the mouse button in use.
        !           599:                 */
        !           600:                c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1;
        !           601:                break;
        !           602:        case WHEEL:
        !           603:                if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) {
        !           604:                        if (where == PANE)
        !           605:                                key = KEYC_WHEELUP_PANE;
        !           606:                        if (where == STATUS)
        !           607:                                key = KEYC_WHEELUP_STATUS;
        !           608:                        if (where == BORDER)
        !           609:                                key = KEYC_WHEELUP_BORDER;
        !           610:                } else {
        !           611:                        if (where == PANE)
        !           612:                                key = KEYC_WHEELDOWN_PANE;
        !           613:                        if (where == STATUS)
        !           614:                                key = KEYC_WHEELDOWN_STATUS;
        !           615:                        if (where == BORDER)
        !           616:                                key = KEYC_WHEELDOWN_BORDER;
        !           617:                }
        !           618:                break;
        !           619:        case UP:
        !           620:                switch (MOUSE_BUTTONS(b)) {
        !           621:                case 0:
        !           622:                        if (where == PANE)
        !           623:                                key = KEYC_MOUSEUP1_PANE;
        !           624:                        if (where == STATUS)
        !           625:                                key = KEYC_MOUSEUP1_STATUS;
        !           626:                        if (where == BORDER)
        !           627:                                key = KEYC_MOUSEUP1_BORDER;
        !           628:                        break;
        !           629:                case 1:
        !           630:                        if (where == PANE)
        !           631:                                key = KEYC_MOUSEUP2_PANE;
        !           632:                        if (where == STATUS)
        !           633:                                key = KEYC_MOUSEUP2_STATUS;
        !           634:                        if (where == BORDER)
        !           635:                                key = KEYC_MOUSEUP2_BORDER;
        !           636:                        break;
        !           637:                case 2:
        !           638:                        if (where == PANE)
        !           639:                                key = KEYC_MOUSEUP3_PANE;
        !           640:                        if (where == STATUS)
        !           641:                                key = KEYC_MOUSEUP3_STATUS;
        !           642:                        if (where == BORDER)
        !           643:                                key = KEYC_MOUSEUP3_BORDER;
        !           644:                        break;
        !           645:                }
        !           646:                break;
        !           647:        case DOWN:
        !           648:                switch (MOUSE_BUTTONS(b)) {
        !           649:                case 0:
        !           650:                        if (where == PANE)
        !           651:                                key = KEYC_MOUSEDOWN1_PANE;
        !           652:                        if (where == STATUS)
        !           653:                                key = KEYC_MOUSEDOWN1_STATUS;
        !           654:                        if (where == BORDER)
        !           655:                                key = KEYC_MOUSEDOWN1_BORDER;
        !           656:                        break;
        !           657:                case 1:
        !           658:                        if (where == PANE)
        !           659:                                key = KEYC_MOUSEDOWN2_PANE;
        !           660:                        if (where == STATUS)
        !           661:                                key = KEYC_MOUSEDOWN2_STATUS;
        !           662:                        if (where == BORDER)
        !           663:                                key = KEYC_MOUSEDOWN2_BORDER;
        !           664:                        break;
        !           665:                case 2:
        !           666:                        if (where == PANE)
        !           667:                                key = KEYC_MOUSEDOWN3_PANE;
        !           668:                        if (where == STATUS)
        !           669:                                key = KEYC_MOUSEDOWN3_STATUS;
        !           670:                        if (where == BORDER)
        !           671:                                key = KEYC_MOUSEDOWN3_BORDER;
        !           672:                        break;
        !           673:                }
        !           674:                break;
        !           675:        case DOUBLE:
        !           676:                switch (MOUSE_BUTTONS(b)) {
        !           677:                case 0:
        !           678:                        if (where == PANE)
        !           679:                                key = KEYC_DOUBLECLICK1_PANE;
        !           680:                        if (where == STATUS)
        !           681:                                key = KEYC_DOUBLECLICK1_STATUS;
        !           682:                        if (where == BORDER)
        !           683:                                key = KEYC_DOUBLECLICK1_BORDER;
        !           684:                        break;
        !           685:                case 1:
        !           686:                        if (where == PANE)
        !           687:                                key = KEYC_DOUBLECLICK2_PANE;
        !           688:                        if (where == STATUS)
        !           689:                                key = KEYC_DOUBLECLICK2_STATUS;
        !           690:                        if (where == BORDER)
        !           691:                                key = KEYC_DOUBLECLICK2_BORDER;
        !           692:                        break;
        !           693:                case 2:
        !           694:                        if (where == PANE)
        !           695:                                key = KEYC_DOUBLECLICK3_PANE;
        !           696:                        if (where == STATUS)
        !           697:                                key = KEYC_DOUBLECLICK3_STATUS;
        !           698:                        if (where == BORDER)
        !           699:                                key = KEYC_DOUBLECLICK3_BORDER;
        !           700:                        break;
        !           701:                }
        !           702:                break;
        !           703:        case TRIPLE:
        !           704:                switch (MOUSE_BUTTONS(b)) {
        !           705:                case 0:
        !           706:                        if (where == PANE)
        !           707:                                key = KEYC_TRIPLECLICK1_PANE;
        !           708:                        if (where == STATUS)
        !           709:                                key = KEYC_TRIPLECLICK1_STATUS;
        !           710:                        if (where == BORDER)
        !           711:                                key = KEYC_TRIPLECLICK1_BORDER;
        !           712:                        break;
        !           713:                case 1:
        !           714:                        if (where == PANE)
        !           715:                                key = KEYC_TRIPLECLICK2_PANE;
        !           716:                        if (where == STATUS)
        !           717:                                key = KEYC_TRIPLECLICK2_STATUS;
        !           718:                        if (where == BORDER)
        !           719:                                key = KEYC_TRIPLECLICK2_BORDER;
        !           720:                        break;
        !           721:                case 2:
        !           722:                        if (where == PANE)
        !           723:                                key = KEYC_TRIPLECLICK3_PANE;
        !           724:                        if (where == STATUS)
        !           725:                                key = KEYC_TRIPLECLICK3_STATUS;
        !           726:                        if (where == BORDER)
        !           727:                                key = KEYC_TRIPLECLICK3_BORDER;
        !           728:                        break;
        !           729:                }
        !           730:                break;
        !           731:        }
        !           732:        if (key == KEYC_UNKNOWN)
        !           733:                return (KEYC_UNKNOWN);
        !           734: 
        !           735:        /* Apply modifiers if any. */
        !           736:        if (b & MOUSE_MASK_META)
        !           737:                key |= KEYC_ESCAPE;
        !           738:        if (b & MOUSE_MASK_CTRL)
        !           739:                key |= KEYC_CTRL;
        !           740:        if (b & MOUSE_MASK_SHIFT)
        !           741:                key |= KEYC_SHIFT;
        !           742: 
        !           743:        return (key);
        !           744: }
        !           745: 
        !           746: /* Is this fast enough to probably be a paste? */
        !           747: static int
        !           748: server_client_assume_paste(struct session *s)
        !           749: {
        !           750:        struct timeval  tv;
        !           751:        int             t;
        !           752: 
        !           753:        if ((t = options_get_number(s->options, "assume-paste-time")) == 0)
        !           754:                return (0);
        !           755: 
        !           756:        timersub(&s->activity_time, &s->last_activity_time, &tv);
        !           757:        if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) {
        !           758:                log_debug("session %s pasting (flag %d)", s->name,
        !           759:                    !!(s->flags & SESSION_PASTING));
        !           760:                if (s->flags & SESSION_PASTING)
        !           761:                        return (1);
        !           762:                s->flags |= SESSION_PASTING;
        !           763:                return (0);
        !           764:        }
        !           765:        log_debug("session %s not pasting", s->name);
        !           766:        s->flags &= ~SESSION_PASTING;
        !           767:        return (0);
        !           768: }
        !           769: 
        !           770: /* Handle data key input from client. */
        !           771: void
        !           772: server_client_handle_key(struct client *c, key_code key)
        !           773: {
        !           774:        struct mouse_event      *m = &c->tty.mouse;
        !           775:        struct session          *s = c->session;
        !           776:        struct window           *w;
        !           777:        struct window_pane      *wp;
        !           778:        struct timeval           tv;
        !           779:        const char              *name;
        !           780:        struct key_table        *table;
        !           781:        struct key_binding       bd_find, *bd;
        !           782:        int                      xtimeout;
        !           783:        struct cmd_find_state    fs;
        !           784: 
        !           785:        /* Check the client is good to accept input. */
        !           786:        if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
        !           787:                return;
        !           788:        w = s->curw->window;
        !           789: 
        !           790:        /* Update the activity timer. */
        !           791:        if (gettimeofday(&c->activity_time, NULL) != 0)
        !           792:                fatal("gettimeofday failed");
        !           793:        session_update_activity(s, &c->activity_time);
        !           794: 
        !           795:        /* Number keys jump to pane in identify mode. */
        !           796:        if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
        !           797:                if (c->flags & CLIENT_READONLY)
        !           798:                        return;
        !           799:                window_unzoom(w);
        !           800:                wp = window_pane_at_index(w, key - '0');
        !           801:                if (wp != NULL && !window_pane_visible(wp))
        !           802:                        wp = NULL;
        !           803:                server_client_clear_identify(c, wp);
        !           804:                return;
        !           805:        }
        !           806: 
        !           807:        /* Handle status line. */
        !           808:        if (!(c->flags & CLIENT_READONLY)) {
        !           809:                status_message_clear(c);
        !           810:                server_client_clear_identify(c, NULL);
        !           811:        }
        !           812:        if (c->prompt_string != NULL) {
        !           813:                if (c->flags & CLIENT_READONLY)
        !           814:                        return;
        !           815:                if (status_prompt_key(c, key) == 0)
        !           816:                        return;
        !           817:        }
        !           818: 
        !           819:        /* Check for mouse keys. */
        !           820:        m->valid = 0;
        !           821:        if (key == KEYC_MOUSE) {
        !           822:                if (c->flags & CLIENT_READONLY)
        !           823:                        return;
        !           824:                key = server_client_check_mouse(c);
        !           825:                if (key == KEYC_UNKNOWN)
        !           826:                        return;
        !           827: 
        !           828:                m->valid = 1;
        !           829:                m->key = key;
        !           830: 
        !           831:                /*
        !           832:                 * Mouse drag is in progress, so fire the callback (now that
        !           833:                 * the mouse event is valid).
        !           834:                 */
        !           835:                if (key == KEYC_DRAGGING) {
        !           836:                        c->tty.mouse_drag_update(c, m);
        !           837:                        return;
        !           838:                }
        !           839:        } else
        !           840:                m->valid = 0;
        !           841: 
        !           842:        /* Find affected pane. */
        !           843:        if (KEYC_IS_MOUSE(key) && m->valid)
        !           844:                wp = cmd_mouse_pane(m, NULL, NULL);
        !           845:        else
        !           846:                wp = w->active;
        !           847: 
        !           848:        /* Forward mouse keys if disabled. */
        !           849:        if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
        !           850:                goto forward;
        !           851: 
        !           852:        /* Treat everything as a regular key when pasting is detected. */
        !           853:        if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s))
        !           854:                goto forward;
        !           855: 
        !           856: retry:
        !           857:        /*
        !           858:         * Work out the current key table. If the pane is in a mode, use
        !           859:         * the mode table instead of the default key table.
        !           860:         */
        !           861:        name = NULL;
        !           862:        if (wp != NULL && wp->mode != NULL && wp->mode->key_table != NULL)
        !           863:                name = wp->mode->key_table(wp);
        !           864:        if (name == NULL || !server_client_is_default_key_table(c))
        !           865:                table = c->keytable;
        !           866:        else
        !           867:                table = key_bindings_get_table(name, 1);
        !           868:        if (wp == NULL)
        !           869:                log_debug("key table %s (no pane)", table->name);
        !           870:        else
        !           871:                log_debug("key table %s (pane %%%u)", table->name, wp->id);
        !           872: 
        !           873:        /*
        !           874:         * The prefix always takes precedence and forces a switch to the prefix
        !           875:         * table, unless we are already there.
        !           876:         */
        !           877:        if ((key == (key_code)options_get_number(s->options, "prefix") ||
        !           878:            key == (key_code)options_get_number(s->options, "prefix2")) &&
        !           879:            strcmp(table->name, "prefix") != 0) {
        !           880:                server_client_set_key_table(c, "prefix");
        !           881:                server_status_client(c);
        !           882:                return;
        !           883:        }
        !           884: 
        !           885:        /* Try to see if there is a key binding in the current table. */
        !           886:        bd_find.key = key;
        !           887:        bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
        !           888:        if (bd != NULL) {
        !           889:                /*
        !           890:                 * Key was matched in this table. If currently repeating but a
        !           891:                 * non-repeating binding was found, stop repeating and try
        !           892:                 * again in the root table.
        !           893:                 */
        !           894:                if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
        !           895:                        server_client_set_key_table(c, NULL);
        !           896:                        c->flags &= ~CLIENT_REPEAT;
        !           897:                        server_status_client(c);
        !           898:                        goto retry;
        !           899:                }
        !           900: 
        !           901:                /*
        !           902:                 * Take a reference to this table to make sure the key binding
        !           903:                 * doesn't disappear.
        !           904:                 */
        !           905:                table->references++;
        !           906: 
        !           907:                /*
        !           908:                 * If this is a repeating key, start the timer. Otherwise reset
        !           909:                 * the client back to the root table.
        !           910:                 */
        !           911:                xtimeout = options_get_number(s->options, "repeat-time");
        !           912:                if (xtimeout != 0 && bd->can_repeat) {
        !           913:                        c->flags |= CLIENT_REPEAT;
        !           914: 
        !           915:                        tv.tv_sec = xtimeout / 1000;
        !           916:                        tv.tv_usec = (xtimeout % 1000) * 1000L;
        !           917:                        evtimer_del(&c->repeat_timer);
        !           918:                        evtimer_add(&c->repeat_timer, &tv);
        !           919:                } else {
        !           920:                        c->flags &= ~CLIENT_REPEAT;
        !           921:                        server_client_set_key_table(c, NULL);
        !           922:                }
        !           923:                server_status_client(c);
        !           924: 
        !           925:                /* Find default state if the pane is known. */
        !           926:                cmd_find_clear_state(&fs, NULL, 0);
        !           927:                if (wp != NULL) {
        !           928:                        fs.s = s;
        !           929:                        fs.wl = fs.s->curw;
        !           930:                        fs.w = fs.wl->window;
        !           931:                        fs.wp = wp;
        !           932:                        cmd_find_log_state(__func__, &fs);
        !           933: 
        !           934:                        if (!cmd_find_valid_state(&fs))
        !           935:                                fatalx("invalid key state");
        !           936:                }
        !           937: 
        !           938:                /* Dispatch the key binding. */
        !           939:                key_bindings_dispatch(bd, c, m, &fs);
        !           940:                key_bindings_unref_table(table);
        !           941:                return;
        !           942:        }
        !           943: 
        !           944:        /*
        !           945:         * No match in this table. If repeating, switch the client back to the
        !           946:         * root table and try again.
        !           947:         */
        !           948:        if (c->flags & CLIENT_REPEAT) {
        !           949:                server_client_set_key_table(c, NULL);
        !           950:                c->flags &= ~CLIENT_REPEAT;
        !           951:                server_status_client(c);
        !           952:                goto retry;
        !           953:        }
        !           954: 
        !           955:        /* If no match and we're not in the root table, that's it. */
        !           956:        if (name == NULL && !server_client_is_default_key_table(c)) {
        !           957:                log_debug("no key in key table %s", table->name);
        !           958:                server_client_set_key_table(c, NULL);
        !           959:                server_status_client(c);
        !           960:                return;
        !           961:        }
        !           962: 
        !           963: forward:
        !           964:        if (c->flags & CLIENT_READONLY)
        !           965:                return;
        !           966:        if (wp != NULL)
        !           967:                window_pane_key(wp, c, s, key, m);
        !           968: }
        !           969: 
        !           970: /* Client functions that need to happen every loop. */
        !           971: void
        !           972: server_client_loop(void)
        !           973: {
        !           974:        struct client           *c;
        !           975:        struct window           *w;
        !           976:        struct window_pane      *wp;
        !           977:        int                      focus;
        !           978: 
        !           979:        TAILQ_FOREACH(c, &clients, entry) {
        !           980:                server_client_check_exit(c);
        !           981:                if (c->session != NULL) {
        !           982:                        server_client_check_redraw(c);
        !           983:                        server_client_reset_state(c);
        !           984:                }
        !           985:        }
        !           986: 
        !           987:        /*
        !           988:         * Any windows will have been redrawn as part of clients, so clear
        !           989:         * their flags now. Also check pane focus and resize.
        !           990:         */
        !           991:        focus = options_get_number(global_options, "focus-events");
        !           992:        RB_FOREACH(w, windows, &windows) {
        !           993:                TAILQ_FOREACH(wp, &w->panes, entry) {
        !           994:                        if (wp->fd != -1) {
        !           995:                                if (focus)
        !           996:                                        server_client_check_focus(wp);
        !           997:                                server_client_check_resize(wp);
        !           998:                        }
        !           999:                        wp->flags &= ~PANE_REDRAW;
        !          1000:                }
        !          1001:                check_window_name(w);
        !          1002:        }
        !          1003: }
        !          1004: 
        !          1005: /* Resize timer event. */
        !          1006: static void
        !          1007: server_client_resize_event(__unused int fd, __unused short events, void *data)
        !          1008: {
        !          1009:        struct window_pane      *wp = data;
        !          1010:        struct winsize           ws;
        !          1011: 
        !          1012:        evtimer_del(&wp->resize_timer);
        !          1013: 
        !          1014:        if (!(wp->flags & PANE_RESIZE))
        !          1015:                return;
        !          1016: 
        !          1017:        memset(&ws, 0, sizeof ws);
        !          1018:        ws.ws_col = wp->sx;
        !          1019:        ws.ws_row = wp->sy;
        !          1020: 
        !          1021:        if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) {
        !          1022: #ifdef __sun
        !          1023:                /*
        !          1024:                 * Some versions of Solaris apparently can return an error when
        !          1025:                 * resizing; don't know why this happens, can't reproduce on
        !          1026:                 * other platforms and ignoring it doesn't seem to cause any
        !          1027:                 * issues.
        !          1028:                 */
        !          1029:                if (errno != EINVAL && errno != ENXIO)
        !          1030: #endif
        !          1031:                fatal("ioctl failed");
        !          1032:        }
        !          1033: 
        !          1034:        wp->flags &= ~PANE_RESIZE;
        !          1035: }
        !          1036: 
        !          1037: /* Check if pane should be resized. */
        !          1038: static void
        !          1039: server_client_check_resize(struct window_pane *wp)
        !          1040: {
        !          1041:        struct timeval   tv = { .tv_usec = 250000 };
        !          1042: 
        !          1043:        if (!(wp->flags & PANE_RESIZE))
        !          1044:                return;
        !          1045: 
        !          1046:        if (!event_initialized(&wp->resize_timer))
        !          1047:                evtimer_set(&wp->resize_timer, server_client_resize_event, wp);
        !          1048: 
        !          1049:        /*
        !          1050:         * The first resize should happen immediately, so if the timer is not
        !          1051:         * running, do it now.
        !          1052:         */
        !          1053:        if (!evtimer_pending(&wp->resize_timer, NULL))
        !          1054:                server_client_resize_event(-1, 0, wp);
        !          1055: 
        !          1056:        /*
        !          1057:         * If the pane is in the alternate screen, let the timer expire and
        !          1058:         * resize to give the application a chance to redraw. If not, keep
        !          1059:         * pushing the timer back.
        !          1060:         */
        !          1061:        if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL))
        !          1062:                return;
        !          1063:        evtimer_del(&wp->resize_timer);
        !          1064:        evtimer_add(&wp->resize_timer, &tv);
        !          1065: }
        !          1066: 
        !          1067: /* Check whether pane should be focused. */
        !          1068: static void
        !          1069: server_client_check_focus(struct window_pane *wp)
        !          1070: {
        !          1071:        struct client   *c;
        !          1072:        int              push;
        !          1073: 
        !          1074:        /* Do we need to push the focus state? */
        !          1075:        push = wp->flags & PANE_FOCUSPUSH;
        !          1076:        wp->flags &= ~PANE_FOCUSPUSH;
        !          1077: 
        !          1078:        /* If we don't care about focus, forget it. */
        !          1079:        if (!(wp->base.mode & MODE_FOCUSON))
        !          1080:                return;
        !          1081: 
        !          1082:        /* If we're not the active pane in our window, we're not focused. */
        !          1083:        if (wp->window->active != wp)
        !          1084:                goto not_focused;
        !          1085: 
        !          1086:        /* If we're in a mode, we're not focused. */
        !          1087:        if (wp->screen != &wp->base)
        !          1088:                goto not_focused;
        !          1089: 
        !          1090:        /*
        !          1091:         * If our window is the current window in any focused clients with an
        !          1092:         * attached session, we're focused.
        !          1093:         */
        !          1094:        TAILQ_FOREACH(c, &clients, entry) {
        !          1095:                if (c->session == NULL || !(c->flags & CLIENT_FOCUSED))
        !          1096:                        continue;
        !          1097:                if (c->session->flags & SESSION_UNATTACHED)
        !          1098:                        continue;
        !          1099: 
        !          1100:                if (c->session->curw->window == wp->window)
        !          1101:                        goto focused;
        !          1102:        }
        !          1103: 
        !          1104: not_focused:
        !          1105:        if (push || (wp->flags & PANE_FOCUSED))
        !          1106:                bufferevent_write(wp->event, "\033[O", 3);
        !          1107:        wp->flags &= ~PANE_FOCUSED;
        !          1108:        return;
        !          1109: 
        !          1110: focused:
        !          1111:        if (push || !(wp->flags & PANE_FOCUSED))
        !          1112:                bufferevent_write(wp->event, "\033[I", 3);
        !          1113:        wp->flags |= PANE_FOCUSED;
        !          1114: }
        !          1115: 
        !          1116: /*
        !          1117:  * Update cursor position and mode settings. The scroll region and attributes
        !          1118:  * are cleared when idle (waiting for an event) as this is the most likely time
        !          1119:  * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
        !          1120:  * compromise between excessive resets and likelihood of an interrupt.
        !          1121:  *
        !          1122:  * tty_region/tty_reset/tty_update_mode already take care of not resetting
        !          1123:  * things that are already in their default state.
        !          1124:  */
        !          1125: static void
        !          1126: server_client_reset_state(struct client *c)
        !          1127: {
        !          1128:        struct window           *w = c->session->curw->window;
        !          1129:        struct window_pane      *wp = w->active, *loop;
        !          1130:        struct screen           *s = wp->screen;
        !          1131:        struct options          *oo = c->session->options;
        !          1132:        int                      status, mode, o;
        !          1133: 
        !          1134:        if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
        !          1135:                return;
        !          1136: 
        !          1137:        tty_region_off(&c->tty);
        !          1138:        tty_margin_off(&c->tty);
        !          1139: 
        !          1140:        status = options_get_number(oo, "status");
        !          1141:        if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
        !          1142:                tty_cursor(&c->tty, 0, 0);
        !          1143:        else {
        !          1144:                o = status && options_get_number(oo, "status-position") == 0;
        !          1145:                tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
        !          1146:        }
        !          1147: 
        !          1148:        /*
        !          1149:         * Set mouse mode if requested. To support dragging, always use button
        !          1150:         * mode.
        !          1151:         */
        !          1152:        mode = s->mode;
        !          1153:        if (options_get_number(oo, "mouse")) {
        !          1154:                mode &= ~ALL_MOUSE_MODES;
        !          1155:                TAILQ_FOREACH(loop, &w->panes, entry) {
        !          1156:                        if (loop->screen->mode & MODE_MOUSE_ALL)
        !          1157:                                mode |= MODE_MOUSE_ALL;
        !          1158:                }
        !          1159:                if (~mode & MODE_MOUSE_ALL)
        !          1160:                        mode |= MODE_MOUSE_BUTTON;
        !          1161:        }
        !          1162: 
        !          1163:        /* Clear bracketed paste mode if at the prompt. */
        !          1164:        if (c->prompt_string != NULL)
        !          1165:                mode &= ~MODE_BRACKETPASTE;
        !          1166: 
        !          1167:        /* Set the terminal mode and reset attributes. */
        !          1168:        tty_update_mode(&c->tty, mode, s);
        !          1169:        tty_reset(&c->tty);
        !          1170: }
        !          1171: 
        !          1172: /* Repeat time callback. */
        !          1173: static void
        !          1174: server_client_repeat_timer(__unused int fd, __unused short events, void *data)
        !          1175: {
        !          1176:        struct client   *c = data;
        !          1177: 
        !          1178:        if (c->flags & CLIENT_REPEAT) {
        !          1179:                server_client_set_key_table(c, NULL);
        !          1180:                c->flags &= ~CLIENT_REPEAT;
        !          1181:                server_status_client(c);
        !          1182:        }
        !          1183: }
        !          1184: 
        !          1185: /* Double-click callback. */
        !          1186: static void
        !          1187: server_client_click_timer(__unused int fd, __unused short events, void *data)
        !          1188: {
        !          1189:        struct client   *c = data;
        !          1190: 
        !          1191:        c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
        !          1192: }
        !          1193: 
        !          1194: /* Check if client should be exited. */
        !          1195: static void
        !          1196: server_client_check_exit(struct client *c)
        !          1197: {
        !          1198:        if (!(c->flags & CLIENT_EXIT))
        !          1199:                return;
        !          1200: 
        !          1201:        if (EVBUFFER_LENGTH(c->stdin_data) != 0)
        !          1202:                return;
        !          1203:        if (EVBUFFER_LENGTH(c->stdout_data) != 0)
        !          1204:                return;
        !          1205:        if (EVBUFFER_LENGTH(c->stderr_data) != 0)
        !          1206:                return;
        !          1207: 
        !          1208:        proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
        !          1209:        c->flags &= ~CLIENT_EXIT;
        !          1210: }
        !          1211: 
        !          1212: /* Check for client redraws. */
        !          1213: static void
        !          1214: server_client_check_redraw(struct client *c)
        !          1215: {
        !          1216:        struct session          *s = c->session;
        !          1217:        struct tty              *tty = &c->tty;
        !          1218:        struct window_pane      *wp;
        !          1219:        int                      flags, masked;
        !          1220: 
        !          1221:        if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
        !          1222:                return;
        !          1223: 
        !          1224:        if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
        !          1225:                if (options_get_number(s->options, "set-titles"))
        !          1226:                        server_client_set_title(c);
        !          1227:                screen_redraw_update(c); /* will adjust flags */
        !          1228:        }
        !          1229: 
        !          1230:        flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR);
        !          1231:        tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR;
        !          1232: 
        !          1233:        if (c->flags & CLIENT_REDRAW) {
        !          1234:                tty_update_mode(tty, tty->mode, NULL);
        !          1235:                screen_redraw_screen(c, 1, 1, 1);
        !          1236:                c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
        !          1237:        } else {
        !          1238:                TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
        !          1239:                        if (wp->flags & PANE_REDRAW) {
        !          1240:                                tty_update_mode(tty, tty->mode, NULL);
        !          1241:                                screen_redraw_pane(c, wp);
        !          1242:                        }
        !          1243:                }
        !          1244:        }
        !          1245: 
        !          1246:        masked = c->flags & (CLIENT_BORDERS|CLIENT_STATUS);
        !          1247:        if (masked != 0)
        !          1248:                tty_update_mode(tty, tty->mode, NULL);
        !          1249:        if (masked == CLIENT_BORDERS)
        !          1250:                screen_redraw_screen(c, 0, 0, 1);
        !          1251:        else if (masked == CLIENT_STATUS)
        !          1252:                screen_redraw_screen(c, 0, 1, 0);
        !          1253:        else if (masked != 0)
        !          1254:                screen_redraw_screen(c, 0, 1, 1);
        !          1255: 
        !          1256:        tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags;
        !          1257:        tty_update_mode(tty, tty->mode, NULL);
        !          1258: 
        !          1259:        c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS|
        !          1260:            CLIENT_STATUSFORCE);
        !          1261: }
        !          1262: 
        !          1263: /* Set client title. */
        !          1264: static void
        !          1265: server_client_set_title(struct client *c)
        !          1266: {
        !          1267:        struct session          *s = c->session;
        !          1268:        const char              *template;
        !          1269:        char                    *title;
        !          1270:        struct format_tree      *ft;
        !          1271: 
        !          1272:        template = options_get_string(s->options, "set-titles-string");
        !          1273: 
        !          1274:        ft = format_create(NULL, FORMAT_NONE, 0);
        !          1275:        format_defaults(ft, c, NULL, NULL, NULL);
        !          1276: 
        !          1277:        title = format_expand_time(ft, template, time(NULL));
        !          1278:        if (c->title == NULL || strcmp(title, c->title) != 0) {
        !          1279:                free(c->title);
        !          1280:                c->title = xstrdup(title);
        !          1281:                tty_set_title(&c->tty, c->title);
        !          1282:        }
        !          1283:        free(title);
        !          1284: 
        !          1285:        format_free(ft);
        !          1286: }
        !          1287: 
        !          1288: /* Dispatch message from client. */
        !          1289: static void
        !          1290: server_client_dispatch(struct imsg *imsg, void *arg)
        !          1291: {
        !          1292:        struct client           *c = arg;
        !          1293:        struct msg_stdin_data    stdindata;
        !          1294:        const char              *data;
        !          1295:        ssize_t                  datalen;
        !          1296:        struct session          *s;
        !          1297: 
        !          1298:        if (c->flags & CLIENT_DEAD)
        !          1299:                return;
        !          1300: 
        !          1301:        if (imsg == NULL) {
        !          1302:                server_client_lost(c);
        !          1303:                return;
        !          1304:        }
        !          1305: 
        !          1306:        data = imsg->data;
        !          1307:        datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
        !          1308: 
        !          1309:        switch (imsg->hdr.type) {
        !          1310:        case MSG_IDENTIFY_FLAGS:
        !          1311:        case MSG_IDENTIFY_TERM:
        !          1312:        case MSG_IDENTIFY_TTYNAME:
        !          1313:        case MSG_IDENTIFY_CWD:
        !          1314:        case MSG_IDENTIFY_STDIN:
        !          1315:        case MSG_IDENTIFY_ENVIRON:
        !          1316:        case MSG_IDENTIFY_CLIENTPID:
        !          1317:        case MSG_IDENTIFY_DONE:
        !          1318:                server_client_dispatch_identify(c, imsg);
        !          1319:                break;
        !          1320:        case MSG_COMMAND:
        !          1321:                server_client_dispatch_command(c, imsg);
        !          1322:                break;
        !          1323:        case MSG_STDIN:
        !          1324:                if (datalen != sizeof stdindata)
        !          1325:                        fatalx("bad MSG_STDIN size");
        !          1326:                memcpy(&stdindata, data, sizeof stdindata);
        !          1327: 
        !          1328:                if (c->stdin_callback == NULL)
        !          1329:                        break;
        !          1330:                if (stdindata.size <= 0)
        !          1331:                        c->stdin_closed = 1;
        !          1332:                else {
        !          1333:                        evbuffer_add(c->stdin_data, stdindata.data,
        !          1334:                            stdindata.size);
        !          1335:                }
        !          1336:                c->stdin_callback(c, c->stdin_closed,
        !          1337:                    c->stdin_callback_data);
        !          1338:                break;
        !          1339:        case MSG_RESIZE:
        !          1340:                if (datalen != 0)
        !          1341:                        fatalx("bad MSG_RESIZE size");
        !          1342: 
        !          1343:                if (c->flags & CLIENT_CONTROL)
        !          1344:                        break;
        !          1345:                if (tty_resize(&c->tty)) {
        !          1346:                        recalculate_sizes();
        !          1347:                        server_redraw_client(c);
        !          1348:                }
        !          1349:                if (c->session != NULL)
        !          1350:                        notify_client("client-resized", c);
        !          1351:                break;
        !          1352:        case MSG_EXITING:
        !          1353:                if (datalen != 0)
        !          1354:                        fatalx("bad MSG_EXITING size");
        !          1355: 
        !          1356:                c->session = NULL;
        !          1357:                tty_close(&c->tty);
        !          1358:                proc_send(c->peer, MSG_EXITED, -1, NULL, 0);
        !          1359:                break;
        !          1360:        case MSG_WAKEUP:
        !          1361:        case MSG_UNLOCK:
        !          1362:                if (datalen != 0)
        !          1363:                        fatalx("bad MSG_WAKEUP size");
        !          1364: 
        !          1365:                if (!(c->flags & CLIENT_SUSPENDED))
        !          1366:                        break;
        !          1367:                c->flags &= ~CLIENT_SUSPENDED;
        !          1368: 
        !          1369:                if (c->tty.fd == -1) /* exited in the meantime */
        !          1370:                        break;
        !          1371:                s = c->session;
        !          1372: 
        !          1373:                if (gettimeofday(&c->activity_time, NULL) != 0)
        !          1374:                        fatal("gettimeofday failed");
        !          1375: 
        !          1376:                tty_start_tty(&c->tty);
        !          1377:                server_redraw_client(c);
        !          1378:                recalculate_sizes();
        !          1379: 
        !          1380:                if (s != NULL)
        !          1381:                        session_update_activity(s, &c->activity_time);
        !          1382:                break;
        !          1383:        case MSG_SHELL:
        !          1384:                if (datalen != 0)
        !          1385:                        fatalx("bad MSG_SHELL size");
        !          1386: 
        !          1387:                server_client_dispatch_shell(c);
        !          1388:                break;
        !          1389:        }
        !          1390: }
        !          1391: 
        !          1392: /* Callback when command is done. */
        !          1393: static enum cmd_retval
        !          1394: server_client_command_done(struct cmdq_item *item, __unused void *data)
        !          1395: {
        !          1396:        struct client   *c = item->client;
        !          1397: 
        !          1398:        if (~c->flags & CLIENT_ATTACHED)
        !          1399:                c->flags |= CLIENT_EXIT;
        !          1400:        return (CMD_RETURN_NORMAL);
        !          1401: }
        !          1402: 
        !          1403: /* Show an error message. */
        !          1404: static enum cmd_retval
        !          1405: server_client_command_error(struct cmdq_item *item, void *data)
        !          1406: {
        !          1407:        char    *error = data;
        !          1408: 
        !          1409:        cmdq_error(item, "%s", error);
        !          1410:        free(error);
        !          1411: 
        !          1412:        return (CMD_RETURN_NORMAL);
        !          1413: }
        !          1414: 
        !          1415: /* Handle command message. */
        !          1416: static void
        !          1417: server_client_dispatch_command(struct client *c, struct imsg *imsg)
        !          1418: {
        !          1419:        struct msg_command_data   data;
        !          1420:        char                     *buf;
        !          1421:        size_t                    len;
        !          1422:        struct cmd_list          *cmdlist = NULL;
        !          1423:        int                       argc;
        !          1424:        char                    **argv, *cause;
        !          1425: 
        !          1426:        if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
        !          1427:                fatalx("bad MSG_COMMAND size");
        !          1428:        memcpy(&data, imsg->data, sizeof data);
        !          1429: 
        !          1430:        buf = (char *)imsg->data + sizeof data;
        !          1431:        len = imsg->hdr.len  - IMSG_HEADER_SIZE - sizeof data;
        !          1432:        if (len > 0 && buf[len - 1] != '\0')
        !          1433:                fatalx("bad MSG_COMMAND string");
        !          1434: 
        !          1435:        argc = data.argc;
        !          1436:        if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
        !          1437:                cause = xstrdup("command too long");
        !          1438:                goto error;
        !          1439:        }
        !          1440: 
        !          1441:        if (argc == 0) {
        !          1442:                argc = 1;
        !          1443:                argv = xcalloc(1, sizeof *argv);
        !          1444:                *argv = xstrdup("new-session");
        !          1445:        }
        !          1446: 
        !          1447:        if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
        !          1448:                cmd_free_argv(argc, argv);
        !          1449:                goto error;
        !          1450:        }
        !          1451:        cmd_free_argv(argc, argv);
        !          1452: 
        !          1453:        cmdq_append(c, cmdq_get_command(cmdlist, NULL, NULL, 0));
        !          1454:        cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
        !          1455:        cmd_list_free(cmdlist);
        !          1456:        return;
        !          1457: 
        !          1458: error:
        !          1459:        cmdq_append(c, cmdq_get_callback(server_client_command_error, cause));
        !          1460: 
        !          1461:        if (cmdlist != NULL)
        !          1462:                cmd_list_free(cmdlist);
        !          1463: 
        !          1464:        c->flags |= CLIENT_EXIT;
        !          1465: }
        !          1466: 
        !          1467: /* Handle identify message. */
        !          1468: static void
        !          1469: server_client_dispatch_identify(struct client *c, struct imsg *imsg)
        !          1470: {
        !          1471:        const char      *data, *home;
        !          1472:        size_t           datalen;
        !          1473:        int              flags;
        !          1474:        char            *name;
        !          1475: 
        !          1476:        if (c->flags & CLIENT_IDENTIFIED)
        !          1477:                fatalx("out-of-order identify message");
        !          1478: 
        !          1479:        data = imsg->data;
        !          1480:        datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
        !          1481: 
        !          1482:        switch (imsg->hdr.type) {
        !          1483:        case MSG_IDENTIFY_FLAGS:
        !          1484:                if (datalen != sizeof flags)
        !          1485:                        fatalx("bad MSG_IDENTIFY_FLAGS size");
        !          1486:                memcpy(&flags, data, sizeof flags);
        !          1487:                c->flags |= flags;
        !          1488:                log_debug("client %p IDENTIFY_FLAGS %#x", c, flags);
        !          1489:                break;
        !          1490:        case MSG_IDENTIFY_TERM:
        !          1491:                if (datalen == 0 || data[datalen - 1] != '\0')
        !          1492:                        fatalx("bad MSG_IDENTIFY_TERM string");
        !          1493:                c->term = xstrdup(data);
        !          1494:                log_debug("client %p IDENTIFY_TERM %s", c, data);
        !          1495:                break;
        !          1496:        case MSG_IDENTIFY_TTYNAME:
        !          1497:                if (datalen == 0 || data[datalen - 1] != '\0')
        !          1498:                        fatalx("bad MSG_IDENTIFY_TTYNAME string");
        !          1499:                c->ttyname = xstrdup(data);
        !          1500:                log_debug("client %p IDENTIFY_TTYNAME %s", c, data);
        !          1501:                break;
        !          1502:        case MSG_IDENTIFY_CWD:
        !          1503:                if (datalen == 0 || data[datalen - 1] != '\0')
        !          1504:                        fatalx("bad MSG_IDENTIFY_CWD string");
        !          1505:                if (access(data, X_OK) == 0)
        !          1506:                        c->cwd = xstrdup(data);
        !          1507:                else if ((home = find_home()) != NULL)
        !          1508:                        c->cwd = xstrdup(home);
        !          1509:                else
        !          1510:                        c->cwd = xstrdup("/");
        !          1511:                log_debug("client %p IDENTIFY_CWD %s", c, data);
        !          1512:                break;
        !          1513:        case MSG_IDENTIFY_STDIN:
        !          1514:                if (datalen != 0)
        !          1515:                        fatalx("bad MSG_IDENTIFY_STDIN size");
        !          1516:                c->fd = imsg->fd;
        !          1517:                log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd);
        !          1518:                break;
        !          1519:        case MSG_IDENTIFY_ENVIRON:
        !          1520:                if (datalen == 0 || data[datalen - 1] != '\0')
        !          1521:                        fatalx("bad MSG_IDENTIFY_ENVIRON string");
        !          1522:                if (strchr(data, '=') != NULL)
        !          1523:                        environ_put(c->environ, data);
        !          1524:                log_debug("client %p IDENTIFY_ENVIRON %s", c, data);
        !          1525:                break;
        !          1526:        case MSG_IDENTIFY_CLIENTPID:
        !          1527:                if (datalen != sizeof c->pid)
        !          1528:                        fatalx("bad MSG_IDENTIFY_CLIENTPID size");
        !          1529:                memcpy(&c->pid, data, sizeof c->pid);
        !          1530:                log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid);
        !          1531:                break;
        !          1532:        default:
        !          1533:                break;
        !          1534:        }
        !          1535: 
        !          1536:        if (imsg->hdr.type != MSG_IDENTIFY_DONE)
        !          1537:                return;
        !          1538:        c->flags |= CLIENT_IDENTIFIED;
        !          1539: 
        !          1540:        if (*c->ttyname != '\0')
        !          1541:                name = xstrdup(c->ttyname);
        !          1542:        else
        !          1543:                xasprintf(&name, "client-%ld", (long)c->pid);
        !          1544:        c->name = name;
        !          1545:        log_debug("client %p name is %s", c, c->name);
        !          1546: 
        !          1547: #ifdef __CYGWIN__
        !          1548:        c->fd = open(c->ttyname, O_RDWR|O_NOCTTY);
        !          1549: #endif
        !          1550: 
        !          1551:        if (c->flags & CLIENT_CONTROL) {
        !          1552:                c->stdin_callback = control_callback;
        !          1553: 
        !          1554:                evbuffer_free(c->stderr_data);
        !          1555:                c->stderr_data = c->stdout_data;
        !          1556: 
        !          1557:                if (c->flags & CLIENT_CONTROLCONTROL)
        !          1558:                        evbuffer_add_printf(c->stdout_data, "\033P1000p");
        !          1559:                proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
        !          1560: 
        !          1561:                c->tty.fd = -1;
        !          1562: 
        !          1563:                close(c->fd);
        !          1564:                c->fd = -1;
        !          1565: 
        !          1566:                return;
        !          1567:        }
        !          1568: 
        !          1569:        if (c->fd == -1)
        !          1570:                return;
        !          1571:        if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
        !          1572:                close(c->fd);
        !          1573:                c->fd = -1;
        !          1574:                return;
        !          1575:        }
        !          1576:        if (c->flags & CLIENT_UTF8)
        !          1577:                c->tty.flags |= TTY_UTF8;
        !          1578:        if (c->flags & CLIENT_256COLOURS)
        !          1579:                c->tty.term_flags |= TERM_256COLOURS;
        !          1580: 
        !          1581:        tty_resize(&c->tty);
        !          1582: 
        !          1583:        if (!(c->flags & CLIENT_CONTROL))
        !          1584:                c->flags |= CLIENT_TERMINAL;
        !          1585: }
        !          1586: 
        !          1587: /* Handle shell message. */
        !          1588: static void
        !          1589: server_client_dispatch_shell(struct client *c)
        !          1590: {
        !          1591:        const char      *shell;
        !          1592: 
        !          1593:        shell = options_get_string(global_s_options, "default-shell");
        !          1594:        if (*shell == '\0' || areshell(shell))
        !          1595:                shell = _PATH_BSHELL;
        !          1596:        proc_send_s(c->peer, MSG_SHELL, shell);
        !          1597: 
        !          1598:        proc_kill_peer(c->peer);
        !          1599: }
        !          1600: 
        !          1601: /* Event callback to push more stdout data if any left. */
        !          1602: static void
        !          1603: server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
        !          1604: {
        !          1605:        struct client   *c = arg;
        !          1606: 
        !          1607:        if (~c->flags & CLIENT_DEAD)
        !          1608:                server_client_push_stdout(c);
        !          1609:        server_client_unref(c);
        !          1610: }
        !          1611: 
        !          1612: /* Push stdout to client if possible. */
        !          1613: void
        !          1614: server_client_push_stdout(struct client *c)
        !          1615: {
        !          1616:        struct msg_stdout_data data;
        !          1617:        size_t                 sent, left;
        !          1618: 
        !          1619:        left = EVBUFFER_LENGTH(c->stdout_data);
        !          1620:        while (left != 0) {
        !          1621:                sent = left;
        !          1622:                if (sent > sizeof data.data)
        !          1623:                        sent = sizeof data.data;
        !          1624:                memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
        !          1625:                data.size = sent;
        !          1626: 
        !          1627:                if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
        !          1628:                        break;
        !          1629:                evbuffer_drain(c->stdout_data, sent);
        !          1630: 
        !          1631:                left = EVBUFFER_LENGTH(c->stdout_data);
        !          1632:                log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
        !          1633:                    sent, left);
        !          1634:        }
        !          1635:        if (left != 0) {
        !          1636:                c->references++;
        !          1637:                event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
        !          1638:                log_debug("%s: client %p, queued", __func__, c);
        !          1639:        }
        !          1640: }
        !          1641: 
        !          1642: /* Event callback to push more stderr data if any left. */
        !          1643: static void
        !          1644: server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
        !          1645: {
        !          1646:        struct client   *c = arg;
        !          1647: 
        !          1648:        if (~c->flags & CLIENT_DEAD)
        !          1649:                server_client_push_stderr(c);
        !          1650:        server_client_unref(c);
        !          1651: }
        !          1652: 
        !          1653: /* Push stderr to client if possible. */
        !          1654: void
        !          1655: server_client_push_stderr(struct client *c)
        !          1656: {
        !          1657:        struct msg_stderr_data data;
        !          1658:        size_t                 sent, left;
        !          1659: 
        !          1660:        if (c->stderr_data == c->stdout_data) {
        !          1661:                server_client_push_stdout(c);
        !          1662:                return;
        !          1663:        }
        !          1664: 
        !          1665:        left = EVBUFFER_LENGTH(c->stderr_data);
        !          1666:        while (left != 0) {
        !          1667:                sent = left;
        !          1668:                if (sent > sizeof data.data)
        !          1669:                        sent = sizeof data.data;
        !          1670:                memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
        !          1671:                data.size = sent;
        !          1672: 
        !          1673:                if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
        !          1674:                        break;
        !          1675:                evbuffer_drain(c->stderr_data, sent);
        !          1676: 
        !          1677:                left = EVBUFFER_LENGTH(c->stderr_data);
        !          1678:                log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
        !          1679:                    sent, left);
        !          1680:        }
        !          1681:        if (left != 0) {
        !          1682:                c->references++;
        !          1683:                event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
        !          1684:                log_debug("%s: client %p, queued", __func__, c);
        !          1685:        }
        !          1686: }
        !          1687: 
        !          1688: /* Add to client message log. */
        !          1689: void
        !          1690: server_client_add_message(struct client *c, const char *fmt, ...)
        !          1691: {
        !          1692:        struct message_entry    *msg, *msg1;
        !          1693:        char                    *s;
        !          1694:        va_list                  ap;
        !          1695:        u_int                    limit;
        !          1696: 
        !          1697:        va_start(ap, fmt);
        !          1698:        xvasprintf(&s, fmt, ap);
        !          1699:        va_end(ap);
        !          1700: 
        !          1701:        log_debug("message %s (client %p)", s, c);
        !          1702: 
        !          1703:        msg = xcalloc(1, sizeof *msg);
        !          1704:        msg->msg_time = time(NULL);
        !          1705:        msg->msg_num = c->message_next++;
        !          1706:        msg->msg = s;
        !          1707:        TAILQ_INSERT_TAIL(&c->message_log, msg, entry);
        !          1708: 
        !          1709:        limit = options_get_number(global_options, "message-limit");
        !          1710:        TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
        !          1711:                if (msg->msg_num + limit >= c->message_next)
        !          1712:                        break;
        !          1713:                free(msg->msg);
        !          1714:                TAILQ_REMOVE(&c->message_log, msg, entry);
        !          1715:                free(msg);
        !          1716:        }
        !          1717: }
        !          1718: 
        !          1719: /* Get client working directory. */
        !          1720: const char *
        !          1721: server_client_get_cwd(struct client *c)
        !          1722: {
        !          1723:        struct session  *s;
        !          1724: 
        !          1725:        if (c != NULL && c->session == NULL && c->cwd != NULL)
        !          1726:                return (c->cwd);
        !          1727:        if (c != NULL && (s = c->session) != NULL && s->cwd != NULL)
        !          1728:                return (s->cwd);
        !          1729:        return (".");
        !          1730: }
        !          1731: 
        !          1732: /* Resolve an absolute path or relative to client working directory. */
        !          1733: char *
        !          1734: server_client_get_path(struct client *c, const char *file)
        !          1735: {
        !          1736:        char    *path, resolved[PATH_MAX];
        !          1737: 
        !          1738:        if (*file == '/')
        !          1739:                path = xstrdup(file);
        !          1740:        else
        !          1741:                xasprintf(&path, "%s/%s", server_client_get_cwd(c), file);
        !          1742:        if (realpath(path, resolved) == NULL)
        !          1743:                return (path);
        !          1744:        free(path);
        !          1745:        return (xstrdup(resolved));
        !          1746: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>