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

Annotation of src/usr.bin/tmux/cmd-parse.y, Revision 1.8

1.8     ! nicm        1: /* $OpenBSD: cmd-parse.y,v 1.7 2019/05/29 10:08:36 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2019 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: %{
                     20:
                     21: #include <sys/types.h>
                     22:
                     23: #include <ctype.h>
                     24: #include <errno.h>
                     25: #include <pwd.h>
                     26: #include <string.h>
                     27: #include <unistd.h>
                     28:
                     29: #include "tmux.h"
                     30:
                     31: static int                      yylex(void);
                     32: static int                      yyparse(void);
                     33: static int printflike(1,2)      yyerror(const char *, ...);
                     34:
                     35: static char                    *yylex_token(int);
                     36: static char                    *yylex_format(void);
                     37:
                     38: struct cmd_parse_scope {
                     39:        int                              flag;
                     40:        TAILQ_ENTRY (cmd_parse_scope)    entry;
                     41: };
                     42:
                     43: struct cmd_parse_command {
                     44:        char                             *name;
                     45:        u_int                             line;
                     46:
                     47:        int                               argc;
                     48:        char                            **argv;
                     49:
                     50:        TAILQ_ENTRY(cmd_parse_command)    entry;
                     51: };
                     52: TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
                     53:
                     54: struct cmd_parse_state {
                     55:        FILE                            *f;
1.5       nicm       56:
                     57:        const char                      *buf;
                     58:        size_t                           len;
                     59:        size_t                           off;
                     60:
1.8     ! nicm       61:        int                              eol;
1.1       nicm       62:        int                              eof;
                     63:        struct cmd_parse_input          *input;
                     64:        u_int                            escapes;
                     65:
                     66:        char                            *error;
                     67:        struct cmd_parse_commands        commands;
                     68:
                     69:        struct cmd_parse_scope          *scope;
                     70:        TAILQ_HEAD(, cmd_parse_scope)    stack;
                     71: };
                     72: static struct cmd_parse_state parse_state;
                     73:
                     74: static char    *cmd_parse_get_error(const char *, u_int, const char *);
                     75: static void     cmd_parse_free_command(struct cmd_parse_command *);
                     76: static void     cmd_parse_free_commands(struct cmd_parse_commands *);
                     77:
                     78: %}
                     79:
                     80: %union
                     81: {
                     82:        char                                     *token;
                     83:        struct {
                     84:                int                               argc;
                     85:                char                            **argv;
                     86:        } arguments;
                     87:        int                                       flag;
                     88:        struct {
                     89:                int                               flag;
                     90:                struct cmd_parse_commands         commands;
                     91:        } elif;
                     92:        struct cmd_parse_commands                 commands;
                     93:        struct cmd_parse_command                 *command;
                     94: }
                     95:
                     96: %token ERROR
                     97: %token IF
                     98: %token ELSE
                     99: %token ELIF
                    100: %token ENDIF
                    101: %token <token> FORMAT TOKEN EQUALS
                    102:
                    103: %type <token> argument expanded
                    104: %type <arguments> arguments
                    105: %type <flag> if_open if_elif
                    106: %type <elif> elif elif1
                    107: %type <commands> statements statement commands condition condition1
                    108: %type <command> command
                    109:
                    110: %%
                    111:
                    112: lines          : /* empty */
                    113:                | statements
                    114:                {
                    115:                        struct cmd_parse_state  *ps = &parse_state;
                    116:
                    117:                        TAILQ_CONCAT(&ps->commands, &$1, entry);
                    118:                }
                    119:
                    120: statements     : statement '\n'
                    121:                {
                    122:                        TAILQ_INIT(&$$);
                    123:                        TAILQ_CONCAT(&$$, &$1, entry);
                    124:                }
                    125:                | statements statement '\n'
                    126:                {
                    127:                        TAILQ_INIT(&$$);
                    128:                        TAILQ_CONCAT(&$$, &$1, entry);
                    129:                        TAILQ_CONCAT(&$$, &$2, entry);
                    130:                }
                    131:
                    132:
                    133: statement      : condition
                    134:                {
                    135:                        struct cmd_parse_state  *ps = &parse_state;
                    136:
                    137:                        TAILQ_INIT(&$$);
                    138:                        if (ps->scope == NULL || ps->scope->flag)
                    139:                                TAILQ_CONCAT(&$$, &$1, entry);
                    140:                        else
                    141:                                cmd_parse_free_commands(&$1);
                    142:                }
                    143:                | assignment
                    144:                {
                    145:                        TAILQ_INIT(&$$);
                    146:                }
                    147:                | commands
                    148:                {
                    149:                        struct cmd_parse_state  *ps = &parse_state;
                    150:
                    151:                        TAILQ_INIT(&$$);
                    152:                        if (ps->scope == NULL || ps->scope->flag)
                    153:                                TAILQ_CONCAT(&$$, &$1, entry);
                    154:                        else
                    155:                                cmd_parse_free_commands(&$1);
                    156:                }
                    157:
                    158: expanded       : FORMAT
                    159:                {
                    160:                        struct cmd_parse_state  *ps = &parse_state;
                    161:                        struct cmd_parse_input  *pi = ps->input;
                    162:                        struct format_tree      *ft;
                    163:                        struct client           *c = pi->c;
                    164:                        struct cmd_find_state   *fs;
                    165:                        int                      flags = FORMAT_NOJOBS;
                    166:
                    167:                        if (cmd_find_valid_state(&pi->fs))
                    168:                                fs = &pi->fs;
                    169:                        else
                    170:                                fs = NULL;
                    171:                        ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
                    172:                        if (fs != NULL)
                    173:                                format_defaults(ft, c, fs->s, fs->wl, fs->wp);
                    174:                        else
                    175:                                format_defaults(ft, c, NULL, NULL, NULL);
                    176:
                    177:                        $$ = format_expand(ft, $1);
                    178:                        format_free(ft);
                    179:                        free($1);
                    180:                }
                    181:
                    182: assignment     : /* empty */
                    183:                | EQUALS
                    184:                {
                    185:                        struct cmd_parse_state  *ps = &parse_state;
                    186:                        int                      flags = ps->input->flags;
                    187:
                    188:                        if ((~flags & CMD_PARSE_PARSEONLY) &&
                    189:                            (ps->scope == NULL || ps->scope->flag))
                    190:                                environ_put(global_environ, $1);
                    191:                        free($1);
                    192:                }
                    193:
                    194: if_open                : IF expanded
                    195:                {
                    196:                        struct cmd_parse_state  *ps = &parse_state;
                    197:                        struct cmd_parse_scope  *scope;
                    198:
                    199:                        scope = xmalloc(sizeof *scope);
                    200:                        $$ = scope->flag = format_true($2);
                    201:                        free($2);
                    202:
                    203:                        if (ps->scope != NULL)
                    204:                                TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
                    205:                        ps->scope = scope;
                    206:                }
                    207:
                    208: if_else                : ELSE
                    209:                {
                    210:                        struct cmd_parse_state  *ps = &parse_state;
                    211:                        struct cmd_parse_scope  *scope;
                    212:
                    213:                        scope = xmalloc(sizeof *scope);
                    214:                        scope->flag = !ps->scope->flag;
                    215:
                    216:                        free(ps->scope);
                    217:                        ps->scope = scope;
                    218:                }
                    219:
                    220: if_elif                : ELIF expanded
                    221:                {
                    222:                        struct cmd_parse_state  *ps = &parse_state;
                    223:                        struct cmd_parse_scope  *scope;
                    224:
                    225:                        scope = xmalloc(sizeof *scope);
                    226:                        $$ = scope->flag = format_true($2);
                    227:                        free($2);
                    228:
                    229:                        free(ps->scope);
                    230:                        ps->scope = scope;
                    231:                }
                    232:
                    233: if_close       : ENDIF
                    234:                {
                    235:                        struct cmd_parse_state  *ps = &parse_state;
                    236:
                    237:                        free(ps->scope);
                    238:                        ps->scope = TAILQ_FIRST(&ps->stack);
                    239:                        if (ps->scope != NULL)
                    240:                                TAILQ_REMOVE(&ps->stack, ps->scope, entry);
                    241:                }
                    242:
                    243: condition      : if_open '\n' statements if_close
                    244:                {
                    245:                        TAILQ_INIT(&$$);
                    246:                        if ($1)
                    247:                                TAILQ_CONCAT(&$$, &$3, entry);
                    248:                        else
                    249:                                cmd_parse_free_commands(&$3);
                    250:                }
                    251:                | if_open '\n' statements if_else '\n' statements if_close
                    252:                {
                    253:                        TAILQ_INIT(&$$);
                    254:                        if ($1) {
                    255:                                TAILQ_CONCAT(&$$, &$3, entry);
                    256:                                cmd_parse_free_commands(&$6);
                    257:                        } else {
                    258:                                TAILQ_CONCAT(&$$, &$6, entry);
                    259:                                cmd_parse_free_commands(&$3);
                    260:                        }
                    261:                }
                    262:                | if_open '\n' statements elif if_close
                    263:                {
                    264:                        TAILQ_INIT(&$$);
                    265:                        if ($1) {
                    266:                                TAILQ_CONCAT(&$$, &$3, entry);
                    267:                                cmd_parse_free_commands(&$4.commands);
                    268:                        } else if ($4.flag) {
                    269:                                TAILQ_CONCAT(&$$, &$4.commands, entry);
                    270:                                cmd_parse_free_commands(&$3);
                    271:                        } else {
                    272:                                cmd_parse_free_commands(&$3);
                    273:                                cmd_parse_free_commands(&$4.commands);
                    274:                        }
                    275:                }
                    276:                | if_open '\n' statements elif if_else '\n' statements if_close
                    277:                {
                    278:                        TAILQ_INIT(&$$);
                    279:                        if ($1) {
                    280:                                TAILQ_CONCAT(&$$, &$3, entry);
                    281:                                cmd_parse_free_commands(&$4.commands);
                    282:                                cmd_parse_free_commands(&$7);
                    283:                        } else if ($4.flag) {
                    284:                                TAILQ_CONCAT(&$$, &$4.commands, entry);
                    285:                                cmd_parse_free_commands(&$3);
                    286:                                cmd_parse_free_commands(&$7);
                    287:                        } else {
                    288:                                TAILQ_CONCAT(&$$, &$7, entry);
                    289:                                cmd_parse_free_commands(&$3);
                    290:                                cmd_parse_free_commands(&$4.commands);
                    291:                        }
                    292:                }
                    293:
                    294: elif           : if_elif '\n' statements
                    295:                {
                    296:                        TAILQ_INIT(&$$.commands);
                    297:                        if ($1)
                    298:                                TAILQ_CONCAT(&$$.commands, &$3, entry);
                    299:                        else
                    300:                                cmd_parse_free_commands(&$3);
                    301:                        $$.flag = $1;
                    302:                }
                    303:                | if_elif '\n' statements elif
                    304:                {
                    305:                        TAILQ_INIT(&$$.commands);
                    306:                        if ($1) {
                    307:                                $$.flag = 1;
                    308:                                TAILQ_CONCAT(&$$.commands, &$3, entry);
                    309:                                cmd_parse_free_commands(&$4.commands);
                    310:                        } else {
                    311:                                $$.flag = $4.flag;
                    312:                                TAILQ_CONCAT(&$$.commands, &$4.commands, entry);
                    313:                                cmd_parse_free_commands(&$3);
                    314:                        }
                    315:                }
                    316:
                    317:
                    318: commands       : command
                    319:                {
                    320:                        struct cmd_parse_state  *ps = &parse_state;
                    321:
                    322:                        TAILQ_INIT(&$$);
                    323:                        if (ps->scope == NULL || ps->scope->flag)
                    324:                                TAILQ_INSERT_TAIL(&$$, $1, entry);
                    325:                        else
                    326:                                cmd_parse_free_command($1);
                    327:                }
                    328:                | commands ';'
                    329:                {
                    330:                        TAILQ_INIT(&$$);
                    331:                        TAILQ_CONCAT(&$$, &$1, entry);
                    332:                }
                    333:                | commands ';' condition1
                    334:                {
                    335:                        TAILQ_INIT(&$$);
                    336:                        TAILQ_CONCAT(&$$, &$1, entry);
                    337:                        TAILQ_CONCAT(&$$, &$3, entry);
                    338:                }
                    339:                | commands ';' command
                    340:                {
                    341:                        struct cmd_parse_state  *ps = &parse_state;
                    342:
                    343:                        TAILQ_INIT(&$$);
                    344:                        if (ps->scope == NULL || ps->scope->flag) {
                    345:                                TAILQ_CONCAT(&$$, &$1, entry);
                    346:                                TAILQ_INSERT_TAIL(&$$, $3, entry);
                    347:                        } else {
                    348:                                cmd_parse_free_commands(&$1);
                    349:                                cmd_parse_free_command($3);
                    350:                        }
                    351:                }
                    352:                | condition1
                    353:                {
                    354:                        TAILQ_INIT(&$$);
                    355:                        TAILQ_CONCAT(&$$, &$1, entry);
                    356:                }
                    357:
                    358: command                : assignment TOKEN
                    359:                {
                    360:                        struct cmd_parse_state  *ps = &parse_state;
                    361:
                    362:                        $$ = xcalloc(1, sizeof *$$);
                    363:                        $$->name = $2;
1.3       nicm      364:                        $$->line = ps->input->line - 1;
1.1       nicm      365:
                    366:                }
                    367:                | assignment TOKEN arguments
                    368:                {
                    369:                        struct cmd_parse_state  *ps = &parse_state;
                    370:
                    371:                        $$ = xcalloc(1, sizeof *$$);
                    372:                        $$->name = $2;
1.3       nicm      373:                        $$->line = ps->input->line - 1;
1.1       nicm      374:
                    375:                        $$->argc = $3.argc;
                    376:                        $$->argv = $3.argv;
                    377:                }
                    378:
                    379: condition1     : if_open commands if_close
                    380:                {
                    381:                        TAILQ_INIT(&$$);
                    382:                        if ($1)
                    383:                                TAILQ_CONCAT(&$$, &$2, entry);
                    384:                        else
                    385:                                cmd_parse_free_commands(&$2);
                    386:                }
                    387:                | if_open commands if_else commands if_close
                    388:                {
                    389:                        TAILQ_INIT(&$$);
                    390:                        if ($1) {
                    391:                                TAILQ_CONCAT(&$$, &$2, entry);
                    392:                                cmd_parse_free_commands(&$4);
                    393:                        } else {
                    394:                                TAILQ_CONCAT(&$$, &$4, entry);
                    395:                                cmd_parse_free_commands(&$2);
                    396:                        }
                    397:                }
                    398:                | if_open commands elif1 if_close
                    399:                {
                    400:                        TAILQ_INIT(&$$);
                    401:                        if ($1) {
                    402:                                TAILQ_CONCAT(&$$, &$2, entry);
                    403:                                cmd_parse_free_commands(&$3.commands);
                    404:                        } else if ($3.flag) {
                    405:                                TAILQ_CONCAT(&$$, &$3.commands, entry);
                    406:                                cmd_parse_free_commands(&$2);
                    407:                        } else {
                    408:                                cmd_parse_free_commands(&$2);
                    409:                                cmd_parse_free_commands(&$3.commands);
                    410:                        }
                    411:                }
                    412:                | if_open commands elif1 if_else commands if_close
                    413:                {
                    414:                        TAILQ_INIT(&$$);
                    415:                        if ($1) {
                    416:                                TAILQ_CONCAT(&$$, &$2, entry);
                    417:                                cmd_parse_free_commands(&$3.commands);
                    418:                                cmd_parse_free_commands(&$5);
                    419:                        } else if ($3.flag) {
                    420:                                TAILQ_CONCAT(&$$, &$3.commands, entry);
                    421:                                cmd_parse_free_commands(&$2);
                    422:                                cmd_parse_free_commands(&$5);
                    423:                        } else {
                    424:                                TAILQ_CONCAT(&$$, &$5, entry);
                    425:                                cmd_parse_free_commands(&$2);
                    426:                                cmd_parse_free_commands(&$3.commands);
                    427:                        }
                    428:
                    429:                }
                    430:
                    431: elif1          : if_elif commands
                    432:                {
                    433:                        TAILQ_INIT(&$$.commands);
                    434:                        if ($1)
                    435:                                TAILQ_CONCAT(&$$.commands, &$2, entry);
                    436:                        else
                    437:                                cmd_parse_free_commands(&$2);
                    438:                        $$.flag = $1;
                    439:                }
                    440:                | if_elif commands elif1
                    441:                {
                    442:                        TAILQ_INIT(&$$.commands);
                    443:                        if ($1) {
                    444:                                $$.flag = 1;
                    445:                                TAILQ_CONCAT(&$$.commands, &$2, entry);
                    446:                                cmd_parse_free_commands(&$3.commands);
                    447:                        } else {
                    448:                                $$.flag = $3.flag;
                    449:                                TAILQ_CONCAT(&$$.commands, &$3.commands, entry);
                    450:                                cmd_parse_free_commands(&$2);
                    451:                        }
                    452:                }
                    453:
                    454: arguments      : argument
                    455:                {
                    456:                        $$.argc = 1;
                    457:                        $$.argv = xreallocarray(NULL, 1, sizeof *$$.argv);
                    458:
                    459:                        $$.argv[0] = $1;
                    460:                }
                    461:                | argument arguments
                    462:                {
                    463:                        cmd_prepend_argv(&$2.argc, &$2.argv, $1);
                    464:                        free($1);
                    465:                        $$ = $2;
                    466:                }
                    467:
                    468: argument       : TOKEN
                    469:                {
                    470:                        $$ = $1;
                    471:                }
                    472:                | EQUALS
                    473:                {
                    474:                        $$ = $1;
                    475:                }
                    476:
                    477: %%
                    478:
                    479: static char *
                    480: cmd_parse_get_error(const char *file, u_int line, const char *error)
                    481: {
                    482:        char    *s;
                    483:
                    484:        if (file == NULL)
                    485:                s = xstrdup(error);
                    486:        else
                    487:                xasprintf (&s, "%s:%u: %s", file, line, error);
                    488:        return (s);
                    489: }
                    490:
                    491: static void
                    492: cmd_parse_free_command(struct cmd_parse_command *cmd)
                    493: {
                    494:        free(cmd->name);
                    495:        cmd_free_argv(cmd->argc, cmd->argv);
                    496:        free(cmd);
                    497: }
                    498:
                    499: static void
                    500: cmd_parse_free_commands(struct cmd_parse_commands *cmds)
                    501: {
                    502:        struct cmd_parse_command        *cmd, *cmd1;
                    503:
                    504:        TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
                    505:                TAILQ_REMOVE(cmds, cmd, entry);
                    506:                cmd_parse_free_command(cmd);
                    507:        }
                    508: }
                    509:
                    510: static struct cmd_parse_commands *
