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 if
((~flags
& CMD_PARSE_PARSEONLY
) &&
228 (ps
->scope
== NULL || ps
->scope
->flag
))
229 environ_put
(global_environ
, $1, 0);
233 hidden_assignment
: HIDDEN EQUALS
235 struct cmd_parse_state
*ps
= &parse_state
;
236 int flags
= ps
->input
->flags
;
238 if
((~flags
& CMD_PARSE_PARSEONLY
) &&
239 (ps
->scope
== NULL || ps
->scope
->flag
))
240 environ_put
(global_environ
, $2, ENVIRON_HIDDEN
);
244 if_open
: IF expanded
246 struct cmd_parse_state
*ps
= &parse_state
;
247 struct cmd_parse_scope
*scope
;
249 scope
= xmalloc
(sizeof
*scope
);
250 $$
= scope
->flag
= format_true
($2);
253 if
(ps
->scope
!= NULL
)
254 TAILQ_INSERT_HEAD
(&ps
->stack
, ps
->scope
, entry
);
260 struct cmd_parse_state
*ps
= &parse_state
;
261 struct cmd_parse_scope
*scope
;
263 scope
= xmalloc
(sizeof
*scope
);
264 scope
->flag
= !ps
->scope
->flag
;
270 if_elif
: ELIF expanded
272 struct cmd_parse_state
*ps
= &parse_state
;
273 struct cmd_parse_scope
*scope
;
275 scope
= xmalloc
(sizeof
*scope
);
276 $$
= scope
->flag
= format_true
($2);
285 struct cmd_parse_state
*ps
= &parse_state
;
288 ps
->scope
= TAILQ_FIRST
(&ps
->stack
);
289 if
(ps
->scope
!= NULL
)
290 TAILQ_REMOVE
(&ps
->stack
, ps
->scope
, entry
);
293 condition
: if_open
'\n' statements if_close
298 $$
= cmd_parse_new_commands
();
299 cmd_parse_free_commands
($3);
302 | if_open
'\n' statements if_else
'\n' statements if_close
306 cmd_parse_free_commands
($6);
309 cmd_parse_free_commands
($3);
312 | if_open
'\n' statements elif if_close
316 cmd_parse_free_commands
($4.commands
);
317 } else if
($4.flag
) {
319 cmd_parse_free_commands
($3);
321 $$
= cmd_parse_new_commands
();
322 cmd_parse_free_commands
($3);
323 cmd_parse_free_commands
($4.commands
);
326 | if_open
'\n' statements elif if_else
'\n' statements if_close
330 cmd_parse_free_commands
($4.commands
);
331 cmd_parse_free_commands
($7);
332 } else if
($4.flag
) {
334 cmd_parse_free_commands
($3);
335 cmd_parse_free_commands
($7);
338 cmd_parse_free_commands
($3);
339 cmd_parse_free_commands
($4.commands
);
343 elif
: if_elif
'\n' statements
350 $$.commands
= cmd_parse_new_commands
();
351 cmd_parse_free_commands
($3);
354 | if_elif
'\n' statements elif
359 cmd_parse_free_commands
($4.commands
);
360 } else if
($4.flag
) {
362 $$.commands
= $4.commands
;
363 cmd_parse_free_commands
($3);
366 $$.commands
= cmd_parse_new_commands
();
367 cmd_parse_free_commands
($3);
368 cmd_parse_free_commands
($4.commands
);
374 struct cmd_parse_state
*ps
= &parse_state
;
376 $$
= cmd_parse_new_commands
();
377 if
(!TAILQ_EMPTY
(&$1->arguments
) &&
378 (ps
->scope
== NULL || ps
->scope
->flag
))
379 TAILQ_INSERT_TAIL
($$
, $1, entry
);
381 cmd_parse_free_command
($1);
387 | commands
';' condition1
390 TAILQ_CONCAT
($$
, $3, entry
);
393 | commands
';' command
395 struct cmd_parse_state
*ps
= &parse_state
;
397 if
(!TAILQ_EMPTY
(&$3->arguments
) &&
398 (ps
->scope
== NULL || ps
->scope
->flag
)) {
400 TAILQ_INSERT_TAIL
($$
, $3, entry
);
402 $$
= cmd_parse_new_commands
();
403 cmd_parse_free_commands
($1);
404 cmd_parse_free_command
($3);
414 struct cmd_parse_state
*ps
= &parse_state
;
416 $$
= xcalloc
(1, sizeof
*$$
);
417 $$
->line
= ps
->input
->line
;
418 TAILQ_INIT
(&$$
->arguments
);
420 | optional_assignment TOKEN
422 struct cmd_parse_state
*ps
= &parse_state
;
423 struct cmd_parse_argument
*arg
;
425 $$
= xcalloc
(1, sizeof
*$$
);
426 $$
->line
= ps
->input
->line
;
427 TAILQ_INIT
(&$$
->arguments
);
429 arg
= xcalloc
(1, sizeof
*arg
);
430 arg
->type
= CMD_PARSE_STRING
;
432 TAILQ_INSERT_HEAD
(&$$
->arguments
, arg
, entry
);
434 | optional_assignment TOKEN arguments
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 TAILQ_CONCAT
(&$$
->arguments
, $3, entry
);
446 arg
= xcalloc
(1, sizeof
*arg
);
447 arg
->type
= CMD_PARSE_STRING
;
449 TAILQ_INSERT_HEAD
(&$$
->arguments
, arg
, entry
);
452 condition1
: if_open commands if_close
457 $$
= cmd_parse_new_commands
();
458 cmd_parse_free_commands
($2);
461 | if_open commands if_else commands if_close
465 cmd_parse_free_commands
($4);
468 cmd_parse_free_commands
($2);
471 | if_open commands elif1 if_close
475 cmd_parse_free_commands
($3.commands
);
476 } else if
($3.flag
) {
478 cmd_parse_free_commands
($2);
480 $$
= cmd_parse_new_commands
();
481 cmd_parse_free_commands
($2);
482 cmd_parse_free_commands
($3.commands
);
485 | if_open commands elif1 if_else commands if_close
489 cmd_parse_free_commands
($3.commands
);
490 cmd_parse_free_commands
($5);
491 } else if
($3.flag
) {
493 cmd_parse_free_commands
($2);
494 cmd_parse_free_commands
($5);
497 cmd_parse_free_commands
($2);
498 cmd_parse_free_commands
($3.commands
);
502 elif1
: if_elif commands
509 $$.commands
= cmd_parse_new_commands
();
510 cmd_parse_free_commands
($2);
513 | if_elif commands elif1
518 cmd_parse_free_commands
($3.commands
);
519 } else if
($3.flag
) {
521 $$.commands
= $3.commands
;
522 cmd_parse_free_commands
($2);
525 $$.commands
= cmd_parse_new_commands
();
526 cmd_parse_free_commands
($2);
527 cmd_parse_free_commands
($3.commands
);
533 $$
= xcalloc
(1, sizeof
*$$
);
536 TAILQ_INSERT_HEAD
($$
, $1, entry
);
540 TAILQ_INSERT_HEAD
($2, $1, entry
);
546 $$
= xcalloc
(1, sizeof
*$$
);
547 $$
->type
= CMD_PARSE_STRING
;
552 $$
= xcalloc
(1, sizeof
*$$
);
553 $$
->type
= CMD_PARSE_STRING
;
556 |
'{' argument_statements
558 $$
= xcalloc
(1, sizeof
*$$
);
559 $$
->type
= CMD_PARSE_COMMANDS
;
563 argument_statements
: statement
'}'
567 | statements statement
'}'
570 TAILQ_CONCAT
($$
, $2, entry
);
577 cmd_parse_get_error
(const char *file
, u_int line
, const char *error)
584 xasprintf
(&s
, "%s:%u: %s", file
, line
, error);
589 cmd_parse_print_commands
(struct cmd_parse_input
*pi
, struct cmd_list
*cmdlist
)
593 if
(pi
->item
== NULL ||
(~pi
->flags
& CMD_PARSE_VERBOSE
))
595 s
= cmd_list_print
(cmdlist
, 0);
596 if
(pi
->file
!= NULL
)
597 cmdq_print
(pi
->item
, "%s:%u: %s", pi
->file
, pi
->line
, s
);
599 cmdq_print
(pi
->item
, "%u: %s", pi
->line
, s
);
604 cmd_parse_free_argument
(struct cmd_parse_argument
*arg
)
607 case CMD_PARSE_STRING
:
610 case CMD_PARSE_COMMANDS
:
611 cmd_parse_free_commands
(arg
->commands
);
613 case CMD_PARSE_PARSED_COMMANDS
:
614 cmd_list_free
(arg
->cmdlist
);
621 cmd_parse_free_arguments
(struct cmd_parse_arguments
*args
)
623 struct cmd_parse_argument
*arg
, *arg1
;
625 TAILQ_FOREACH_SAFE
(arg
, args
, entry
, arg1
) {
626 TAILQ_REMOVE
(args
, arg
, entry
);
627 cmd_parse_free_argument
(arg
);
632 cmd_parse_free_command
(struct cmd_parse_command
*cmd
)
634 cmd_parse_free_arguments
(&cmd
->arguments
);
638 static struct cmd_parse_commands
*
639 cmd_parse_new_commands
(void)
641 struct cmd_parse_commands
*cmds
;
643 cmds
= xmalloc
(sizeof
*cmds
);
649 cmd_parse_free_commands
(struct cmd_parse_commands
*cmds
)
651 struct cmd_parse_command
*cmd
, *cmd1
;
653 TAILQ_FOREACH_SAFE
(cmd
, cmds
, entry
, cmd1
) {
654 TAILQ_REMOVE
(cmds
, cmd
, entry
);
655 cmd_parse_free_command
(cmd
);
660 static struct cmd_parse_commands
*
661 cmd_parse_run_parser
(char **cause
)
663 struct cmd_parse_state
*ps
= &parse_state
;
664 struct cmd_parse_scope
*scope
, *scope1
;
668 TAILQ_INIT
(&ps
->stack
);
671 TAILQ_FOREACH_SAFE
(scope
, &ps
->stack
, entry
, scope1
) {
672 TAILQ_REMOVE
(&ps
->stack
, scope
, entry
);
680 if
(ps
->commands
== NULL
)
681 return
(cmd_parse_new_commands
());
682 return
(ps
->commands
);
685 static struct cmd_parse_commands
*
686 cmd_parse_do_file
(FILE *f
, struct cmd_parse_input
*pi
, char **cause
)
688 struct cmd_parse_state
*ps
= &parse_state
;
690 memset
(ps
, 0, sizeof
*ps
);
693 return
(cmd_parse_run_parser
(cause
));
696 static struct cmd_parse_commands
*
697 cmd_parse_do_buffer
(const char *buf
, size_t len
, struct cmd_parse_input
*pi
,
700 struct cmd_parse_state
*ps
= &parse_state
;
702 memset
(ps
, 0, sizeof
*ps
);
706 return
(cmd_parse_run_parser
(cause
));
710 cmd_parse_log_commands
(struct cmd_parse_commands
*cmds
, const char *prefix
)
712 struct cmd_parse_command
*cmd
;
713 struct cmd_parse_argument
*arg
;
718 TAILQ_FOREACH
(cmd
, cmds
, entry
) {
720 TAILQ_FOREACH
(arg
, &cmd
->arguments
, entry
) {
722 case CMD_PARSE_STRING
:
723 log_debug
("%s %u:%u: %s", prefix
, i
, j
,
726 case CMD_PARSE_COMMANDS
:
727 xasprintf
(&s
, "%s %u:%u", prefix
, i
, j
);
728 cmd_parse_log_commands
(arg
->commands
, s
);
731 case CMD_PARSE_PARSED_COMMANDS
:
732 s
= cmd_list_print
(arg
->cmdlist
, 0);
733 log_debug
("%s %u:%u: %s", prefix
, i
, j
, s
);
744 cmd_parse_expand_alias
(struct cmd_parse_command
*cmd
,
745 struct cmd_parse_input
*pi
, struct cmd_parse_result
*pr
)
747 struct cmd_parse_argument
*arg
, *arg1
, *first
;
748 struct cmd_parse_commands
*cmds
;
749 struct cmd_parse_command
*last
;
750 char *alias
, *name
, *cause
;
752 if
(pi
->flags
& CMD_PARSE_NOALIAS
)
754 memset
(pr
, 0, sizeof
*pr
);
756 first
= TAILQ_FIRST
(&cmd
->arguments
);
757 if
(first
== NULL || first
->type
!= CMD_PARSE_STRING
) {
758 pr
->status
= CMD_PARSE_SUCCESS
;
759 pr
->cmdlist
= cmd_list_new
();
762 name
= first
->string;
764 alias
= cmd_get_alias
(name
);
767 log_debug
("%s: %u alias %s = %s", __func__
, pi
->line
, name
, alias
);
769 cmds
= cmd_parse_do_buffer
(alias
, strlen
(alias
), pi
, &cause
);
772 pr
->status
= CMD_PARSE_ERROR
;
777 last
= TAILQ_LAST
(cmds
, cmd_parse_commands
);
779 pr
->status
= CMD_PARSE_SUCCESS
;
780 pr
->cmdlist
= cmd_list_new
();
784 TAILQ_REMOVE
(&cmd
->arguments
, first
, entry
);
785 cmd_parse_free_argument
(first
);
787 TAILQ_FOREACH_SAFE
(arg
, &cmd
->arguments
, entry
, arg1
) {
788 TAILQ_REMOVE
(&cmd
->arguments
, arg
, entry
);
789 TAILQ_INSERT_TAIL
(&last
->arguments
, arg
, entry
);
791 cmd_parse_log_commands
(cmds
, __func__
);
793 pi
->flags |
= CMD_PARSE_NOALIAS
;
794 cmd_parse_build_commands
(cmds
, pi
, pr
);
795 pi
->flags
&= ~CMD_PARSE_NOALIAS
;
800 cmd_parse_build_command
(struct cmd_parse_command
*cmd
,
801 struct cmd_parse_input
*pi
, struct cmd_parse_result
*pr
)
803 struct cmd_parse_argument
*arg
;
806 struct args_value
*values
= NULL
;
807 u_int count
= 0, idx
;
809 memset
(pr
, 0, sizeof
*pr
);
811 if
(cmd_parse_expand_alias
(cmd
, pi
, pr
))
814 TAILQ_FOREACH
(arg
, &cmd
->arguments
, entry
) {
815 values
= xrecallocarray
(values
, count
, count
+ 1,
818 case CMD_PARSE_STRING
:
819 values
[count
].type
= ARGS_STRING
;
820 values
[count
].
string = xstrdup
(arg
->string);
822 case CMD_PARSE_COMMANDS
:
823 cmd_parse_build_commands
(arg
->commands
, pi
, pr
);
824 if
(pr
->status
!= CMD_PARSE_SUCCESS
)
826 values
[count
].type
= ARGS_COMMANDS
;
827 values
[count
].cmdlist
= pr
->cmdlist
;
829 case CMD_PARSE_PARSED_COMMANDS
:
830 values
[count
].type
= ARGS_COMMANDS
;
831 values
[count
].cmdlist
= arg
->cmdlist
;
832 values
[count
].cmdlist
->references
++;
838 add
= cmd_parse
(values
, count
, pi
->file
, pi
->line
, &cause
);
840 pr
->status
= CMD_PARSE_ERROR
;
841 pr
->error = cmd_parse_get_error
(pi
->file
, pi
->line
, cause
);
845 pr
->status
= CMD_PARSE_SUCCESS
;
846 pr
->cmdlist
= cmd_list_new
();
847 cmd_list_append
(pr
->cmdlist
, add
);
850 for
(idx
= 0; idx
< count
; idx
++)
851 args_free_value
(&values
[idx
]);
856 cmd_parse_build_commands
(struct cmd_parse_commands
*cmds
,
857 struct cmd_parse_input
*pi
, struct cmd_parse_result
*pr
)
859 struct cmd_parse_command
*cmd
;
860 u_int line
= UINT_MAX
;
861 struct cmd_list
*current
= NULL
, *result
;
864 memset
(pr
, 0, sizeof
*pr
);
866 /* Check for an empty list. */
867 if
(TAILQ_EMPTY
(cmds
)) {
868 pr
->status
= CMD_PARSE_SUCCESS
;
869 pr
->cmdlist
= cmd_list_new
();
872 cmd_parse_log_commands
(cmds
, __func__
);
875 * Parse each command into a command list. Create a new command list
876 * for each line (unless the flag is set) so they get a new group (so
877 * the queue knows which ones to remove if a command fails when
880 result
= cmd_list_new
();
881 TAILQ_FOREACH
(cmd
, cmds
, entry
) {
882 if
(((~pi
->flags
& CMD_PARSE_ONEGROUP
) && cmd
->line
!= line
)) {
883 if
(current
!= NULL
) {
884 cmd_parse_print_commands
(pi
, current
);
885 cmd_list_move
(result
, current
);
886 cmd_list_free
(current
);
888 current
= cmd_list_new
();
891 current
= cmd_list_new
();
892 line
= pi
->line
= cmd
->line
;
894 cmd_parse_build_command
(cmd
, pi
, pr
);
895 if
(pr
->status
!= CMD_PARSE_SUCCESS
) {
896 cmd_list_free
(result
);
897 cmd_list_free
(current
);
900 cmd_list_append_all
(current
, pr
->cmdlist
);
901 cmd_list_free
(pr
->cmdlist
);
903 if
(current
!= NULL
) {
904 cmd_parse_print_commands
(pi
, current
);
905 cmd_list_move
(result
, current
);
906 cmd_list_free
(current
);
909 s
= cmd_list_print
(result
, 0);
910 log_debug
("%s: %s", __func__
, s
);
913 pr
->status
= CMD_PARSE_SUCCESS
;
914 pr
->cmdlist
= result
;
917 struct cmd_parse_result
*
918 cmd_parse_from_file
(FILE *f
, struct cmd_parse_input
*pi
)
920 static struct cmd_parse_result pr
;
921 struct cmd_parse_input input
;
922 struct cmd_parse_commands
*cmds
;
926 memset
(&input
, 0, sizeof input
);
929 memset
(&pr
, 0, sizeof pr
);
931 cmds
= cmd_parse_do_file
(f
, pi
, &cause
);
933 pr.status
= CMD_PARSE_ERROR
;
937 cmd_parse_build_commands
(cmds
, pi
, &pr
);
938 cmd_parse_free_commands
(cmds
);
943 struct cmd_parse_result
*
944 cmd_parse_from_string
(const char *s
, struct cmd_parse_input
*pi
)
946 struct cmd_parse_input input
;
949 memset
(&input
, 0, sizeof input
);
954 * When parsing a string, put commands in one group even if there are
955 * multiple lines. This means { a \n b } is identical to "a ; b" when
956 * given as an argument to another command.
958 pi
->flags |
= CMD_PARSE_ONEGROUP
;
959 return
(cmd_parse_from_buffer
(s
, strlen
(s
), pi
));
962 enum cmd_parse_status
963 cmd_parse_and_insert
(const char *s
, struct cmd_parse_input
*pi
,
964 struct cmdq_item
*after
, struct cmdq_state
*state
, char **error)
966 struct cmd_parse_result
*pr
;
967 struct cmdq_item
*item
;
969 pr
= cmd_parse_from_string
(s
, pi
);
970 switch
(pr
->status
) {
971 case CMD_PARSE_ERROR
:
977 case CMD_PARSE_SUCCESS
:
978 item
= cmdq_get_command
(pr
->cmdlist
, state
);
979 cmdq_insert_after
(after
, item
);
980 cmd_list_free
(pr
->cmdlist
);
986 enum cmd_parse_status
987 cmd_parse_and_append
(const char *s
, struct cmd_parse_input
*pi
,
988 struct client
*c
, struct cmdq_state
*state
, char **error)
990 struct cmd_parse_result
*pr
;
991 struct cmdq_item
*item
;
993 pr
= cmd_parse_from_string
(s
, pi
);
994 switch
(pr
->status
) {
995 case CMD_PARSE_ERROR
:
1001 case CMD_PARSE_SUCCESS
:
1002 item
= cmdq_get_command
(pr
->cmdlist
, state
);
1003 cmdq_append
(c
, item
);
1004 cmd_list_free
(pr
->cmdlist
);
1007 return
(pr
->status
);
1010 struct cmd_parse_result
*
1011 cmd_parse_from_buffer
(const void *buf
, size_t len
, struct cmd_parse_input
*pi
)
1013 static struct cmd_parse_result pr
;
1014 struct cmd_parse_input input
;
1015 struct cmd_parse_commands
*cmds
;
1019 memset
(&input
, 0, sizeof input
);
1022 memset
(&pr
, 0, sizeof pr
);
1025 pr.status
= CMD_PARSE_SUCCESS
;
1026 pr.cmdlist
= cmd_list_new
();
1030 cmds
= cmd_parse_do_buffer
(buf
, len
, pi
, &cause
);
1032 pr.status
= CMD_PARSE_ERROR
;
1036 cmd_parse_build_commands
(cmds
, pi
, &pr
);
1037 cmd_parse_free_commands
(cmds
);
1041 struct cmd_parse_result
*
1042 cmd_parse_from_arguments
(struct args_value
*values
, u_int count
,
1043 struct cmd_parse_input
*pi
)
1045 static struct cmd_parse_result pr
;
1046 struct cmd_parse_input input
;
1047 struct cmd_parse_commands
*cmds
;
1048 struct cmd_parse_command
*cmd
;
1049 struct cmd_parse_argument
*arg
;
1056 * The commands are already split up into arguments, so just separate
1057 * into a set of commands by ';'.
1061 memset
(&input
, 0, sizeof input
);
1064 memset
(&pr
, 0, sizeof pr
);
1066 cmds
= cmd_parse_new_commands
();
1068 cmd
= xcalloc
(1, sizeof
*cmd
);
1069 cmd
->line
= pi
->line
;
1070 TAILQ_INIT
(&cmd
->arguments
);
1072 for
(i
= 0; i
< count
; i
++) {
1074 if
(values
[i
].type
== ARGS_STRING
) {
1075 copy
= xstrdup
(values
[i
].
string);
1076 size
= strlen
(copy
);
1077 if
(size
!= 0 && copy
[size
- 1] == ';') {
1078 copy
[--size
] = '\0';
1079 if
(size
> 0 && copy
[size
- 1] == '\\')
1080 copy
[size
- 1] = ';';
1084 if
(!end || size
!= 0) {
1085 arg
= xcalloc
(1, sizeof
*arg
);
1086 arg
->type
= CMD_PARSE_STRING
;
1088 TAILQ_INSERT_TAIL
(&cmd
->arguments
, arg
, entry
);
1091 } else if
(values
[i
].type
== ARGS_COMMANDS
) {
1092 arg
= xcalloc
(1, sizeof
*arg
);
1093 arg
->type
= CMD_PARSE_PARSED_COMMANDS
;
1094 arg
->cmdlist
= values
[i
].cmdlist
;
1095 arg
->cmdlist
->references
++;
1096 TAILQ_INSERT_TAIL
(&cmd
->arguments
, arg
, entry
);
1098 fatalx
("unknown argument type");
1100 TAILQ_INSERT_TAIL
(cmds
, cmd
, entry
);
1101 cmd
= xcalloc
(1, sizeof
*cmd
);
1102 cmd
->line
= pi
->line
;
1103 TAILQ_INIT
(&cmd
->arguments
);
1106 if
(!TAILQ_EMPTY
(&cmd
->arguments
))
1107 TAILQ_INSERT_TAIL
(cmds
, cmd
, entry
);
1111 cmd_parse_build_commands
(cmds
, pi
, &pr
);
1112 cmd_parse_free_commands
(cmds
);
1116 static int printflike
(1, 2)
1117 yyerror(const char *fmt
, ...
)
1119 struct cmd_parse_state
*ps
= &parse_state
;
1120 struct cmd_parse_input
*pi
= ps
->input
;
1124 if
(ps
->error != NULL
)
1128 xvasprintf
(&error, fmt
, ap
);
1131 ps
->error = cmd_parse_get_error
(pi
->file
, pi
->line
, error);
1137 yylex_is_var
(char ch
, int first
)
1141 if
(first
&& isdigit
((u_char
)ch
))
1143 return
(isalnum
((u_char
)ch
) || ch
== '_');
1147 yylex_append
(char **buf
, size_t *len
, const char *add
, size_t addlen
)
1149 if
(addlen
> SIZE_MAX
- 1 ||
*len
> SIZE_MAX
- 1 - addlen
)
1150 fatalx
("buffer is too big");
1151 *buf
= xrealloc
(*buf
, (*len
) + 1 + addlen
);
1152 memcpy
((*buf
) + *len
, add
, addlen
);
1157 yylex_append1
(char **buf
, size_t *len
, char add
)
1159 yylex_append
(buf
, len
, &add
, 1);
1165 struct cmd_parse_state
*ps
= &parse_state
;
1171 if
(ps
->off
== ps
->len
)
1174 ch
= ps
->buf
[ps
->off
++];
1180 yylex_ungetc
(int ch
)
1182 struct cmd_parse_state
*ps
= &parse_state
;
1186 else if
(ps
->off
> 0 && ch
!= EOF
)
1193 struct cmd_parse_state
*ps
= &parse_state
;
1196 if
(ps
->escapes
!= 0) {
1206 if
(ch
== '\n' && (ps
->escapes %
2) == 1) {
1212 if
(ps
->escapes
!= 0) {
1222 yylex_get_word
(int ch
)
1231 yylex_append1
(&buf
, &len
, ch
);
1232 while
((ch
= yylex_getc
()) != EOF
&& strchr
(" \t\n", ch
) == NULL
);
1236 log_debug
("%s: %s", __func__
, buf
);
1243 struct cmd_parse_state
*ps
= &parse_state
;
1245 int ch
, next
, condition
;
1251 condition
= ps
->condition
;
1259 * Ensure every file or string is terminated by a
1260 * newline. This keeps the parser simpler and avoids
1261 * having to add a newline to each string.
1269 if
(ch
== ' ' || ch
== '\t') {
1271 * Ignore whitespace.
1278 * End of line. Update the line number.
1284 if
(ch
== ';' || ch
== '{' || ch
== '}') {
1286 * A semicolon or { or } is itself.
1293 * #{ after a condition opens a format; anything else
1294 * is a comment, ignore up to the end of the line.
1296 next
= yylex_getc
();
1297 if
(condition
&& next
== '{') {
1298 yylval.token
= yylex_format
();
1299 if
(yylval.token
== NULL
)
1303 while
(next
!= '\n' && next
!= EOF
)
1304 next
= yylex_getc
();
1314 * % is a condition unless it is all % or all numbers,
1315 * then it is a token.
1317 yylval.token
= yylex_get_word
('%');
1318 for
(cp
= yylval.token
; *cp
!= '\0'; cp
++) {
1319 if
(*cp
!= '%' && !isdigit
((u_char
)*cp
))
1325 if
(strcmp
(yylval.token
, "%hidden") == 0) {
1329 if
(strcmp
(yylval.token
, "%if") == 0) {
1333 if
(strcmp
(yylval.token
, "%else") == 0) {
1337 if
(strcmp
(yylval.token
, "%elif") == 0) {
1341 if
(strcmp
(yylval.token
, "%endif") == 0) {
1350 * Otherwise this is a token.
1352 token
= yylex_token
(ch
);
1355 yylval.token
= token
;
1357 if
(strchr
(token
, '=') != NULL
&& yylex_is_var
(*token
, 1)) {
1358 for
(cp
= token
+ 1; *cp
!= '='; cp
++) {
1359 if
(!yylex_is_var
(*cp
, 0))
1375 int ch
, brackets
= 1;
1380 yylex_append
(&buf
, &len
, "#{", 2);
1382 if
((ch
= yylex_getc
()) == EOF || ch
== '\n')
1385 if
((ch
= yylex_getc
()) == EOF || ch
== '\n')
1389 yylex_append1
(&buf
, &len
, '#');
1390 } else if
(ch
== '}') {
1391 if
(brackets
!= 0 && --brackets
== 0) {
1392 yylex_append1
(&buf
, &len
, ch
);
1396 yylex_append1
(&buf
, &len
, ch
);
1402 log_debug
("%s: %s", __func__
, buf
);
1411 yylex_token_escape
(char **buf
, size_t *len
)
1413 int ch
, type
, o2
, o3
, mlen
;
1415 char s
[9], m
[MB_LEN_MAX
];
1419 if
(ch
>= '4' && ch
<= '7') {
1420 yyerror("invalid octal escape");
1423 if
(ch
>= '0' && ch
<= '3') {
1425 if
(o2
>= '0' && o2
<= '7') {
1427 if
(o3
>= '0' && o3
<= '7') {
1428 ch
= 64 * (ch
- '0') +
1431 yylex_append1
(buf
, len
, ch
);
1435 yyerror("invalid octal escape");
1479 yylex_append1
(buf
, len
, ch
);
1483 for
(i
= 0; i
< size
; i
++) {
1485 if
(ch
== EOF || ch
== '\n')
1487 if
(!isxdigit
((u_char
)ch
)) {
1488 yyerror("invalid \\%c argument", type
);
1495 if
((size
== 4 && sscanf
(s
, "%4x", &tmp
) != 1) ||
1496 (size
== 8 && sscanf
(s
, "%8x", &tmp
) != 1)) {
1497 yyerror("invalid \\%c argument", type
);
1500 mlen
= wctomb
(m
, tmp
);
1501 if
(mlen
<= 0 || mlen
> (int)sizeof m
) {
1502 yyerror("invalid \\%c argument", type
);
1505 yylex_append
(buf
, len
, m
, mlen
);
1510 yylex_token_variable
(char **buf
, size_t *len
)
1512 struct environ_entry
*envent
;
1513 int ch
, brackets
= 0;
1524 if
(!yylex_is_var
(ch
, 1)) {
1525 yylex_append1
(buf
, len
, '$');
1529 name
[namelen
++] = ch
;
1534 if
(brackets
&& ch
== '}')
1536 if
(ch
== EOF ||
!yylex_is_var
(ch
, 0)) {
1541 yyerror("invalid environment variable");
1544 if
(namelen
== (sizeof name
) - 2) {
1545 yyerror("environment variable is too long");
1548 name
[namelen
++] = ch
;
1550 name
[namelen
] = '\0';
1552 envent
= environ_find
(global_environ
, name
);
1553 if
(envent
!= NULL
&& envent
->value
!= NULL
) {
1554 value
= envent
->value
;
1555 log_debug
("%s: %s -> %s", __func__
, name
, value
);
1556 yylex_append
(buf
, len
, value
, strlen
(value
));
1562 yylex_token_tilde
(char **buf
, size_t *len
)
1564 struct environ_entry
*envent
;
1569 const char *home
= NULL
;
1573 if
(ch
== EOF || strchr
("/ \t\n\"'", ch
) != NULL
) {
1577 if
(namelen
== (sizeof name
) - 2) {
1578 yyerror("user name is too long");
1581 name
[namelen
++] = ch
;
1583 name
[namelen
] = '\0';
1585 if
(*name
== '\0') {
1586 envent
= environ_find
(global_environ
, "HOME");
1587 if
(envent
!= NULL
&& *envent
->value
!= '\0')
1588 home
= envent
->value
;
1589 else if
((pw
= getpwuid
(getuid
())) != NULL
)
1592 if
((pw
= getpwnam
(name
)) != NULL
)
1598 log_debug
("%s: ~%s -> %s", __func__
, name
, home
);
1599 yylex_append
(buf
, len
, home
, strlen
(home
));
1611 SINGLE_QUOTES
} state
= NONE
, last
= START
;
1617 /* EOF or \n are always the end of the token. */
1619 log_debug
("%s: end at EOF", __func__
);
1622 if
(state
== NONE
&& ch
== '\n') {
1623 log_debug
("%s: end at EOL", __func__
);
1627 /* Whitespace or ; or } ends a token unless inside quotes. */
1628 if
(state
== NONE
&& (ch
== ' ' || ch
== '\t')) {
1629 log_debug
("%s: end at WS", __func__
);
1632 if
(state
== NONE
&& (ch
== ';' || ch
== '}')) {
1633 log_debug
("%s: end at %c", __func__
, ch
);
1638 * Spaces and comments inside quotes after \n are removed but
1641 if
(ch
== '\n' && state
!= NONE
) {
1642 yylex_append1
(&buf
, &len
, '\n');
1643 while
((ch
= yylex_getc
()) == ' ' || ch
== '\t')
1648 if
(strchr
(",#{}:", ch
) != NULL
) {
1652 while
((ch
= yylex_getc
()) != '\n' && ch
!= EOF
)
1658 /* \ ~ and $ are expanded except in single quotes. */
1659 if
(ch
== '\\' && state
!= SINGLE_QUOTES
) {
1660 if
(!yylex_token_escape
(&buf
, &len
))
1664 if
(ch
== '~' && last
!= state
&& state
!= SINGLE_QUOTES
) {
1665 if
(!yylex_token_tilde
(&buf
, &len
))
1669 if
(ch
== '$' && state
!= SINGLE_QUOTES
) {
1670 if
(!yylex_token_variable
(&buf
, &len
))
1674 if
(ch
== '}' && state
== NONE
)
1675 goto
error; /* unmatched (matched ones were handled) */
1677 /* ' and " starts or end quotes (and is consumed). */
1679 if
(state
== NONE
) {
1680 state
= SINGLE_QUOTES
;
1683 if
(state
== SINGLE_QUOTES
) {
1689 if
(state
== NONE
) {
1690 state
= DOUBLE_QUOTES
;
1693 if
(state
== DOUBLE_QUOTES
) {
1699 /* Otherwise add the character to the buffer. */
1700 yylex_append1
(&buf
, &len
, ch
);
1711 log_debug
("%s: %s", __func__
, buf
);