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