1.5       nicm      511: cmd_parse_run_parser(char **cause)
1.1       nicm      512: {
                    513:        struct cmd_parse_state          *ps = &parse_state;
                    514:        struct cmd_parse_commands       *cmds;
                    515:        struct cmd_parse_scope          *scope, *scope1;
                    516:        int                              retval;
                    517:
                    518:        TAILQ_INIT(&ps->commands);
                    519:        TAILQ_INIT(&ps->stack);
                    520:
                    521:        retval = yyparse();
                    522:        TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
                    523:                TAILQ_REMOVE(&ps->stack, scope, entry);
                    524:                free(scope);
                    525:        }
                    526:        if (retval != 0) {
                    527:                *cause = ps->error;
                    528:                return (NULL);
                    529:        }
                    530:
                    531:        cmds = xmalloc(sizeof *cmds);
                    532:        TAILQ_INIT(cmds);
                    533:        TAILQ_CONCAT(cmds, &ps->commands, entry);
                    534:        return (cmds);
                    535: }
                    536:
1.5       nicm      537: static struct cmd_parse_commands *
                    538: cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
                    539: {
                    540:        struct cmd_parse_state  *ps = &parse_state;
                    541:
                    542:        memset(ps, 0, sizeof *ps);
                    543:        ps->input = pi;
                    544:        ps->f = f;
                    545:        return (cmd_parse_run_parser(cause));
                    546: }
                    547:
                    548: static struct cmd_parse_commands *
                    549: cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
                    550:     char **cause)
                    551: {
                    552:        struct cmd_parse_state  *ps = &parse_state;
                    553:
                    554:        memset(ps, 0, sizeof *ps);
                    555:        ps->input = pi;
                    556:        ps->buf = buf;
                    557:        ps->len = len;
                    558:        return (cmd_parse_run_parser(cause));
                    559: }
                    560:
