[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.64

1.64    ! nicm        1: /* $OpenBSD: cfg.c,v 1.63 2019/02/16 11:42:08 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.56      nicm      105:        load_cfg(TMUX_CONF, NULL, NULL, 1);
1.40      nicm      106:
1.41      nicm      107:        if (cfg_file == NULL && (home = find_home()) != NULL) {
                    108:                xasprintf(&cfg_file, "%s/.tmux.conf", home);
1.45      tim       109:                quiet = 1;
1.41      nicm      110:        }
1.45      tim       111:        if (cfg_file != NULL)
1.56      nicm      112:                load_cfg(cfg_file, NULL, NULL, quiet);
1.40      nicm      113:
1.56      nicm      114:        cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
1.40      nicm      115: }
1.1       nicm      116:
1.61      nicm      117: static int
                    118: cfg_check_condition(const char *path, size_t line, const char *p, int *skip)
                    119: {
                    120:        struct format_tree      *ft;
                    121:        char                    *s;
                    122:        int                      result;
                    123:
                    124:        while (isspace((u_char)*p))
                    125:                p++;
                    126:        if (p[0] == '\0') {
                    127:                cfg_add_cause("%s:%zu: invalid condition", path, line);
                    128:                *skip = 1;
                    129:                return (0);
                    130:        }
                    131:
                    132:        ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS);
                    133:        s = format_expand(ft, p);
                    134:        result = format_true(s);
                    135:        free(s);
                    136:        format_free(ft);
                    137:
                    138:        *skip = result;
                    139:        return (result);
                    140: }
                    141:
                    142: static void
                    143: cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
                    144:     const char *p)
                    145: {
                    146:        struct cfg_cond *cond;
                    147:        struct cfg_cond *parent = TAILQ_FIRST(conds);
                    148:
                    149:        /*
                    150:         * Add a new condition. If a previous condition exists and isn't
                    151:         * currently met, this new one also can't be met.
                    152:         */
                    153:        cond = xcalloc(1, sizeof *cond);
                    154:        cond->line = line;
                    155:        if (parent == NULL || parent->met)
                    156:                cond->met = cfg_check_condition(path, line, p, &cond->skip);
                    157:        else
                    158:                cond->skip = 1;
                    159:        cond->saw_else = 0;
                    160:        TAILQ_INSERT_HEAD(conds, cond, entry);
                    161: }
                    162:
                    163: static void
                    164: cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds,
                    165:     const char *p)
                    166: {
                    167:        struct cfg_cond *cond = TAILQ_FIRST(conds);
                    168:
                    169:        /*
                    170:         * If a previous condition exists and wasn't met, check this
                    171:         * one instead and change the state.
                    172:         */
                    173:        if (cond == NULL || cond->saw_else)
                    174:                cfg_add_cause("%s:%zu: unexpected %%elif", path, line);
                    175:        else if (!cond->skip)
                    176:                cond->met = cfg_check_condition(path, line, p, &cond->skip);
                    177:        else
                    178:                cond->met = 0;
                    179: }
                    180:
                    181: static void
                    182: cfg_handle_else(const char *path, size_t line, struct cfg_conds *conds)
                    183: {
                    184:        struct cfg_cond *cond = TAILQ_FIRST(conds);
                    185:
                    186:        /*
                    187:         * If a previous condition exists and wasn't met and wasn't already
                    188:         * %else, use this one instead.
                    189:         */
                    190:        if (cond == NULL || cond->saw_else) {
                    191:                cfg_add_cause("%s:%zu: unexpected %%else", path, line);
                    192:                return;
                    193:        }
                    194:        cond->saw_else = 1;
                    195:        cond->met = !cond->skip;
                    196:        cond->skip = 1;
                    197: }
                    198:
                    199: static void
                    200: cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds)
                    201: {
                    202:        struct cfg_cond *cond = TAILQ_FIRST(conds);
                    203:
                    204:        /*
                    205:         * Remove previous condition if one exists.
                    206:         */
                    207:        if (cond == NULL) {
                    208:                cfg_add_cause("%s:%zu: unexpected %%endif", path, line);
                    209:                return;
                    210:        }
                    211:        TAILQ_REMOVE(conds, cond, entry);
                    212:        free(cond);
                    213: }
                    214:
                    215: static void
                    216: cfg_handle_directive(const char *p, const char *path, size_t line,
                    217:     struct cfg_conds *conds)
                    218: {
                    219:        int     n = 0;
                    220:
                    221:        while (p[n] != '\0' && !isspace((u_char)p[n]))
                    222:                n++;
                    223:        if (strncmp(p, "%if", n) == 0)
                    224:                cfg_handle_if(path, line, conds, p + n);
                    225:        else if (strncmp(p, "%elif", n) == 0)
                    226:                cfg_handle_elif(path, line, conds, p + n);
                    227:        else if (strcmp(p, "%else") == 0)
                    228:                cfg_handle_else(path, line, conds);
                    229:        else if (strcmp(p, "%endif") == 0)
                    230:                cfg_handle_endif(path, line, conds);
                    231:        else
                    232:                cfg_add_cause("%s:%zu: invalid directive: %s", path, line, p);
                    233: }
                    234:
