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

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