1.4       nicm      561: static struct cmd_parse_result *
                    562: cmd_parse_build_commands(struct cmd_parse_commands *cmds,
                    563:     struct cmd_parse_input *pi)
1.1       nicm      564: {
                    565:        static struct cmd_parse_result   pr;
1.4       nicm      566:        struct cmd_parse_commands       *cmds2;
1.1       nicm      567:        struct cmd_parse_command        *cmd, *cmd2, *next, *next2, *after;
                    568:        u_int                            line = UINT_MAX;
                    569:        int                              i;
                    570:        struct cmd_list                 *cmdlist = NULL, *result;
                    571:        struct cmd                      *add;
                    572:        char                            *alias, *cause, *s;
                    573:
1.4       nicm      574:        /* Check for an empty list. */
1.1       nicm      575:        if (TAILQ_EMPTY(cmds)) {
                    576:                free(cmds);
                    577:                pr.status = CMD_PARSE_EMPTY;
                    578:                return (&pr);
                    579:        }
                    580:
                    581:        /*
                    582:         * Walk the commands and expand any aliases. Each alias is parsed
                    583:         * individually to a new command list, any trailing arguments appended
                    584:         * to the last command, and all commands inserted into the original
                    585:         * command list.
                    586:         */
                    587:        TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
                    588:                alias = cmd_get_alias(cmd->name);
                    589:                if (alias == NULL)
                    590:                        continue;
                    591:
                    592:                line = cmd->line;
                    593:                log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias);
                    594:
                    595:                pi->line = line;
