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

1.1     ! nicm        1: /* $OpenBSD$ */
        !             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;
        !           359:                        $$->line = ps->input->line;
        !           360:
        !           361:                }
        !           362:                | assignment TOKEN arguments
        !           363:                {
        !           364:                        struct cmd_parse_state  *ps = &parse_state;
        !           365:
        !           366:                        $$ = xcalloc(1, sizeof *$$);
        !           367:                        $$->name = $2;
        !           368:                        $$->line = ps->input->line;
        !           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:
        !           666:        s = cmd_list_print(result);
        !           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: }