Annotation of embedaddon/tmux/session.c, revision 1.1.1.1

1.1       misho       1: /* $OpenBSD$ */
                      2: 
                      3: /*
                      4:  * Copyright (c) 2007 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/time.h>
                     21: 
                     22: #include <string.h>
                     23: #include <stdlib.h>
                     24: #include <unistd.h>
                     25: #include <time.h>
                     26: 
                     27: #include "tmux.h"
                     28: 
                     29: struct sessions                sessions;
                     30: static u_int           next_session_id;
                     31: struct session_groups  session_groups;
                     32: 
                     33: static void    session_free(int, short, void *);
                     34: 
                     35: static void    session_lock_timer(int, short, void *);
                     36: 
                     37: static struct winlink *session_next_alert(struct winlink *);
                     38: static struct winlink *session_previous_alert(struct winlink *);
                     39: 
                     40: static void    session_group_remove(struct session *);
                     41: static u_int   session_group_count(struct session_group *);
                     42: static void    session_group_synchronize1(struct session *, struct session *);
                     43: 
                     44: static u_int   session_group_count(struct session_group *);
                     45: static void    session_group_synchronize1(struct session *, struct session *);
                     46: 
                     47: RB_GENERATE(sessions, session, entry, session_cmp);
                     48: 
                     49: int
                     50: session_cmp(struct session *s1, struct session *s2)
                     51: {
                     52:        return (strcmp(s1->name, s2->name));
                     53: }
                     54: 
                     55: RB_GENERATE(session_groups, session_group, entry, session_group_cmp);
                     56: 
                     57: int
                     58: session_group_cmp(struct session_group *s1, struct session_group *s2)
                     59: {
                     60:        return (strcmp(s1->name, s2->name));
                     61: }
                     62: 
                     63: /*
                     64:  * Find if session is still alive. This is true if it is still on the global
                     65:  * sessions list.
                     66:  */
                     67: int
                     68: session_alive(struct session *s)
                     69: {
                     70:        struct session *s_loop;
                     71: 
                     72:        RB_FOREACH(s_loop, sessions, &sessions) {
                     73:                if (s_loop == s)
                     74:                        return (1);
                     75:        }
                     76:        return (0);
                     77: }
                     78: 
                     79: /* Find session by name. */
                     80: struct session *
                     81: session_find(const char *name)
                     82: {
                     83:        struct session  s;
                     84: 
                     85:        s.name = (char *) name;
                     86:        return (RB_FIND(sessions, &sessions, &s));
                     87: }
                     88: 
                     89: /* Find session by id parsed from a string. */
                     90: struct session *
                     91: session_find_by_id_str(const char *s)
                     92: {
                     93:        const char      *errstr;
                     94:        u_int            id;
                     95: 
                     96:        if (*s != '$')
                     97:                return (NULL);
                     98: 
                     99:        id = strtonum(s + 1, 0, UINT_MAX, &errstr);
                    100:        if (errstr != NULL)
                    101:                return (NULL);
                    102:        return (session_find_by_id(id));
                    103: }
                    104: 
                    105: /* Find session by id. */
                    106: struct session *
                    107: session_find_by_id(u_int id)
                    108: {
                    109:        struct session  *s;
                    110: 
                    111:        RB_FOREACH(s, sessions, &sessions) {
                    112:                if (s->id == id)
                    113:                        return (s);
                    114:        }
                    115:        return (NULL);
                    116: }
                    117: 
                    118: /* Create a new session. */
                    119: struct session *
                    120: session_create(const char *prefix, const char *name, int argc, char **argv,
                    121:     const char *path, const char *cwd, struct environ *env, struct termios *tio,
                    122:     int idx, u_int sx, u_int sy, char **cause)
                    123: {
                    124:        struct session  *s;
                    125:        struct winlink  *wl;
                    126: 
                    127:        s = xcalloc(1, sizeof *s);
                    128:        s->references = 1;
                    129:        s->flags = 0;
                    130: 
                    131:        s->cwd = xstrdup(cwd);
                    132: 
                    133:        s->curw = NULL;
                    134:        TAILQ_INIT(&s->lastw);
                    135:        RB_INIT(&s->windows);
                    136: 
                    137:        s->environ = environ_create();
                    138:        if (env != NULL)
                    139:                environ_copy(env, s->environ);
                    140: 
                    141:        s->options = options_create(global_s_options);
                    142:        s->hooks = hooks_create(global_hooks);
                    143: 
                    144:        status_update_saved(s);
                    145: 
                    146:        s->tio = NULL;
                    147:        if (tio != NULL) {
                    148:                s->tio = xmalloc(sizeof *s->tio);
                    149:                memcpy(s->tio, tio, sizeof *s->tio);
                    150:        }
                    151: 
                    152:        s->sx = sx;
                    153:        s->sy = sy;
                    154: 
                    155:        if (name != NULL) {
                    156:                s->name = xstrdup(name);
                    157:                s->id = next_session_id++;
                    158:        } else {
                    159:                s->name = NULL;
                    160:                do {
                    161:                        s->id = next_session_id++;
                    162:                        free(s->name);
                    163:                        if (prefix != NULL)
                    164:                                xasprintf(&s->name, "%s-%u", prefix, s->id);
                    165:                        else
                    166:                                xasprintf(&s->name, "%u", s->id);
                    167:                } while (RB_FIND(sessions, &sessions, s) != NULL);
                    168:        }
                    169:        RB_INSERT(sessions, &sessions, s);
                    170: 
                    171:        log_debug("new session %s $%u", s->name, s->id);
                    172: 
                    173:        if (gettimeofday(&s->creation_time, NULL) != 0)
                    174:                fatal("gettimeofday failed");
                    175:        session_update_activity(s, &s->creation_time);
                    176: 
                    177:        if (argc >= 0) {
                    178:                wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause);
                    179:                if (wl == NULL) {
                    180:                        session_destroy(s);
                    181:                        return (NULL);
                    182:                }
                    183:                session_select(s, RB_ROOT(&s->windows)->idx);
                    184:        }
                    185: 
                    186:        log_debug("session %s created", s->name);
                    187: 
                    188:        return (s);
                    189: }
                    190: 
                    191: /* Remove a reference from a session. */
                    192: void
                    193: session_unref(struct session *s)
                    194: {
                    195:        log_debug("session %s has %d references", s->name, s->references);
                    196: 
                    197:        s->references--;
                    198:        if (s->references == 0)
                    199:                event_once(-1, EV_TIMEOUT, session_free, s, NULL);
                    200: }
                    201: 
                    202: /* Free session. */
                    203: static void
                    204: session_free(__unused int fd, __unused short events, void *arg)
                    205: {
                    206:        struct session  *s = arg;
                    207: 
                    208:        log_debug("session %s freed (%d references)", s->name, s->references);
                    209: 
                    210:        if (s->references == 0) {
                    211:                environ_free(s->environ);
                    212: 
                    213:                options_free(s->options);
                    214:                hooks_free(s->hooks);
                    215: 
                    216:                free(s->name);
                    217:                free(s);
                    218:        }
                    219: }
                    220: 
                    221: /* Destroy a session. */
                    222: void
                    223: session_destroy(struct session *s)
                    224: {
                    225:        struct winlink  *wl;
                    226: 
                    227:        log_debug("session %s destroyed", s->name);
                    228:        s->curw = NULL;
                    229: 
                    230:        RB_REMOVE(sessions, &sessions, s);
                    231:        notify_session("session-closed", s);
                    232: 
                    233:        free(s->tio);
                    234: 
                    235:        if (event_initialized(&s->lock_timer))
                    236:                event_del(&s->lock_timer);
                    237: 
                    238:        session_group_remove(s);
                    239: 
                    240:        while (!TAILQ_EMPTY(&s->lastw))
                    241:                winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
                    242:        while (!RB_EMPTY(&s->windows)) {
                    243:                wl = RB_ROOT(&s->windows);
                    244:                notify_session_window("window-unlinked", s, wl->window);
                    245:                winlink_remove(&s->windows, wl);
                    246:        }
                    247: 
                    248:        free((void *)s->cwd);
                    249: 
                    250:        session_unref(s);
                    251: }
                    252: 
                    253: /* Check a session name is valid: not empty and no colons or periods. */
                    254: int
                    255: session_check_name(const char *name)
                    256: {
                    257:        return (*name != '\0' && name[strcspn(name, ":.")] == '\0');
                    258: }
                    259: 
                    260: /* Lock session if it has timed out. */
                    261: static void
                    262: session_lock_timer(__unused int fd, __unused short events, void *arg)
                    263: {
                    264:        struct session  *s = arg;
                    265: 
                    266:        if (s->flags & SESSION_UNATTACHED)
                    267:                return;
                    268: 
                    269:        log_debug("session %s locked, activity time %lld", s->name,
                    270:            (long long)s->activity_time.tv_sec);
                    271: 
                    272:        server_lock_session(s);
                    273:        recalculate_sizes();
                    274: }
                    275: 
                    276: /* Update activity time. */
                    277: void
                    278: session_update_activity(struct session *s, struct timeval *from)
                    279: {
                    280:        struct timeval  *last = &s->last_activity_time;
                    281:        struct timeval   tv;
                    282: 
                    283:        memcpy(last, &s->activity_time, sizeof *last);
                    284:        if (from == NULL)
                    285:                gettimeofday(&s->activity_time, NULL);
                    286:        else
                    287:                memcpy(&s->activity_time, from, sizeof s->activity_time);
                    288: 
                    289:        log_debug("session %s activity %lld.%06d (last %lld.%06d)", s->name,
                    290:            (long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec,
                    291:            (long long)last->tv_sec, (int)last->tv_usec);
                    292: 
                    293:        if (evtimer_initialized(&s->lock_timer))
                    294:                evtimer_del(&s->lock_timer);
                    295:        else
                    296:                evtimer_set(&s->lock_timer, session_lock_timer, s);
                    297: 
                    298:        if (~s->flags & SESSION_UNATTACHED) {
                    299:                timerclear(&tv);
                    300:                tv.tv_sec = options_get_number(s->options, "lock-after-time");
                    301:                if (tv.tv_sec != 0)
                    302:                        evtimer_add(&s->lock_timer, &tv);
                    303:        }
                    304: }
                    305: 
                    306: /* Find the next usable session. */
                    307: struct session *
                    308: session_next_session(struct session *s)
                    309: {
                    310:        struct session *s2;
                    311: 
                    312:        if (RB_EMPTY(&sessions) || !session_alive(s))
                    313:                return (NULL);
                    314: 
                    315:        s2 = RB_NEXT(sessions, &sessions, s);
                    316:        if (s2 == NULL)
                    317:                s2 = RB_MIN(sessions, &sessions);
                    318:        if (s2 == s)
                    319:                return (NULL);
                    320:        return (s2);
                    321: }
                    322: 
                    323: /* Find the previous usable session. */
                    324: struct session *
                    325: session_previous_session(struct session *s)
                    326: {
                    327:        struct session *s2;
                    328: 
                    329:        if (RB_EMPTY(&sessions) || !session_alive(s))
                    330:                return (NULL);
                    331: 
                    332:        s2 = RB_PREV(sessions, &sessions, s);
                    333:        if (s2 == NULL)
                    334:                s2 = RB_MAX(sessions, &sessions);
                    335:        if (s2 == s)
                    336:                return (NULL);
                    337:        return (s2);
                    338: }
                    339: 
                    340: /* Create a new window on a session. */
                    341: struct winlink *
                    342: session_new(struct session *s, const char *name, int argc, char **argv,
                    343:     const char *path, const char *cwd, int idx, char **cause)
                    344: {
                    345:        struct window   *w;
                    346:        struct winlink  *wl;
                    347:        struct environ  *env;
                    348:        const char      *shell;
                    349:        u_int            hlimit;
                    350: 
                    351:        if ((wl = winlink_add(&s->windows, idx)) == NULL) {
                    352:                xasprintf(cause, "index in use: %d", idx);
                    353:                return (NULL);
                    354:        }
                    355:        wl->session = s;
                    356: 
                    357:        shell = options_get_string(s->options, "default-shell");
                    358:        if (*shell == '\0' || areshell(shell))
                    359:                shell = _PATH_BSHELL;
                    360: 
                    361:        hlimit = options_get_number(s->options, "history-limit");
                    362:        env = environ_for_session(s);
                    363:        w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio,
                    364:            s->sx, s->sy, hlimit, cause);
                    365:        if (w == NULL) {
                    366:                winlink_remove(&s->windows, wl);
                    367:                environ_free(env);
                    368:                return (NULL);
                    369:        }
                    370:        winlink_set_window(wl, w);
                    371:        environ_free(env);
                    372:        notify_session_window("window-linked", s, w);
                    373: 
                    374:        session_group_synchronize_from(s);
                    375:        return (wl);
                    376: }
                    377: 
                    378: /* Attach a window to a session. */
                    379: struct winlink *
                    380: session_attach(struct session *s, struct window *w, int idx, char **cause)
                    381: {
                    382:        struct winlink  *wl;
                    383: 
                    384:        if ((wl = winlink_add(&s->windows, idx)) == NULL) {
                    385:                xasprintf(cause, "index in use: %d", idx);
                    386:                return (NULL);
                    387:        }
                    388:        wl->session = s;
                    389:        winlink_set_window(wl, w);
                    390:        notify_session_window("window-linked", s, w);
                    391: 
                    392:        session_group_synchronize_from(s);
                    393:        return (wl);
                    394: }
                    395: 
                    396: /* Detach a window from a session. */
                    397: int
                    398: session_detach(struct session *s, struct winlink *wl)
                    399: {
                    400:        if (s->curw == wl &&
                    401:            session_last(s) != 0 &&
                    402:            session_previous(s, 0) != 0)
                    403:                session_next(s, 0);
                    404: 
                    405:        wl->flags &= ~WINLINK_ALERTFLAGS;
                    406:        notify_session_window("window-unlinked", s, wl->window);
                    407:        winlink_stack_remove(&s->lastw, wl);
                    408:        winlink_remove(&s->windows, wl);
                    409: 
                    410:        session_group_synchronize_from(s);
                    411: 
                    412:        if (RB_EMPTY(&s->windows)) {
                    413:                session_destroy(s);
                    414:                return (1);
                    415:        }
                    416:        return (0);
                    417: }
                    418: 
                    419: /* Return if session has window. */
                    420: int
                    421: session_has(struct session *s, struct window *w)
                    422: {
                    423:        struct winlink  *wl;
                    424: 
                    425:        TAILQ_FOREACH(wl, &w->winlinks, wentry) {
                    426:                if (wl->session == s)
                    427:                        return (1);
                    428:        }
                    429:        return (0);
                    430: }
                    431: 
                    432: /*
                    433:  * Return 1 if a window is linked outside this session (not including session
                    434:  * groups). The window must be in this session!
                    435:  */
                    436: int
                    437: session_is_linked(struct session *s, struct window *w)
                    438: {
                    439:        struct session_group    *sg;
                    440: 
                    441:        if ((sg = session_group_contains(s)) != NULL)
                    442:                return (w->references != session_group_count(sg));
                    443:        return (w->references != 1);
                    444: }
                    445: 
                    446: static struct winlink *
                    447: session_next_alert(struct winlink *wl)
                    448: {
                    449:        while (wl != NULL) {
                    450:                if (wl->flags & WINLINK_ALERTFLAGS)
                    451:                        break;
                    452:                wl = winlink_next(wl);
                    453:        }
                    454:        return (wl);
                    455: }
                    456: 
                    457: /* Move session to next window. */
                    458: int
                    459: session_next(struct session *s, int alert)
                    460: {
                    461:        struct winlink  *wl;
                    462: 
                    463:        if (s->curw == NULL)
                    464:                return (-1);
                    465: 
                    466:        wl = winlink_next(s->curw);
                    467:        if (alert)
                    468:                wl = session_next_alert(wl);
                    469:        if (wl == NULL) {
                    470:                wl = RB_MIN(winlinks, &s->windows);
                    471:                if (alert && ((wl = session_next_alert(wl)) == NULL))
                    472:                        return (-1);
                    473:        }
                    474:        return (session_set_current(s, wl));
                    475: }
                    476: 
                    477: static struct winlink *
                    478: session_previous_alert(struct winlink *wl)
                    479: {
                    480:        while (wl != NULL) {
                    481:                if (wl->flags & WINLINK_ALERTFLAGS)
                    482:                        break;
                    483:                wl = winlink_previous(wl);
                    484:        }
                    485:        return (wl);
                    486: }
                    487: 
                    488: /* Move session to previous window. */
                    489: int
                    490: session_previous(struct session *s, int alert)
                    491: {
                    492:        struct winlink  *wl;
                    493: 
                    494:        if (s->curw == NULL)
                    495:                return (-1);
                    496: 
                    497:        wl = winlink_previous(s->curw);
                    498:        if (alert)
                    499:                wl = session_previous_alert(wl);
                    500:        if (wl == NULL) {
                    501:                wl = RB_MAX(winlinks, &s->windows);
                    502:                if (alert && (wl = session_previous_alert(wl)) == NULL)
                    503:                        return (-1);
                    504:        }
                    505:        return (session_set_current(s, wl));
                    506: }
                    507: 
                    508: /* Move session to specific window. */
                    509: int
                    510: session_select(struct session *s, int idx)
                    511: {
                    512:        struct winlink  *wl;
                    513: 
                    514:        wl = winlink_find_by_index(&s->windows, idx);
                    515:        return (session_set_current(s, wl));
                    516: }
                    517: 
                    518: /* Move session to last used window. */
                    519: int
                    520: session_last(struct session *s)
                    521: {
                    522:        struct winlink  *wl;
                    523: 
                    524:        wl = TAILQ_FIRST(&s->lastw);
                    525:        if (wl == NULL)
                    526:                return (-1);
                    527:        if (wl == s->curw)
                    528:                return (1);
                    529: 
                    530:        return (session_set_current(s, wl));
                    531: }
                    532: 
                    533: /* Set current winlink to wl .*/
                    534: int
                    535: session_set_current(struct session *s, struct winlink *wl)
                    536: {
                    537:        if (wl == NULL)
                    538:                return (-1);
                    539:        if (wl == s->curw)
                    540:                return (1);
                    541: 
                    542:        winlink_stack_remove(&s->lastw, wl);
                    543:        winlink_stack_push(&s->lastw, s->curw);
                    544:        s->curw = wl;
                    545:        winlink_clear_flags(wl);
                    546:        window_update_activity(wl->window);
                    547:        return (0);
                    548: }
                    549: 
                    550: /* Find the session group containing a session. */
                    551: struct session_group *
                    552: session_group_contains(struct session *target)
                    553: {
                    554:        struct session_group    *sg;
                    555:        struct session          *s;
                    556: 
                    557:        RB_FOREACH(sg, session_groups, &session_groups) {
                    558:                TAILQ_FOREACH(s, &sg->sessions, gentry) {
                    559:                        if (s == target)
                    560:                                return (sg);
                    561:                }
                    562:        }
                    563:        return (NULL);
                    564: }
                    565: 
                    566: /* Find session group by name. */
                    567: struct session_group *
                    568: session_group_find(const char *name)
                    569: {
                    570:        struct session_group    sg;
                    571: 
                    572:        sg.name = name;
                    573:        return (RB_FIND(session_groups, &session_groups, &sg));
                    574: }
                    575: 
                    576: /* Create a new session group. */
                    577: struct session_group *
                    578: session_group_new(const char *name)
                    579: {
                    580:        struct session_group    *sg;
                    581: 
                    582:        if ((sg = session_group_find(name)) != NULL)
                    583:                return (sg);
                    584: 
                    585:        sg = xcalloc(1, sizeof *sg);
                    586:        sg->name = xstrdup(name);
                    587:        TAILQ_INIT(&sg->sessions);
                    588: 
                    589:        RB_INSERT(session_groups, &session_groups, sg);
                    590:        return (sg);
                    591: }
                    592: 
                    593: /* Add a session to a session group. */
                    594: void
                    595: session_group_add(struct session_group *sg, struct session *s)
                    596: {
                    597:        if (session_group_contains(s) == NULL)
                    598:                TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
                    599: }
                    600: 
                    601: /* Remove a session from its group and destroy the group if empty. */
                    602: static void
                    603: session_group_remove(struct session *s)
                    604: {
                    605:        struct session_group    *sg;
                    606: 
                    607:        if ((sg = session_group_contains(s)) == NULL)
                    608:                return;
                    609:        TAILQ_REMOVE(&sg->sessions, s, gentry);
                    610:        if (TAILQ_EMPTY(&sg->sessions)) {
                    611:                RB_REMOVE(session_groups, &session_groups, sg);
                    612:                free(sg);
                    613:        }
                    614: }
                    615: 
                    616: /* Count number of sessions in session group. */
                    617: static u_int
                    618: session_group_count(struct session_group *sg)
                    619: {
                    620:        struct session  *s;
                    621:        u_int            n;
                    622: 
                    623:        n = 0;
                    624:        TAILQ_FOREACH(s, &sg->sessions, gentry)
                    625:            n++;
                    626:        return (n);
                    627: }
                    628: 
                    629: /* Synchronize a session to its session group. */
                    630: void
                    631: session_group_synchronize_to(struct session *s)
                    632: {
                    633:        struct session_group    *sg;
                    634:        struct session          *target;
                    635: 
                    636:        if ((sg = session_group_contains(s)) == NULL)
                    637:                return;
                    638: 
                    639:        target = NULL;
                    640:        TAILQ_FOREACH(target, &sg->sessions, gentry) {
                    641:                if (target != s)
                    642:                        break;
                    643:        }
                    644:        if (target != NULL)
                    645:                session_group_synchronize1(target, s);
                    646: }
                    647: 
                    648: /* Synchronize a session group to a session. */
                    649: void
                    650: session_group_synchronize_from(struct session *target)
                    651: {
                    652:        struct session_group    *sg;
                    653:        struct session          *s;
                    654: 
                    655:        if ((sg = session_group_contains(target)) == NULL)
                    656:                return;
                    657: 
                    658:        TAILQ_FOREACH(s, &sg->sessions, gentry) {
                    659:                if (s != target)
                    660:                        session_group_synchronize1(target, s);
                    661:        }
                    662: }
                    663: 
                    664: /*
                    665:  * Synchronize a session with a target session. This means destroying all
                    666:  * winlinks then recreating them, then updating the current window, last window
                    667:  * stack and alerts.
                    668:  */
                    669: static void
                    670: session_group_synchronize1(struct session *target, struct session *s)
                    671: {
                    672:        struct winlinks          old_windows, *ww;
                    673:        struct winlink_stack     old_lastw;
                    674:        struct winlink          *wl, *wl2;
                    675: 
                    676:        /* Don't do anything if the session is empty (it'll be destroyed). */
                    677:        ww = &target->windows;
                    678:        if (RB_EMPTY(ww))
                    679:                return;
                    680: 
                    681:        /* If the current window has vanished, move to the next now. */
                    682:        if (s->curw != NULL &&
                    683:            winlink_find_by_index(ww, s->curw->idx) == NULL &&
                    684:            session_last(s) != 0 && session_previous(s, 0) != 0)
                    685:                session_next(s, 0);
                    686: 
                    687:        /* Save the old pointer and reset it. */
                    688:        memcpy(&old_windows, &s->windows, sizeof old_windows);
                    689:        RB_INIT(&s->windows);
                    690: 
                    691:        /* Link all the windows from the target. */
                    692:        RB_FOREACH(wl, winlinks, ww) {
                    693:                wl2 = winlink_add(&s->windows, wl->idx);
                    694:                wl2->session = s;
                    695:                winlink_set_window(wl2, wl->window);
                    696:                notify_session_window("window-linked", s, wl2->window);
                    697:                wl2->flags |= wl->flags & WINLINK_ALERTFLAGS;
                    698:        }
                    699: 
                    700:        /* Fix up the current window. */
                    701:        if (s->curw != NULL)
                    702:                s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
                    703:        else
                    704:                s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
                    705: 
                    706:        /* Fix up the last window stack. */
                    707:        memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
                    708:        TAILQ_INIT(&s->lastw);
                    709:        TAILQ_FOREACH(wl, &old_lastw, sentry) {
                    710:                wl2 = winlink_find_by_index(&s->windows, wl->idx);
                    711:                if (wl2 != NULL)
                    712:                        TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
                    713:        }
                    714: 
                    715:        /* Then free the old winlinks list. */
                    716:        while (!RB_EMPTY(&old_windows)) {
                    717:                wl = RB_ROOT(&old_windows);
                    718:                wl2 = winlink_find_by_window_id(&s->windows, wl->window->id);
                    719:                if (wl2 == NULL)
                    720:                        notify_session_window("window-unlinked", s, wl->window);
                    721:                winlink_remove(&old_windows, wl);
                    722:        }
                    723: }
                    724: 
                    725: /* Renumber the windows across winlinks attached to a specific session. */
                    726: void
                    727: session_renumber_windows(struct session *s)
                    728: {
                    729:        struct winlink          *wl, *wl1, *wl_new;
                    730:        struct winlinks          old_wins;
                    731:        struct winlink_stack     old_lastw;
                    732:        int                      new_idx, new_curw_idx;
                    733: 
                    734:        /* Save and replace old window list. */
                    735:        memcpy(&old_wins, &s->windows, sizeof old_wins);
                    736:        RB_INIT(&s->windows);
                    737: 
                    738:        /* Start renumbering from the base-index if it's set. */
                    739:        new_idx = options_get_number(s->options, "base-index");
                    740:        new_curw_idx = 0;
                    741: 
                    742:        /* Go through the winlinks and assign new indexes. */
                    743:        RB_FOREACH(wl, winlinks, &old_wins) {
                    744:                wl_new = winlink_add(&s->windows, new_idx);
                    745:                wl_new->session = s;
                    746:                winlink_set_window(wl_new, wl->window);
                    747:                wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
                    748: 
                    749:                if (wl == s->curw)
                    750:                        new_curw_idx = wl_new->idx;
                    751: 
                    752:                new_idx++;
                    753:        }
                    754: 
                    755:        /* Fix the stack of last windows now. */
                    756:        memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
                    757:        TAILQ_INIT(&s->lastw);
                    758:        TAILQ_FOREACH(wl, &old_lastw, sentry) {
                    759:                wl_new = winlink_find_by_window(&s->windows, wl->window);
                    760:                if (wl_new != NULL)
                    761:                        TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
                    762:        }
                    763: 
                    764:        /* Set the current window. */
                    765:        s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
                    766: 
                    767:        /* Free the old winlinks (reducing window references too). */
                    768:        RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
                    769:                winlink_remove(&old_wins, wl);
                    770: }

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