1.5       nicm      596:                cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
1.1       nicm      597:                free(alias);
                    598:                if (cmds2 == NULL) {
                    599:                        pr.status = CMD_PARSE_ERROR;
                    600:                        pr.error = cause;
                    601:                        goto out;
                    602:                }
                    603:
                    604:                cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands);
                    605:                if (cmd2 == NULL) {
                    606:                        TAILQ_REMOVE(cmds, cmd, entry);
                    607:                        cmd_parse_free_command(cmd);
                    608:                        continue;
                    609:                }
                    610:                for (i = 0; i < cmd->argc; i++)
                    611:                        cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
                    612:
                    613:                after = cmd;
                    614:                TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) {
                    615:                        cmd2->line = line;
                    616:                        TAILQ_REMOVE(cmds2, cmd2, entry);
                    617:                        TAILQ_INSERT_AFTER(cmds, after, cmd2, entry);
                    618:                        after = cmd2;
                    619:                }
                    620:                cmd_parse_free_commands(cmds2);
                    621:
                    622:                TAILQ_REMOVE(cmds, cmd, entry);
                    623:                cmd_parse_free_command(cmd);
                    624:        }
                    625:
                    626:        /*
                    627:         * Parse each command into a command list. Create a new command list
                    628:         * for each line so they get a new group (so the queue knows which ones
                    629:         * to remove if a command fails when executed).
                    630:         */
                    631:        result = cmd_list_new();
                    632:        TAILQ_FOREACH(cmd, cmds, entry) {
                    633:                log_debug("%s: %u %s", __func__, cmd->line, cmd->name);
                    634:                cmd_log_argv(cmd->argc, cmd->argv, __func__);
                    635:
                    636:                if (cmdlist == NULL || cmd->line != line) {
                    637:                        if (cmdlist != NULL) {
                    638:                                cmd_list_move(result, cmdlist);
                    639:                                cmd_list_free(cmdlist);
                    640:                        }
                    641:                        cmdlist = cmd_list_new();
                    642:                }
                    643:                line = cmd->line;
                    644:
                    645:                cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name);
                    646:                add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
                    647:                if (add == NULL) {
                    648:                        cmd_list_free(result);
                    649:                        pr.status = CMD_PARSE_ERROR;
                    650:                        pr.error = cmd_parse_get_error(pi->file, line, cause);
                    651:                        free(cause);
                    652:                        goto out;
                    653:                }
                    654:                cmd_list_append(cmdlist, add);
                    655:        }
                    656:        if (cmdlist != NULL) {
                    657:                cmd_list_move(result, cmdlist);
                    658:                cmd_list_free(cmdlist);
                    659:        }
                    660:
1.2       nicm      661:        s = cmd_list_print(result, 0);
1.1       nicm      662:        log_debug("%s: %s", __func__, s);
                    663:        free(s);
                    664:
                    665:        pr.status = CMD_PARSE_SUCCESS;
                    666:        pr.cmdlist = result;
                    667:
                    668: out:
                    669:        cmd_parse_free_commands(cmds);
                    670:        free(cmds);
                    671:
                    672:        return (&pr);
                    673: }
                    674:
                    675: struct cmd_parse_result *
1.4       nicm      676: cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
                    677: {
                    678:        static struct cmd_parse_result   pr;
                    679:        struct cmd_parse_input           input;
                    680:        struct cmd_parse_commands       *cmds;
                    681:        char                            *cause;
                    682:
                    683:        if (pi == NULL) {
                    684:                memset(&input, 0, sizeof input);
                    685:                pi = &input;
                    686:        }
                    687:        memset(&pr, 0, sizeof pr);
                    688:
1.5       nicm      689:        cmds = cmd_parse_do_file(f, pi, &cause);
1.4       nicm      690:        if (cmds == NULL) {
                    691:                pr.status = CMD_PARSE_ERROR;
                    692:                pr.error = cause;
                    693:                return (&pr);
                    694:        }
                    695:        return (cmd_parse_build_commands(cmds, pi));
                    696: }
                    697:
                    698: struct cmd_parse_result *
