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