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