1.1       nicm      699: cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
                    700: {
                    701:        static struct cmd_parse_result   pr;
1.4       nicm      702:        struct cmd_parse_input           input;
1.5       nicm      703:        struct cmd_parse_commands       *cmds;
                    704:        char                            *cause;
1.1       nicm      705:
1.4       nicm      706:        if (pi == NULL) {
                    707:                memset(&input, 0, sizeof input);
                    708:                pi = &input;
                    709:        }
                    710:        memset(&pr, 0, sizeof pr);
                    711:
1.1       nicm      712:        if (*s == '\0') {
                    713:                pr.status = CMD_PARSE_EMPTY;
                    714:                pr.cmdlist = NULL;
                    715:                pr.error = NULL;
                    716:                return (&pr);
                    717:        }
                    718:
1.5       nicm      719:        cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause);
                    720:        if (cmds == NULL) {
1.1       nicm      721:                pr.status = CMD_PARSE_ERROR;
1.5       nicm      722:                pr.error = cause;
                    723:                return (&pr);
1.1       nicm      724:        }
1.5       nicm      725:        return (cmd_parse_build_commands(cmds, pi));
1.4       nicm      726: }
                    727:
                    728: struct cmd_parse_result *
                    729: cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
                    730: {
                    731:        struct cmd_parse_input            input;
                    732:        struct cmd_parse_commands        *cmds;
                    733:        struct cmd_parse_command         *cmd;
                    734:        char                            **copy, **new_argv;
                    735:        size_t                            size;
                    736:        int                               i, last, new_argc;
                    737:
                    738:        /*
                    739:         * The commands are already split up into arguments, so just separate
                    740:         * into a set of commands by ';'.
                    741:         */
                    742:
                    743:        if (pi == NULL) {
                    744:                memset(&input, 0, sizeof input);
                    745:                pi = &input;
                    746:        }
                    747:        cmd_log_argv(argc, argv, "%s", __func__);
                    748:
                    749:        cmds = xmalloc(sizeof *cmds);
                    750:        TAILQ_INIT(cmds);
                    751:        copy = cmd_copy_argv(argc, argv);
                    752:
                    753:        last = 0;
                    754:        for (i = 0; i < argc; i++) {
                    755:                size = strlen(copy[i]);
                    756:                if (size == 0 || copy[i][size - 1] != ';')
                    757:                        continue;
                    758:                copy[i][--size] = '\0';
                    759:                if (size > 0 && copy[i][size - 1] == '\\') {
                    760:                        copy[i][size - 1] = ';';
                    761:                        continue;
                    762:                }
                    763:
                    764:                new_argc = i - last;
                    765:                new_argv = copy + last;
                    766:                if (size != 0)
                    767:                        new_argc++;
                    768:
                    769:                if (new_argc != 0) {
                    770:                        cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
                    771:                            i);
                    772:
                    773:                        cmd = xcalloc(1, sizeof *cmd);
                    774:                        cmd->name = xstrdup(new_argv[0]);
                    775:                        cmd->line = pi->line;
                    776:
                    777:                        cmd->argc = new_argc - 1;
                    778:                        cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
                    779:
                    780:                        TAILQ_INSERT_TAIL(cmds, cmd, entry);
                    781:                }
                    782:
                    783:                last = i + 1;
                    784:        }
                    785:        if (last != argc) {
                    786:                new_argv = copy + last;
                    787:                new_argc = argc - last;
                    788:
                    789:                if (new_argc != 0) {
                    790:                        cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
                    791:                            last);
                    792:
                    793:                        cmd = xcalloc(1, sizeof *cmd);
                    794:                        cmd->name = xstrdup(new_argv[0]);
                    795:                        cmd->line = pi->line;
                    796:
                    797:                        cmd->argc = new_argc - 1;
                    798:                        cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
                    799:
                    800:                        TAILQ_INSERT_TAIL(cmds, cmd, entry);
                    801:                }
                    802:        }
                    803:        return (cmd_parse_build_commands(cmds, pi));
1.1       nicm      804: }
                    805:
                    806: static int printflike(1, 2)
                    807: yyerror(const char *fmt, ...)
                    808: {
                    809:        struct cmd_parse_state  *ps = &parse_state;
                    810:        struct cmd_parse_input  *pi = ps->input;
                    811:        va_list                  ap;
                    812:        char                    *error;
                    813:
                    814:        if (ps->error != NULL)
                    815:                return (0);
                    816:
                    817:        va_start(ap, fmt);
                    818:        xvasprintf(&error, fmt, ap);
                    819:        va_end(ap);
                    820:
                    821:        ps->error = cmd_parse_get_error(pi->file, pi->line, error);
                    822:        free(error);
                    823:        return (0);
                    824: }
                    825:
                    826: static int
                    827: yylex_is_var(char ch, int first)
                    828: {
                    829:        if (ch == '=')
                    830:                return (0);
                    831:        if (first && isdigit((u_char)ch))
                    832:                return (0);
                    833:        return (isalnum((u_char)ch) || ch == '_');
                    834: }
                    835:
                    836: static void
                    837: yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
                    838: {
                    839:        if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
                    840:                fatalx("buffer is too big");
                    841:        *buf = xrealloc(*buf, (*len) + 1 + addlen);
                    842:        memcpy((*buf) + *len, add, addlen);
                    843:        (*len) += addlen;
                    844: }
                    845:
                    846: static void
                    847: yylex_append1(char **buf, size_t *len, char add)
                    848: {
                    849:        yylex_append(buf, len, &add, 1);
                    850: }
                    851:
                    852: static int
1.5       nicm      853: yylex_getc1(void)
                    854: {
                    855:        struct cmd_parse_state  *ps = &parse_state;
                    856:        int                      ch;
                    857:
                    858:        if (ps->f != NULL)
                    859:                ch = getc(ps->f);
                    860:        else {
                    861:                if (ps->off == ps->len)
                    862:                        ch = EOF;
                    863:                else
                    864:                        ch = ps->buf[ps->off++];
                    865:        }
                    866:        return (ch);
                    867: }
                    868:
                    869: static void
                    870: yylex_ungetc(int ch)
                    871: {
                    872:        struct cmd_parse_state  *ps = &parse_state;
                    873:
                    874:        if (ps->f != NULL)
                    875:                ungetc(ch, ps->f);
                    876:        else if (ps->off > 0 && ch != EOF)
                    877:                ps->off--;
                    878: }
                    879:
                    880: static int
