aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2017-04-12 17:43:20 +0200
committerMarc André Tanner <mat@brain-dump.org>2017-04-12 18:38:03 +0200
commit71130ad9fa945623e4a3128b318c8612d6f37792 (patch)
tree8c90124cfdf6bdb5fa041f327c63f4998154edf4
parent964189735954a9e89ad507c68c20423fe02eaccf (diff)
downloadvis-71130ad9fa945623e4a3128b318c8612d6f37792.tar.gz
vis-71130ad9fa945623e4a3128b318c8612d6f37792.tar.xz
sam: add support for count specifier to `g` and `y` commands
-rw-r--r--sam.c67
-rw-r--r--sam.h1
2 files changed, 56 insertions, 12 deletions
diff --git a/sam.c b/sam.c
index 1991c9e..f607721 100644
--- a/sam.c
+++ b/sam.c
@@ -60,12 +60,17 @@ struct Address {
Address *right; /* either right hand side of a compound address or next address */
};
+typedef struct {
+ int start, end; /* interval [n,m] */
+} Count;
+
struct Command {
const char *argv[MAX_ARGV];/* [0]=cmd-name, [1..MAX_ARGV-2]=arguments, last element always NULL */
Address *address; /* range of text for command */
Regex *regex; /* regex to match, used by x, y, g, v, X, Y */
const CommandDef *cmddef; /* which command is this? */
- int count; /* command count if any */
+ Count count; /* command count, defaults to [0,+inf] */
+ int iteration; /* current command loop iteration */
char flags; /* command specific flags */
Command *cmd; /* target of x, y, g, v, X, Y, { */
Command *next; /* next command in {} group */
@@ -149,7 +154,7 @@ static const CommandDef cmds[] = {
CMD_NONE, NULL, cmd_delete
}, {
"g", VIS_HELP("If range contains regexp, run command")
- CMD_CMD|CMD_REGEX, "p", cmd_guard
+ CMD_COUNT|CMD_REGEX|CMD_CMD, "p", cmd_guard
}, {
"i", VIS_HELP("Insert text before range")
CMD_TEXT, NULL, cmd_insert
@@ -161,7 +166,7 @@ static const CommandDef cmds[] = {
CMD_SHELL|CMD_ADDRESS_LINE, NULL, cmd_substitute
}, {
"v", VIS_HELP("If range does not contain regexp, run command")
- CMD_CMD|CMD_REGEX, "p", cmd_guard
+ CMD_COUNT|CMD_REGEX|CMD_CMD, "p", cmd_guard
}, {
"x", VIS_HELP("Set range and run command on each match")
CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT|CMD_ADDRESS_ALL_1CURSOR|CMD_LOOP, "p", cmd_extract
@@ -399,6 +404,7 @@ const char *sam_error(enum SamError err) {
[SAM_ERR_WRITE_CONFLICT] = "Can not write while changing",
[SAM_ERR_LOOP_INVALID_CMD] = "Destructive command in looping construct",
[SAM_ERR_GROUP_INVALID_CMD] = "Destructive command in group",
+ [SAM_ERR_COUNT] = "Invalid count",
};
size_t idx = err;
@@ -637,11 +643,29 @@ static int parse_number(const char **s) {
char *end = NULL;
int number = strtoull(*s, &end, 10);
if (end == *s)
- return 1;
+ return 0;
*s = end;
return number;
}
+static enum SamError parse_count(const char **s, Count *count) {
+ const char *before = *s;
+ if (!(count->start = parse_number(s)) && *s != before)
+ return SAM_ERR_COUNT;
+ if (**s != ',') {
+ count->end = count->start ? count->start : INT_MAX;
+ return SAM_ERR_OK;
+ } else {
+ (*s)++;
+ }
+ before = *s;
+ if (!(count->end = parse_number(s)) && *s != before)
+ return SAM_ERR_COUNT;
+ if (!count->end)
+ count->end = INT_MAX;
+ return SAM_ERR_OK;
+}
+
static Address *address_parse_simple(Vis *vis, const char **s, enum SamError *err) {
skip_spaces(s);
@@ -849,20 +873,24 @@ static Command *command_parse(Vis *vis, const char **s, enum SamError *err) {
goto fail;
}
- if (cmddef->flags & CMD_COUNT)
- cmd->count = parse_number(s);
-
if (cmddef->flags & CMD_FORCE && **s == '!') {
cmd->flags = '!';
(*s)++;
}
+ if ((cmddef->flags & CMD_COUNT) && (*err = parse_count(s, &cmd->count)))
+ goto fail;
+
if (cmddef->flags & CMD_REGEX) {
if ((cmddef->flags & CMD_REGEX_DEFAULT) && (!**s || **s == ' ')) {
skip_spaces(s);
- } else if (!(cmd->regex = parse_regex(vis, s))) {
- *err = SAM_ERR_REGEX;
- goto fail;
+ } else {
+ const char *before = *s;
+ cmd->regex = parse_regex(vis, s);
+ if (!cmd->regex && (*s != before || !(cmddef->flags & CMD_COUNT))) {
+ *err = SAM_ERR_REGEX;
+ goto fail;
+ }
}
}
@@ -1036,11 +1064,17 @@ static Filerange address_evaluate(Address *addr, File *file, Filerange *range, i
return ret;
}
+static bool count_evaluate(Command *cmd) {
+ Count *count = &cmd->count;
+ return count->start <= cmd->iteration && cmd->iteration <= count->end;
+}
+
static bool sam_execute(Vis *vis, Win *win, Command *cmd, Cursor *cur, Filerange *range) {
bool ret = true;
if (cmd->address && win)
*range = address_evaluate(cmd->address, win->file, range, 0);
+ cmd->iteration++;
switch (cmd->argv[0][0]) {
case '{':
{
@@ -1078,6 +1112,14 @@ static enum SamError command_validate(Command *cmd) {
return validate(cmd, false, false);
}
+static void count_init(Command *cmd) {
+ cmd->iteration = 0;
+ for (Command *c = cmd->cmd; c; c = c->next) {
+ if (c->cmddef->func != cmd_extract)
+ count_init(c);
+ }
+}
+
enum SamError sam_cmd(Vis *vis, const char *s) {
enum SamError err = SAM_ERR_OK;
if (!s)
@@ -1258,9 +1300,9 @@ static bool cmd_delete(Vis *vis, Win *win, Command *cmd, const char *argv[], Cur
static bool cmd_guard(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) {
if (!win)
return false;
- bool match = !text_search_range_forward(win->file->text, range->start,
+ bool match = !cmd->regex || !text_search_range_forward(win->file->text, range->start,
text_range_size(range), cmd->regex, 0, NULL, 0);
- if (match ^ (argv[0][0] == 'v'))
+ if ((count_evaluate(cmd) && match) ^ (argv[0][0] == 'v'))
return sam_execute(vis, win, cmd->cmd, cur, range);
view_cursors_dispose_force(cur);
return true;
@@ -1271,6 +1313,7 @@ static bool cmd_extract(Vis *vis, Win *win, Command *cmd, const char *argv[], Cu
return false;
bool ret = true;
Text *txt = win->file->text;
+ count_init(cmd->cmd);
if (cmd->regex) {
bool trailing_match = false;
diff --git a/sam.h b/sam.h
index e94b175..0de831e 100644
--- a/sam.h
+++ b/sam.h
@@ -20,6 +20,7 @@ enum SamError {
SAM_ERR_WRITE_CONFLICT,
SAM_ERR_LOOP_INVALID_CMD,
SAM_ERR_GROUP_INVALID_CMD,
+ SAM_ERR_COUNT,
};
bool sam_init(Vis*);