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

Annotation of src/usr.bin/tmux/cmd-string.c, Revision 1.30

1.30    ! nicm        1: /* $OpenBSD: cmd-string.c,v 1.29 2017/06/14 07:42:41 nicm Exp $ */
1.1       nicm        2:
                      3: /*
1.22      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:
                     21: #include <errno.h>
1.4       nicm       22: #include <pwd.h>
1.1       nicm       23: #include <stdio.h>
                     24: #include <string.h>
                     25: #include <stdlib.h>
1.4       nicm       26: #include <unistd.h>
1.1       nicm       27:
                     28: #include "tmux.h"
                     29:
                     30: /*
                     31:  * Parse a command from a string.
                     32:  */
                     33:
1.23      nicm       34: static int      cmd_string_getc(const char *, size_t *);
                     35: static void     cmd_string_ungetc(size_t *);
                     36: static void     cmd_string_copy(char **, char *, size_t *);
                     37: static char    *cmd_string_string(const char *, size_t *, char, int);
                     38: static char    *cmd_string_variable(const char *, size_t *);
                     39: static char    *cmd_string_expand_tilde(const char *, size_t *);
1.1       nicm       40:
1.23      nicm       41: static int
1.1       nicm       42: cmd_string_getc(const char *s, size_t *p)
                     43: {
1.11      nicm       44:        const u_char    *ucs = s;
                     45:
                     46:        if (ucs[*p] == '\0')
1.1       nicm       47:                return (EOF);
1.11      nicm       48:        return (ucs[(*p)++]);
1.1       nicm       49: }
                     50:
1.23      nicm       51: static void
1.11      nicm       52: cmd_string_ungetc(size_t *p)
1.1       nicm       53: {
                     54:        (*p)--;
                     55: }
                     56:
1.30    ! nicm       57: static int
        !            58: cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch)
        !            59: {
        !            60:        int     size = (ch == 'u') ? 4 : 8;
        !            61:        u_int   tmp;
        !            62:
        !            63:        if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1)
        !            64:                return (-1);
        !            65:        if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1)
        !            66:                return (-1);
        !            67:        *p += size;
        !            68:
        !            69:        *wc = (wchar_t)tmp;
        !            70:        return (0);
        !            71: }
        !            72:
1.28      nicm       73: int
                     74: cmd_string_split(const char *s, int *rargc, char ***rargv)
1.1       nicm       75: {
1.28      nicm       76:        size_t          p = 0;
                     77:        int             ch, argc = 0, append = 0;
                     78:        char          **argv = NULL, *buf = NULL, *t;
                     79:        const char     *whitespace, *equals;
                     80:        size_t          len = 0;
1.1       nicm       81:
                     82:        for (;;) {
                     83:                ch = cmd_string_getc(s, &p);
                     84:                switch (ch) {
                     85:                case '\'':
                     86:                        if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
                     87:                                goto error;
1.17      nicm       88:                        cmd_string_copy(&buf, t, &len);
1.1       nicm       89:                        break;
                     90:                case '"':
                     91:                        if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
                     92:                                goto error;
1.17      nicm       93:                        cmd_string_copy(&buf, t, &len);
1.1       nicm       94:                        break;
                     95:                case '$':
                     96:                        if ((t = cmd_string_variable(s, &p)) == NULL)
                     97:                                goto error;
1.17      nicm       98:                        cmd_string_copy(&buf, t, &len);
1.1       nicm       99:                        break;
                    100:                case '#':
                    101:                        /* Comment: discard rest of line. */
                    102:                        while ((ch = cmd_string_getc(s, &p)) != EOF)
                    103:                                ;
                    104:                        /* FALLTHROUGH */
                    105:                case EOF:
                    106:                case ' ':
                    107:                case '\t':
1.14      nicm      108:                        if (buf != NULL) {
1.20      nicm      109:                                buf = xrealloc(buf, len + 1);
1.1       nicm      110:                                buf[len] = '\0';
                    111:
1.20      nicm      112:                                argv = xreallocarray(argv, argc + 1,
                    113:                                    sizeof *argv);
1.1       nicm      114:                                argv[argc++] = buf;
                    115:
                    116:                                buf = NULL;
                    117:                                len = 0;
                    118:                        }
                    119:
                    120:                        if (ch != EOF)
                    121:                                break;
1.6       nicm      122:
1.8       nicm      123:                        while (argc != 0) {
                    124:                                equals = strchr(argv[0], '=');
                    125:                                whitespace = argv[0] + strcspn(argv[0], " \t");
1.6       nicm      126:                                if (equals == NULL || equals > whitespace)
                    127:                                        break;
1.21      nicm      128:                                environ_put(global_environ, argv[0]);
1.6       nicm      129:                                argc--;
1.8       nicm      130:                                memmove(argv, argv + 1, argc * (sizeof *argv));
1.6       nicm      131:                        }
1.28      nicm      132:                        goto done;
1.4       nicm      133:                case '~':
1.28      nicm      134:                        if (buf != NULL) {
                    135:                                append = 1;
1.4       nicm      136:                                break;
                    137:                        }
1.28      nicm      138:                        t = cmd_string_expand_tilde(s, &p);
                    139:                        if (t == NULL)
                    140:                                goto error;
                    141:                        cmd_string_copy(&buf, t, &len);
                    142:                        break;
1.1       nicm      143:                default:
1.28      nicm      144:                        append = 1;
                    145:                        break;
                    146:                }
                    147:                if (append) {
1.1       nicm      148:                        if (len >= SIZE_MAX - 2)
                    149:                                goto error;
1.20      nicm      150:                        buf = xrealloc(buf, len + 1);
1.1       nicm      151:                        buf[len++] = ch;
                    152:                }
1.28      nicm      153:                append = 0;
1.1       nicm      154:        }
                    155:
1.28      nicm      156: done:
                    157:        *rargc = argc;
                    158:        *rargv = argv;
                    159:
                    160:        free(buf);
                    161:        return (0);
                    162:
1.27      nicm      163: error:
1.28      nicm      164:        if (argv != NULL)
                    165:                cmd_free_argv(argc, argv);
                    166:        free(buf);
                    167:        return (-1);
                    168: }
