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