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