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

1.3     ! nicm        1: /* $OpenBSD: cmd-parse.y,v 1.2 2019/05/23 14:03:44 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>
                     26: #include <string.h>
                     27: #include <unistd.h>
                     28:
                     29: #include "tmux.h"
                     30:
                     31: static int                      yylex(void);
                     32: static int                      yyparse(void);
                     33: static int printflike(1,2)      yyerror(const char *, ...);
                     34:
                     35: static char                    *yylex_token(int);
                     36: static char                    *yylex_format(void);
                     37:
                     38: struct cmd_parse_scope {
                     39:        int                              flag;
                     40:        TAILQ_ENTRY (cmd_parse_scope)    entry;
                     41: };
                     42:
                     43: struct cmd_parse_command {
                     44:        char                             *name;
                     45:        u_int                             line;
                     46:
                     47:        int                               argc;
                     48:        char                            **argv;
                     49:
                     50:        TAILQ_ENTRY(cmd_parse_command)    entry;
                     51: };
                     52: TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
                     53:
                     54: struct cmd_parse_state {
                     55:        FILE                            *f;
                     56:        int                              eof;
                     57:        struct cmd_parse_input          *input;
                     58:        u_int                            escapes;
                     59:
                     60:        char                            *error;
                     61:        struct cmd_parse_commands        commands;
                     62:
                     63:        struct cmd_parse_scope          *scope;
                     64:        TAILQ_HEAD(, cmd_parse_scope)    stack;
                     65: };
                     66: static struct cmd_parse_state parse_state;
                     67:
                     68: static char    *cmd_parse_get_error(const char *, u_int, const char *);
                     69: static char    *cmd_parse_get_strerror(const char *, u_int);
                     70: static void     cmd_parse_free_command(struct cmd_parse_command *);
                     71: static void     cmd_parse_free_commands(struct cmd_parse_commands *);
                     72:
                     73: %}
                     74:
                     75: %union
                     76: {
                     77:        char                                     *token;
                     78:        struct {
                     79:                int                               argc;
                     80:                char                            **argv;
                     81:        } arguments;
                     82:        int                                       flag;
                     83:        struct {
                     84:                int                               flag;
                     85:                struct cmd_parse_commands         commands;
                     86:        } elif;
                     87:        struct cmd_parse_commands                 commands;
                     88:        struct cmd_parse_command                 *command;
                     89: }
                     90:
                     91: %token ERROR
                     92: %token IF
                     93: %token ELSE
                     94: %token ELIF
                     95: %token ENDIF
                     96: %token <token> FORMAT TOKEN EQUALS
                     97:
                     98: %type <token> argument expanded
                     99: %type <arguments> arguments
                    100: %type <flag> if_open if_elif
                    101: %type <elif> elif elif1
                    102: %type <commands> statements statement commands condition condition1
                    103: %type <command> command
                    104:
                    105: %%
                    106:
                    107: lines          : /* empty */
                    108:                | statements
                    109:                {
                    110:                        struct cmd_parse_state  *ps = &parse_state;
                    111:
                    112:                        TAILQ_CONCAT(&ps->commands, &$1, entry);
                    113:                }
                    114:
                    115: statements     : statement '\n'
                    116:                {
                    117:                        TAILQ_INIT(&$$);
                    118:                        TAILQ_CONCAT(&$$, &$1, entry);
                    119:                }
                    120:                | statements statement '\n'
                    121:                {
                    122:                        TAILQ_INIT(&$$);
                    123:                        TAILQ_CONCAT(&$$, &$1, entry);
                    124:                        TAILQ_CONCAT(&$$, &$2, entry);
                    125:                }
                    126:
                    127:
                    128: statement      : condition
                    129:                {
                    130:                        struct cmd_parse_state  *ps = &parse_state;
                    131:
                    132:                        TAILQ_INIT(&$$);
                    133:                        if (ps->scope == NULL || ps->scope->flag)
                    134:                                TAILQ_CONCAT(&$$, &$1, entry);
                    135:                        else
                    136:                                cmd_parse_free_commands(&$1);
                    137:                }
                    138:                | assignment
                    139:                {
                    140:                        TAILQ_INIT(&$$);
                    141:                }
                    142:                | commands
                    143:                {
                    144:                        struct cmd_parse_state  *ps = &parse_state;
                    145:
                    146:                        TAILQ_INIT(&$$);
                    147:                        if (ps->scope == NULL || ps->scope->flag)
                    148:                                TAILQ_CONCAT(&$$, &$1, entry);
                    149:                        else
                    150:                                cmd_parse_free_commands(&$1);
                    151:                }
                    152:
                    153: expanded       : FORMAT
                    154:                {
                    155:                        struct cmd_parse_state  *ps = &parse_state;
                    156:                        struct cmd_parse_input  *pi = ps->input;
                    157:                        struct format_tree      *ft;
                    158:                        struct client           *c = pi->c;
                    159:                        struct cmd_find_state   *fs;
                    160:                        int                      flags = FORMAT_NOJOBS;
                    161:
                    162:                        if (cmd_find_valid_state(&pi->fs))
                    163:                                fs = &pi->fs;
                    164:                        else
                    165:                                fs = NULL;
                    166:                        ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
                    167:                        if (fs != NULL)
                    168:                                format_defaults(ft, c, fs->s, fs->wl, fs->wp);
                    169:                        else
                    170:                                format_defaults(ft, c, NULL, NULL, NULL);
                    171:
                    172:                        $$ = format_expand(ft, $1);
                    173:                        format_free(ft);
                    174:                        free($1);
                    175:                }
                    176:
                    177: assignment     : /* empty */
                    178:                | EQUALS
                    179:                {
                    180:                        struct cmd_parse_state  *ps = &parse_state;
                    181:                        int                      flags = ps->input->flags;
                    182:
                    183:                        if ((~flags & CMD_PARSE_PARSEONLY) &&
                    184:                            (ps->scope == NULL || ps->scope->flag))
                    185:                                environ_put(global_environ, $1);
                    186:                        free($1);
                    187:                }
                    188:
                    189: if_open                : IF expanded
                    190:                {
                    191:                        struct cmd_parse_state  *ps = &parse_state;
                    192:                        struct cmd_parse_scope  *scope;
                    193:
                    194:                        scope = xmalloc(sizeof *scope);
                    195:                        $$ = scope->flag = format_true($2);
                    196:                        free($2);
                    197:
                    198:                        if (ps->scope != NULL)
                    199:                                TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
                    200:                        ps->scope = scope;
                    201:                }
                    202:
                    203: if_else                : ELSE
                    204:                {
                    205:                        struct cmd_parse_state  *ps = &parse_state;
                    206:                        struct cmd_parse_scope  *scope;
                    207:
                    208:                        scope = xmalloc(sizeof *scope);
                    209:                        scope->flag = !ps->scope->flag;
                    210:
                    211:                        free(ps->scope);
                    212:                        ps->scope = scope;
                    213:                }
                    214:
                    215: if_elif                : ELIF expanded
                    216:                {
                    217:                        struct cmd_parse_state  *ps = &parse_state;
                    218:                        struct cmd_parse_scope  *scope;
                    219:
                    220:                        scope = xmalloc(sizeof *scope);
                    221:                        $$ = scope->flag = format_true($2);
                    222:                        free($2);
                    223:
                    224:                        free(ps->scope);
                    225:                        ps->scope = scope;
                    226:                }
                    227:
                    228: if_close       : ENDIF
                    229:                {
                    230:                        struct cmd_parse_state  *ps = &parse_state;
                    231:
                    232:                        free(ps->scope);
                    233:                        ps->scope = TAILQ_FIRST(&ps->stack);
                    234:                        if (ps->scope != NULL)
                    235:                                TAILQ_REMOVE(&ps->stack, ps->scope, entry);
                    236:                }
                    237:
                    238: condition      : if_open '\n' statements if_close
                    239:                {
                    240:                        TAILQ_INIT(&$$);
                    241:                        if ($1)
                    242:                                TAILQ_CONCAT(&$$, &$3, entry);
                    243:                        else
                    244:                                cmd_parse_free_commands(&$3);
                    245:                }
                    246:                | if_open '\n' statements if_else '\n' statements if_close
                    247:                {
                    248:                        TAILQ_INIT(&$$);
                    249:                        if ($1) {
                    250:                                TAILQ_CONCAT(&$$, &$3, entry);
                    251:                                cmd_parse_free_commands(&$6);
                    252:                        } else {
                    253:                                TAILQ_CONCAT(&$$, &$6, entry);
                    254:                                cmd_parse_free_commands(&$3);
                    255:                        }
                    256:                }
                    257:                | if_open '\n' statements elif if_close
                    258:                {
                    259:                        TAILQ_INIT(&$$);
                    260:                        if ($1) {
                    261:                                TAILQ_CONCAT(&$$, &$3, entry);
                    262:                                cmd_parse_free_commands(&$4.commands);
                    263:                        } else if ($4.flag) {
                    264:                                TAILQ_CONCAT(&$$, &$4.commands, entry);
                    265:                                cmd_parse_free_commands(&$3);
                    266:                        } else {
                    267:                                cmd_parse_free_commands(&$3);
                    268:                                cmd_parse_free_commands(&$4.commands);
                    269:                        }
                    270:                }
                    271:                | if_open '\n' statements elif if_else '\n' statements if_close
                    272:                {
                    273:                        TAILQ_INIT(&$$);
                    274:                        if ($1) {
                    275:                                TAILQ_CONCAT(&$$, &$3, entry);
                    276:                                cmd_parse_free_commands(&$4.commands);
                    277:                                cmd_parse_free_commands(&$7);
                    278:                        } else if ($4.flag) {
                    279:                                TAILQ_CONCAT(&$$, &$4.commands, entry);
                    280:                                cmd_parse_free_commands(&$3);
                    281:                                cmd_parse_free_commands(&$7);
                    282:                        } else {
                    283:                                TAILQ_CONCAT(&$$, &$7, entry);
                    284:                                cmd_parse_free_commands(&$3);
                    285:                                cmd_parse_free_commands(&$4.commands);
                    286:                        }
                    287:                }
                    288:
                    289: elif           : if_elif '\n' statements
                    290:                {
                    291:                        TAILQ_INIT(&$$.commands);
                    292:                        if ($1)
                    293:                                TAILQ_CONCAT(&$$.commands, &$3, entry);
                    294:                        else
                    295:                                cmd_parse_free_commands(&$3);
                    296:                        $$.flag = $1;
                    297:                }
                    298:                | if_elif '\n' statements elif
                    299:                {
                    300:                        TAILQ_INIT(&$$.commands);
                    301:                        if ($1) {
                    302:                                $$.flag = 1;
                    303:                                TAILQ_CONCAT(&$$.commands, &$3, entry);
                    304:                                cmd_parse_free_commands(&$4.commands);
                    305:                        } else {
                    306:                                $$.flag = $4.flag;
                    307:                                TAILQ_CONCAT(&$$.commands, &$4.commands, entry);
                    308:                                cmd_parse_free_commands(&$3);
                    309:                        }
                    310:                }
                    311:
                    312:
                    313: commands       : command
                    314:                {
                    315:                        struct cmd_parse_state  *ps = &parse_state;
                    316:
                    317:                        TAILQ_INIT(&$$);
                    318:                        if (ps->scope == NULL || ps->scope->flag)
                    319:                                TAILQ_INSERT_TAIL(&$$, $1, entry);
                    320:                        else
                    321:                                cmd_parse_free_command($1);
                    322:                }
                    323:                | commands ';'
                    324:                {
                    325:                        TAILQ_INIT(&$$);
                    326:                        TAILQ_CONCAT(&$$, &$1, entry);
                    327:                }
                    328:                | commands ';' condition1
                    329:                {
                    330:                        TAILQ_INIT(&$$);
                    331:                        TAILQ_CONCAT(&$$, &$1, entry);
                    332:                        TAILQ_CONCAT(&$$, &$3, entry);
                    333:                }
                    334:                | commands ';' command
                    335:                {
                    336:                        struct cmd_parse_state  *ps = &parse_state;
                    337:
                    338:                        TAILQ_INIT(&$$);
                    339:                        if (ps->scope == NULL || ps->scope->flag) {
                    340:                                TAILQ_CONCAT(&$$, &$1, entry);
                    341:                                TAILQ_INSERT_TAIL(&$$, $3, entry);
                    342:                        } else {
                    343:                                cmd_parse_free_commands(&$1);
                    344:                                cmd_parse_free_command($3);
                    345:                        }
                    346:                }
                    347:                | condition1
                    348:                {
                    349:                        TAILQ_INIT(&$$);
                    350:                        TAILQ_CONCAT(&$$, &$1, entry);
                    351:                }
                    352:
                    353: command                : assignment TOKEN
                    354:                {
                    355:                        struct cmd_parse_state  *ps = &parse_state;
                    356:
                    357:                        $$ = xcalloc(1, sizeof *$$);
                    358:                        $$->name = $2;
1.3     ! nicm      359:                        $$->line = ps->input->line - 1;
1.1       nicm      360:
                    361:                }
                    362:                | assignment TOKEN arguments
                    363:                {
                    364:                        struct cmd_parse_state  *ps = &parse_state;
                    365:
                    366:                        $$ = xcalloc(1, sizeof *$$);
                    367:                        $$->name = $2;
1.3     ! nicm      368:                        $$->line = ps->input->line - 1;
1.1       nicm      369:
                    370:                        $$->argc = $3.argc;
                    371:                        $$->argv = $3.argv;
                    372:                }
                    373:
                    374: condition1     : if_open commands if_close
                    375:                {
                    376:                        TAILQ_INIT(&$$);
                    377:                        if ($1)
                    378:                                TAILQ_CONCAT(&$$, &$2, entry);
                    379:                        else
                    380:                                cmd_parse_free_commands(&$2);
                    381:                }
                    382:                | if_open commands if_else commands if_close
                    383:                {
                    384:                        TAILQ_INIT(&$$);
                    385:                        if ($1) {
                    386:                                TAILQ_CONCAT(&$$, &$2, entry);
                    387:                                cmd_parse_free_commands(&$4);
                    388:                        } else {
                    389:                                TAILQ_CONCAT(&$$, &$4, entry);
                    390:                                cmd_parse_free_commands(&$2);
                    391:                        }
                    392:                }
                    393:                | if_open commands elif1 if_close
                    394:                {
                    395:                        TAILQ_INIT(&$$);
                    396:                        if ($1) {
                    397:                                TAILQ_CONCAT(&$$, &$2, entry);
                    398:                                cmd_parse_free_commands(&$3.commands);
                    399:                        } else if ($3.flag) {
                    400:                                TAILQ_CONCAT(&$$, &$3.commands, entry);
                    401:                                cmd_parse_free_commands(&$2);
                    402:                        } else {
                    403:                                cmd_parse_free_commands(&$2);
                    404:                                cmd_parse_free_commands(&$3.commands);
                    405:                        }
                    406:                }
                    407:                | if_open commands elif1 if_else commands if_close
                    408:                {
                    409:                        TAILQ_INIT(&$$);
                    410:                        if ($1) {
                    411:                                TAILQ_CONCAT(&$$, &$2, entry);
                    412:                                cmd_parse_free_commands(&$3.commands);
                    413:                                cmd_parse_free_commands(&$5);
                    414:                        } else if ($3.flag) {
                    415:                                TAILQ_CONCAT(&$$, &$3.commands, entry);
                    416:                                cmd_parse_free_commands(&$2);
                    417:                                cmd_parse_free_commands(&$5);
                    418:                        } else {
                    419:                                TAILQ_CONCAT(&$$, &$5, entry);
                    420:                                cmd_parse_free_commands(&$2);
                    421:                                cmd_parse_free_commands(&$3.commands);
                    422:                        }
                    423:
                    424:                }
                    425:
                    426: elif1          : if_elif commands
                    427:                {
                    428:                        TAILQ_INIT(&$$.commands);
                    429:                        if ($1)
                    430:                                TAILQ_CONCAT(&$$.commands, &$2, entry);
                    431:                        else
                    432:                                cmd_parse_free_commands(&$2);
                    433:                        $$.flag = $1;
                    434:                }
                    435:                | if_elif commands elif1
                    436:                {
                    437:                        TAILQ_INIT(&$$.commands);
                    438:                        if ($1) {
                    439:                                $$.flag = 1;
                    440:                                TAILQ_CONCAT(&$$.commands, &$2, entry);
                    441:                                cmd_parse_free_commands(&$3.commands);
                    442:                        } else {
                    443:                                $$.flag = $3.flag;
                    444:                                TAILQ_CONCAT(&$$.commands, &$3.commands, entry);
                    445:                                cmd_parse_free_commands(&$2);
                    446:                        }
                    447:                }
                    448:
                    449: arguments      : argument
                    450:                {
                    451:                        $$.argc = 1;
                    452:                        $$.argv = xreallocarray(NULL, 1, sizeof *$$.argv);
                    453:
                    454:                        $$.argv[0] = $1;
                    455:                }
                    456:                | argument arguments
                    457:                {
                    458:                        cmd_prepend_argv(&$2.argc, &$2.argv, $1);
                    459:                        free($1);
                    460:                        $$ = $2;
                    461:                }
                    462:
                    463: argument       : TOKEN
                    464:                {
                    465:                        $$ = $1;
                    466:                }
                    467:                | EQUALS
                    468:                {
                    469:                        $$ = $1;
                    470:                }
                    471:
                    472: %%
                    473:
                    474: static char *
                    475: cmd_parse_get_error(const char *file, u_int line, const char *error)
                    476: {
                    477:        char    *s;
                    478:
                    479:        if (file == NULL)
                    480:                s = xstrdup(error);
                    481:        else
                    482:                xasprintf (&s, "%s:%u: %s", file, line, error);
                    483:        return (s);
                    484: }
                    485:
                    486: static char *
                    487: cmd_parse_get_strerror(const char *file, u_int line)
                    488: {
                    489:        return (cmd_parse_get_error(file, line, strerror(errno)));
                    490: }
                    491:
                    492: static void
                    493: cmd_parse_free_command(struct cmd_parse_command *cmd)
                    494: {
                    495:        free(cmd->name);
                    496:        cmd_free_argv(cmd->argc, cmd->argv);
                    497:        free(cmd);
                    498: }
                    499:
                    500: static void
                    501: cmd_parse_free_commands(struct cmd_parse_commands *cmds)
                    502: {
                    503:        struct cmd_parse_command        *cmd, *cmd1;
                    504:
                    505:        TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
                    506:                TAILQ_REMOVE(cmds, cmd, entry);
                    507:                cmd_parse_free_command(cmd);
                    508:        }
                    509: }
                    510:
                    511: static struct cmd_parse_commands *
                    512: cmd_parse_run_parser(FILE *f, struct cmd_parse_input *pi, char **cause)
                    513: {
                    514:        struct cmd_parse_state          *ps = &parse_state;
                    515:        struct cmd_parse_commands       *cmds;
                    516:        struct cmd_parse_scope          *scope, *scope1;
                    517:        int                              retval;
                    518:
                    519:        memset(ps, 0, sizeof *ps);
                    520:
                    521:        ps->f = f;
                    522:        ps->eof = 0;
                    523:        ps->input = pi;
                    524:
                    525:        TAILQ_INIT(&ps->commands);
                    526:        TAILQ_INIT(&ps->stack);
                    527:
                    528:        retval = yyparse();
                    529:        TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
                    530:                TAILQ_REMOVE(&ps->stack, scope, entry);
                    531:                free(scope);
                    532:        }
                    533:        if (retval != 0) {
                    534:                *cause = ps->error;
                    535:                return (NULL);
                    536:        }
                    537:
                    538:        cmds = xmalloc(sizeof *cmds);
                    539:        TAILQ_INIT(cmds);
                    540:        TAILQ_CONCAT(cmds, &ps->commands, entry);
                    541:        return (cmds);
                    542: }
                    543:
                    544: struct cmd_parse_result *
                    545: cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
                    546: {
                    547:        static struct cmd_parse_result   pr;
                    548:        struct cmd_parse_input           input;
                    549:        struct cmd_parse_commands       *cmds, *cmds2;
                    550:        struct cmd_parse_command        *cmd, *cmd2, *next, *next2, *after;
                    551:        u_int                            line = UINT_MAX;
                    552:        int                              i;
                    553:        struct cmd_list                 *cmdlist = NULL, *result;
                    554:        struct cmd                      *add;
                    555:        char                            *alias, *cause, *s;
                    556:
                    557:        if (pi == NULL) {
                    558:                memset(&input, 0, sizeof input);
                    559:                pi = &input;
                    560:        }
                    561:        memset(&pr, 0, sizeof pr);
                    562:
                    563:        /*
                    564:         * Parse the file into a list of commands.
                    565:         */
                    566:        cmds = cmd_parse_run_parser(f, pi, &cause);
                    567:        if (cmds == NULL) {
                    568:                pr.status = CMD_PARSE_ERROR;
                    569:                pr.error = cause;
                    570:                return (&pr);
                    571:        }
                    572:        if (TAILQ_EMPTY(cmds)) {
                    573:                free(cmds);
                    574:                pr.status = CMD_PARSE_EMPTY;
                    575:                return (&pr);
                    576:        }
                    577:
                    578:        /*
                    579:         * Walk the commands and expand any aliases. Each alias is parsed
                    580:         * individually to a new command list, any trailing arguments appended
                    581:         * to the last command, and all commands inserted into the original
                    582:         * command list.
                    583:         */
                    584:        TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
                    585:                alias = cmd_get_alias(cmd->name);
                    586:                if (alias == NULL)
                    587:                        continue;
                    588:
                    589:                line = cmd->line;
                    590:                log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias);
                    591:
                    592:                f = fmemopen(alias, strlen(alias), "r");
                    593:                if (f == NULL) {
                    594:                        free(alias);
                    595:                        pr.status = CMD_PARSE_ERROR;
                    596:                        pr.error = cmd_parse_get_strerror(pi->file, line);
                    597:                        goto out;
                    598:                }
                    599:                pi->line = line;
                    600:                cmds2 = cmd_parse_run_parser(f, pi, &cause);
                    601:                fclose(f);
                    602:                free(alias);
                    603:                if (cmds2 == NULL) {
                    604:                        pr.status = CMD_PARSE_ERROR;
                    605:                        pr.error = cause;
                    606:                        goto out;
                    607:                }
                    608:
                    609:                cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands);
                    610:                if (cmd2 == NULL) {
                    611:                        TAILQ_REMOVE(cmds, cmd, entry);
                    612:                        cmd_parse_free_command(cmd);
                    613:                        continue;
                    614:                }
                    615:                for (i = 0; i < cmd->argc; i++)
                    616:                        cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
                    617:
                    618:                after = cmd;
                    619:                TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) {
                    620:                        cmd2->line = line;
                    621:                        TAILQ_REMOVE(cmds2, cmd2, entry);
                    622:                        TAILQ_INSERT_AFTER(cmds, after, cmd2, entry);
                    623:                        after = cmd2;
                    624:                }
                    625:                cmd_parse_free_commands(cmds2);
                    626:
                    627:                TAILQ_REMOVE(cmds, cmd, entry);
                    628:                cmd_parse_free_command(cmd);
                    629:        }
                    630:
                    631:        /*
                    632:         * Parse each command into a command list. Create a new command list
                    633:         * for each line so they get a new group (so the queue knows which ones
                    634:         * to remove if a command fails when executed).
                    635:         */
                    636:        result = cmd_list_new();
                    637:        TAILQ_FOREACH(cmd, cmds, entry) {
                    638:                log_debug("%s: %u %s", __func__, cmd->line, cmd->name);
                    639:                cmd_log_argv(cmd->argc, cmd->argv, __func__);
                    640:
                    641:                if (cmdlist == NULL || cmd->line != line) {
                    642:                        if (cmdlist != NULL) {
                    643:                                cmd_list_move(result, cmdlist);
                    644:                                cmd_list_free(cmdlist);
                    645:                        }
                    646:                        cmdlist = cmd_list_new();
                    647:                }
                    648:                line = cmd->line;
                    649:
                    650:                cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name);
                    651:                add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
                    652:                if (add == NULL) {
                    653:                        cmd_list_free(result);
                    654:                        pr.status = CMD_PARSE_ERROR;
                    655:                        pr.error = cmd_parse_get_error(pi->file, line, cause);
                    656:                        free(cause);
                    657:                        goto out;
                    658:                }
                    659:                cmd_list_append(cmdlist, add);
                    660:        }
                    661:        if (cmdlist != NULL) {
                    662:                cmd_list_move(result, cmdlist);
                    663:                cmd_list_free(cmdlist);
                    664:        }
                    665:
