Annotation of src/usr.bin/tmux/cmd-parse.y, Revision 1.35
1.35 ! nicm 1: /* $OpenBSD: cmd-parse.y,v 1.34 2021/08/18 15:16:33 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.35 ! nicm 781: static struct cmd *
! 782: cmd_parse_build_command(struct cmd_parse_command *cmd,
! 783: struct cmd_parse_input *pi, u_int line, struct cmd_parse_result *pr)
! 784: {
! 785: struct cmd *add;
! 786: char *cause;
! 787:
! 788: add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
! 789: if (add == NULL) {
! 790: pr->status = CMD_PARSE_ERROR;
! 791: pr->error = cmd_parse_get_error(pi->file, line, cause);
! 792: free(cause);
! 793: return (NULL);
! 794: }
! 795: return (add);
! 796: }
! 797:
1.4 nicm 798: static struct cmd_parse_result *
799: cmd_parse_build_commands(struct cmd_parse_commands *cmds,
800: struct cmd_parse_input *pi)
1.1 nicm 801: {
802: static struct cmd_parse_result pr;
1.4 nicm 803: struct cmd_parse_commands *cmds2;
1.1 nicm 804: struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after;
805: u_int line = UINT_MAX;
806: int i;
1.35 ! nicm 807: struct cmd_list *current = NULL, *result;
1.1 nicm 808: struct cmd *add;
1.28 nicm 809: char *name, *alias, *cause, *s;
1.1 nicm 810:
1.4 nicm 811: /* Check for an empty list. */
1.1 nicm 812: if (TAILQ_EMPTY(cmds)) {
1.13 nicm 813: cmd_parse_free_commands(cmds);
1.1 nicm 814: pr.status = CMD_PARSE_EMPTY;
815: return (&pr);
816: }
817:
1.33 nicm 818: /* Flatten command arguments. */
819: cmd_parse_log_commands(cmds, __func__);
820: TAILQ_FOREACH(cmd, cmds, entry)
821: cmd_parse_flatten_command(cmd);
822:
1.1 nicm 823: /*
824: * Walk the commands and expand any aliases. Each alias is parsed
825: * individually to a new command list, any trailing arguments appended
826: * to the last command, and all commands inserted into the original
827: * command list.
828: */
829: TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
1.28 nicm 830: name = cmd->argv[0];
831:
832: alias = cmd_get_alias(name);
1.1 nicm 833: if (alias == NULL)
834: continue;
835:
836: line = cmd->line;
1.28 nicm 837: log_debug("%s: %u %s = %s", __func__, line, name, alias);
1.1 nicm 838:
839: pi->line = line;
1.5 nicm 840: cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
1.1 nicm 841: free(alias);
842: if (cmds2 == NULL) {
843: pr.status = CMD_PARSE_ERROR;
844: pr.error = cause;
845: goto out;
846: }
847:
848: cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands);
849: if (cmd2 == NULL) {
850: TAILQ_REMOVE(cmds, cmd, entry);
851: cmd_parse_free_command(cmd);
852: continue;
853: }
1.34 nicm 854: cmd_parse_flatten_command(cmd2);
1.28 nicm 855: for (i = 1; i < cmd->argc; i++)
1.1 nicm 856: cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
857:
858: after = cmd;
859: TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) {
860: cmd2->line = line;
861: TAILQ_REMOVE(cmds2, cmd2, entry);
862: TAILQ_INSERT_AFTER(cmds, after, cmd2, entry);
863: after = cmd2;
864: }
865: cmd_parse_free_commands(cmds2);
866:
867: TAILQ_REMOVE(cmds, cmd, entry);
868: cmd_parse_free_command(cmd);
869: }
870:
871: /*
872: * Parse each command into a command list. Create a new command list
1.25 nicm 873: * for each line (unless the flag is set) so they get a new group (so
874: * the queue knows which ones to remove if a command fails when
875: * executed).
1.1 nicm 876: */
877: result = cmd_list_new();
878: TAILQ_FOREACH(cmd, cmds, entry) {
1.35 ! nicm 879: if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
! 880: if (current != NULL) {
! 881: cmd_parse_print_commands(pi, line, current);
! 882: cmd_list_move(result, current);
! 883: cmd_list_free(current);
1.1 nicm 884: }
1.35 ! nicm 885: current = cmd_list_new();
1.1 nicm 886: }
1.35 ! nicm 887: if (current == NULL)
! 888: current = cmd_list_new();
1.1 nicm 889: line = cmd->line;
890:
1.35 ! nicm 891: add = cmd_parse_build_command(cmd, pi, line, &pr);
1.1 nicm 892: if (add == NULL) {
893: cmd_list_free(result);
1.35 ! nicm 894: cmd_list_free(current);
1.1 nicm 895: goto out;
896: }
1.35 ! nicm 897: cmd_list_append(current, add);
1.1 nicm 898: }
1.35 ! nicm 899: if (current != NULL) {
! 900: cmd_parse_print_commands(pi, line, current);
! 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:
909: pr.status = CMD_PARSE_SUCCESS;
910: pr.cmdlist = result;
911:
912: out:
913: cmd_parse_free_commands(cmds);
914:
915: return (&pr);
916: }
917:
918: struct cmd_parse_result *
1.4 nicm 919: cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
920: {
921: static struct cmd_parse_result pr;
922: struct cmd_parse_input input;
923: struct cmd_parse_commands *cmds;
924: char *cause;
925:
926: if (pi == NULL) {
927: memset(&input, 0, sizeof input);
928: pi = &input;
929: }
930: memset(&pr, 0, sizeof pr);
931:
1.5 nicm 932: cmds = cmd_parse_do_file(f, pi, &cause);
1.4 nicm 933: if (cmds == NULL) {
934: pr.status = CMD_PARSE_ERROR;
935: pr.error = cause;
936: return (&pr);
937: }
938: return (cmd_parse_build_commands(cmds, pi));
939: }
940:
941: struct cmd_parse_result *
1.1 nicm 942: cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
943: {
1.25 nicm 944: struct cmd_parse_input input;
945:
946: if (pi == NULL) {
947: memset(&input, 0, sizeof input);
948: pi = &input;
949: }
950:
951: /*
952: * When parsing a string, put commands in one group even if there are
953: * multiple lines. This means { a \n b } is identical to "a ; b" when
954: * given as an argument to another command.
955: */
956: pi->flags |= CMD_PARSE_ONEGROUP;
1.21 nicm 957: return (cmd_parse_from_buffer(s, strlen(s), pi));
1.26 nicm 958: }
959:
960: enum cmd_parse_status
961: cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi,
962: struct cmdq_item *after, struct cmdq_state *state, char **error)
963: {
964: struct cmd_parse_result *pr;
965: struct cmdq_item *item;
966:
967: pr = cmd_parse_from_string(s, pi);
968: switch (pr->status) {
969: case CMD_PARSE_EMPTY:
970: break;
971: case CMD_PARSE_ERROR:
972: if (error != NULL)
973: *error = pr->error;
974: else
975: free(pr->error);
976: break;
977: case CMD_PARSE_SUCCESS:
978: item = cmdq_get_command(pr->cmdlist, state);
979: cmdq_insert_after(after, item);
980: cmd_list_free(pr->cmdlist);
981: break;
982: }
983: return (pr->status);
984: }
985:
986: enum cmd_parse_status
987: cmd_parse_and_append(const char *s, struct cmd_parse_input *pi,
988: struct client *c, struct cmdq_state *state, char **error)
989: {
990: struct cmd_parse_result *pr;
991: struct cmdq_item *item;
992:
993: pr = cmd_parse_from_string(s, pi);
994: switch (pr->status) {
995: case CMD_PARSE_EMPTY:
996: break;
997: case CMD_PARSE_ERROR:
998: if (error != NULL)
999: *error = pr->error;
1000: else
1001: free(pr->error);
1002: break;
1003: case CMD_PARSE_SUCCESS:
1004: item = cmdq_get_command(pr->cmdlist, state);
1005: cmdq_append(c, item);
1006: cmd_list_free(pr->cmdlist);
1007: break;
1008: }
1009: return (pr->status);
1.21 nicm 1010: }
1011:
1012: struct cmd_parse_result *
1013: cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
1014: {
1.1 nicm 1015: static struct cmd_parse_result pr;
1.4 nicm 1016: struct cmd_parse_input input;
1.5 nicm 1017: struct cmd_parse_commands *cmds;
1018: char *cause;
1.1 nicm 1019:
1.4 nicm 1020: if (pi == NULL) {
1021: memset(&input, 0, sizeof input);
1022: pi = &input;
1023: }
1024: memset(&pr, 0, sizeof pr);
1025:
1.21 nicm 1026: if (len == 0) {
1.1 nicm 1027: pr.status = CMD_PARSE_EMPTY;
1028: pr.cmdlist = NULL;
1029: pr.error = NULL;
1030: return (&pr);
1031: }
1032:
1.21 nicm 1033: cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
1.5 nicm 1034: if (cmds == NULL) {
1.1 nicm 1035: pr.status = CMD_PARSE_ERROR;
1.5 nicm 1036: pr.error = cause;
1037: return (&pr);
1.1 nicm 1038: }
1.5 nicm 1039: return (cmd_parse_build_commands(cmds, pi));
1.4 nicm 1040: }
1041:
1.33 nicm 1042: static void
1043: cmd_parse_add_command(struct cmd_parse_commands *cmds,
1044: struct cmd_parse_input *pi, int argc, char **argv)
1045: {
1046: struct cmd_parse_command *cmd;
1047: struct cmd_parse_argument *arg;
1048: int i;
1049:
1050: cmd_log_argv(argc, argv, "%s", __func__);
1051:
1052: cmd = xcalloc(1, sizeof *cmd);
1053: cmd->line = pi->line;
1054:
1055: TAILQ_INIT(&cmd->arguments);
1056: for (i = 0; i < argc; i++) {
1057: arg = xcalloc(1, sizeof *arg);
1058: arg->type = CMD_PARSE_STRING;
1059: arg->string = xstrdup(argv[i]);
1060: TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1061: }
1062:
1063: TAILQ_INSERT_TAIL(cmds, cmd, entry);
1064: }
1065:
1.4 nicm 1066: struct cmd_parse_result *
1067: cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
1068: {
1069: struct cmd_parse_input input;
1070: struct cmd_parse_commands *cmds;
1071: char **copy, **new_argv;
1072: size_t size;
1073: int i, last, new_argc;
1074:
1075: /*
1076: * The commands are already split up into arguments, so just separate
1077: * into a set of commands by ';'.
1078: */
1079:
1080: if (pi == NULL) {
1081: memset(&input, 0, sizeof input);
1082: pi = &input;
1083: }
1084: cmd_log_argv(argc, argv, "%s", __func__);
1085:
1.13 nicm 1086: cmds = cmd_parse_new_commands();
1.4 nicm 1087: copy = cmd_copy_argv(argc, argv);
1088:
1089: last = 0;
1090: for (i = 0; i < argc; i++) {
1091: size = strlen(copy[i]);
1092: if (size == 0 || copy[i][size - 1] != ';')
1093: continue;
1094: copy[i][--size] = '\0';
1095: if (size > 0 && copy[i][size - 1] == '\\') {
1096: copy[i][size - 1] = ';';
1097: continue;
1098: }
1099:
1100: new_argc = i - last;
1101: new_argv = copy + last;
1102: if (size != 0)
1103: new_argc++;
1104:
1.33 nicm 1105: if (new_argc != 0)
1106: cmd_parse_add_command(cmds, pi, new_argc, new_argv);
1.4 nicm 1107: last = i + 1;
1108: }
1109: if (last != argc) {
1110: new_argv = copy + last;
1111: new_argc = argc - last;
1112:
1.33 nicm 1113: if (new_argc != 0)
1114: cmd_parse_add_command(cmds, pi, new_argc, new_argv);
1.4 nicm 1115: }
1.13 nicm 1116:
1117: cmd_free_argv(argc, copy);
1.4 nicm 1118: return (cmd_parse_build_commands(cmds, pi));
1.1 nicm 1119: }
1120:
1121: static int printflike(1, 2)
1122: yyerror(const char *fmt, ...)
1123: {
1124: struct cmd_parse_state *ps = &parse_state;
1125: struct cmd_parse_input *pi = ps->input;
1126: va_list ap;
1127: char *error;
1128:
1129: if (ps->error != NULL)
1130: return (0);
1131:
1132: va_start(ap, fmt);
1133: xvasprintf(&error, fmt, ap);
1134: va_end(ap);
1135:
1136: ps->error = cmd_parse_get_error(pi->file, pi->line, error);
1137: free(error);
1138: return (0);
1139: }
1140:
1141: static int
1142: yylex_is_var(char ch, int first)
1143: {
1144: if (ch == '=')
1145: return (0);
1146: if (first && isdigit((u_char)ch))
1147: return (0);
1148: return (isalnum((u_char)ch) || ch == '_');
1149: }
1150:
1151: static void
1152: yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
1153: {
1154: if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
1155: fatalx("buffer is too big");
1156: *buf = xrealloc(*buf, (*len) + 1 + addlen);
1157: memcpy((*buf) + *len, add, addlen);
1158: (*len) += addlen;
1159: }
1160:
1161: static void
1162: yylex_append1(char **buf, size_t *len, char add)
1163: {
1164: yylex_append(buf, len, &add, 1);
1165: }
1166:
1167: static int
1.5 nicm 1168: yylex_getc1(void)
1169: {
1170: struct cmd_parse_state *ps = &parse_state;
1171: int ch;
1172:
1173: if (ps->f != NULL)
1174: ch = getc(ps->f);
1175: else {
1176: if (ps->off == ps->len)
1177: ch = EOF;
1178: else
1179: ch = ps->buf[ps->off++];
1180: }
1181: return (ch);
1182: }
1183:
1184: static void
1185: yylex_ungetc(int ch)
1186: {
1187: struct cmd_parse_state *ps = &parse_state;
1188:
1189: if (ps->f != NULL)
1190: ungetc(ch, ps->f);
1191: else if (ps->off > 0 && ch != EOF)
1192: ps->off--;
1193: }
1194:
1195: static int
1.1 nicm 1196: yylex_getc(void)
1197: {
1198: struct cmd_parse_state *ps = &parse_state;
1199: int ch;
1200:
1201: if (ps->escapes != 0) {
1202: ps->escapes--;
1203: return ('\\');
1204: }
1205: for (;;) {
1.5 nicm 1206: ch = yylex_getc1();
1.1 nicm 1207: if (ch == '\\') {
1208: ps->escapes++;
1209: continue;
1210: }
1211: if (ch == '\n' && (ps->escapes % 2) == 1) {
1212: ps->input->line++;
1213: ps->escapes--;
1214: continue;
1215: }
1216:
1217: if (ps->escapes != 0) {
1.5 nicm 1218: yylex_ungetc(ch);
1.1 nicm 1219: ps->escapes--;
1220: return ('\\');
1221: }
1222: return (ch);
1223: }
1224: }
1225:
1226: static char *
1227: yylex_get_word(int ch)
1228: {
1.5 nicm 1229: char *buf;
1230: size_t len;
1.1 nicm 1231:
1232: len = 0;
1233: buf = xmalloc(1);
1234:
1235: do
1236: yylex_append1(&buf, &len, ch);
1237: while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
1.5 nicm 1238: yylex_ungetc(ch);
1.1 nicm 1239:
1240: buf[len] = '\0';
1241: log_debug("%s: %s", __func__, buf);
1242: return (buf);
1243: }
1244:
1245: static int
1246: yylex(void)
1247: {
1248: struct cmd_parse_state *ps = &parse_state;
1249: char *token, *cp;
1.15 nicm 1250: int ch, next, condition;
1.1 nicm 1251:
1.8 nicm 1252: if (ps->eol)
1253: ps->input->line++;
1254: ps->eol = 0;
1255:
1.15 nicm 1256: condition = ps->condition;
1257: ps->condition = 0;
1258:
1.1 nicm 1259: for (;;) {
1260: ch = yylex_getc();
1261:
1262: if (ch == EOF) {
1263: /*
1264: * Ensure every file or string is terminated by a
1265: * newline. This keeps the parser simpler and avoids
1266: * having to add a newline to each string.
1267: */
1268: if (ps->eof)
1269: break;
1270: ps->eof = 1;
1271: return ('\n');
1272: }
1273:
1274: if (ch == ' ' || ch == '\t') {
1275: /*
1276: * Ignore whitespace.
1277: */
1278: continue;
1279: }
1280:
1281: if (ch == '\n') {
1282: /*
1283: * End of line. Update the line number.
1284: */
1.8 nicm 1285: ps->eol = 1;
1.1 nicm 1286: return ('\n');
1287: }
1288:
1.28 nicm 1289: if (ch == ';' || ch == '{' || ch == '}') {
1.1 nicm 1290: /*
1.28 nicm 1291: * A semicolon or { or } is itself.
1.1 nicm 1292: */
1.28 nicm 1293: return (ch);
1.1 nicm 1294: }
1295:
1296: if (ch == '#') {
1297: /*
1.15 nicm 1298: * #{ after a condition opens a format; anything else
1299: * is a comment, ignore up to the end of the line.
1.1 nicm 1300: */
1301: next = yylex_getc();
1.15 nicm 1302: if (condition && next == '{') {
1.1 nicm 1303: yylval.token = yylex_format();
1304: if (yylval.token == NULL)
1305: return (ERROR);
1306: return (FORMAT);
1307: }
1308: while (next != '\n' && next != EOF)
1309: next = yylex_getc();
1310: if (next == '\n') {
1311: ps->input->line++;
1312: return ('\n');
1313: }
1314: continue;
1315: }
1316:
1317: if (ch == '%') {
1318: /*
1.11 nicm 1319: * % is a condition unless it is all % or all numbers,
1320: * then it is a token.
1.1 nicm 1321: */
1322: yylval.token = yylex_get_word('%');
1.11 nicm 1323: for (cp = yylval.token; *cp != '\0'; cp++) {
1324: if (*cp != '%' && !isdigit((u_char)*cp))
1325: break;
1326: }
1327: if (*cp == '\0')
1.1 nicm 1328: return (TOKEN);
1.15 nicm 1329: ps->condition = 1;
1.24 nicm 1330: if (strcmp(yylval.token, "%hidden") == 0) {
1331: free(yylval.token);
1332: return (HIDDEN);
1333: }
1.1 nicm 1334: if (strcmp(yylval.token, "%if") == 0) {
1335: free(yylval.token);
1336: return (IF);
1337: }
1338: if (strcmp(yylval.token, "%else") == 0) {
1339: free(yylval.token);
1340: return (ELSE);
1341: }
1342: if (strcmp(yylval.token, "%elif") == 0) {
1343: free(yylval.token);
1344: return (ELIF);
1345: }
1346: if (strcmp(yylval.token, "%endif") == 0) {
1347: free(yylval.token);
1348: return (ENDIF);
1349: }
1350: free(yylval.token);
1351: return (ERROR);
1352: }
1353:
1354: /*
1355: * Otherwise this is a token.
1356: */
1357: token = yylex_token(ch);
1358: if (token == NULL)
1359: return (ERROR);
1360: yylval.token = token;
1361:
1362: if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1363: for (cp = token + 1; *cp != '='; cp++) {
1364: if (!yylex_is_var(*cp, 0))
1365: break;
1366: }
1367: if (*cp == '=')
1368: return (EQUALS);
1369: }
1370: return (TOKEN);
1371: }
1372: return (0);
1373: }
1374:
1375: static char *
1376: yylex_format(void)
1377: {
1378: char *buf;
1379: size_t len;
1380: int ch, brackets = 1;
1381:
1382: len = 0;
1383: buf = xmalloc(1);
1384:
1385: yylex_append(&buf, &len, "#{", 2);
1386: for (;;) {
1387: if ((ch = yylex_getc()) == EOF || ch == '\n')
1388: goto error;
1389: if (ch == '#') {
1390: if ((ch = yylex_getc()) == EOF || ch == '\n')
1391: goto error;
1392: if (ch == '{')
1393: brackets++;
1394: yylex_append1(&buf, &len, '#');
1395: } else if (ch == '}') {
1396: if (brackets != 0 && --brackets == 0) {
1397: yylex_append1(&buf, &len, ch);
1398: break;
1399: }
1400: }
1401: yylex_append1(&buf, &len, ch);
1402: }
1403: if (brackets != 0)
1404: goto error;
1405:
1406: buf[len] = '\0';
1407: log_debug("%s: %s", __func__, buf);
1408: return (buf);
1409:
1410: error:
1411: free(buf);
1412: return (NULL);
1413: }
1414:
1415: static int
1416: yylex_token_escape(char **buf, size_t *len)
1417: {
1.27 nicm 1418: int ch, type, o2, o3, mlen;
1419: u_int size, i, tmp;
1420: char s[9], m[MB_LEN_MAX];
1.1 nicm 1421:
1.7 nicm 1422: ch = yylex_getc();
1423:
1424: if (ch >= '4' && ch <= '7') {
1425: yyerror("invalid octal escape");
1426: return (0);
1427: }
1428: if (ch >= '0' && ch <= '3') {
1429: o2 = yylex_getc();
1430: if (o2 >= '0' && o2 <= '7') {
1431: o3 = yylex_getc();
1432: if (o3 >= '0' && o3 <= '7') {
1433: ch = 64 * (ch - '0') +
1434: 8 * (o2 - '0') +
1435: (o3 - '0');
1436: yylex_append1(buf, len, ch);
1437: return (1);
1438: }
1439: }
1440: yyerror("invalid octal escape");
1441: return (0);
1442: }
1443:
1444: switch (ch) {
1.1 nicm 1445: case EOF:
1446: return (0);
1.9 nicm 1447: case 'a':
1448: ch = '\a';
1449: break;
1450: case 'b':
1451: ch = '\b';
1452: break;
1.1 nicm 1453: case 'e':
1454: ch = '\033';
1.9 nicm 1455: break;
1456: case 'f':
1457: ch = '\f';
1458: break;
1459: case 's':
1460: ch = ' ';
1461: break;
1462: case 'v':
1463: ch = '\v';
1.1 nicm 1464: break;
1465: case 'r':
1466: ch = '\r';
1467: break;
1468: case 'n':
1469: ch = '\n';
1470: break;
1471: case 't':
1472: ch = '\t';
1473: break;
1474: case 'u':
1475: type = 'u';
1476: size = 4;
1477: goto unicode;
1478: case 'U':
1479: type = 'U';
1480: size = 8;
1481: goto unicode;
1482: }
1483:
1484: yylex_append1(buf, len, ch);
1485: return (1);
1486:
1487: unicode:
1488: for (i = 0; i < size; i++) {
1489: ch = yylex_getc();
1490: if (ch == EOF || ch == '\n')
1491: return (0);
1492: if (!isxdigit((u_char)ch)) {
1493: yyerror("invalid \\%c argument", type);
1494: return (0);
1495: }
1496: s[i] = ch;
1497: }
1498: s[i] = '\0';
1499:
1500: if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1501: (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1502: yyerror("invalid \\%c argument", type);
1503: return (0);
1504: }
1.27 nicm 1505: mlen = wctomb(m, tmp);
1506: if (mlen <= 0 || mlen > (int)sizeof m) {
1.1 nicm 1507: yyerror("invalid \\%c argument", type);
1508: return (0);
1509: }
1.27 nicm 1510: yylex_append(buf, len, m, mlen);
1.1 nicm 1511: return (1);
1512: }
1513:
1514: static int
1515: yylex_token_variable(char **buf, size_t *len)
1516: {
1517: struct environ_entry *envent;
1518: int ch, brackets = 0;
1.19 nicm 1519: char name[1024];
1.1 nicm 1520: size_t namelen = 0;
1521: const char *value;
1522:
1523: ch = yylex_getc();
1524: if (ch == EOF)
1525: return (0);
1526: if (ch == '{')
1527: brackets = 1;
1528: else {
1529: if (!yylex_is_var(ch, 1)) {
1530: yylex_append1(buf, len, '$');
1.5 nicm 1531: yylex_ungetc(ch);
1.1 nicm 1532: return (1);
1533: }
1534: name[namelen++] = ch;
1535: }
1536:
1537: for (;;) {
1538: ch = yylex_getc();
1539: if (brackets && ch == '}')
1540: break;
1541: if (ch == EOF || !yylex_is_var(ch, 0)) {
1542: if (!brackets) {
1.5 nicm 1543: yylex_ungetc(ch);
1.1 nicm 1544: break;
1545: }
1546: yyerror("invalid environment variable");
1547: return (0);
1548: }
1549: if (namelen == (sizeof name) - 2) {
1550: yyerror("environment variable is too long");
1551: return (0);
1552: }
1553: name[namelen++] = ch;
1554: }
1555: name[namelen] = '\0';
1556:
1557: envent = environ_find(global_environ, name);
1.31 nicm 1558: if (envent != NULL && envent->value != NULL) {
1.1 nicm 1559: value = envent->value;
1560: log_debug("%s: %s -> %s", __func__, name, value);
1561: yylex_append(buf, len, value, strlen(value));
1562: }
1563: return (1);
1564: }
1565:
1566: static int
1567: yylex_token_tilde(char **buf, size_t *len)
1568: {
1569: struct environ_entry *envent;
1570: int ch;
1.19 nicm 1571: char name[1024];
1.1 nicm 1572: size_t namelen = 0;
1573: struct passwd *pw;
1574: const char *home = NULL;
1575:
1576: for (;;) {
1577: ch = yylex_getc();
1578: if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1.5 nicm 1579: yylex_ungetc(ch);
1.1 nicm 1580: break;
1581: }
1582: if (namelen == (sizeof name) - 2) {
1583: yyerror("user name is too long");
1584: return (0);
1585: }
1586: name[namelen++] = ch;
1587: }
1588: name[namelen] = '\0';
1589:
1590: if (*name == '\0') {
1591: envent = environ_find(global_environ, "HOME");
1592: if (envent != NULL && *envent->value != '\0')
1593: home = envent->value;
1594: else if ((pw = getpwuid(getuid())) != NULL)
1595: home = pw->pw_dir;
1596: } else {
1597: if ((pw = getpwnam(name)) != NULL)
1598: home = pw->pw_dir;
1599: }
1600: if (home == NULL)
1601: return (0);
1602:
1603: log_debug("%s: ~%s -> %s", __func__, name, home);
1604: yylex_append(buf, len, home, strlen(home));
1605: return (1);
1606: }
1607:
1608: static char *
1609: yylex_token(int ch)
1610: {
1611: char *buf;
1612: size_t len;
1613: enum { START,
1614: NONE,
1615: DOUBLE_QUOTES,
1616: SINGLE_QUOTES } state = NONE, last = START;
1617:
1618: len = 0;
1619: buf = xmalloc(1);
1620:
1621: for (;;) {
1.29 nicm 1622: /* EOF or \n are always the end of the token. */
1623: if (ch == EOF || (state == NONE && ch == '\n'))
1.1 nicm 1624: break;
1625:
1.29 nicm 1626: /* Whitespace or ; or } ends a token unless inside quotes. */
1.28 nicm 1627: if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') &&
1628: state == NONE)
1.1 nicm 1629: break;
1630:
1.32 nicm 1631: /*
1632: * Spaces and comments inside quotes after \n are removed but
1633: * the \n is left.
1634: */
1.29 nicm 1635: if (ch == '\n' && state != NONE) {
1.32 nicm 1636: yylex_append1(&buf, &len, '\n');
1.29 nicm 1637: while ((ch = yylex_getc()) == ' ' || ch == '\t')
1638: /* nothing */;
1639: if (ch != '#')
1640: continue;
1641: ch = yylex_getc();
1642: if (strchr(",#{}:", ch) != NULL) {
1643: yylex_ungetc(ch);
1644: ch = '#';
1645: } else {
1646: while ((ch = yylex_getc()) != '\n' && ch != EOF)
1647: /* nothing */;
1648: }
1649: continue;
1650: }
1651:
1652: /* \ ~ and $ are expanded except in single quotes. */
1.1 nicm 1653: if (ch == '\\' && state != SINGLE_QUOTES) {
1654: if (!yylex_token_escape(&buf, &len))
1655: goto error;
1656: goto skip;
1657: }
1658: if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1659: if (!yylex_token_tilde(&buf, &len))
1660: goto error;
1661: goto skip;
1662: }
1663: if (ch == '$' && state != SINGLE_QUOTES) {
1664: if (!yylex_token_variable(&buf, &len))
1.6 nicm 1665: goto error;
1666: goto skip;
1667: }
1668: if (ch == '}' && state == NONE)
1669: goto error; /* unmatched (matched ones were handled) */
1.1 nicm 1670:
1.29 nicm 1671: /* ' and " starts or end quotes (and is consumed). */
1.1 nicm 1672: if (ch == '\'') {
1673: if (state == NONE) {
1674: state = SINGLE_QUOTES;
1675: goto next;
1676: }
1677: if (state == SINGLE_QUOTES) {
1678: state = NONE;
1679: goto next;
1680: }
1681: }
1682: if (ch == '"') {
1683: if (state == NONE) {
1684: state = DOUBLE_QUOTES;
1685: goto next;
1686: }
1687: if (state == DOUBLE_QUOTES) {
1688: state = NONE;
1689: goto next;
1690: }
1691: }
1692:
1.29 nicm 1693: /* Otherwise add the character to the buffer. */
1.1 nicm 1694: yylex_append1(&buf, &len, ch);
1695:
1696: skip:
1697: last = state;
1698:
1699: next:
1700: ch = yylex_getc();
1701: }
1.5 nicm 1702: yylex_ungetc(ch);
1.1 nicm 1703:
1704: buf[len] = '\0';
1705: log_debug("%s: %s", __func__, buf);
1706: return (buf);
1707:
1708: error:
1709: free(buf);
1710: return (NULL);
1711: }