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

Annotation of src/usr.bin/tmux/cfg.c, Revision 1.68

1.68    ! nicm        1: /* $OpenBSD: cfg.c,v 1.67 2019/04/03 06:43:19 nicm Exp $ */
1.1       nicm        2:
                      3: /*
1.44      nicm        4:  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1       nicm        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:
1.21      nicm       21: #include <ctype.h>
1.1       nicm       22: #include <errno.h>
                     23: #include <stdio.h>
1.15      nicm       24: #include <stdlib.h>
1.1       nicm       25: #include <string.h>
1.32      tobias     26: #include <util.h>
1.1       nicm       27:
                     28: #include "tmux.h"
                     29:
1.61      nicm       30: /* Condition for %if, %elif, %else and %endif. */
                     31: struct cfg_cond {
                     32:        size_t                  line;           /* line number of %if */
                     33:        int                     met;            /* condition was met */
                     34:        int                     skip;           /* skip later %elif/%else */
                     35:        int                     saw_else;       /* saw a %else */
                     36:
                     37:        TAILQ_ENTRY(cfg_cond)   entry;
                     38: };
                     39: TAILQ_HEAD(cfg_conds, cfg_cond);
                     40:
1.63      nicm       41: struct client           *cfg_client;
1.58      nicm       42: static char             *cfg_file;
                     43: int                      cfg_finished;
                     44: static char            **cfg_causes;
                     45: static u_int             cfg_ncauses;
                     46: static struct cmdq_item         *cfg_item;
                     47:
                     48: static enum cmd_retval
                     49: cfg_client_done(__unused struct cmdq_item *item, __unused void *data)
                     50: {
                     51:        if (!cfg_finished)
                     52:                return (CMD_RETURN_WAIT);
                     53:        return (CMD_RETURN_NORMAL);
                     54: }
1.40      nicm       55:
1.49      nicm       56: static enum cmd_retval
1.50      nicm       57: cfg_done(__unused struct cmdq_item *item, __unused void *data)
1.49      nicm       58: {
                     59:        if (cfg_finished)
                     60:                return (CMD_RETURN_NORMAL);
                     61:        cfg_finished = 1;
                     62:
                     63:        if (!RB_EMPTY(&sessions))
                     64:                cfg_show_causes(RB_MIN(sessions, &sessions));
1.57      nicm       65:
1.58      nicm       66:        if (cfg_item != NULL)
                     67:                cfg_item->flags &= ~CMDQ_WAITING;
                     68:
1.57      nicm       69:        status_prompt_load_history();
1.49      nicm       70:
                     71:        return (CMD_RETURN_NORMAL);
                     72: }
1.40      nicm       73:
                     74: void
1.41      nicm       75: set_cfg_file(const char *path)
                     76: {
                     77:        free(cfg_file);
                     78:        cfg_file = xstrdup(path);
                     79: }
                     80:
                     81: void
1.40      nicm       82: start_cfg(void)
                     83: {
1.41      nicm       84:        const char      *home;
1.45      tim        85:        int              quiet = 0;
1.58      nicm       86:        struct client   *c;
1.40      nicm       87:
1.56      nicm       88:        /*
1.58      nicm       89:         * Configuration files are loaded without a client, so NULL is passed
                     90:         * into load_cfg() and commands run in the global queue with
                     91:         * item->client NULL.
                     92:         *
                     93:         * However, we must block the initial client (but just the initial
                     94:         * client) so that its command runs after the configuration is loaded.
                     95:         * Because start_cfg() is called so early, we can be sure the client's
                     96:         * command queue is currently empty and our callback will be at the
                     97:         * front - we need to get in before MSG_COMMAND.
1.56      nicm       98:         */
1.63      nicm       99:        cfg_client = c = TAILQ_FIRST(&clients);
1.58      nicm      100:        if (c != NULL) {
                    101:                cfg_item = cmdq_get_callback(cfg_client_done, NULL);
                    102:                cmdq_append(c, cfg_item);
                    103:        }
1.40      nicm      104:
1.67      nicm      105:        if (cfg_file == NULL)
                    106:                load_cfg(TMUX_CONF, NULL, NULL, 1);
1.40      nicm      107:
1.41      nicm      108:        if (cfg_file == NULL && (home = find_home()) != NULL) {
                    109:                xasprintf(&cfg_file, "%s/.tmux.conf", home);
1.45      tim       110:                quiet = 1;
1.41      nicm      111:        }
1.45      tim       112:        if (cfg_file != NULL)
1.56      nicm      113:                load_cfg(cfg_file, NULL, NULL, quiet);
1.40      nicm      114:
1.56      nicm      115:        cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
1.40      nicm      116: }
1.1       nicm      117:
1.61      nicm      118: static int
1.68    ! nicm      119: cfg_check_cond(const char *path, size_t line, const char *p, int *skip,
        !           120:     struct client *c, struct cmd_find_state *fs)
