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