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