1.2       nicm      666:        s = cmd_list_print(result, 0);
1.1       nicm      667:        log_debug("%s: %s", __func__, s);
                    668:        free(s);
                    669:
                    670:        pr.status = CMD_PARSE_SUCCESS;
                    671:        pr.cmdlist = result;
                    672:
                    673: out:
                    674:        cmd_parse_free_commands(cmds);
                    675:        free(cmds);
                    676:
                    677:        return (&pr);
                    678: }
                    679:
                    680: struct cmd_parse_result *
                    681: cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
                    682: {
                    683:        static struct cmd_parse_result   pr;
                    684:        struct cmd_parse_result         *prp;
                    685:        FILE                            *f;
                    686:
                    687:        if (*s == '\0') {
                    688:                pr.status = CMD_PARSE_EMPTY;
                    689:                pr.cmdlist = NULL;
                    690:                pr.error = NULL;
                    691:                return (&pr);
                    692:        }
                    693:
                    694:        f = fmemopen((void *)s, strlen(s), "r");
                    695:        if (f == NULL) {
                    696:                pr.status = CMD_PARSE_ERROR;
                    697:                pr.cmdlist = NULL;
                    698:                pr.error = cmd_parse_get_strerror(pi->file, pi->line);
                    699:                return (NULL);
                    700:        }
                    701:        prp = cmd_parse_from_file(f, pi);
                    702:        fclose(f);
                    703:        return (prp);
                    704: }
                    705:
                    706: static int printflike(1, 2)
                    707: yyerror(const char *fmt, ...)
                    708: {
                    709:        struct cmd_parse_state  *ps = &parse_state;
                    710:        struct cmd_parse_input  *pi = ps->input;
                    711:        va_list                  ap;
                    712:        char                    *error;
                    713:
                    714:        if (ps->error != NULL)
                    715:                return (0);
                    716:
                    717:        va_start(ap, fmt);
                    718:        xvasprintf(&error, fmt, ap);
                    719:        va_end(ap);
                    720:
                    721:        ps->error = cmd_parse_get_error(pi->file, pi->line, error);
                    722:        free(error);
                    723:        return (0);
                    724: }
                    725:
                    726: static int
                    727: yylex_is_var(char ch, int first)
                    728: {
                    729:        if (ch == '=')
                    730:                return (0);
                    731:        if (first && isdigit((u_char)ch))
                    732:                return (0);
                    733:        return (isalnum((u_char)ch) || ch == '_');
                    734: }
                    735:
                    736: static void
                    737: yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
                    738: {
                    739:        if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
                    740:                fatalx("buffer is too big");
                    741:        *buf = xrealloc(*buf, (*len) + 1 + addlen);
                    742:        memcpy((*buf) + *len, add, addlen);
                    743:        (*len) += addlen;
                    744: }
                    745:
                    746: static void
                    747: yylex_append1(char **buf, size_t *len, char add)
                    748: {
                    749:        yylex_append(buf, len, &add, 1);
                    750: }
                    751:
                    752: static int
                    753: yylex_getc(void)
                    754: {
                    755:        struct cmd_parse_state  *ps = &parse_state;
                    756:        int                      ch;
                    757:
                    758:        if (ps->escapes != 0) {
                    759:                ps->escapes--;
                    760:                return ('\\');
                    761:        }
                    762:        for (;;) {
                    763:                ch = getc(ps->f);
                    764:                if (ch == '\\') {
                    765:                        ps->escapes++;
                    766:                        continue;
                    767:                }
                    768:                if (ch == '\n' && (ps->escapes % 2) == 1) {
                    769:                        ps->input->line++;
                    770:                        ps->escapes--;
                    771:                        continue;
                    772:                }
                    773:
                    774:                if (ps->escapes != 0) {
                    775:                        ungetc(ch, ps->f);
                    776:                        ps->escapes--;
                    777:                        return ('\\');
                    778:                }
                    779:                return (ch);
                    780:        }
                    781: }
                    782:
                    783: static char *
                    784: yylex_get_word(int ch)
                    785: {
                    786:        struct cmd_parse_state  *ps = &parse_state;
                    787:        char                    *buf;
                    788:        size_t                   len;
                    789:
                    790:        len = 0;
                    791:        buf = xmalloc(1);
                    792:
                    793:        do
                    794:                yylex_append1(&buf, &len, ch);
                    795:        while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
                    796:        ungetc(ch, ps->f);
                    797:
                    798:        buf[len] = '\0';
                    799:        log_debug("%s: %s", __func__, buf);
                    800:        return (buf);
                    801: }
                    802:
                    803: static int
                    804: yylex(void)
                    805: {
                    806:        struct cmd_parse_state  *ps = &parse_state;
                    807:        char                    *token, *cp;
                    808:        int                      ch, next;
                    809:
                    810:        for (;;) {
                    811:                ch = yylex_getc();
                    812:
                    813:                if (ch == EOF) {
                    814:                        /*
                    815:                         * Ensure every file or string is terminated by a
                    816:                         * newline. This keeps the parser simpler and avoids
                    817:                         * having to add a newline to each string.
                    818:                         */
                    819:                        if (ps->eof)
                    820:                                break;
                    821:                        ps->eof = 1;
                    822:                        return ('\n');
                    823:                }
                    824:
                    825:                if (ch == ' ' || ch == '\t') {
                    826:                        /*
                    827:                         * Ignore whitespace.
                    828:                         */
                    829:                        continue;
                    830:                }
                    831:
                    832:                if (ch == '\n') {
                    833:                        /*
                    834:                         * End of line. Update the line number.
                    835:                         */
                    836:                        ps->input->line++;
                    837:                        return ('\n');
                    838:                }
                    839:
                    840:                if (ch == ';') {
                    841:                        /*
                    842:                         * A semicolon is itself.
                    843:                         */
                    844:                        return (';');
                    845:                }
                    846:
                    847:                if (ch == '#') {
                    848:                        /*
                    849:                         * #{ opens a format; anything else is a comment,
                    850:                         * ignore up to the end of the line.
                    851:                         */
                    852:                        next = yylex_getc();
                    853:                        if (next == '{') {
                    854:                                yylval.token = yylex_format();
                    855:                                if (yylval.token == NULL)
                    856:                                        return (ERROR);
                    857:                                return (FORMAT);
                    858:                        }
                    859:                        while (next != '\n' && next != EOF)
                    860:                                next = yylex_getc();
                    861:                        if (next == '\n') {
                    862:                                ps->input->line++;
                    863:                                return ('\n');
                    864:                        }
                    865:                        continue;
                    866:                }
                    867:
                    868:                if (ch == '%') {
                    869:                        /*
                    870:                         * % is a condition unless it is alone, then it is a
                    871:                         * token.
                    872:                         */
                    873:                        yylval.token = yylex_get_word('%');
                    874:                        if (strcmp(yylval.token, "%") == 0)
                    875:                                return (TOKEN);
                    876:                        if (strcmp(yylval.token, "%if") == 0) {
                    877:                                free(yylval.token);
                    878:                                return (IF);
                    879:                        }
                    880:                        if (strcmp(yylval.token, "%else") == 0) {
                    881:                                free(yylval.token);
                    882:                                return (ELSE);
                    883:                        }
                    884:                        if (strcmp(yylval.token, "%elif") == 0) {
                    885:                                free(yylval.token);
                    886:                                return (ELIF);
                    887:                        }
                    888:                        if (strcmp(yylval.token, "%endif") == 0) {
                    889:                                free(yylval.token);
                    890:                                return (ENDIF);
                    891:                        }
                    892:                        free(yylval.token);
                    893:                        return (ERROR);
                    894:                }
                    895:
                    896:                /*
                    897:                 * Otherwise this is a token.
                    898:                 */
                    899:                token = yylex_token(ch);
                    900:                if (token == NULL)
                    901:                        return (ERROR);
                    902:                yylval.token = token;
                    903:
                    904:                if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
                    905:                        for (cp = token + 1; *cp != '='; cp++) {
                    906:                                if (!yylex_is_var(*cp, 0))
                    907:                                        break;
                    908:                        }
                    909:                        if (*cp == '=')
                    910:                                return (EQUALS);
                    911:                }
                    912:                return (TOKEN);
                    913:        }
                    914:        return (0);
                    915: }
                    916:
                    917: static char *
                    918: yylex_format(void)
                    919: {
                    920:        char    *buf;
                    921:        size_t   len;
                    922:        int      ch, brackets = 1;
                    923:
                    924:        len = 0;
                    925:        buf = xmalloc(1);
                    926:
                    927:        yylex_append(&buf, &len, "#{", 2);
                    928:        for (;;) {
                    929:                if ((ch = yylex_getc()) == EOF || ch == '\n')
                    930:                        goto error;
                    931:                if (ch == '#') {
                    932:                        if ((ch = yylex_getc()) == EOF || ch == '\n')
                    933:                                goto error;
                    934:                        if (ch == '{')
                    935:                                brackets++;
                    936:                        yylex_append1(&buf, &len, '#');
                    937:                } else if (ch == '}') {
                    938:                        if (brackets != 0 && --brackets == 0) {
                    939:                                yylex_append1(&buf, &len, ch);
                    940:                                break;
                    941:                        }
                    942:                }
                    943:                yylex_append1(&buf, &len, ch);
                    944:        }
                    945:        if (brackets != 0)
                    946:                goto error;
                    947:
                    948:        buf[len] = '\0';
                    949:        log_debug("%s: %s", __func__, buf);
                    950:        return (buf);
                    951:
                    952: error:
                    953:        free(buf);
                    954:        return (NULL);
                    955: }
                    956:
                    957: static int
                    958: yylex_token_escape(char **buf, size_t *len)
                    959: {
                    960:        int                      ch, type;
                    961:        u_int                    size, i, tmp;
                    962:        char                     s[9];
                    963:        struct utf8_data         ud;
                    964:
                    965:        switch (ch = yylex_getc()) {
                    966:        case EOF:
                    967:                return (0);
                    968:        case 'e':
                    969:                ch = '\033';
                    970:                break;
                    971:        case 'r':
                    972:                ch = '\r';
                    973:                break;
                    974:        case 'n':
                    975:                ch = '\n';
                    976:                break;
                    977:        case 't':
                    978:                ch = '\t';
                    979:                break;
                    980:        case 'u':
                    981:                type = 'u';
                    982:                size = 4;
                    983:                goto unicode;
                    984:        case 'U':
                    985:                type = 'U';
                    986:                size = 8;
                    987:                goto unicode;
                    988:        }
                    989:
                    990:        yylex_append1(buf, len, ch);
                    991:        return (1);
                    992:
                    993: unicode:
                    994:        for (i = 0; i < size; i++) {
                    995:                ch = yylex_getc();
                    996:                if (ch == EOF || ch == '\n')
                    997:                        return (0);
                    998:                if (!isxdigit((u_char)ch)) {
                    999:                        yyerror("invalid \\%c argument", type);
                   1000:                        return (0);
                   1001:                }
                   1002:                s[i] = ch;
                   1003:        }
                   1004:        s[i] = '\0';
                   1005:
                   1006:        if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
                   1007:            (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
                   1008:                yyerror("invalid \\%c argument", type);
                   1009:                return (0);
                   1010:        }
                   1011:        if (utf8_split(tmp, &ud) != UTF8_DONE) {
                   1012:                yyerror("invalid \\%c argument", type);
                   1013:                return (0);
                   1014:        }
                   1015:        yylex_append(buf, len, ud.data, ud.size);
                   1016:        return (1);
                   1017: }
                   1018:
                   1019: static int
                   1020: yylex_token_variable(char **buf, size_t *len)
                   1021: {
                   1022:        struct cmd_parse_state  *ps = &parse_state;
                   1023:        struct environ_entry    *envent;
                   1024:        int                      ch, brackets = 0;
                   1025:        char                     name[BUFSIZ];
                   1026:        size_t                   namelen = 0;
                   1027:        const char              *value;
                   1028:
                   1029:        ch = yylex_getc();
                   1030:        if (ch == EOF)
                   1031:                return (0);
                   1032:        if (ch == '{')
                   1033:                brackets = 1;
                   1034:        else {
                   1035:                if (!yylex_is_var(ch, 1)) {
                   1036:                        yylex_append1(buf, len, '$');
                   1037:                        ungetc(ch, ps->f);
                   1038:                        return (1);
                   1039:                }
                   1040:                name[namelen++] = ch;
                   1041:        }
                   1042:
                   1043:        for (;;) {
                   1044:                ch = yylex_getc();
                   1045:                if (brackets && ch == '}')
                   1046:                        break;
                   1047:                if (ch == EOF || !yylex_is_var(ch, 0)) {
                   1048:                        if (!brackets) {
                   1049:                                ungetc(ch, ps->f);
                   1050:                                break;
                   1051:                        }
                   1052:                        yyerror("invalid environment variable");
                   1053:                        return (0);
                   1054:                }
                   1055:                if (namelen == (sizeof name) - 2) {
                   1056:                        yyerror("environment variable is too long");
                   1057:                        return (0);
                   1058:                }
                   1059:                name[namelen++] = ch;
                   1060:        }
                   1061:        name[namelen] = '\0';
                   1062:
                   1063:        envent = environ_find(global_environ, name);
                   1064:        if (envent != NULL) {
                   1065:                value = envent->value;
                   1066:                log_debug("%s: %s -> %s", __func__, name, value);
                   1067:                yylex_append(buf, len, value, strlen(value));
                   1068:        }
                   1069:        return (1);
                   1070: }
                   1071:
                   1072: static int
                   1073: yylex_token_tilde(char **buf, size_t *len)
                   1074: {
                   1075:        struct cmd_parse_state  *ps = &parse_state;
                   1076:        struct environ_entry    *envent;
                   1077:        int                      ch;
                   1078:        char                     name[BUFSIZ];
                   1079:        size_t                   namelen = 0;
                   1080:        struct passwd           *pw;
                   1081:        const char              *home = NULL;
                   1082:
                   1083:        for (;;) {
                   1084:                ch = yylex_getc();
                   1085:                if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
                   1086:                        ungetc(ch, ps->f);
                   1087:                        break;
                   1088:                }
                   1089:                if (namelen == (sizeof name) - 2) {
                   1090:                        yyerror("user name is too long");
                   1091:                        return (0);
                   1092:                }
                   1093:                name[namelen++] = ch;
                   1094:        }
                   1095:        name[namelen] = '\0';
                   1096:
                   1097:        if (*name == '\0') {
                   1098:                envent = environ_find(global_environ, "HOME");
                   1099:                if (envent != NULL && *envent->value != '\0')
                   1100:                        home = envent->value;
                   1101:                else if ((pw = getpwuid(getuid())) != NULL)
                   1102:                        home = pw->pw_dir;
                   1103:        } else {
                   1104:                if ((pw = getpwnam(name)) != NULL)
                   1105:                        home = pw->pw_dir;
                   1106:        }
                   1107:        if (home == NULL)
                   1108:                return (0);
                   1109:
                   1110:        log_debug("%s: ~%s -> %s", __func__, name, home);
                   1111:        yylex_append(buf, len, home, strlen(home));
                   1112:        return (1);
                   1113: }
                   1114:
                   1115: static char *
                   1116: yylex_token(int ch)
                   1117: {
                   1118:        struct cmd_parse_state  *ps = &parse_state;
                   1119:        char                    *buf;
                   1120:        size_t                   len;
                   1121:        enum { START,
                   1122:               NONE,
                   1123:               DOUBLE_QUOTES,
                   1124:               SINGLE_QUOTES }   state = NONE, last = START;
                   1125:
                   1126:        len = 0;
                   1127:        buf = xmalloc(1);
                   1128:
                   1129:        for (;;) {
                   1130:                /*
                   1131:                 * EOF or \n are always the end of the token. If inside quotes
                   1132:                 * they are an error.
                   1133:                 */
                   1134:                if (ch == EOF || ch == '\n') {
                   1135:                        if (state != NONE)
                   1136:                                goto error;
                   1137:                        break;
                   1138:                }
                   1139:
                   1140:                /* Whitespace or ; ends a token unless inside quotes. */
                   1141:                if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE)
                   1142:                        break;
                   1143:
                   1144:                /*
                   1145:                 * \ ~ and $ are expanded except in single quotes.
                   1146:                 */
                   1147:                if (ch == '\\' && state != SINGLE_QUOTES) {
                   1148:                        if (!yylex_token_escape(&buf, &len))
                   1149:                                goto error;
                   1150:                        goto skip;
                   1151:                }
                   1152:                if (ch == '~' && last != state && state != SINGLE_QUOTES) {
                   1153:                        if (!yylex_token_tilde(&buf, &len))
                   1154:                                goto error;
                   1155:                        goto skip;
                   1156:                }
                   1157:                if (ch == '$' && state != SINGLE_QUOTES) {
                   1158:                        if (!yylex_token_variable(&buf, &len))
                   1159:                                goto error;
                   1160:                        goto skip;
                   1161:                }
                   1162:
                   1163:                /*
                   1164:                 * ' and " starts or end quotes (and is consumed).
                   1165:                 */
                   1166:                if (ch == '\'') {
                   1167:                        if (state == NONE) {
                   1168:                                state = SINGLE_QUOTES;
                   1169:                                goto next;
                   1170:                        }
                   1171:                        if (state == SINGLE_QUOTES) {
                   1172:                                state = NONE;
                   1173:                                goto next;
                   1174:                        }
                   1175:                }
                   1176:                if (ch == '"') {
                   1177:                        if (state == NONE) {
                   1178:                                state = DOUBLE_QUOTES;
                   1179:                                goto next;
                   1180:                        }
                   1181:                        if (state == DOUBLE_QUOTES) {
                   1182:                                state = NONE;
                   1183:                                goto next;
                   1184:                        }
                   1185:                }
                   1186:
                   1187:                /*
                   1188:                 * Otherwise add the character to the buffer.
                   1189:                 */
                   1190:                yylex_append1(&buf, &len, ch);
                   1191:
                   1192:        skip:
                   1193:                last = state;
                   1194:
                   1195:        next:
                   1196:                ch = yylex_getc();
                   1197:        }
                   1198:        ungetc(ch, ps->f);
                   1199:
                   1200:        buf[len] = '\0';
                   1201:        log_debug("%s: %s", __func__, buf);
                   1202:        return (buf);
                   1203:
                   1204: error:
                   1205:        free(buf);
                   1206:        return (NULL);
                   1207: }