1.26      nicm      169:
1.28      nicm      170: struct cmd_list *
                    171: cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
                    172: {
                    173:        struct cmd_list  *cmdlist = NULL;
                    174:        int               argc;
                    175:        char            **argv;
1.26      nicm      176:
1.28      nicm      177:        *cause = NULL;
1.29      nicm      178:        if (cmd_string_split(s, &argc, &argv) != 0) {
                    179:                xasprintf(cause, "invalid or unknown command: %s", s);
                    180:                return (NULL);
                    181:        }
1.28      nicm      182:        if (argc != 0) {
                    183:                cmdlist = cmd_list_parse(argc, argv, file, line, cause);
                    184:                if (cmdlist == NULL) {
                    185:                        cmd_free_argv(argc, argv);
1.29      nicm      186:                        return (NULL);
1.28      nicm      187:                }
1.27      nicm      188:        }
1.28      nicm      189:        cmd_free_argv(argc, argv);
                    190:        return (cmdlist);
1.1       nicm      191: }
                    192:
1.23      nicm      193: static void
1.17      nicm      194: cmd_string_copy(char **dst, char *src, size_t *len)
                    195: {
                    196:        size_t srclen;
                    197:
                    198:        srclen = strlen(src);
                    199:
1.20      nicm      200:        *dst = xrealloc(*dst, *len + srclen + 1);
1.17      nicm      201:        strlcpy(*dst + *len, src, srclen + 1);
                    202:
                    203:        *len += srclen;
                    204:        free(src);
                    205: }
                    206:
1.23      nicm      207: static char *
1.1       nicm      208: cmd_string_string(const char *s, size_t *p, char endch, int esc)
                    209: {
1.30    ! nicm      210:        int                     ch;
        !           211:        wchar_t                 wc;
        !           212:        struct utf8_data        ud;
        !           213:        char                   *buf = NULL, *t;
        !           214:        size_t                  len = 0;
1.1       nicm      215:
1.7       deraadt   216:        while ((ch = cmd_string_getc(s, p)) != endch) {
                    217:                switch (ch) {
1.1       nicm      218:                case EOF:
                    219:                        goto error;
1.7       deraadt   220:                case '\\':
1.1       nicm      221:                        if (!esc)
                    222:                                break;
1.7       deraadt   223:                        switch (ch = cmd_string_getc(s, p)) {
1.1       nicm      224:                        case EOF:
                    225:                                goto error;
1.5       nicm      226:                        case 'e':
                    227:                                ch = '\033';
                    228:                                break;
1.7       deraadt   229:                        case 'r':
                    230:                                ch = '\r';
                    231:                                break;
                    232:                        case 'n':
                    233:                                ch = '\n';
                    234:                                break;
                    235:                        case 't':
                    236:                                ch = '\t';
                    237:                                break;
1.30    ! nicm      238:                        case 'u':
        !           239:                        case 'U':
        !           240:                                if (cmd_string_unicode(&wc, s, p, ch) != 0)
        !           241:                                        goto error;
        !           242:                                if (utf8_split(wc, &ud) != UTF8_DONE)
        !           243:                                        goto error;
        !           244:                                if (len >= SIZE_MAX - ud.size - 1)
        !           245:                                        goto error;
        !           246:                                buf = xrealloc(buf, len + ud.size);
        !           247:                                memcpy(buf + len, ud.data, ud.size);
        !           248:                                len += ud.size;
        !           249:                                continue;
1.7       deraadt   250:                        }
                    251:                        break;
1.1       nicm      252:                case '$':
                    253:                        if (!esc)
                    254:                                break;
                    255:                        if ((t = cmd_string_variable(s, p)) == NULL)
                    256:                                goto error;
1.17      nicm      257:                        cmd_string_copy(&buf, t, &len);
1.1       nicm      258:                        continue;
1.7       deraadt   259:                }
1.1       nicm      260:
                    261:                if (len >= SIZE_MAX - 2)
                    262:                        goto error;
1.20      nicm      263:                buf = xrealloc(buf, len + 1);
1.7       deraadt   264:                buf[len++] = ch;
                    265:        }
1.1       nicm      266:
1.20      nicm      267:        buf = xrealloc(buf, len + 1);
1.1       nicm      268:        buf[len] = '\0';
                    269:        return (buf);
                    270:
                    271: error:
1.16      nicm      272:        free(buf);
1.1       nicm      273:        return (NULL);
                    274: }
                    275:
