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

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