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

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