1.1       nicm      881: yylex_getc(void)
                    882: {
                    883:        struct cmd_parse_state  *ps = &parse_state;
                    884:        int                      ch;
                    885:
                    886:        if (ps->escapes != 0) {
                    887:                ps->escapes--;
                    888:                return ('\\');
                    889:        }
                    890:        for (;;) {
1.5       nicm      891:                ch = yylex_getc1();
1.1       nicm      892:                if (ch == '\\') {
                    893:                        ps->escapes++;
                    894:                        continue;
                    895:                }
                    896:                if (ch == '\n' && (ps->escapes % 2) == 1) {
                    897:                        ps->input->line++;
                    898:                        ps->escapes--;
                    899:                        continue;
                    900:                }
                    901:
                    902:                if (ps->escapes != 0) {
1.5       nicm      903:                        yylex_ungetc(ch);
1.1       nicm      904:                        ps->escapes--;
                    905:                        return ('\\');
                    906:                }
                    907:                return (ch);
                    908:        }
                    909: }
                    910:
                    911: static char *
                    912: yylex_get_word(int ch)
                    913: {
1.5       nicm      914:        char    *buf;
                    915:        size_t   len;
1.1       nicm      916:
                    917:        len = 0;
                    918:        buf = xmalloc(1);
                    919:
                    920:        do
                    921:                yylex_append1(&buf, &len, ch);
                    922:        while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
1.5       nicm      923:        yylex_ungetc(ch);
1.1       nicm      924:
                    925:        buf[len] = '\0';
                    926:        log_debug("%s: %s", __func__, buf);
                    927:        return (buf);
                    928: }
                    929:
                    930: static int
                    931: yylex(void)
                    932: {
                    933:        struct cmd_parse_state  *ps = &parse_state;
                    934:        char                    *token, *cp;
                    935:        int                      ch, next;
                    936:
1.8     ! nicm      937:        if (ps->eol)
        !           938:                ps->input->line++;
        !           939:        ps->eol = 0;
        !           940:
1.1       nicm      941:        for (;;) {
                    942:                ch = yylex_getc();
                    943:
                    944:                if (ch == EOF) {
                    945:                        /*
                    946:                         * Ensure every file or string is terminated by a
                    947:                         * newline. This keeps the parser simpler and avoids
                    948:                         * having to add a newline to each string.
                    949:                         */
                    950:                        if (ps->eof)
                    951:                                break;
                    952:                        ps->eof = 1;
                    953:                        return ('\n');
                    954:                }
                    955:
                    956:                if (ch == ' ' || ch == '\t') {
                    957:                        /*
                    958:                         * Ignore whitespace.
                    959:                         */
                    960:                        continue;
                    961:                }
                    962:
                    963:                if (ch == '\n') {
                    964:                        /*
                    965:                         * End of line. Update the line number.
                    966:                         */
1.8     ! nicm      967:                        ps->eol = 1;
1.1       nicm      968:                        return ('\n');
                    969:                }
                    970:
                    971:                if (ch == ';') {
                    972:                        /*
                    973:                         * A semicolon is itself.
                    974:                         */
                    975:                        return (';');
                    976:                }
                    977:
                    978:                if (ch == '#') {
                    979:                        /*
                    980:                         * #{ opens a format; anything else is a comment,
                    981:                         * ignore up to the end of the line.
                    982:                         */
                    983:                        next = yylex_getc();
                    984:                        if (next == '{') {
                    985:                                yylval.token = yylex_format();
                    986:                                if (yylval.token == NULL)
                    987:                                        return (ERROR);
                    988:                                return (FORMAT);
                    989:                        }
                    990:                        while (next != '\n' && next != EOF)
                    991:                                next = yylex_getc();
                    992:                        if (next == '\n') {
                    993:                                ps->input->line++;
                    994:                                return ('\n');
                    995:                        }
                    996:                        continue;
                    997:                }
                    998:
                    999:                if (ch == '%') {
                   1000:                        /*
                   1001:                         * % is a condition unless it is alone, then it is a
                   1002:                         * token.
                   1003:                         */
                   1004:                        yylval.token = yylex_get_word('%');
                   1005:                        if (strcmp(yylval.token, "%") == 0)
                   1006:                                return (TOKEN);
                   1007:                        if (strcmp(yylval.token, "%if") == 0) {
                   1008:                                free(yylval.token);
                   1009:                                return (IF);
                   1010:                        }
                   1011:                        if (strcmp(yylval.token, "%else") == 0) {
                   1012:                                free(yylval.token);
                   1013:                                return (ELSE);
                   1014:                        }
                   1015:                        if (strcmp(yylval.token, "%elif") == 0) {
                   1016:                                free(yylval.token);
                   1017:                                return (ELIF);
                   1018:                        }
                   1019:                        if (strcmp(yylval.token, "%endif") == 0) {
                   1020:                                free(yylval.token);
                   1021:                                return (ENDIF);
                   1022:                        }
                   1023:                        free(yylval.token);
                   1024:                        return (ERROR);
                   1025:                }
                   1026:
                   1027:                /*
                   1028:                 * Otherwise this is a token.
                   1029:                 */
                   1030:                token = yylex_token(ch);
                   1031:                if (token == NULL)
                   1032:                        return (ERROR);
                   1033:                yylval.token = token;
                   1034:
                   1035:                if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
                   1036:                        for (cp = token + 1; *cp != '='; cp++) {
                   1037:                                if (!yylex_is_var(*cp, 0))
                   1038:                                        break;
                   1039:                        }
                   1040:                        if (*cp == '=')
                   1041:                                return (EQUALS);
                   1042:                }
                   1043:                return (TOKEN);
                   1044:        }
                   1045:        return (0);
                   1046: }
                   1047:
                   1048: static char *
                   1049: yylex_format(void)
                   1050: {
                   1051:        char    *buf;
                   1052:        size_t   len;
                   1053:        int      ch, brackets = 1;
                   1054:
                   1055:        len = 0;
                   1056:        buf = xmalloc(1);
                   1057:
                   1058:        yylex_append(&buf, &len, "#{", 2);
                   1059:        for (;;) {
                   1060:                if ((ch = yylex_getc()) == EOF || ch == '\n')
                   1061:                        goto error;
                   1062:                if (ch == '#') {
                   1063:                        if ((ch = yylex_getc()) == EOF || ch == '\n')
                   1064:                                goto error;
                   1065:                        if (ch == '{')
                   1066:                                brackets++;
                   1067:                        yylex_append1(&buf, &len, '#');
                   1068:                } else if (ch == '}') {
                   1069:                        if (brackets != 0 && --brackets == 0) {
                   1070:                                yylex_append1(&buf, &len, ch);
                   1071:                                break;
                   1072:                        }
                   1073:                }
                   1074:                yylex_append1(&buf, &len, ch);
                   1075:        }
                   1076:        if (brackets != 0)
                   1077:                goto error;
                   1078:
                   1079:        buf[len] = '\0';
                   1080:        log_debug("%s: %s", __func__, buf);
                   1081:        return (buf);
                   1082:
                   1083: error:
                   1084:        free(buf);
                   1085:        return (NULL);
                   1086: }
                   1087:
                   1088: static int
                   1089: yylex_token_escape(char **buf, size_t *len)
                   1090: {
1.7       nicm     1091:        int                      ch, type, o2, o3;
1.1       nicm     1092:        u_int                    size, i, tmp;
                   1093:        char                     s[9];
                   1094:        struct utf8_data         ud;
                   1095:
1.7       nicm     1096:        ch = yylex_getc();
                   1097:
                   1098:        if (ch >= '4' && ch <= '7') {
                   1099:                yyerror("invalid octal escape");
                   1100:                return (0);
                   1101:        }
                   1102:        if (ch >= '0' && ch <= '3') {
                   1103:                o2 = yylex_getc();
                   1104:                if (o2 >= '0' && o2 <= '7') {
                   1105:                        o3 = yylex_getc();
                   1106:                        if (o3 >= '0' && o3 <= '7') {
                   1107:                                ch = 64 * (ch - '0') +
                   1108:                                      8 * (o2 - '0') +
                   1109:                                          (o3 - '0');
                   1110:                                yylex_append1(buf, len, ch);
                   1111:                                return (1);
                   1112:                        }
                   1113:                }
                   1114:                yyerror("invalid octal escape");
                   1115:                return (0);
                   1116:        }
                   1117:
                   1118:        switch (ch) {
1.1       nicm     1119:        case EOF:
                   1120:                return (0);
                   1121:        case 'e':
                   1122:                ch = '\033';
                   1123:                break;
                   1124:        case 'r':
                   1125:                ch = '\r';
                   1126:                break;
                   1127:        case 'n':
                   1128:                ch = '\n';
                   1129:                break;
                   1130:        case 't':
                   1131:                ch = '\t';
                   1132:                break;
                   1133:        case 'u':
                   1134:                type = 'u';
                   1135:                size = 4;
                   1136:                goto unicode;
                   1137:        case 'U':
                   1138:                type = 'U';
                   1139:                size = 8;
                   1140:                goto unicode;
                   1141:        }
                   1142:
                   1143:        yylex_append1(buf, len, ch);
                   1144:        return (1);
                   1145:
                   1146: unicode:
                   1147:        for (i = 0; i < size; i++) {
                   1148:                ch = yylex_getc();
                   1149:                if (ch == EOF || ch == '\n')
                   1150:                        return (0);
                   1151:                if (!isxdigit((u_char)ch)) {
                   1152:                        yyerror("invalid \\%c argument", type);
                   1153:                        return (0);
                   1154:                }
                   1155:                s[i] = ch;
                   1156:        }
                   1157:        s[i] = '\0';
                   1158:
                   1159:        if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
                   1160:            (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
                   1161:                yyerror("invalid \\%c argument", type);
                   1162:                return (0);
                   1163:        }
                   1164:        if (utf8_split(tmp, &ud) != UTF8_DONE) {
                   1165:                yyerror("invalid \\%c argument", type);
                   1166:                return (0);
                   1167:        }
                   1168:        yylex_append(buf, len, ud.data, ud.size);
                   1169:        return (1);
                   1170: }
                   1171:
                   1172: static int
                   1173: yylex_token_variable(char **buf, size_t *len)
                   1174: {
                   1175:        struct environ_entry    *envent;
                   1176:        int                      ch, brackets = 0;
                   1177:        char                     name[BUFSIZ];
                   1178:        size_t                   namelen = 0;
                   1179:        const char              *value;
                   1180:
                   1181:        ch = yylex_getc();
                   1182:        if (ch == EOF)
                   1183:                return (0);
                   1184:        if (ch == '{')
                   1185:                brackets = 1;
                   1186:        else {
                   1187:                if (!yylex_is_var(ch, 1)) {
                   1188:                        yylex_append1(buf, len, '$');
1.5       nicm     1189:                        yylex_ungetc(ch);
1.1       nicm     1190:                        return (1);
                   1191:                }
                   1192:                name[namelen++] = ch;
                   1193:        }
                   1194:
                   1195:        for (;;) {
                   1196:                ch = yylex_getc();
                   1197:                if (brackets && ch == '}')
                   1198:                        break;
                   1199:                if (ch == EOF || !yylex_is_var(ch, 0)) {
                   1200:                        if (!brackets) {
1.5       nicm     1201:                                yylex_ungetc(ch);
1.1       nicm     1202:                                break;
                   1203:                        }
                   1204:                        yyerror("invalid environment variable");
                   1205:                        return (0);
                   1206:                }
                   1207:                if (namelen == (sizeof name) - 2) {
                   1208:                        yyerror("environment variable is too long");
                   1209:                        return (0);
                   1210:                }
                   1211:                name[namelen++] = ch;
                   1212:        }
                   1213:        name[namelen] = '\0';
                   1214:
                   1215:        envent = environ_find(global_environ, name);
                   1216:        if (envent != NULL) {
                   1217:                value = envent->value;
                   1218:                log_debug("%s: %s -> %s", __func__, name, value);
                   1219:                yylex_append(buf, len, value, strlen(value));
                   1220:        }
                   1221:        return (1);
                   1222: }
                   1223:
                   1224: static int
                   1225: yylex_token_tilde(char **buf, size_t *len)
                   1226: {
                   1227:        struct environ_entry    *envent;
                   1228:        int                      ch;
                   1229:        char                     name[BUFSIZ];
                   1230:        size_t                   namelen = 0;
                   1231:        struct passwd           *pw;
                   1232:        const char              *home = NULL;
                   1233:
                   1234:        for (;;) {
                   1235:                ch = yylex_getc();
                   1236:                if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1.5       nicm     1237:                        yylex_ungetc(ch);
1.1       nicm     1238:                        break;
                   1239:                }
                   1240:                if (namelen == (sizeof name) - 2) {
                   1241:                        yyerror("user name is too long");
                   1242:                        return (0);
                   1243:                }
                   1244:                name[namelen++] = ch;
                   1245:        }
                   1246:        name[namelen] = '\0';
                   1247:
                   1248:        if (*name == '\0') {
                   1249:                envent = environ_find(global_environ, "HOME");
                   1250:                if (envent != NULL && *envent->value != '\0')
                   1251:                        home = envent->value;
                   1252:                else if ((pw = getpwuid(getuid())) != NULL)
                   1253:                        home = pw->pw_dir;
                   1254:        } else {
                   1255:                if ((pw = getpwnam(name)) != NULL)
                   1256:                        home = pw->pw_dir;
                   1257:        }
                   1258:        if (home == NULL)
                   1259:                return (0);
                   1260:
                   1261:        log_debug("%s: ~%s -> %s", __func__, name, home);
                   1262:        yylex_append(buf, len, home, strlen(home));
                   1263:        return (1);
                   1264: }
                   1265:
