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

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