1.61      nicm      121: {
                    122:        struct format_tree      *ft;
                    123:        char                    *s;
                    124:        int                      result;
                    125:
                    126:        while (isspace((u_char)*p))
                    127:                p++;
                    128:        if (p[0] == '\0') {
                    129:                cfg_add_cause("%s:%zu: invalid condition", path, line);
                    130:                *skip = 1;
                    131:                return (0);
                    132:        }
                    133:
                    134:        ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS);
1.68    ! nicm      135:        if (fs != NULL)
        !           136:                format_defaults(ft, c, fs->s, fs->wl, fs->wp);
        !           137:        else
        !           138:                format_defaults(ft, c, NULL, NULL, NULL);
1.61      nicm      139:        s = format_expand(ft, p);
                    140:        result = format_true(s);
                    141:        free(s);
                    142:        format_free(ft);
                    143:
                    144:        *skip = result;
                    145:        return (result);
                    146: }
                    147:
                    148: static void
                    149: cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
1.68    ! nicm      150:     const char *p, struct client *c, struct cmd_find_state *fs)
1.61      nicm      151: {
                    152:        struct cfg_cond *cond;
                    153:        struct cfg_cond *parent = TAILQ_FIRST(conds);
                    154:
                    155:        /*
                    156:         * Add a new condition. If a previous condition exists and isn't
                    157:         * currently met, this new one also can't be met.
                    158:         */
                    159:        cond = xcalloc(1, sizeof *cond);
                    160:        cond->line = line;
                    161:        if (parent == NULL || parent->met)
1.68    ! nicm      162:                cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs);
1.61      nicm      163:        else
                    164:                cond->skip = 1;
                    165:        cond->saw_else = 0;
                    166:        TAILQ_INSERT_HEAD(conds, cond, entry);
                    167: }
                    168:
                    169: static void
                    170: cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds,
