From 6a566b9cf5aa38f5f0d4e03c98c84e3a86961e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Sun, 27 Nov 2016 21:09:44 +0100 Subject: sam: stricter command parsing Properly detect unbalanced curly braces and spurious output at the end of a group. --- sam.c | 25 ++++++++++++++++++++----- sam.h | 1 + vis-core.h | 1 + 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/sam.c b/sam.c index 594de1c..7499242 100644 --- a/sam.c +++ b/sam.c @@ -404,6 +404,7 @@ const char *sam_error(enum SamError err) { [SAM_ERR_SHELL] = "Shell command expected", [SAM_ERR_COMMAND] = "Unknown command", [SAM_ERR_EXECUTE] = "Error executing command", + [SAM_ERR_NEWLINE] = "Newline expected", }; return err < LENGTH(error_msg) ? error_msg[err] : NULL; @@ -695,7 +696,7 @@ static const CommandDef *command_lookup(Vis *vis, const char *name) { return map_closest(vis->cmds, name); } -static Command *command_parse(Vis *vis, const char **s, int level, enum SamError *err) { +static Command *command_parse(Vis *vis, const char **s, enum SamError *err) { if (!**s) { *err = SAM_ERR_COMMAND; return NULL; @@ -727,17 +728,22 @@ static Command *command_parse(Vis *vis, const char **s, int level, enum SamError if (strcmp(cmd->argv[0], "{") == 0) { Command *prev = NULL, *next; + int level = vis->nesting_level++; do { while (**s == ' ' || **s == '\t' || **s == '\n') (*s)++; - next = command_parse(vis, s, level+1, err); + next = command_parse(vis, s, err); if (prev) prev->next = next; else cmd->cmd = next; } while ((prev = next)); + if (level != vis->nesting_level) { + *err = SAM_ERR_UNMATCHED_BRACE; + goto fail; + } } else if (strcmp(cmd->argv[0], "}") == 0) { - if (level == 0) { + if (vis->nesting_level-- == 0) { *err = SAM_ERR_UNMATCHED_BRACE; goto fail; } @@ -791,7 +797,7 @@ static Command *command_parse(Vis *vis, const char **s, int level, enum SamError goto fail; cmd->cmd->cmddef = command_lookup(vis, cmddef->defcmd); } else { - if (!(cmd->cmd = command_parse(vis, s, level, err))) + if (!(cmd->cmd = command_parse(vis, s, err))) goto fail; if (strcmp(cmd->argv[0], "X") == 0 || strcmp(cmd->argv[0], "Y") == 0) { Command *sel = command_new("s"); @@ -811,10 +817,19 @@ fail: } static Command *sam_parse(Vis *vis, const char *cmd, enum SamError *err) { + vis->nesting_level = 0; const char **s = &cmd; - Command *c = command_parse(vis, s, 0, err); + Command *c = command_parse(vis, s, err); if (!c) return NULL; + while (**s == ' ' || **s == '\t' || **s == '\n') + (*s)++; + if (**s) { + *err = SAM_ERR_NEWLINE; + command_free(c); + return NULL; + } + Command *sel = command_new("s"); if (!sel) { command_free(c); diff --git a/sam.h b/sam.h index de7e910..7d063f4 100644 --- a/sam.h +++ b/sam.h @@ -14,6 +14,7 @@ enum SamError { SAM_ERR_SHELL, SAM_ERR_COMMAND, SAM_ERR_EXECUTE, + SAM_ERR_NEWLINE, }; bool sam_init(Vis*); diff --git a/vis-core.h b/vis-core.h index 79270f6..842c753 100644 --- a/vis-core.h +++ b/vis-core.h @@ -174,6 +174,7 @@ struct Vis { Mode *mode; /* currently active mode, used to search for keybindings */ Mode *mode_prev; /* previsouly active user mode */ bool initialized; /* whether UI and Lua integration has been initialized */ + int nesting_level; /* parsing state to hold keep track of { } nesting level */ volatile bool running; /* exit main loop once this becomes false */ int exit_status; /* exit status when terminating main loop */ volatile sig_atomic_t cancel_filter; /* abort external command/filter (SIGINT occured) */ -- cgit v1.2.3