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