Annotation of src/usr.bin/tmux/cmd-parse.y, Revision 1.30
1.30 ! nicm 1: /* $OpenBSD: cmd-parse.y,v 1.29 2020/06/04 08:30:44 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: }
1.30 ! nicm 538: | statements statement '}'
1.28 nicm 539: {
540: $$ = $1;
1.30 ! nicm 541: TAILQ_CONCAT($$, $2, entry);
! 542: free($2);
1.28 nicm 543: }
1.1 nicm 544:
545: %%
546:
547: static char *
548: cmd_parse_get_error(const char *file, u_int line, const char *error)
549: {
550: char *s;
551:
552: if (file == NULL)
553: s = xstrdup(error);
554: else
555: xasprintf (&s, "%s:%u: %s", file, line, error);
556: return (s);
557: }
558:
559: static void
1.14 nicm 560: cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
561: struct cmd_list *cmdlist)
562: {
563: char *s;
564:
565: if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
566: s = cmd_list_print(cmdlist, 0);
1.16 nicm 567: if (pi->file != NULL)
568: cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
569: else
570: cmdq_print(pi->item, "%u: %s", line, s);
1.14 nicm 571: free(s);
572: }
573: }
574:
575: static void
1.1 nicm 576: cmd_parse_free_command(struct cmd_parse_command *cmd)
577: {
578: cmd_free_argv(cmd->argc, cmd->argv);
579: free(cmd);
580: }
581:
1.13 nicm 582: static struct cmd_parse_commands *
583: cmd_parse_new_commands(void)
584: {
585: struct cmd_parse_commands *cmds;
586:
587: cmds = xmalloc(sizeof *cmds);
588: TAILQ_INIT (cmds);
589: return (cmds);
590: }
591:
1.1 nicm 592: static void
593: cmd_parse_free_commands(struct cmd_parse_commands *cmds)
594: {
595: struct cmd_parse_command *cmd, *cmd1;
596:
597: TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
598: TAILQ_REMOVE(cmds, cmd, entry);
599: cmd_parse_free_command(cmd);
600: }
1.13 nicm 601: free(cmds);
1.1 nicm 602: }
603:
1.28 nicm 604: static char *
605: cmd_parse_commands_to_string(struct cmd_parse_commands *cmds)
606: {
607: struct cmd_parse_command *cmd;
608: char *string = NULL, *s, *line;
609:
610: TAILQ_FOREACH(cmd, cmds, entry) {
611: line = cmd_stringify_argv(cmd->argc, cmd->argv);
612: if (string == NULL)
613: s = line;
614: else {
615: xasprintf(&s, "%s ; %s", s, line);
616: free(line);
617: }
618:
619: free(string);
620: string = s;
621: }
622: if (string == NULL)
623: string = xstrdup("");
624: log_debug("%s: %s", __func__, string);
625: return (string);
626: }
627:
1.1 nicm 628: static struct cmd_parse_commands *
1.5 nicm 629: cmd_parse_run_parser(char **cause)
1.1 nicm 630: {
1.13 nicm 631: struct cmd_parse_state *ps = &parse_state;
632: struct cmd_parse_scope *scope, *scope1;
633: int retval;
1.1 nicm 634:
1.13 nicm 635: ps->commands = NULL;
1.1 nicm 636: TAILQ_INIT(&ps->stack);
637:
638: retval = yyparse();
639: TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
640: TAILQ_REMOVE(&ps->stack, scope, entry);
641: free(scope);
642: }
643: if (retval != 0) {
644: *cause = ps->error;
645: return (NULL);
646: }
647:
1.13 nicm 648: if (ps->commands == NULL)
649: return (cmd_parse_new_commands());
650: return (ps->commands);
1.1 nicm 651: }
652:
1.5 nicm 653: static struct cmd_parse_commands *
654: cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
655: {
656: struct cmd_parse_state *ps = &parse_state;
657:
658: memset(ps, 0, sizeof *ps);
659: ps->input = pi;
660: ps->f = f;
661: return (cmd_parse_run_parser(cause));
662: }
663:
664: static struct cmd_parse_commands *
665: cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
666: char **cause)
667: {
668: struct cmd_parse_state *ps = &parse_state;
669:
670: memset(ps, 0, sizeof *ps);
671: ps->input = pi;
672: ps->buf = buf;
673: ps->len = len;
674: return (cmd_parse_run_parser(cause));
675: }
676:
1.4 nicm 677: static struct cmd_parse_result *
678: cmd_parse_build_commands(struct cmd_parse_commands *cmds,
679: struct cmd_parse_input *pi)
1.1 nicm 680: {
681: static struct cmd_parse_result pr;
1.4 nicm 682: struct cmd_parse_commands *cmds2;
1.1 nicm 683: struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after;
684: u_int line = UINT_MAX;
685: int i;
686: struct cmd_list *cmdlist = NULL, *result;
687: struct cmd *add;
1.28 nicm 688: char *name, *alias, *cause, *s;
1.1 nicm 689:
1.4 nicm 690: /* Check for an empty list. */
1.1 nicm 691: if (TAILQ_EMPTY(cmds)) {
1.13 nicm 692: cmd_parse_free_commands(cmds);
1.1 nicm 693: pr.status = CMD_PARSE_EMPTY;
694: return (&pr);
695: }
696:
697: /*
698: * Walk the commands and expand any aliases. Each alias is parsed
699: * individually to a new command list, any trailing arguments appended
700: * to the last command, and all commands inserted into the original
701: * command list.
702: */
703: TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
1.28 nicm 704: name = cmd->argv[0];
705:
706: alias = cmd_get_alias(name);
1.1 nicm 707: if (alias == NULL)
708: continue;
709:
710: line = cmd->line;
1.28 nicm 711: log_debug("%s: %u %s = %s", __func__, line, name, alias);
1.1 nicm 712:
713: pi->line = line;
1.5 nicm 714: cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
1.1 nicm 715: free(alias);
716: if (cmds2 == NULL) {
717: pr.status = CMD_PARSE_ERROR;
718: pr.error = cause;
719: goto out;
720: }
721:
722: cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands);
723: if (cmd2 == NULL) {
724: TAILQ_REMOVE(cmds, cmd, entry);
725: cmd_parse_free_command(cmd);
726: continue;
727: }
1.28 nicm 728: for (i = 1; i < cmd->argc; i++)
1.1 nicm 729: cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
730:
731: after = cmd;
732: TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) {
733: cmd2->line = line;
734: TAILQ_REMOVE(cmds2, cmd2, entry);
735: TAILQ_INSERT_AFTER(cmds, after, cmd2, entry);
736: after = cmd2;
737: }
738: cmd_parse_free_commands(cmds2);
739:
740: TAILQ_REMOVE(cmds, cmd, entry);
741: cmd_parse_free_command(cmd);
742: }
743:
744: /*
745: * Parse each command into a command list. Create a new command list
1.25 nicm 746: * for each line (unless the flag is set) so they get a new group (so
747: * the queue knows which ones to remove if a command fails when
748: * executed).
1.1 nicm 749: */
750: result = cmd_list_new();
751: TAILQ_FOREACH(cmd, cmds, entry) {
1.28 nicm 752: name = cmd->argv[0];
753: log_debug("%s: %u %s", __func__, cmd->line, name);
1.1 nicm 754: cmd_log_argv(cmd->argc, cmd->argv, __func__);
755:
1.25 nicm 756: if (cmdlist == NULL ||
757: ((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
1.1 nicm 758: if (cmdlist != NULL) {
1.14 nicm 759: cmd_parse_print_commands(pi, line, cmdlist);
1.1 nicm 760: cmd_list_move(result, cmdlist);
761: cmd_list_free(cmdlist);
762: }
763: cmdlist = cmd_list_new();
764: }
765: line = cmd->line;
766:
767: add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
768: if (add == NULL) {
769: cmd_list_free(result);
770: pr.status = CMD_PARSE_ERROR;
771: pr.error = cmd_parse_get_error(pi->file, line, cause);
772: free(cause);
1.20 nicm 773: cmd_list_free(cmdlist);
1.1 nicm 774: goto out;
775: }
776: cmd_list_append(cmdlist, add);
777: }
778: if (cmdlist != NULL) {
1.14 nicm 779: cmd_parse_print_commands(pi, line, cmdlist);
1.1 nicm 780: cmd_list_move(result, cmdlist);
781: cmd_list_free(cmdlist);
782: }
783:
1.2 nicm 784: s = cmd_list_print(result, 0);
1.1 nicm 785: log_debug("%s: %s", __func__, s);
786: free(s);
787:
788: pr.status = CMD_PARSE_SUCCESS;
789: pr.cmdlist = result;
790:
791: out:
792: cmd_parse_free_commands(cmds);
793:
794: return (&pr);
795: }
796:
797: struct cmd_parse_result *
1.4 nicm 798: cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
799: {
800: static struct cmd_parse_result pr;
801: struct cmd_parse_input input;
802: struct cmd_parse_commands *cmds;
803: char *cause;
804:
805: if (pi == NULL) {
806: memset(&input, 0, sizeof input);
807: pi = &input;
808: }
809: memset(&pr, 0, sizeof pr);
810:
1.5 nicm 811: cmds = cmd_parse_do_file(f, pi, &cause);
1.4 nicm 812: if (cmds == NULL) {
813: pr.status = CMD_PARSE_ERROR;
814: pr.error = cause;
815: return (&pr);
816: }
817: return (cmd_parse_build_commands(cmds, pi));
818: }
819:
820: struct cmd_parse_result *
1.1 nicm 821: cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
822: {
1.25 nicm 823: struct cmd_parse_input input;
824:
825: if (pi == NULL) {
826: memset(&input, 0, sizeof input);
827: pi = &input;
828: }
829:
830: /*
831: * When parsing a string, put commands in one group even if there are
832: * multiple lines. This means { a \n b } is identical to "a ; b" when
833: * given as an argument to another command.
834: */
835: pi->flags |= CMD_PARSE_ONEGROUP;
1.21 nicm 836: return (cmd_parse_from_buffer(s, strlen(s), pi));
1.26 nicm 837: }
838:
839: enum cmd_parse_status
840: cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi,
841: struct cmdq_item *after, struct cmdq_state *state, char **error)
842: {
843: struct cmd_parse_result *pr;
844: struct cmdq_item *item;
845:
846: pr = cmd_parse_from_string(s, pi);
847: switch (pr->status) {
848: case CMD_PARSE_EMPTY:
849: break;
850: case CMD_PARSE_ERROR:
851: if (error != NULL)
852: *error = pr->error;
853: else
854: free(pr->error);
855: break;
856: case CMD_PARSE_SUCCESS:
857: item = cmdq_get_command(pr->cmdlist, state);
858: cmdq_insert_after(after, item);
859: cmd_list_free(pr->cmdlist);
860: break;
861: }
862: return (pr->status);
863: }
864:
865: enum cmd_parse_status
866: cmd_parse_and_append(const char *s, struct cmd_parse_input *pi,
867: struct client *c, struct cmdq_state *state, char **error)
868: {
869: struct cmd_parse_result *pr;
870: struct cmdq_item *item;
871:
872: pr = cmd_parse_from_string(s, pi);
873: switch (pr->status) {
874: case CMD_PARSE_EMPTY:
875: break;
876: case CMD_PARSE_ERROR:
877: if (error != NULL)
878: *error = pr->error;
879: else
880: free(pr->error);
881: break;
882: case CMD_PARSE_SUCCESS:
883: item = cmdq_get_command(pr->cmdlist, state);
884: cmdq_append(c, item);
885: cmd_list_free(pr->cmdlist);
886: break;
887: }
888: return (pr->status);
1.21 nicm 889: }
890:
891: struct cmd_parse_result *
892: cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
893: {
1.1 nicm 894: static struct cmd_parse_result pr;
1.4 nicm 895: struct cmd_parse_input input;
1.5 nicm 896: struct cmd_parse_commands *cmds;
897: char *cause;
1.1 nicm 898:
1.4 nicm 899: if (pi == NULL) {
900: memset(&input, 0, sizeof input);
901: pi = &input;
902: }
903: memset(&pr, 0, sizeof pr);
904:
1.21 nicm 905: if (len == 0) {
1.1 nicm 906: pr.status = CMD_PARSE_EMPTY;
907: pr.cmdlist = NULL;
908: pr.error = NULL;
909: return (&pr);
910: }
911:
1.21 nicm 912: cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
1.5 nicm 913: if (cmds == NULL) {
1.1 nicm 914: pr.status = CMD_PARSE_ERROR;
1.5 nicm 915: pr.error = cause;
916: return (&pr);
1.1 nicm 917: }
1.5 nicm 918: return (cmd_parse_build_commands(cmds, pi));
1.4 nicm 919: }
920:
921: struct cmd_parse_result *
922: cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
923: {
924: struct cmd_parse_input input;
925: struct cmd_parse_commands *cmds;
926: struct cmd_parse_command *cmd;
927: char **copy, **new_argv;
928: size_t size;
929: int i, last, new_argc;
930:
931: /*
932: * The commands are already split up into arguments, so just separate
933: * into a set of commands by ';'.
934: */
935:
936: if (pi == NULL) {
937: memset(&input, 0, sizeof input);
938: pi = &input;
939: }
940: cmd_log_argv(argc, argv, "%s", __func__);
941:
1.13 nicm 942: cmds = cmd_parse_new_commands();
1.4 nicm 943: copy = cmd_copy_argv(argc, argv);
944:
945: last = 0;
946: for (i = 0; i < argc; i++) {
947: size = strlen(copy[i]);
948: if (size == 0 || copy[i][size - 1] != ';')
949: continue;
950: copy[i][--size] = '\0';
951: if (size > 0 && copy[i][size - 1] == '\\') {
952: copy[i][size - 1] = ';';
953: continue;
954: }
955:
956: new_argc = i - last;
957: new_argv = copy + last;
958: if (size != 0)
959: new_argc++;
960:
961: if (new_argc != 0) {
962: cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
963: i);
964:
965: cmd = xcalloc(1, sizeof *cmd);
966: cmd->line = pi->line;
967:
1.28 nicm 968: cmd->argc = new_argc;
969: cmd->argv = cmd_copy_argv(new_argc, new_argv);
1.4 nicm 970:
971: TAILQ_INSERT_TAIL(cmds, cmd, entry);
972: }
973:
974: last = i + 1;
975: }
976: if (last != argc) {
977: new_argv = copy + last;
978: new_argc = argc - last;
979:
980: if (new_argc != 0) {
981: cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
982: last);
983:
984: cmd = xcalloc(1, sizeof *cmd);
985: cmd->line = pi->line;
986:
1.28 nicm 987: cmd->argc = new_argc;
988: cmd->argv = cmd_copy_argv(new_argc, new_argv);
1.4 nicm 989:
990: TAILQ_INSERT_TAIL(cmds, cmd, entry);
991: }
992: }
1.13 nicm 993:
994: cmd_free_argv(argc, copy);
1.4 nicm 995: return (cmd_parse_build_commands(cmds, pi));
1.1 nicm 996: }
997:
998: static int printflike(1, 2)
999: yyerror(const char *fmt, ...)
1000: {
1001: struct cmd_parse_state *ps = &parse_state;
1002: struct cmd_parse_input *pi = ps->input;
1003: va_list ap;
1004: char *error;
1005:
1006: if (ps->error != NULL)
1007: return (0);
1008:
1009: va_start(ap, fmt);
1010: xvasprintf(&error, fmt, ap);
1011: va_end(ap);
1012:
1013: ps->error = cmd_parse_get_error(pi->file, pi->line, error);
1014: free(error);
1015: return (0);
1016: }
1017:
1018: static int
1019: yylex_is_var(char ch, int first)
1020: {
1021: if (ch == '=')
1022: return (0);
1023: if (first && isdigit((u_char)ch))
1024: return (0);
1025: return (isalnum((u_char)ch) || ch == '_');
1026: }
1027:
1028: static void
1029: yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
1030: {
1031: if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
1032: fatalx("buffer is too big");
1033: *buf = xrealloc(*buf, (*len) + 1 + addlen);
1034: memcpy((*buf) + *len, add, addlen);
1035: (*len) += addlen;
1036: }
1037:
1038: static void
1039: yylex_append1(char **buf, size_t *len, char add)
1040: {
1041: yylex_append(buf, len, &add, 1);
1042: }
1043:
1044: static int
1.5 nicm 1045: yylex_getc1(void)
1046: {
1047: struct cmd_parse_state *ps = &parse_state;
1048: int ch;
1049:
1050: if (ps->f != NULL)
1051: ch = getc(ps->f);
1052: else {
1053: if (ps->off == ps->len)
1054: ch = EOF;
1055: else
1056: ch = ps->buf[ps->off++];
1057: }
1058: return (ch);
1059: }
1060:
1061: static void
1062: yylex_ungetc(int ch)
1063: {
1064: struct cmd_parse_state *ps = &parse_state;
1065:
1066: if (ps->f != NULL)
1067: ungetc(ch, ps->f);
1068: else if (ps->off > 0 && ch != EOF)
1069: ps->off--;
1070: }
1071:
1072: static int
1.1 nicm 1073: yylex_getc(void)
1074: {
1075: struct cmd_parse_state *ps = &parse_state;
1076: int ch;
1077:
1078: if (ps->escapes != 0) {
1079: ps->escapes--;
1080: return ('\\');
1081: }
1082: for (;;) {
1.5 nicm 1083: ch = yylex_getc1();
1.1 nicm 1084: if (ch == '\\') {
1085: ps->escapes++;
1086: continue;
1087: }
1088: if (ch == '\n' && (ps->escapes % 2) == 1) {
1089: ps->input->line++;
1090: ps->escapes--;
1091: continue;
1092: }
1093:
1094: if (ps->escapes != 0) {
1.5 nicm 1095: yylex_ungetc(ch);
1.1 nicm 1096: ps->escapes--;
1097: return ('\\');
1098: }
1099: return (ch);
1100: }
1101: }
1102:
1103: static char *
1104: yylex_get_word(int ch)
1105: {
1.5 nicm 1106: char *buf;
1107: size_t len;
1.1 nicm 1108:
1109: len = 0;
1110: buf = xmalloc(1);
1111:
1112: do
1113: yylex_append1(&buf, &len, ch);
1114: while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
1.5 nicm 1115: yylex_ungetc(ch);
1.1 nicm 1116:
1117: buf[len] = '\0';
1118: log_debug("%s: %s", __func__, buf);
1119: return (buf);
1120: }
1121:
1122: static int
1123: yylex(void)
1124: {
1125: struct cmd_parse_state *ps = &parse_state;
1126: char *token, *cp;
1.15 nicm 1127: int ch, next, condition;
1.1 nicm 1128:
1.8 nicm 1129: if (ps->eol)
1130: ps->input->line++;
1131: ps->eol = 0;
1132:
1.15 nicm 1133: condition = ps->condition;
1134: ps->condition = 0;
1135:
1.1 nicm 1136: for (;;) {
1137: ch = yylex_getc();
1138:
1139: if (ch == EOF) {
1140: /*
1141: * Ensure every file or string is terminated by a
1142: * newline. This keeps the parser simpler and avoids
1143: * having to add a newline to each string.
1144: */
1145: if (ps->eof)
1146: break;
1147: ps->eof = 1;
1148: return ('\n');
1149: }
1150:
1151: if (ch == ' ' || ch == '\t') {
1152: /*
1153: * Ignore whitespace.
1154: */
1155: continue;
1156: }
1157:
1158: if (ch == '\n') {
1159: /*
1160: * End of line. Update the line number.
1161: */
1.8 nicm 1162: ps->eol = 1;
1.1 nicm 1163: return ('\n');
1164: }
1165:
1.28 nicm 1166: if (ch == ';' || ch == '{' || ch == '}') {
1.1 nicm 1167: /*
1.28 nicm 1168: * A semicolon or { or } is itself.
1.1 nicm 1169: */
1.28 nicm 1170: return (ch);
1.1 nicm 1171: }
1172:
1173: if (ch == '#') {
1174: /*
1.15 nicm 1175: * #{ after a condition opens a format; anything else
1176: * is a comment, ignore up to the end of the line.
1.1 nicm 1177: */
1178: next = yylex_getc();
1.15 nicm 1179: if (condition && next == '{') {
1.1 nicm 1180: yylval.token = yylex_format();
1181: if (yylval.token == NULL)
1182: return (ERROR);
1183: return (FORMAT);
1184: }
1185: while (next != '\n' && next != EOF)
1186: next = yylex_getc();
1187: if (next == '\n') {
1188: ps->input->line++;
1189: return ('\n');
1190: }
1191: continue;
1192: }
1193:
1194: if (ch == '%') {
1195: /*
1.11 nicm 1196: * % is a condition unless it is all % or all numbers,
1197: * then it is a token.
1.1 nicm 1198: */
1199: yylval.token = yylex_get_word('%');
1.11 nicm 1200: for (cp = yylval.token; *cp != '\0'; cp++) {
1201: if (*cp != '%' && !isdigit((u_char)*cp))
1202: break;
1203: }
1204: if (*cp == '\0')
1.1 nicm 1205: return (TOKEN);
1.15 nicm 1206: ps->condition = 1;
1.24 nicm 1207: if (strcmp(yylval.token, "%hidden") == 0) {
1208: free(yylval.token);
1209: return (HIDDEN);
1210: }
1.1 nicm 1211: if (strcmp(yylval.token, "%if") == 0) {
1212: free(yylval.token);
1213: return (IF);
1214: }
1215: if (strcmp(yylval.token, "%else") == 0) {
1216: free(yylval.token);
1217: return (ELSE);
1218: }
1219: if (strcmp(yylval.token, "%elif") == 0) {
1220: free(yylval.token);
1221: return (ELIF);
1222: }
1223: if (strcmp(yylval.token, "%endif") == 0) {
1224: free(yylval.token);
1225: return (ENDIF);
1226: }
1227: free(yylval.token);
1228: return (ERROR);
1229: }
1230:
1231: /*
1232: * Otherwise this is a token.
1233: */
1234: token = yylex_token(ch);
1235: if (token == NULL)
1236: return (ERROR);
1237: yylval.token = token;
1238:
1239: if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1240: for (cp = token + 1; *cp != '='; cp++) {
1241: if (!yylex_is_var(*cp, 0))
1242: break;
1243: }
1244: if (*cp == '=')
1245: return (EQUALS);
1246: }
1247: return (TOKEN);
1248: }
1249: return (0);
1250: }
1251:
1252: static char *
1253: yylex_format(void)
1254: {
1255: char *buf;
1256: size_t len;
1257: int ch, brackets = 1;
1258:
1259: len = 0;
1260: buf = xmalloc(1);
1261:
1262: yylex_append(&buf, &len, "#{", 2);
1263: for (;;) {
1264: if ((ch = yylex_getc()) == EOF || ch == '\n')
1265: goto error;
1266: if (ch == '#') {
1267: if ((ch = yylex_getc()) == EOF || ch == '\n')
1268: goto error;
1269: if (ch == '{')
1270: brackets++;
1271: yylex_append1(&buf, &len, '#');
1272: } else if (ch == '}') {
1273: if (brackets != 0 && --brackets == 0) {
1274: yylex_append1(&buf, &len, ch);
1275: break;
1276: }
1277: }
1278: yylex_append1(&buf, &len, ch);
1279: }
1280: if (brackets != 0)
1281: goto error;
1282:
1283: buf[len] = '\0';
1284: log_debug("%s: %s", __func__, buf);
1285: return (buf);
1286:
1287: error:
1288: free(buf);
1289: return (NULL);
1290: }
1291:
1292: static int
1293: yylex_token_escape(char **buf, size_t *len)
1294: {
1.27 nicm 1295: int ch, type, o2, o3, mlen;
1296: u_int size, i, tmp;
1297: char s[9], m[MB_LEN_MAX];
1.1 nicm 1298:
1.7 nicm 1299: ch = yylex_getc();
1300:
1301: if (ch >= '4' && ch <= '7') {
1302: yyerror("invalid octal escape");
1303: return (0);
1304: }
1305: if (ch >= '0' && ch <= '3') {
1306: o2 = yylex_getc();
1307: if (o2 >= '0' && o2 <= '7') {
1308: o3 = yylex_getc();
1309: if (o3 >= '0' && o3 <= '7') {
1310: ch = 64 * (ch - '0') +
1311: 8 * (o2 - '0') +
1312: (o3 - '0');
1313: yylex_append1(buf, len, ch);
1314: return (1);
1315: }
1316: }
1317: yyerror("invalid octal escape");
1318: return (0);
1319: }
1320:
1321: switch (ch) {
1.1 nicm 1322: case EOF:
1323: return (0);
1.9 nicm 1324: case 'a':
1325: ch = '\a';
1326: break;
1327: case 'b':
1328: ch = '\b';
1329: break;
1.1 nicm 1330: case 'e':
1331: ch = '\033';
1.9 nicm 1332: break;
1333: case 'f':
1334: ch = '\f';
1335: break;
1336: case 's':
1337: ch = ' ';
1338: break;
1339: case 'v':
1340: ch = '\v';
1.1 nicm 1341: break;
1342: case 'r':
1343: ch = '\r';
1344: break;
1345: case 'n':
1346: ch = '\n';
1347: break;
1348: case 't':
1349: ch = '\t';
1350: break;
1351: case 'u':
1352: type = 'u';
1353: size = 4;
1354: goto unicode;
1355: case 'U':
1356: type = 'U';
1357: size = 8;
1358: goto unicode;
1359: }
1360:
1361: yylex_append1(buf, len, ch);
1362: return (1);
1363:
1364: unicode:
1365: for (i = 0; i < size; i++) {
1366: ch = yylex_getc();
1367: if (ch == EOF || ch == '\n')
1368: return (0);
1369: if (!isxdigit((u_char)ch)) {
1370: yyerror("invalid \\%c argument", type);
1371: return (0);
1372: }
1373: s[i] = ch;
1374: }
1375: s[i] = '\0';
1376:
1377: if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1378: (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1379: yyerror("invalid \\%c argument", type);
1380: return (0);
1381: }
1.27 nicm 1382: mlen = wctomb(m, tmp);
1383: if (mlen <= 0 || mlen > (int)sizeof m) {
1.1 nicm 1384: yyerror("invalid \\%c argument", type);
1385: return (0);
1386: }
1.27 nicm 1387: yylex_append(buf, len, m, mlen);
1.1 nicm 1388: return (1);
1389: }
1390:
1391: static int
1392: yylex_token_variable(char **buf, size_t *len)
1393: {
1394: struct environ_entry *envent;
1395: int ch, brackets = 0;
1.19 nicm 1396: char name[1024];
1.1 nicm 1397: size_t namelen = 0;
1398: const char *value;
1399:
1400: ch = yylex_getc();
1401: if (ch == EOF)
1402: return (0);
1403: if (ch == '{')
1404: brackets = 1;
1405: else {
1406: if (!yylex_is_var(ch, 1)) {
1407: yylex_append1(buf, len, '$');
1.5 nicm 1408: yylex_ungetc(ch);
1.1 nicm 1409: return (1);
1410: }
1411: name[namelen++] = ch;
1412: }
1413:
1414: for (;;) {
1415: ch = yylex_getc();
1416: if (brackets && ch == '}')
1417: break;
1418: if (ch == EOF || !yylex_is_var(ch, 0)) {
1419: if (!brackets) {
1.5 nicm 1420: yylex_ungetc(ch);
1.1 nicm 1421: break;
1422: }
1423: yyerror("invalid environment variable");
1424: return (0);
1425: }
1426: if (namelen == (sizeof name) - 2) {
1427: yyerror("environment variable is too long");
1428: return (0);
1429: }
1430: name[namelen++] = ch;
1431: }
1432: name[namelen] = '\0';
1433:
1434: envent = environ_find(global_environ, name);
1435: if (envent != NULL) {
1436: value = envent->value;
1437: log_debug("%s: %s -> %s", __func__, name, value);
1438: yylex_append(buf, len, value, strlen(value));
1439: }
1440: return (1);
1441: }
1442:
1443: static int
1444: yylex_token_tilde(char **buf, size_t *len)
1445: {
1446: struct environ_entry *envent;
1447: int ch;
1.19 nicm 1448: char name[1024];
1.1 nicm 1449: size_t namelen = 0;
1450: struct passwd *pw;
1451: const char *home = NULL;
1452:
1453: for (;;) {
1454: ch = yylex_getc();
1455: if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1.5 nicm 1456: yylex_ungetc(ch);
1.1 nicm 1457: break;
1458: }
1459: if (namelen == (sizeof name) - 2) {
1460: yyerror("user name is too long");
1461: return (0);
1462: }
1463: name[namelen++] = ch;
1464: }
1465: name[namelen] = '\0';
1466:
1467: if (*name == '\0') {
1468: envent = environ_find(global_environ, "HOME");
1469: if (envent != NULL && *envent->value != '\0')
1470: home = envent->value;
1471: else if ((pw = getpwuid(getuid())) != NULL)
1472: home = pw->pw_dir;
1473: } else {
1474: if ((pw = getpwnam(name)) != NULL)
1475: home = pw->pw_dir;
1476: }
1477: if (home == NULL)
1478: return (0);
1479:
1480: log_debug("%s: ~%s -> %s", __func__, name, home);
1481: yylex_append(buf, len, home, strlen(home));
1482: return (1);
1483: }
1484:
1485: static char *
1486: yylex_token(int ch)
1487: {
1488: char *buf;
1489: size_t len;
1490: enum { START,
1491: NONE,
1492: DOUBLE_QUOTES,
1493: SINGLE_QUOTES } state = NONE, last = START;
1494:
1495: len = 0;
1496: buf = xmalloc(1);
1497:
1498: for (;;) {
1.29 nicm 1499: /* EOF or \n are always the end of the token. */
1500: if (ch == EOF || (state == NONE && ch == '\n'))
1.1 nicm 1501: break;
1502:
1.29 nicm 1503: /* Whitespace or ; or } ends a token unless inside quotes. */
1.28 nicm 1504: if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') &&
1505: state == NONE)
1.1 nicm 1506: break;
1507:
1.29 nicm 1508: /* Spaces and comments inside quotes after \n are removed. */
1509: if (ch == '\n' && state != NONE) {
1510: while ((ch = yylex_getc()) == ' ' || ch == '\t')
1511: /* nothing */;
1512: if (ch != '#')
1513: continue;
1514: ch = yylex_getc();
1515: if (strchr(",#{}:", ch) != NULL) {
1516: yylex_ungetc(ch);
1517: ch = '#';
1518: } else {
1519: while ((ch = yylex_getc()) != '\n' && ch != EOF)
1520: /* nothing */;
1521: }
1522: continue;
1523: }
1524:
1525: /* \ ~ and $ are expanded except in single quotes. */
1.1 nicm 1526: if (ch == '\\' && state != SINGLE_QUOTES) {
1527: if (!yylex_token_escape(&buf, &len))
1528: goto error;
1529: goto skip;
1530: }
1531: if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1532: if (!yylex_token_tilde(&buf, &len))
1533: goto error;
1534: goto skip;
1535: }
1536: if (ch == '$' && state != SINGLE_QUOTES) {
1537: if (!yylex_token_variable(&buf, &len))
1.6 nicm 1538: goto error;
1539: goto skip;
1540: }
1541: if (ch == '}' && state == NONE)
1542: goto error; /* unmatched (matched ones were handled) */
1.1 nicm 1543:
1.29 nicm 1544: /* ' and " starts or end quotes (and is consumed). */
1.1 nicm 1545: if (ch == '\'') {
1546: if (state == NONE) {
1547: state = SINGLE_QUOTES;
1548: goto next;
1549: }
1550: if (state == SINGLE_QUOTES) {
1551: state = NONE;
1552: goto next;
1553: }
1554: }
1555: if (ch == '"') {
1556: if (state == NONE) {
1557: state = DOUBLE_QUOTES;
1558: goto next;
1559: }
1560: if (state == DOUBLE_QUOTES) {
1561: state = NONE;
1562: goto next;
1563: }
1564: }
1565:
1.29 nicm 1566: /* Otherwise add the character to the buffer. */
1.1 nicm 1567: yylex_append1(&buf, &len, ch);
1568:
1569: skip:
1570: last = state;
1571:
1572: next:
1573: ch = yylex_getc();
1574: }
1.5 nicm 1575: yylex_ungetc(ch);
1.1 nicm 1576:
1577: buf[len] = '\0';
1578: log_debug("%s: %s", __func__, buf);
1579: return (buf);
1580:
1581: error:
1582: free(buf);
1583: return (NULL);
1584: }