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