1.6       nicm     1266: static int
                   1267: yylex_token_brace(char **buf, size_t *len)
                   1268: {
                   1269:        struct cmd_parse_state  *ps = &parse_state;
                   1270:        int                      ch, nesting = 1, escape = 0, quote = '\0';
                   1271:        int                      lines = 0;
                   1272:
                   1273:        /*
                   1274:         * Extract a string up to the matching unquoted '}', including newlines
                   1275:         * and handling nested braces.
                   1276:         *
                   1277:         * To detect the final and intermediate braces which affect the nesting
                   1278:         * depth, we scan the input as if it was a tmux config file, and ignore
                   1279:         * braces which would be considered quoted, escaped, or in a comment.
                   1280:         *
                   1281:         * The result is verbatim copy of the input excluding the final brace.
                   1282:         */
                   1283:
                   1284:        for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) {
                   1285:                yylex_append1(buf, len, ch);
                   1286:                if (ch == '\n')
                   1287:                        lines++;
                   1288:
                   1289:                /*
                   1290:                 * If the previous character was a backslash (escape is set),
                   1291:                 * escape anything if unquoted or in double quotes, otherwise
                   1292:                 * escape only '\n' and '\\'.
                   1293:                 */
                   1294:                if (escape &&
                   1295:                    (quote == '\0' ||
                   1296:                    quote == '"' ||
                   1297:                    ch == '\n' ||
                   1298:                    ch == '\\')) {
                   1299:                        escape = 0;
                   1300:                        continue;
                   1301:                }
                   1302:
                   1303:                /*
                   1304:                 * The character is not escaped. If it is a backslash, set the
                   1305:                 * escape flag.
                   1306:                 */
                   1307:                if (ch == '\\') {
                   1308:                        escape = 1;
                   1309:                        continue;
                   1310:                }
                   1311:                escape = 0;
                   1312:
                   1313:                /* A newline always resets to unquoted. */
                   1314:                if (ch == '\n') {
                   1315:                        quote = 0;
                   1316:                        continue;
                   1317:                }
                   1318:
                   1319:                if (quote) {
                   1320:                        /*
                   1321:                         * Inside quotes or comment. Check if this is the
                   1322:                         * closing quote.
                   1323:                         */
                   1324:                        if (ch == quote && quote != '#')
                   1325:                                quote = 0;
                   1326:                } else  {
                   1327:                        /* Not inside quotes or comment. */
                   1328:                        switch (ch) {
                   1329:                        case '"':
                   1330:                        case '\'':
                   1331:                        case '#':
                   1332:                                /* Beginning of quote or comment. */
                   1333:                                quote = ch;
                   1334:                                break;
                   1335:                        case '{':
                   1336:                                nesting++;
                   1337:                                break;
                   1338:                        case '}':
                   1339:                                nesting--;
                   1340:                                if (nesting == 0) {
                   1341:                                        (*len)--; /* remove closing } */
                   1342:                                        ps->input->line += lines;
                   1343:                                        return (1);
                   1344:                                }
                   1345:                                break;
                   1346:                        }
                   1347:                }
                   1348:        }
                   1349:
                   1350:        /*
                   1351:         * Update line count after error as reporting the opening line
                   1352:         * is more useful than EOF.
                   1353:         */
                   1354:        yyerror("unterminated brace string");
                   1355:        ps->input->line += lines;
                   1356:        return (0);
                   1357: }
                   1358:
