4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
21 #include <sys/types.h>
33 static int yylex(void);
34 static int yyparse(void);
35 static int printflike
(1,2) yyerror(const char *, ...
);
37 static char *yylex_token
(int);
38 static char *yylex_format
(void);
40 struct cmd_parse_scope
{
42 TAILQ_ENTRY
(cmd_parse_scope
) entry
;
45 enum cmd_parse_argument_type
{
48 CMD_PARSE_PARSED_COMMANDS
51 struct cmd_parse_argument
{
52 enum cmd_parse_argument_type type
;
54 struct cmd_parse_commands
*commands
;
55 struct cmd_list
*cmdlist
;
57 TAILQ_ENTRY
(cmd_parse_argument
) entry
;
59 TAILQ_HEAD
(cmd_parse_arguments
, cmd_parse_argument
);
61 struct cmd_parse_command
{
63 struct cmd_parse_arguments arguments
;
65 TAILQ_ENTRY
(cmd_parse_command
) entry
;
67 TAILQ_HEAD
(cmd_parse_commands
, cmd_parse_command
);
69 struct cmd_parse_state
{
79 struct cmd_parse_input
*input
;
83 struct cmd_parse_commands
*commands
;
85 struct cmd_parse_scope
*scope
;
86 TAILQ_HEAD
(, cmd_parse_scope
) stack
;
88 static struct cmd_parse_state parse_state
;
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
*,
104 struct cmd_parse_arguments
*arguments
;
105 struct cmd_parse_argument
*argument
;
109 struct cmd_parse_commands
*commands
;
111 struct cmd_parse_commands
*commands
;
112 struct cmd_parse_command
*command
;
121 %token
<token
> FORMAT TOKEN EQUALS
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
137 struct cmd_parse_state
*ps
= &parse_state
;
142 statements
: statement
'\n'
146 | statements statement
'\n'
149 TAILQ_CONCAT
($$
, $2, entry
);
153 statement
: /* empty */
155 $$
= xmalloc
(sizeof
*$$
);
160 $$
= xmalloc
(sizeof
*$$
);
165 struct cmd_parse_state
*ps
= &parse_state
;
167 if
(ps
->scope
== NULL || ps
->scope
->flag
)
170 $$
= cmd_parse_new_commands
();
171 cmd_parse_free_commands
($1);
176 struct cmd_parse_state
*ps
= &parse_state
;
178 if
(ps
->scope
== NULL || ps
->scope
->flag
)
181 $$
= cmd_parse_new_commands
();
182 cmd_parse_free_commands
($1);
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
;
205 if
(cmd_find_valid_state
(&pi
->fs
))
208 cmd_find_from_client
(&fs
, c
, 0);
211 ft
= format_create
(NULL
, pi
->item
, FORMAT_NONE
, flags
);
212 format_defaults
(ft
, c
, fsp
->s
, fsp
->wl
, fsp
->wp
);
214 $$
= format_expand
(ft
, $1);
219 optional_assignment
: /* empty */
224 struct cmd_parse_state
*ps
= &parse_state
;
225 int flags
= ps
->input
->flags
;
227 struct cmd_parse_scope
*scope
;
229 if
(ps
->scope
!= NULL
) {
230 flag
= ps
->scope
->flag
;
231 TAILQ_FOREACH
(scope
, &ps
->stack
, entry
)
232 flag
= flag
&& scope
->flag
;
235 if
((~flags
& CMD_PARSE_PARSEONLY
) && flag
)
236 environ_put
(global_environ
, $1, 0);
240 hidden_assignment
: HIDDEN EQUALS
242 struct cmd_parse_state
*ps
= &parse_state
;
243 int flags
= ps
->input
->flags
;
245 struct cmd_parse_scope
*scope
;
247 if
(ps
->scope
!= NULL
) {
248 flag
= ps
->scope
->flag
;
249 TAILQ_FOREACH
(scope
, &ps
->stack
, entry
)
250 flag
= flag
&& scope
->flag
;
253 if
((~flags
& CMD_PARSE_PARSEONLY
) && flag
)
254 environ_put
(global_environ
, $2, ENVIRON_HIDDEN
);
258 if_open
: IF expanded
260 struct cmd_parse_state
*ps
= &parse_state
;
261 struct cmd_parse_scope
*scope
;
263 scope
= xmalloc
(sizeof
*scope
);
264 $$
= scope
->flag
= format_true
($2);
267 if
(ps
->scope
!= NULL
)
268 TAILQ_INSERT_HEAD
(&ps
->stack
, ps
->scope
, entry
);
274 struct cmd_parse_state
*ps
= &parse_state
;
275 struct cmd_parse_scope
*scope
;
277 scope
= xmalloc
(sizeof
*scope
);
278 scope
->flag
= !ps
->scope
->flag
;
284 if_elif
: ELIF expanded
286 struct cmd_parse_state
*ps
= &parse_state
;
287 struct cmd_parse_scope
*scope
;
289 scope
= xmalloc
(sizeof
*scope
);
290 $$
= scope
->flag
= format_true
($2);
299 struct cmd_parse_state
*ps
= &parse_state
;
302 ps
->scope
= TAILQ_FIRST
(&ps
->stack
);
303 if
(ps
->scope
!= NULL
)
304 TAILQ_REMOVE
(&ps
->stack
, ps
->scope
, entry
);
307 condition
: if_open
'\n' statements if_close
312 $$
= cmd_parse_new_commands
();
313 cmd_parse_free_commands
($3);
316 | if_open
'\n' statements if_else
'\n' statements if_close
320 cmd_parse_free_commands
($6);
323 cmd_parse_free_commands
($3);
326 | if_open
'\n' statements elif if_close
330 cmd_parse_free_commands
($4.commands
);
331 } else if
($4.flag
) {
333 cmd_parse_free_commands
($3);
335 $$
= cmd_parse_new_commands
();
336 cmd_parse_free_commands
($3);
337 cmd_parse_free_commands
($4.commands
);
340 | if_open
'\n' statements elif if_else
'\n' statements if_close
344 cmd_parse_free_commands
($4.commands
);
345 cmd_parse_free_commands
($7);
346 } else if
($4.flag
) {
348 cmd_parse_free_commands
($3);
349 cmd_parse_free_commands
($7);
352 cmd_parse_free_commands
($3);
353 cmd_parse_free_commands
($4.commands
);
357 elif
: if_elif
'\n' statements
364 $$.commands
= cmd_parse_new_commands
();
365 cmd_parse_free_commands
($3);
368 | if_elif
'\n' statements elif
373 cmd_parse_free_commands
($4.commands
);
374 } else if
($4.flag
) {
376 $$.commands
= $4.commands
;
377 cmd_parse_free_commands
($3);
380 $$.commands
= cmd_parse_new_commands
();
381 cmd_parse_free_commands
($3);
382 cmd_parse_free_commands
($4.commands
);
388 struct cmd_parse_state
*ps
= &parse_state
;
390 $$
= cmd_parse_new_commands
();
391 if
(!TAILQ_EMPTY
(&$1->arguments
) &&
392 (ps
->scope
== NULL || ps
->scope
->flag
))
393 TAILQ_INSERT_TAIL
($$
, $1, entry
);
395 cmd_parse_free_command
($1);
401 | commands
';' condition1
404 TAILQ_CONCAT
($$
, $3, entry
);
407 | commands
';' command
409 struct cmd_parse_state
*ps
= &parse_state
;
411 if
(!TAILQ_EMPTY
(&$3->arguments
) &&
412 (ps
->scope
== NULL || ps
->scope
->flag
)) {
414 TAILQ_INSERT_TAIL
($$
, $3, entry
);
416 $$
= cmd_parse_new_commands
();
417 cmd_parse_free_commands
($1);
418 cmd_parse_free_command
($3);
428 struct cmd_parse_state
*ps
= &parse_state
;
430 $$
= xcalloc
(1, sizeof
*$$
);
431 $$
->line
= ps
->input
->line
;
432 TAILQ_INIT
(&$$
->arguments
);
434 | optional_assignment TOKEN
436 struct cmd_parse_state
*ps
= &parse_state
;
437 struct cmd_parse_argument
*arg
;
439 $$
= xcalloc
(1, sizeof
*$$
);
440 $$
->line
= ps
->input
->line
;
441 TAILQ_INIT
(&$$
->arguments
);
443 arg
= xcalloc
(1, sizeof
*arg
);
444 arg
->type
= CMD_PARSE_STRING
;
446 TAILQ_INSERT_HEAD
(&$$
->arguments
, arg
, entry
);
448 | optional_assignment TOKEN arguments
450 struct cmd_parse_state
*ps
= &parse_state
;
451 struct cmd_parse_argument
*arg
;
453 $$
= xcalloc
(1, sizeof
*$$
);
454 $$
->line
= ps
->input
->line
;
455 TAILQ_INIT
(&$$
->arguments
);
457 TAILQ_CONCAT
(&$$
->arguments
, $3, entry
);
460 arg
= xcalloc
(1, sizeof
*arg
);
461 arg
->type
= CMD_PARSE_STRING
;
463 TAILQ_INSERT_HEAD
(&$$
->arguments
, arg
, entry
);
466 condition1
: if_open commands if_close
471 $$
= cmd_parse_new_commands
();
472 cmd_parse_free_commands
($2);
475 | if_open commands if_else commands if_close
479 cmd_parse_free_commands
($4);
482 cmd_parse_free_commands
($2);
485 | if_open commands elif1 if_close
489 cmd_parse_free_commands
($3.commands
);
490 } else if
($3.flag
) {
492 cmd_parse_free_commands
($2);
494 $$
= cmd_parse_new_commands
();
495 cmd_parse_free_commands
($2);
496 cmd_parse_free_commands
($3.commands
);
499 | if_open commands elif1 if_else commands if_close
503 cmd_parse_free_commands
($3.commands
);
504 cmd_parse_free_commands
($5);
505 } else if
($3.flag
) {
507 cmd_parse_free_commands
($2);
508 cmd_parse_free_commands
($5);
511 cmd_parse_free_commands
($2);
512 cmd_parse_free_commands
($3.commands
);
516 elif1
: if_elif commands
523 $$.commands
= cmd_parse_new_commands
();
524 cmd_parse_free_commands
($2);
527 | if_elif commands elif1
532 cmd_parse_free_commands
($3.commands
);
533 } else if
($3.flag
) {
535 $$.commands
= $3.commands
;
536 cmd_parse_free_commands
($2);
539 $$.commands
= cmd_parse_new_commands
();
540 cmd_parse_free_commands
($2);
541 cmd_parse_free_commands
($3.commands
);
547 $$
= xcalloc
(1, sizeof
*$$
);
550 TAILQ_INSERT_HEAD
($$
, $1, entry
);
554 TAILQ_INSERT_HEAD
($2, $1, entry
);
560 $$
= xcalloc
(1, sizeof
*$$
);
561 $$
->type
= CMD_PARSE_STRING
;
566 $$
= xcalloc
(1, sizeof
*$$
);
567 $$
->type
= CMD_PARSE_STRING
;
570 |
'{' argument_statements
572 $$
= xcalloc
(1, sizeof
*$$
);
573 $$
->type
= CMD_PARSE_COMMANDS
;
577 argument_statements
: statement
'}'
581 | statements statement
'}'
584 TAILQ_CONCAT
($$
, $2, entry
);
591 cmd_parse_get_error
(const char *file
, u_int line
, const char *error)
598 xasprintf
(&s
, "%s:%u: %s", file
, line
, error);
603 cmd_parse_print_commands
(struct cmd_parse_input
*pi
, struct cmd_list
*cmdlist
)
607 if
(pi
->item
== NULL ||
(~pi
->flags
& CMD_PARSE_VERBOSE
))
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
);
613 cmdq_print
(pi
->item
, "%u: %s", pi
->line
, s
);
618 cmd_parse_free_argument
(struct cmd_parse_argument
*arg
)
621 case CMD_PARSE_STRING
:
624 case CMD_PARSE_COMMANDS
:
625 cmd_parse_free_commands
(arg
->commands
);
627 case CMD_PARSE_PARSED_COMMANDS
:
628 cmd_list_free
(arg
->cmdlist
);
635 cmd_parse_free_arguments
(struct cmd_parse_arguments
*args
)
637 struct cmd_parse_argument
*arg
, *arg1
;
639 TAILQ_FOREACH_SAFE
(arg
, args
, entry
, arg1
) {
640 TAILQ_REMOVE
(args
, arg
, entry
);
641 cmd_parse_free_argument
(arg
);
646 cmd_parse_free_command
(struct cmd_parse_command
*cmd
)
648 cmd_parse_free_arguments
(&cmd
->arguments
);
652 static struct cmd_parse_commands
*
653 cmd_parse_new_commands
(void)
655 struct cmd_parse_commands
*cmds
;
657 cmds
= xmalloc
(sizeof
*cmds
);
663 cmd_parse_free_commands
(struct cmd_parse_commands
*cmds
)
665 struct cmd_parse_command
*cmd
, *cmd1
;
667 TAILQ_FOREACH_SAFE
(cmd
, cmds
, entry
, cmd1
) {
668 TAILQ_REMOVE
(cmds
, cmd
, entry
);
669 cmd_parse_free_command
(cmd
);
674 static struct cmd_parse_commands
*
675 cmd_parse_run_parser
(char **cause
)
677 struct cmd_parse_state
*ps
= &parse_state
;
678 struct cmd_parse_scope
*scope
, *scope1
;
682 TAILQ_INIT
(&ps
->stack
);
685 TAILQ_FOREACH_SAFE
(scope
, &ps
->stack
, entry
, scope1
) {
686 TAILQ_REMOVE
(&ps
->stack
, scope
, entry
);
694 if
(ps
->commands
== NULL
)
695 return
(cmd_parse_new_commands
());
696 return
(ps
->commands
);
699 static struct cmd_parse_commands
*
700 cmd_parse_do_file
(FILE *f
, struct cmd_parse_input
*pi
, char **cause
)
702 struct cmd_parse_state
*ps
= &parse_state
;
704 memset
(ps
, 0, sizeof
*ps
);
707 return
(cmd_parse_run_parser
(cause
));
710 static struct cmd_parse_commands
*
711 cmd_parse_do_buffer
(const char *buf
, size_t len
, struct cmd_parse_input
*pi
,
714 struct cmd_parse_state
*ps
= &parse_state
;
716 memset
(ps
, 0, sizeof
*ps
);
720 return
(cmd_parse_run_parser
(cause
));
724 cmd_parse_log_commands
(struct cmd_parse_commands
*cmds
, const char *prefix
)
726 struct cmd_parse_command
*cmd
;
727 struct cmd_parse_argument
*arg
;
732 TAILQ_FOREACH
(cmd
, cmds
, entry
) {
734 TAILQ_FOREACH
(arg
, &cmd
->arguments
, entry
) {
736 case CMD_PARSE_STRING
:
737 log_debug
("%s %u:%u: %s", prefix
, i
, j
,
740 case CMD_PARSE_COMMANDS
:
741 xasprintf
(&s
, "%s %u:%u", prefix
, i
, j
);
742 cmd_parse_log_commands
(arg
->commands
, s
);
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
);
758 cmd_parse_expand_alias
(struct cmd_parse_command
*cmd
,
759 struct cmd_parse_input
*pi
, struct cmd_parse_result
*pr
)
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
;
766 if
(pi
->flags
& CMD_PARSE_NOALIAS
)
768 memset
(pr
, 0, sizeof
*pr
);
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
();
776 name
= first
->string;
778 alias
= cmd_get_alias
(name
);
781 log_debug
("%s: %u alias %s = %s", __func__
, pi
->line
, name
, alias
);
783 cmds
= cmd_parse_do_buffer
(alias
, strlen
(alias
), pi
, &cause
);
786 pr
->status
= CMD_PARSE_ERROR
;
791 last
= TAILQ_LAST
(cmds
, cmd_parse_commands
);
793 pr
->status
= CMD_PARSE_SUCCESS
;
794 pr
->cmdlist
= cmd_list_new
();
798 TAILQ_REMOVE
(&cmd
->arguments
, first
, entry
);
799 cmd_parse_free_argument
(first
);
801 TAILQ_FOREACH_SAFE
(arg
, &cmd
->arguments
, entry
, arg1
) {
802 TAILQ_REMOVE
(&cmd
->arguments
, arg
, entry
);
803 TAILQ_INSERT_TAIL
(&last
->arguments
, arg
, entry
);
805 cmd_parse_log_commands
(cmds
, __func__
);
807 pi
->flags |
= CMD_PARSE_NOALIAS
;
808 cmd_parse_build_commands
(cmds
, pi
, pr
);
809 pi
->flags
&= ~CMD_PARSE_NOALIAS
;
814 cmd_parse_build_command
(struct cmd_parse_command
*cmd
,
815 struct cmd_parse_input
*pi
, struct cmd_parse_result
*pr
)
817 struct cmd_parse_argument
*arg
;
820 struct args_value
*values
= NULL
;
821 u_int count
= 0, idx
;
823 memset
(pr
, 0, sizeof
*pr
);
825 if
(cmd_parse_expand_alias
(cmd
, pi
, pr
))
828 TAILQ_FOREACH
(arg
, &cmd
->arguments
, entry
) {
829 values
= xrecallocarray
(values
, count
, count
+ 1,
832 case CMD_PARSE_STRING
:
833 values
[count
].type
= ARGS_STRING
;
834 values
[count
].
string = xstrdup
(arg
->string);
836 case CMD_PARSE_COMMANDS
:
837 cmd_parse_build_commands
(arg
->commands
, pi
, pr
);
838 if
(pr
->status
!= CMD_PARSE_SUCCESS
)
840 values
[count
].type
= ARGS_COMMANDS
;
841 values
[count
].cmdlist
= pr
->cmdlist
;
843 case CMD_PARSE_PARSED_COMMANDS
:
844 values
[count
].type
= ARGS_COMMANDS
;
845 values
[count
].cmdlist
= arg
->cmdlist
;
846 values
[count
].cmdlist
->references
++;
852 add
= cmd_parse
(values
, count
, pi
->file
, pi
->line
, &cause
);
854 pr
->status
= CMD_PARSE_ERROR
;
855 pr
->error = cmd_parse_get_error
(pi
->file
, pi
->line
, cause
);
859 pr
->status
= CMD_PARSE_SUCCESS
;
860 pr
->cmdlist
= cmd_list_new
();
861 cmd_list_append
(pr
->cmdlist
, add
);
864 for
(idx
= 0; idx
< count
; idx
++)
865 args_free_value
(&values
[idx
]);
870 cmd_parse_build_commands
(struct cmd_parse_commands
*cmds
,
871 struct cmd_parse_input
*pi
, struct cmd_parse_result
*pr
)
873 struct cmd_parse_command
*cmd
;
874 u_int line
= UINT_MAX
;
875 struct cmd_list
*current
= NULL
, *result
;
878 memset
(pr
, 0, sizeof
*pr
);
880 /* Check for an empty list. */
881 if
(TAILQ_EMPTY
(cmds
)) {
882 pr
->status
= CMD_PARSE_SUCCESS
;
883 pr
->cmdlist
= cmd_list_new
();
886 cmd_parse_log_commands
(cmds
, __func__
);
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
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
);
902 current
= cmd_list_new
();
905 current
= cmd_list_new
();
906 line
= pi
->line
= cmd
->line
;
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
);
914 cmd_list_append_all
(current
, pr
->cmdlist
);
915 cmd_list_free
(pr
->cmdlist
);
917 if
(current
!= NULL
) {
918 cmd_parse_print_commands
(pi
, current
);
919 cmd_list_move
(result
, current
);
920 cmd_list_free
(current
);
923 s
= cmd_list_print
(result
, 0);
924 log_debug
("%s: %s", __func__
, s
);
927 pr
->status
= CMD_PARSE_SUCCESS
;
928 pr
->cmdlist
= result
;
931 struct cmd_parse_result
*
932 cmd_parse_from_file
(FILE *f
, struct cmd_parse_input
*pi
)
934 static struct cmd_parse_result pr
;
935 struct cmd_parse_input input
;
936 struct cmd_parse_commands
*cmds
;
940 memset
(&input
, 0, sizeof input
);
943 memset
(&pr
, 0, sizeof pr
);
945 cmds
= cmd_parse_do_file
(f
, pi
, &cause
);
947 pr.status
= CMD_PARSE_ERROR
;
951 cmd_parse_build_commands
(cmds
, pi
, &pr
);
952 cmd_parse_free_commands
(cmds
);
957 struct cmd_parse_result
*
958 cmd_parse_from_string
(const char *s
, struct cmd_parse_input
*pi
)
960 struct cmd_parse_input input
;
963 memset
(&input
, 0, sizeof input
);
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.
972 pi
->flags |
= CMD_PARSE_ONEGROUP
;
973 return
(cmd_parse_from_buffer
(s
, strlen
(s
), pi
));
976 enum cmd_parse_status
977 cmd_parse_and_insert
(const char *s
, struct cmd_parse_input
*pi
,
978 struct cmdq_item
*after
, struct cmdq_state
*state
, char **error)
980 struct cmd_parse_result
*pr
;
981 struct cmdq_item
*item
;
983 pr
= cmd_parse_from_string
(s
, pi
);
984 switch
(pr
->status
) {
985 case CMD_PARSE_ERROR
:
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
);
1000 enum cmd_parse_status
1001 cmd_parse_and_append
(const char *s
, struct cmd_parse_input
*pi
,
1002 struct client
*c
, struct cmdq_state
*state
, char **error)
1004 struct cmd_parse_result
*pr
;
1005 struct cmdq_item
*item
;
1007 pr
= cmd_parse_from_string
(s
, pi
);
1008 switch
(pr
->status
) {
1009 case CMD_PARSE_ERROR
:
1015 case CMD_PARSE_SUCCESS
:
1016 item
= cmdq_get_command
(pr
->cmdlist
, state
);
1017 cmdq_append
(c
, item
);
1018 cmd_list_free
(pr
->cmdlist
);
1021 return
(pr
->status
);
1024 struct cmd_parse_result
*
1025 cmd_parse_from_buffer
(const void *buf
, size_t len
, struct cmd_parse_input
*pi
)
1027 static struct cmd_parse_result pr
;
1028 struct cmd_parse_input input
;
1029 struct cmd_parse_commands
*cmds
;
1033 memset
(&input
, 0, sizeof input
);
1036 memset
(&pr
, 0, sizeof pr
);
1039 pr.status
= CMD_PARSE_SUCCESS
;
1040 pr.cmdlist
= cmd_list_new
();
1044 cmds
= cmd_parse_do_buffer
(buf
, len
, pi
, &cause
);
1046 pr.status
= CMD_PARSE_ERROR
;
1050 cmd_parse_build_commands
(cmds
, pi
, &pr
);
1051 cmd_parse_free_commands
(cmds
);
1055 struct cmd_parse_result
*
1056 cmd_parse_from_arguments
(struct args_value
*values
, u_int count
,
1057 struct cmd_parse_input
*pi
)
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
;
1070 * The commands are already split up into arguments, so just separate
1071 * into a set of commands by ';'.
1075 memset
(&input
, 0, sizeof input
);
1078 memset
(&pr
, 0, sizeof pr
);
1080 cmds
= cmd_parse_new_commands
();
1082 cmd
= xcalloc
(1, sizeof
*cmd
);
1083 cmd
->line
= pi
->line
;
1084 TAILQ_INIT
(&cmd
->arguments
);
1086 for
(i
= 0; i
< count
; i
++) {
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] = ';';
1098 if
(!end || size
!= 0) {
1099 arg
= xcalloc
(1, sizeof
*arg
);
1100 arg
->type
= CMD_PARSE_STRING
;
1102 TAILQ_INSERT_TAIL
(&cmd
->arguments
, arg
, entry
);
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
);
1112 fatalx
("unknown argument type");
1114 TAILQ_INSERT_TAIL
(cmds
, cmd
, entry
);
1115 cmd
= xcalloc
(1, sizeof
*cmd
);
1116 cmd
->line
= pi
->line
;
1117 TAILQ_INIT
(&cmd
->arguments
);
1120 if
(!TAILQ_EMPTY
(&cmd
->arguments
))
1121 TAILQ_INSERT_TAIL
(cmds
, cmd
, entry
);
1125 cmd_parse_build_commands
(cmds
, pi
, &pr
);
1126 cmd_parse_free_commands
(cmds
);
1130 static int printflike
(1, 2)
1131 yyerror(const char *fmt
, ...
)
1133 struct cmd_parse_state
*ps
= &parse_state
;
1134 struct cmd_parse_input
*pi
= ps
->input
;
1138 if
(ps
->error != NULL
)
1142 xvasprintf
(&error, fmt
, ap
);
1145 ps
->error = cmd_parse_get_error
(pi
->file
, pi
->line
, error);
1151 yylex_is_var
(char ch
, int first
)
1155 if
(first
&& isdigit
((u_char
)ch
))
1157 return
(isalnum
((u_char
)ch
) || ch
== '_');
1161 yylex_append
(char **buf
, size_t *len
, const char *add
, size_t addlen
)
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
);
1171 yylex_append1
(char **buf
, size_t *len
, char add
)
1173 yylex_append
(buf
, len
, &add
, 1);
1179 struct cmd_parse_state
*ps
= &parse_state
;
1185 if
(ps
->off
== ps
->len
)
1188 ch
= ps
->buf
[ps
->off
++];
1194 yylex_ungetc
(int ch
)
1196 struct cmd_parse_state
*ps
= &parse_state
;
1200 else if
(ps
->off
> 0 && ch
!= EOF
)
1207 struct cmd_parse_state
*ps
= &parse_state
;
1210 if
(ps
->escapes
!= 0) {
1220 if
(ch
== '\n' && (ps
->escapes %
2) == 1) {
1226 if
(ps
->escapes
!= 0) {
1236 yylex_get_word
(int ch
)
1245 yylex_append1
(&buf
, &len
, ch
);
1246 while
((ch
= yylex_getc
()) != EOF
&& strchr
(" \t\n", ch
) == NULL
);
1250 log_debug
("%s: %s", __func__
, buf
);
1257 struct cmd_parse_state
*ps
= &parse_state
;
1259 int ch
, next
, condition
;
1265 condition
= ps
->condition
;
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.
1283 if
(ch
== ' ' || ch
== '\t') {
1285 * Ignore whitespace.
1302 * End of line. Update the line number.
1308 if
(ch
== ';' || ch
== '{' || ch
== '}') {
1310 * A semicolon or { or } is itself.
1317 * #{ after a condition opens a format; anything else
1318 * is a comment, ignore up to the end of the line.
1320 next
= yylex_getc
();
1321 if
(condition
&& next
== '{') {
1322 yylval.token
= yylex_format
();
1323 if
(yylval.token
== NULL
)
1327 while
(next
!= '\n' && next
!= EOF
)
1328 next
= yylex_getc
();
1338 * % is a condition unless it is all % or all numbers,
1339 * then it is a token.
1341 yylval.token
= yylex_get_word
('%');
1342 for
(cp
= yylval.token
; *cp
!= '\0'; cp
++) {
1343 if
(*cp
!= '%' && !isdigit
((u_char
)*cp
))
1349 if
(strcmp
(yylval.token
, "%hidden") == 0) {
1353 if
(strcmp
(yylval.token
, "%if") == 0) {
1357 if
(strcmp
(yylval.token
, "%else") == 0) {
1361 if
(strcmp
(yylval.token
, "%elif") == 0) {
1365 if
(strcmp
(yylval.token
, "%endif") == 0) {
1374 * Otherwise this is a token.
1376 token
= yylex_token
(ch
);
1379 yylval.token
= token
;
1381 if
(strchr
(token
, '=') != NULL
&& yylex_is_var
(*token
, 1)) {
1382 for
(cp
= token
+ 1; *cp
!= '='; cp
++) {
1383 if
(!yylex_is_var
(*cp
, 0))
1399 int ch
, brackets
= 1;
1404 yylex_append
(&buf
, &len
, "#{", 2);
1406 if
((ch
= yylex_getc
()) == EOF || ch
== '\n')
1409 if
((ch
= yylex_getc
()) == EOF || ch
== '\n')
1413 yylex_append1
(&buf
, &len
, '#');
1414 } else if
(ch
== '}') {
1415 if
(brackets
!= 0 && --brackets
== 0) {
1416 yylex_append1
(&buf
, &len
, ch
);
1420 yylex_append1
(&buf
, &len
, ch
);
1426 log_debug
("%s: %s", __func__
, buf
);
1435 yylex_token_escape
(char **buf
, size_t *len
)
1437 int ch
, type
, o2
, o3
, mlen
;
1439 char s
[9], m
[MB_LEN_MAX
];
1443 if
(ch
>= '4' && ch
<= '7') {
1444 yyerror("invalid octal escape");
1447 if
(ch
>= '0' && ch
<= '3') {
1449 if
(o2
>= '0' && o2
<= '7') {
1451 if
(o3
>= '0' && o3
<= '7') {
1452 ch
= 64 * (ch
- '0') +
1455 yylex_append1
(buf
, len
, ch
);
1459 yyerror("invalid octal escape");
1503 yylex_append1
(buf
, len
, ch
);
1507 for
(i
= 0; i
< size
; i
++) {
1509 if
(ch
== EOF || ch
== '\n')
1511 if
(!isxdigit
((u_char
)ch
)) {
1512 yyerror("invalid \\%c argument", type
);
1519 if
((size
== 4 && sscanf
(s
, "%4x", &tmp
) != 1) ||
1520 (size
== 8 && sscanf
(s
, "%8x", &tmp
) != 1)) {
1521 yyerror("invalid \\%c argument", type
);
1524 mlen
= wctomb
(m
, tmp
);
1525 if
(mlen
<= 0 || mlen
> (int)sizeof m
) {
1526 yyerror("invalid \\%c argument", type
);
1529 yylex_append
(buf
, len
, m
, mlen
);
1534 yylex_token_variable
(char **buf
, size_t *len
)
1536 struct environ_entry
*envent
;
1537 int ch
, brackets
= 0;
1548 if
(!yylex_is_var
(ch
, 1)) {
1549 yylex_append1
(buf
, len
, '$');
1553 name
[namelen
++] = ch
;
1558 if
(brackets
&& ch
== '}')
1560 if
(ch
== EOF ||
!yylex_is_var
(ch
, 0)) {
1565 yyerror("invalid environment variable");
1568 if
(namelen
== (sizeof name
) - 2) {
1569 yyerror("environment variable is too long");
1572 name
[namelen
++] = ch
;
1574 name
[namelen
] = '\0';
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
));
1586 yylex_token_tilde
(char **buf
, size_t *len
)
1588 struct environ_entry
*envent
;
1593 const char *home
= NULL
;
1597 if
(ch
== EOF || strchr
("/ \t\n\"'", ch
) != NULL
) {
1601 if
(namelen
== (sizeof name
) - 2) {
1602 yyerror("user name is too long");
1605 name
[namelen
++] = ch
;
1607 name
[namelen
] = '\0';
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
)
1616 if
((pw
= getpwnam
(name
)) != NULL
)
1622 log_debug
("%s: ~%s -> %s", __func__
, name
, home
);
1623 yylex_append
(buf
, len
, home
, strlen
(home
));
1635 SINGLE_QUOTES
} state
= NONE
, last
= START
;
1641 /* EOF or \n are always the end of the token. */
1643 log_debug
("%s: end at EOF", __func__
);
1646 if
(state
== NONE
&& ch
== '\r') {
1653 if
(state
== NONE
&& ch
== '\n') {
1654 log_debug
("%s: end at EOL", __func__
);
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__
);
1663 if
(state
== NONE
&& (ch
== ';' || ch
== '}')) {
1664 log_debug
("%s: end at %c", __func__
, ch
);
1669 * Spaces and comments inside quotes after \n are removed but
1672 if
(ch
== '\n' && state
!= NONE
) {
1673 yylex_append1
(&buf
, &len
, '\n');
1674 while
((ch
= yylex_getc
()) == ' ' || ch
== '\t')
1679 if
(strchr
(",#{}:", ch
) != NULL
) {
1683 while
((ch
= yylex_getc
()) != '\n' && ch
!= EOF
)
1689 /* \ ~ and $ are expanded except in single quotes. */
1690 if
(ch
== '\\' && state
!= SINGLE_QUOTES
) {
1691 if
(!yylex_token_escape
(&buf
, &len
))
1695 if
(ch
== '~' && last
!= state
&& state
!= SINGLE_QUOTES
) {
1696 if
(!yylex_token_tilde
(&buf
, &len
))
1700 if
(ch
== '$' && state
!= SINGLE_QUOTES
) {
1701 if
(!yylex_token_variable
(&buf
, &len
))
1705 if
(ch
== '}' && state
== NONE
)
1706 goto
error; /* unmatched (matched ones were handled) */
1708 /* ' and " starts or end quotes (and is consumed). */
1710 if
(state
== NONE
) {
1711 state
= SINGLE_QUOTES
;
1714 if
(state
== SINGLE_QUOTES
) {
1720 if
(state
== NONE
) {
1721 state
= DOUBLE_QUOTES
;
1724 if
(state
== DOUBLE_QUOTES
) {
1730 /* Otherwise add the character to the buffer. */
1731 yylex_append1
(&buf
, &len
, ch
);
1742 log_debug
("%s: %s", __func__
, buf
);