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

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