1.68    ! nicm      171:     const char *p, struct client *c, struct cmd_find_state *fs)
1.61      nicm      172: {
                    173:        struct cfg_cond *cond = TAILQ_FIRST(conds);
                    174:
                    175:        /*
                    176:         * If a previous condition exists and wasn't met, check this
                    177:         * one instead and change the state.
                    178:         */
                    179:        if (cond == NULL || cond->saw_else)
                    180:                cfg_add_cause("%s:%zu: unexpected %%elif", path, line);
                    181:        else if (!cond->skip)
1.68    ! nicm      182:                cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs);
1.61      nicm      183:        else
                    184:                cond->met = 0;
                    185: }
                    186:
                    187: static void
                    188: cfg_handle_else(const char *path, size_t line, struct cfg_conds *conds)
                    189: {
                    190:        struct cfg_cond *cond = TAILQ_FIRST(conds);
                    191:
                    192:        /*
                    193:         * If a previous condition exists and wasn't met and wasn't already
                    194:         * %else, use this one instead.
                    195:         */
                    196:        if (cond == NULL || cond->saw_else) {
                    197:                cfg_add_cause("%s:%zu: unexpected %%else", path, line);
                    198:                return;
                    199:        }
                    200:        cond->saw_else = 1;
                    201:        cond->met = !cond->skip;
                    202:        cond->skip = 1;
                    203: }
                    204:
                    205: static void
                    206: cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds)
                    207: {
                    208:        struct cfg_cond *cond = TAILQ_FIRST(conds);
                    209:
                    210:        /*
                    211:         * Remove previous condition if one exists.
                    212:         */
                    213:        if (cond == NULL) {
                    214:                cfg_add_cause("%s:%zu: unexpected %%endif", path, line);
                    215:                return;
                    216:        }
                    217:        TAILQ_REMOVE(conds, cond, entry);
                    218:        free(cond);
                    219: }
                    220:
                    221: static void
                    222: cfg_handle_directive(const char *p, const char *path, size_t line,
1.68    ! nicm      223:     struct cfg_conds *conds, struct client *c, struct cmd_find_state *fs)
1.61      nicm      224: {
                    225:        int     n = 0;
                    226:
                    227:        while (p[n] != '\0' && !isspace((u_char)p[n]))
                    228:                n++;
                    229:        if (strncmp(p, "%if", n) == 0)
1.68    ! nicm      230:                cfg_handle_if(path, line, conds, p + n, c, fs);
1.61      nicm      231:        else if (strncmp(p, "%elif", n) == 0)
1.68    ! nicm      232:                cfg_handle_elif(path, line, conds, p + n, c, fs);
1.61      nicm      233:        else if (strcmp(p, "%else") == 0)
                    234:                cfg_handle_else(path, line, conds);
                    235:        else if (strcmp(p, "%endif") == 0)
                    236:                cfg_handle_endif(path, line, conds);
                    237:        else
                    238:                cfg_add_cause("%s:%zu: invalid directive: %s", path, line, p);
                    239: }
                    240:
1.28      nicm      241: int
1.50      nicm      242: load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
1.1       nicm      243: {
1.50      nicm      244:        FILE                    *f;
1.52      nicm      245:        const char               delim[3] = { '\\', '\\', '\0' };
                    246:        u_int                    found = 0;
1.50      nicm      247:        size_t                   line = 0;
1.61      nicm      248:        char                    *buf, *cause1, *p, *q;
1.50      nicm      249:        struct cmd_list         *cmdlist;
                    250:        struct cmdq_item        *new_item;
1.61      nicm      251:        struct cfg_cond         *cond, *cond1;
                    252:        struct cfg_conds         conds;
1.68    ! nicm      253:        struct cmd_find_state   *fs = NULL;
        !           254:        struct client           *fc = NULL;
        !           255:
        !           256:        if (item != NULL) {
        !           257:                fs = &item->target;
        !           258:                fc = cmd_find_client(item, NULL, 1);
        !           259:        }
1.61      nicm      260:
                    261:        TAILQ_INIT(&conds);
1.1       nicm      262:
1.29      nicm      263:        log_debug("loading %s", path);
1.1       nicm      264:        if ((f = fopen(path, "rb")) == NULL) {
1.45      tim       265:                if (errno == ENOENT && quiet)
                    266:                        return (0);
                    267:                cfg_add_cause("%s: %s", path, strerror(errno));
1.28      nicm      268:                return (-1);
1.25      nicm      269:        }
                    270:
1.42      nicm      271:        while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) {
1.32      tobias    272:                log_debug("%s: %s", path, buf);
1.13      nicm      273:
1.32      tobias    274:                p = buf;
1.52      nicm      275:                while (isspace((u_char)*p))
1.32      tobias    276:                        p++;
                    277:                if (*p == '\0') {
                    278:                        free(buf);
1.21      nicm      279:                        continue;
1.22      nicm      280:                }
1.52      nicm      281:                q = p + strlen(p) - 1;
                    282:                while (q != p && isspace((u_char)*q))
                    283:                        *q-- = '\0';
                    284:
1.61      nicm      285:                if (*p == '%') {
1.68    ! nicm      286:                        cfg_handle_directive(p, path, line, &conds, fc, fs);
1.52      nicm      287:                        continue;
                    288:                }
1.61      nicm      289:                cond = TAILQ_FIRST(&conds);
                    290:                if (cond != NULL && !cond->met)
1.52      nicm      291:                        continue;
1.21      nicm      292:
1.53      nicm      293:                cmdlist = cmd_string_parse(p, path, line, &cause1);
                    294:                if (cmdlist == NULL) {
1.32      tobias    295:                        free(buf);
1.28      nicm      296:                        if (cause1 == NULL)
1.1       nicm      297:                                continue;
1.33      nicm      298:                        cfg_add_cause("%s:%zu: %s", path, line, cause1);
1.28      nicm      299:                        free(cause1);
1.9       nicm      300:                        continue;
1.20      nicm      301:                }
1.32      tobias    302:                free(buf);
1.28      nicm      303:
1.50      nicm      304:                new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
                    305:                if (item != NULL)
                    306:                        cmdq_insert_after(item, new_item);
1.49      nicm      307:                else
1.50      nicm      308:                        cmdq_append(c, new_item);
1.1       nicm      309:                cmd_list_free(cmdlist);
1.49      nicm      310:
1.28      nicm      311:                found++;
1.1       nicm      312:        }
                    313:        fclose(f);
