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

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