1.28      nicm      235: int
1.50      nicm      236: load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
1.1       nicm      237: {
1.50      nicm      238:        FILE                    *f;
1.52      nicm      239:        const char               delim[3] = { '\\', '\\', '\0' };
                    240:        u_int                    found = 0;
1.50      nicm      241:        size_t                   line = 0;
1.61      nicm      242:        char                    *buf, *cause1, *p, *q;
1.50      nicm      243:        struct cmd_list         *cmdlist;
                    244:        struct cmdq_item        *new_item;
1.61      nicm      245:        struct cfg_cond         *cond, *cond1;
                    246:        struct cfg_conds         conds;
                    247:
                    248:        TAILQ_INIT(&conds);
1.1       nicm      249:
1.29      nicm      250:        log_debug("loading %s", path);
1.1       nicm      251:        if ((f = fopen(path, "rb")) == NULL) {
1.45      tim       252:                if (errno == ENOENT && quiet)
                    253:                        return (0);
                    254:                cfg_add_cause("%s: %s", path, strerror(errno));
1.28      nicm      255:                return (-1);
1.25      nicm      256:        }
                    257:
1.42      nicm      258:        while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) {
1.32      tobias    259:                log_debug("%s: %s", path, buf);
1.13      nicm      260:
1.32      tobias    261:                p = buf;
1.52      nicm      262:                while (isspace((u_char)*p))
1.32      tobias    263:                        p++;
                    264:                if (*p == '\0') {
                    265:                        free(buf);
1.21      nicm      266:                        continue;
1.22      nicm      267:                }
1.52      nicm      268:                q = p + strlen(p) - 1;
                    269:                while (q != p && isspace((u_char)*q))
                    270:                        *q-- = '\0';
                    271:
1.61      nicm      272:                if (*p == '%') {
                    273:                        cfg_handle_directive(p, path, line, &conds);
1.52      nicm      274:                        continue;
                    275:                }
1.61      nicm      276:                cond = TAILQ_FIRST(&conds);
                    277:                if (cond != NULL && !cond->met)
1.52      nicm      278:                        continue;
1.21      nicm      279:
1.53      nicm      280:                cmdlist = cmd_string_parse(p, path, line, &cause1);
                    281:                if (cmdlist == NULL) {
1.32      tobias    282:                        free(buf);
1.28      nicm      283:                        if (cause1 == NULL)
1.1       nicm      284:                                continue;
1.33      nicm      285:                        cfg_add_cause("%s:%zu: %s", path, line, cause1);
1.28      nicm      286:                        free(cause1);
1.9       nicm      287:                        continue;
1.20      nicm      288:                }
1.32      tobias    289:                free(buf);
1.28      nicm      290:
1.50      nicm      291:                new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
                    292:                if (item != NULL)
                    293:                        cmdq_insert_after(item, new_item);
1.49      nicm      294:                else
1.50      nicm      295:                        cmdq_append(c, new_item);
1.1       nicm      296:                cmd_list_free(cmdlist);
1.49      nicm      297:
1.28      nicm      298:                found++;
1.1       nicm      299:        }
                    300:        fclose(f);
1.61      nicm      301:
                    302:        TAILQ_FOREACH_REVERSE_SAFE(cond, &conds, cfg_conds, entry, cond1) {
                    303:                cfg_add_cause("%s:%zu: unterminated %%if", path, cond->line);
                    304:                TAILQ_REMOVE(&conds, cond, entry);
                    305:                free(cond);
                    306:        }
1.25      nicm      307:
1.28      nicm      308:        return (found);
1.33      nicm      309: }
                    310:
                    311: void
1.36      nicm      312: cfg_add_cause(const char *fmt, ...)
1.33      nicm      313: {
1.36      nicm      314:        va_list  ap;
                    315:        char    *msg;
1.33      nicm      316:
                    317:        va_start(ap, fmt);
                    318:        xvasprintf(&msg, fmt, ap);
1.38      nicm      319:        va_end(ap);
1.33      nicm      320:
1.35      nicm      321:        cfg_ncauses++;
                    322:        cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes);
                    323:        cfg_causes[cfg_ncauses - 1] = msg;
1.33      nicm      324: }
                    325:
                    326: void
1.50      nicm      327: cfg_print_causes(struct cmdq_item *item)
1.33      nicm      328: {
                    329:        u_int    i;
                    330:
1.35      nicm      331:        for (i = 0; i < cfg_ncauses; i++) {
1.50      nicm      332:                cmdq_print(item, "%s", cfg_causes[i]);
1.35      nicm      333:                free(cfg_causes[i]);
1.33      nicm      334:        }
1.35      nicm      335:
                    336:        free(cfg_causes);
                    337:        cfg_causes = NULL;
1.37      nicm      338:        cfg_ncauses = 0;
1.17      nicm      339: }
                    340:
                    341: void
1.28      nicm      342: cfg_show_causes(struct session *s)
1.17      nicm      343: {
                    344:        struct window_pane      *wp;
                    345:        u_int                    i;
                    346:
1.35      nicm      347:        if (s == NULL || cfg_ncauses == 0)
1.17      nicm      348:                return;
                    349:        wp = s->curw->window->active;
                    350:
                    351:        window_copy_init_for_output(wp);
1.35      nicm      352:        for (i = 0; i < cfg_ncauses; i++) {
                    353:                window_copy_add(wp, "%s", cfg_causes[i]);
                    354:                free(cfg_causes[i]);
1.17      nicm      355:        }
1.35      nicm      356:
                    357:        free(cfg_causes);
                    358:        cfg_causes = NULL;
1.37      nicm      359:        cfg_ncauses = 0;
1.1       nicm      360: }