1.61      nicm      314:
                    315:        TAILQ_FOREACH_REVERSE_SAFE(cond, &conds, cfg_conds, entry, cond1) {
                    316:                cfg_add_cause("%s:%zu: unterminated %%if", path, cond->line);
                    317:                TAILQ_REMOVE(&conds, cond, entry);
                    318:                free(cond);
                    319:        }
1.25      nicm      320:
1.28      nicm      321:        return (found);
1.33      nicm      322: }
                    323:
                    324: void
1.36      nicm      325: cfg_add_cause(const char *fmt, ...)
1.33      nicm      326: {
1.36      nicm      327:        va_list  ap;
                    328:        char    *msg;
1.33      nicm      329:
                    330:        va_start(ap, fmt);
                    331:        xvasprintf(&msg, fmt, ap);
1.38      nicm      332:        va_end(ap);
1.33      nicm      333:
1.35      nicm      334:        cfg_ncauses++;
                    335:        cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes);
                    336:        cfg_causes[cfg_ncauses - 1] = msg;
1.33      nicm      337: }
                    338:
                    339: void
1.50      nicm      340: cfg_print_causes(struct cmdq_item *item)
1.33      nicm      341: {
                    342:        u_int    i;
                    343:
1.35      nicm      344:        for (i = 0; i < cfg_ncauses; i++) {
1.50      nicm      345:                cmdq_print(item, "%s", cfg_causes[i]);
1.35      nicm      346:                free(cfg_causes[i]);
1.33      nicm      347:        }
1.35      nicm      348:
                    349:        free(cfg_causes);
                    350:        cfg_causes = NULL;
1.37      nicm      351:        cfg_ncauses = 0;
1.17      nicm      352: }
                    353:
                    354: void
1.28      nicm      355: cfg_show_causes(struct session *s)
1.17      nicm      356: {
1.66      nicm      357:        struct window_pane              *wp;
                    358:        struct window_mode_entry        *wme;
                    359:        u_int                            i;
1.17      nicm      360:
1.35      nicm      361:        if (s == NULL || cfg_ncauses == 0)
1.17      nicm      362:                return;
                    363:        wp = s->curw->window->active;
                    364:
1.66      nicm      365:        wme = TAILQ_FIRST(&wp->modes);
                    366:        if (wme == NULL || wme->mode != &window_view_mode)
1.65      nicm      367:                window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
1.35      nicm      368:        for (i = 0; i < cfg_ncauses; i++) {
                    369:                window_copy_add(wp, "%s", cfg_causes[i]);
                    370:                free(cfg_causes[i]);
1.17      nicm      371:        }
1.35      nicm      372:
                    373:        free(cfg_causes);
                    374:        cfg_causes = NULL;
1.37      nicm      375:        cfg_ncauses = 0;
1.1       nicm      376: }