1.1       nicm     1359: static char *
                   1360: yylex_token(int ch)
                   1361: {
                   1362:        char                    *buf;
                   1363:        size_t                   len;
                   1364:        enum { START,
                   1365:               NONE,
                   1366:               DOUBLE_QUOTES,
                   1367:               SINGLE_QUOTES }   state = NONE, last = START;
                   1368:
                   1369:        len = 0;
                   1370:        buf = xmalloc(1);
                   1371:
                   1372:        for (;;) {
                   1373:                /*
                   1374:                 * EOF or \n are always the end of the token. If inside quotes
                   1375:                 * they are an error.
                   1376:                 */
                   1377:                if (ch == EOF || ch == '\n') {
                   1378:                        if (state != NONE)
                   1379:                                goto error;
                   1380:                        break;
                   1381:                }
                   1382:
                   1383:                /* Whitespace or ; ends a token unless inside quotes. */
                   1384:                if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE)
                   1385:                        break;
                   1386:
                   1387:                /*
                   1388:                 * \ ~ and $ are expanded except in single quotes.
                   1389:                 */
                   1390:                if (ch == '\\' && state != SINGLE_QUOTES) {
                   1391:                        if (!yylex_token_escape(&buf, &len))
                   1392:                                goto error;
                   1393:                        goto skip;
                   1394:                }
                   1395:                if (ch == '~' && last != state && state != SINGLE_QUOTES) {
                   1396:                        if (!yylex_token_tilde(&buf, &len))
                   1397:                                goto error;
                   1398:                        goto skip;
                   1399:                }
                   1400:                if (ch == '$' && state != SINGLE_QUOTES) {
                   1401:                        if (!yylex_token_variable(&buf, &len))
                   1402:                                goto error;
                   1403:                        goto skip;
                   1404:                }
1.6       nicm     1405:                if (ch == '{' && state == NONE) {
                   1406:                        if (!yylex_token_brace(&buf, &len))
                   1407:                                goto error;
                   1408:                        goto skip;
                   1409:                }
                   1410:                if (ch == '}' && state == NONE)
                   1411:                        goto error;  /* unmatched (matched ones were handled) */
1.1       nicm     1412:
                   1413:                /*
                   1414:                 * ' and " starts or end quotes (and is consumed).
                   1415:                 */
                   1416:                if (ch == '\'') {
                   1417:                        if (state == NONE) {
                   1418:                                state = SINGLE_QUOTES;
                   1419:                                goto next;
                   1420:                        }
                   1421:                        if (state == SINGLE_QUOTES) {
                   1422:                                state = NONE;
                   1423:                                goto next;
                   1424:                        }
                   1425:                }
                   1426:                if (ch == '"') {
                   1427:                        if (state == NONE) {
                   1428:                                state = DOUBLE_QUOTES;
                   1429:                                goto next;
                   1430:                        }
                   1431:                        if (state == DOUBLE_QUOTES) {
                   1432:                                state = NONE;
                   1433:                                goto next;
                   1434:                        }
                   1435:                }
                   1436:
                   1437:                /*
                   1438:                 * Otherwise add the character to the buffer.
                   1439:                 */
                   1440:                yylex_append1(&buf, &len, ch);
                   1441:
                   1442:        skip:
                   1443:                last = state;
                   1444:
                   1445:        next:
                   1446:                ch = yylex_getc();
                   1447:        }
1.5       nicm     1448:        yylex_ungetc(ch);
1.1       nicm     1449:
                   1450:        buf[len] = '\0';
                   1451:        log_debug("%s: %s", __func__, buf);
                   1452:        return (buf);
                   1453:
                   1454: error:
                   1455:        free(buf);
                   1456:        return (NULL);
                   1457: }