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