1.23      nicm      276: static char *
1.1       nicm      277: cmd_string_variable(const char *s, size_t *p)
                    278: {
1.15      nicm      279:        int                     ch, fch;
                    280:        char                   *buf, *t;
                    281:        size_t                  len;
                    282:        struct environ_entry   *envent;
1.1       nicm      283:
                    284: #define cmd_string_first(ch) ((ch) == '_' || \
                    285:        ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
                    286: #define cmd_string_other(ch) ((ch) == '_' || \
                    287:        ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
                    288:        ((ch) >= '0' && (ch) <= '9'))
                    289:
1.7       deraadt   290:        buf = NULL;
1.1       nicm      291:        len = 0;
                    292:
                    293:        fch = EOF;
                    294:        switch (ch = cmd_string_getc(s, p)) {
                    295:        case EOF:
                    296:                goto error;
                    297:        case '{':
                    298:                fch = '{';
                    299:
                    300:                ch = cmd_string_getc(s, p);
                    301:                if (!cmd_string_first(ch))
                    302:                        goto error;
                    303:                /* FALLTHROUGH */
                    304:        default:
                    305:                if (!cmd_string_first(ch)) {
                    306:                        xasprintf(&t, "$%c", ch);
                    307:                        return (t);
                    308:                }
                    309:
1.20      nicm      310:                buf = xrealloc(buf, len + 1);
1.1       nicm      311:                buf[len++] = ch;
                    312:
                    313:                for (;;) {
                    314:                        ch = cmd_string_getc(s, p);
                    315:                        if (ch == EOF || !cmd_string_other(ch))
                    316:                                break;
                    317:                        else {
                    318:                                if (len >= SIZE_MAX - 3)
                    319:                                        goto error;
1.20      nicm      320:                                buf = xrealloc(buf, len + 1);
1.1       nicm      321:                                buf[len++] = ch;
                    322:                        }
                    323:                }
                    324:        }
                    325:
                    326:        if (fch == '{' && ch != '}')
                    327:                goto error;
                    328:        if (ch != EOF && fch != '{')
1.11      nicm      329:                cmd_string_ungetc(p); /* ch */
1.1       nicm      330:
1.20      nicm      331:        buf = xrealloc(buf, len + 1);
1.1       nicm      332:        buf[len] = '\0';
                    333:
1.21      nicm      334:        envent = environ_find(global_environ, buf);
1.16      nicm      335:        free(buf);
1.15      nicm      336:        if (envent == NULL)
1.1       nicm      337:                return (xstrdup(""));
1.15      nicm      338:        return (xstrdup(envent->value));
1.1       nicm      339:
                    340: error:
1.16      nicm      341:        free(buf);
1.1       nicm      342:        return (NULL);
1.4       nicm      343: }
                    344:
1.23      nicm      345: static char *
1.4       nicm      346: cmd_string_expand_tilde(const char *s, size_t *p)
                    347: {
1.15      nicm      348:        struct passwd           *pw;
                    349:        struct environ_entry    *envent;
1.19      nicm      350:        char                    *home, *path, *user, *cp;
                    351:        int                      last;
1.4       nicm      352:
                    353:        home = NULL;
1.19      nicm      354:
                    355:        last = cmd_string_getc(s, p);
                    356:        if (last == EOF || last == '/' || last == ' '|| last == '\t') {
1.21      nicm      357:                envent = environ_find(global_environ, "HOME");
1.15      nicm      358:                if (envent != NULL && *envent->value != '\0')
                    359:                        home = envent->value;
                    360:                else if ((pw = getpwuid(getuid())) != NULL)
                    361:                        home = pw->pw_dir;
1.4       nicm      362:        } else {
1.11      nicm      363:                cmd_string_ungetc(p);
1.19      nicm      364:
                    365:                cp = user = xmalloc(strlen(s));
                    366:                for (;;) {
                    367:                        last = cmd_string_getc(s, p);
1.23      nicm      368:                        if (last == EOF ||
                    369:                            last == '/' ||
                    370:                            last == ' '||
                    371:                            last == '\t')
1.19      nicm      372:                                break;
                    373:                        *cp++ = last;
                    374:                }
                    375:                *cp = '\0';
                    376:
                    377:                if ((pw = getpwnam(user)) != NULL)
1.4       nicm      378:                        home = pw->pw_dir;
1.19      nicm      379:                free(user);
1.4       nicm      380:        }
1.19      nicm      381:
1.4       nicm      382:        if (home == NULL)
                    383:                return (NULL);
                    384:
1.19      nicm      385:        if (last != EOF)
                    386:                xasprintf(&path, "%s%c", home, last);
                    387:        else
                    388:                xasprintf(&path, "%s", home);
1.4       nicm      389:        return (path);
1.1       nicm      390: }