version 1.27, 2020/05/25 18:57:24 |
version 1.28, 2020/06/04 07:12:05 |
|
|
}; |
}; |
|
|
struct cmd_parse_command { |
struct cmd_parse_command { |
char *name; |
|
u_int line; |
u_int line; |
|
|
int argc; |
int argc; |
|
|
static void cmd_parse_free_command(struct cmd_parse_command *); |
static void cmd_parse_free_command(struct cmd_parse_command *); |
static struct cmd_parse_commands *cmd_parse_new_commands(void); |
static struct cmd_parse_commands *cmd_parse_new_commands(void); |
static void cmd_parse_free_commands(struct cmd_parse_commands *); |
static void cmd_parse_free_commands(struct cmd_parse_commands *); |
|
static char *cmd_parse_commands_to_string(struct cmd_parse_commands *); |
static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, |
static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, |
struct cmd_list *); |
struct cmd_list *); |
|
|
|
|
%type <arguments> arguments |
%type <arguments> arguments |
%type <flag> if_open if_elif |
%type <flag> if_open if_elif |
%type <elif> elif elif1 |
%type <elif> elif elif1 |
%type <commands> statements statement commands condition condition1 |
%type <commands> argument_statements statements statement |
|
%type <commands> commands condition condition1 |
%type <command> command |
%type <command> command |
|
|
%% |
%% |
|
|
struct cmd_parse_state *ps = &parse_state; |
struct cmd_parse_state *ps = &parse_state; |
|
|
$$ = cmd_parse_new_commands(); |
$$ = cmd_parse_new_commands(); |
if ($1->name != NULL && |
if ($1->argc != 0 && |
(ps->scope == NULL || ps->scope->flag)) |
(ps->scope == NULL || ps->scope->flag)) |
TAILQ_INSERT_TAIL($$, $1, entry); |
TAILQ_INSERT_TAIL($$, $1, entry); |
else |
else |
|
|
{ |
{ |
struct cmd_parse_state *ps = &parse_state; |
struct cmd_parse_state *ps = &parse_state; |
|
|
if ($3->name != NULL && |
if ($3->argc != 0 && |
(ps->scope == NULL || ps->scope->flag)) { |
(ps->scope == NULL || ps->scope->flag)) { |
$$ = $1; |
$$ = $1; |
TAILQ_INSERT_TAIL($$, $3, entry); |
TAILQ_INSERT_TAIL($$, $3, entry); |
|
|
struct cmd_parse_state *ps = &parse_state; |
struct cmd_parse_state *ps = &parse_state; |
|
|
$$ = xcalloc(1, sizeof *$$); |
$$ = xcalloc(1, sizeof *$$); |
$$->name = NULL; |
|
$$->line = ps->input->line; |
$$->line = ps->input->line; |
} |
} |
| optional_assignment TOKEN |
| optional_assignment TOKEN |
|
|
struct cmd_parse_state *ps = &parse_state; |
struct cmd_parse_state *ps = &parse_state; |
|
|
$$ = xcalloc(1, sizeof *$$); |
$$ = xcalloc(1, sizeof *$$); |
$$->name = $2; |
|
$$->line = ps->input->line; |
$$->line = ps->input->line; |
|
|
|
cmd_prepend_argv(&$$->argc, &$$->argv, $2); |
|
|
} |
} |
| optional_assignment TOKEN arguments |
| optional_assignment TOKEN arguments |
{ |
{ |
struct cmd_parse_state *ps = &parse_state; |
struct cmd_parse_state *ps = &parse_state; |
|
|
$$ = xcalloc(1, sizeof *$$); |
$$ = xcalloc(1, sizeof *$$); |
$$->name = $2; |
|
$$->line = ps->input->line; |
$$->line = ps->input->line; |
|
|
$$->argc = $3.argc; |
$$->argc = $3.argc; |
$$->argv = $3.argv; |
$$->argv = $3.argv; |
|
cmd_prepend_argv(&$$->argc, &$$->argv, $2); |
} |
} |
|
|
condition1 : if_open commands if_close |
condition1 : if_open commands if_close |
|
|
{ |
{ |
$$ = $1; |
$$ = $1; |
} |
} |
|
| '{' argument_statements |
|
{ |
|
$$ = cmd_parse_commands_to_string($2); |
|
cmd_parse_free_commands($2); |
|
} |
|
|
|
argument_statements : statement '}' |
|
{ |
|
$$ = $1; |
|
} |
|
| statements '}' |
|
{ |
|
$$ = $1; |
|
} |
|
|
%% |
%% |
|
|
static char * |
static char * |
|
|
static void |
static void |
cmd_parse_free_command(struct cmd_parse_command *cmd) |
cmd_parse_free_command(struct cmd_parse_command *cmd) |
{ |
{ |
free(cmd->name); |
|
cmd_free_argv(cmd->argc, cmd->argv); |
cmd_free_argv(cmd->argc, cmd->argv); |
free(cmd); |
free(cmd); |
} |
} |
|
|
free(cmds); |
free(cmds); |
} |
} |
|
|
|
static char * |
|
cmd_parse_commands_to_string(struct cmd_parse_commands *cmds) |
|
{ |
|
struct cmd_parse_command *cmd; |
|
char *string = NULL, *s, *line; |
|
|
|
TAILQ_FOREACH(cmd, cmds, entry) { |
|
line = cmd_stringify_argv(cmd->argc, cmd->argv); |
|
if (string == NULL) |
|
s = line; |
|
else { |
|
xasprintf(&s, "%s ; %s", s, line); |
|
free(line); |
|
} |
|
|
|
free(string); |
|
string = s; |
|
} |
|
if (string == NULL) |
|
string = xstrdup(""); |
|
log_debug("%s: %s", __func__, string); |
|
return (string); |
|
} |
|
|
static struct cmd_parse_commands * |
static struct cmd_parse_commands * |
cmd_parse_run_parser(char **cause) |
cmd_parse_run_parser(char **cause) |
{ |
{ |
|
|
int i; |
int i; |
struct cmd_list *cmdlist = NULL, *result; |
struct cmd_list *cmdlist = NULL, *result; |
struct cmd *add; |
struct cmd *add; |
char *alias, *cause, *s; |
char *name, *alias, *cause, *s; |
|
|
/* Check for an empty list. */ |
/* Check for an empty list. */ |
if (TAILQ_EMPTY(cmds)) { |
if (TAILQ_EMPTY(cmds)) { |
|
|
* command list. |
* command list. |
*/ |
*/ |
TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { |
TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { |
alias = cmd_get_alias(cmd->name); |
name = cmd->argv[0]; |
|
|
|
alias = cmd_get_alias(name); |
if (alias == NULL) |
if (alias == NULL) |
continue; |
continue; |
|
|
line = cmd->line; |
line = cmd->line; |
log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias); |
log_debug("%s: %u %s = %s", __func__, line, name, alias); |
|
|
pi->line = line; |
pi->line = line; |
cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); |
cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); |
|
|
cmd_parse_free_command(cmd); |
cmd_parse_free_command(cmd); |
continue; |
continue; |
} |
} |
for (i = 0; i < cmd->argc; i++) |
for (i = 1; i < cmd->argc; i++) |
cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); |
cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); |
|
|
after = cmd; |
after = cmd; |
|
|
*/ |
*/ |
result = cmd_list_new(); |
result = cmd_list_new(); |
TAILQ_FOREACH(cmd, cmds, entry) { |
TAILQ_FOREACH(cmd, cmds, entry) { |
log_debug("%s: %u %s", __func__, cmd->line, cmd->name); |
name = cmd->argv[0]; |
|
log_debug("%s: %u %s", __func__, cmd->line, name); |
cmd_log_argv(cmd->argc, cmd->argv, __func__); |
cmd_log_argv(cmd->argc, cmd->argv, __func__); |
|
|
if (cmdlist == NULL || |
if (cmdlist == NULL || |
|
|
} |
} |
line = cmd->line; |
line = cmd->line; |
|
|
cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name); |
|
add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); |
add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); |
if (add == NULL) { |
if (add == NULL) { |
cmd_list_free(result); |
cmd_list_free(result); |
|
|
i); |
i); |
|
|
cmd = xcalloc(1, sizeof *cmd); |
cmd = xcalloc(1, sizeof *cmd); |
cmd->name = xstrdup(new_argv[0]); |
|
cmd->line = pi->line; |
cmd->line = pi->line; |
|
|
cmd->argc = new_argc - 1; |
cmd->argc = new_argc; |
cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); |
cmd->argv = cmd_copy_argv(new_argc, new_argv); |
|
|
TAILQ_INSERT_TAIL(cmds, cmd, entry); |
TAILQ_INSERT_TAIL(cmds, cmd, entry); |
} |
} |
|
|
last); |
last); |
|
|
cmd = xcalloc(1, sizeof *cmd); |
cmd = xcalloc(1, sizeof *cmd); |
cmd->name = xstrdup(new_argv[0]); |
|
cmd->line = pi->line; |
cmd->line = pi->line; |
|
|
cmd->argc = new_argc - 1; |
cmd->argc = new_argc; |
cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); |
cmd->argv = cmd_copy_argv(new_argc, new_argv); |
|
|
TAILQ_INSERT_TAIL(cmds, cmd, entry); |
TAILQ_INSERT_TAIL(cmds, cmd, entry); |
} |
} |
|
|
return ('\n'); |
return ('\n'); |
} |
} |
|
|
if (ch == ';') { |
if (ch == ';' || ch == '{' || ch == '}') { |
/* |
/* |
* A semicolon is itself. |
* A semicolon or { or } is itself. |
*/ |
*/ |
return (';'); |
return (ch); |
} |
} |
|
|
if (ch == '#') { |
if (ch == '#') { |
|
|
return (1); |
return (1); |
} |
} |
|
|
static int |
|
yylex_token_brace(char **buf, size_t *len) |
|
{ |
|
struct cmd_parse_state *ps = &parse_state; |
|
int ch, lines = 0, nesting = 1, escape = 0; |
|
int quote = '\0', token = 0; |
|
|
|
/* |
|
* Extract a string up to the matching unquoted '}', including newlines |
|
* and handling nested braces. |
|
* |
|
* To detect the final and intermediate braces which affect the nesting |
|
* depth, we scan the input as if it was a tmux config file, and ignore |
|
* braces which would be considered quoted, escaped, or in a comment. |
|
* |
|
* We update the token state after every character because '#' begins a |
|
* comment only when it begins a token. For simplicity, we treat an |
|
* unquoted directive format as comment. |
|
* |
|
* The result is verbatim copy of the input excluding the final brace. |
|
*/ |
|
|
|
for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) { |
|
yylex_append1(buf, len, ch); |
|
if (ch == '\n') |
|
lines++; |
|
|
|
/* |
|
* If the previous character was a backslash (escape is set), |
|
* escape anything if unquoted or in double quotes, otherwise |
|
* escape only '\n' and '\\'. |
|
*/ |
|
if (escape && |
|
(quote == '\0' || |
|
quote == '"' || |
|
ch == '\n' || |
|
ch == '\\')) { |
|
escape = 0; |
|
if (ch != '\n') |
|
token = 1; |
|
continue; |
|
} |
|
|
|
/* |
|
* The character is not escaped. If it is a backslash, set the |
|
* escape flag. |
|
*/ |
|
if (ch == '\\') { |
|
escape = 1; |
|
continue; |
|
} |
|
escape = 0; |
|
|
|
/* A newline always resets to unquoted. */ |
|
if (ch == '\n') { |
|
quote = token = 0; |
|
continue; |
|
} |
|
|
|
if (quote) { |
|
/* |
|
* Inside quotes or comment. Check if this is the |
|
* closing quote. |
|
*/ |
|
if (ch == quote && quote != '#') |
|
quote = 0; |
|
token = 1; /* token continues regardless */ |
|
} else { |
|
/* Not inside quotes or comment. */ |
|
switch (ch) { |
|
case '"': |
|
case '\'': |
|
case '#': |
|
/* Beginning of quote or maybe comment. */ |
|
if (ch != '#' || !token) |
|
quote = ch; |
|
token = 1; |
|
break; |
|
case ' ': |
|
case '\t': |
|
case ';': |
|
/* Delimiter - token resets. */ |
|
token = 0; |
|
break; |
|
case '{': |
|
nesting++; |
|
token = 0; /* new commands set - token resets */ |
|
break; |
|
case '}': |
|
nesting--; |
|
token = 1; /* same as after quotes */ |
|
if (nesting == 0) { |
|
(*len)--; /* remove closing } */ |
|
ps->input->line += lines; |
|
return (1); |
|
} |
|
break; |
|
default: |
|
token = 1; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* Update line count after error as reporting the opening line is more |
|
* useful than EOF. |
|
*/ |
|
yyerror("unterminated brace string"); |
|
ps->input->line += lines; |
|
return (0); |
|
} |
|
|
|
static char * |
static char * |
yylex_token(int ch) |
yylex_token(int ch) |
{ |
{ |
|
|
} |
} |
|
|
/* Whitespace or ; ends a token unless inside quotes. */ |
/* Whitespace or ; ends a token unless inside quotes. */ |
if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE) |
if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') && |
|
state == NONE) |
break; |
break; |
|
|
/* |
/* |
|
|
} |
} |
if (ch == '$' && state != SINGLE_QUOTES) { |
if (ch == '$' && state != SINGLE_QUOTES) { |
if (!yylex_token_variable(&buf, &len)) |
if (!yylex_token_variable(&buf, &len)) |
goto error; |
|
goto skip; |
|
} |
|
if (ch == '{' && state == NONE) { |
|
if (!yylex_token_brace(&buf, &len)) |
|
goto error; |
goto error; |
goto skip; |
goto skip; |
} |
} |