summaryrefslogtreecommitdiff
path: root/editors/vi.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-04-15 12:06:51 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2021-04-15 13:09:12 +0200
commitf2277268384d47fbcaba081f19cebc68de819836 (patch)
treeadee727cf5f81119c1d93bd1e1c9981d095fd985 /editors/vi.c
parent47f78913f7576596d44c602a015735cb9c49f4f0 (diff)
downloadbusybox-f2277268384d47fbcaba081f19cebc68de819836.tar.gz
vi: allow line addresses to have an offset
Line addresses in colon commands can be defined using an expression that includes '+' or '-' operators. The implementation follows traditional vi: - The first term in the expression defines an address. It can be an absolute line number, '.', '$', a search or a marker. - The second and subsequent terms must be non-negative integers. - If the first term is missing '.' is assumed. If the operator is missing addition is assumed. If the final term in missing an offset of 1 is assumed. Thus the following are valid addresses: .+1 .+ + .1 .-1 .- - The following are not valid (though they are in vim): .+$ .$ 2+. function old new delta colon 3701 3844 +143 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 143/0) Total: 143 bytes Signed-off-by: Ron Yorston <rmy@pobox.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'editors/vi.c')
-rw-r--r--editors/vi.c118
1 files changed, 72 insertions, 46 deletions
diff --git a/editors/vi.c b/editors/vi.c
index 0866e0fa9..6dd951421 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -2342,67 +2342,93 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
//----- The Colon commands -------------------------------------
#if ENABLE_FEATURE_VI_COLON
-static char *get_one_address(char *p, int *addr) // get colon addr, if present
+static char *get_one_address(char *p, int *result) // get colon addr, if present
{
- int st;
+ int st, num, sign, addr, new_addr;
# if ENABLE_FEATURE_VI_YANKMARK || ENABLE_FEATURE_VI_SEARCH
char *q, c;
# endif
IF_FEATURE_VI_SEARCH(int dir;)
- *addr = -1; // assume no addr
- if (*p == '.') { // the current line
- p++;
- *addr = count_lines(text, dot);
- }
+ addr = -1; // assume no addr
+ sign = 0;
+ for (;;) {
+ new_addr = -1;
+ if (isblank(*p)) {
+ p++;
+ } else if (*p == '.') { // the current line
+ p++;
+ new_addr = count_lines(text, dot);
+ }
# if ENABLE_FEATURE_VI_YANKMARK
- else if (*p == '\'') { // is this a mark addr
- p++;
- c = tolower(*p);
- p++;
- q = NULL;
- if (c >= 'a' && c <= 'z') {
- // we have a mark
- c = c - 'a';
- q = mark[(unsigned char) c];
+ else if (*p == '\'') { // is this a mark addr
+ p++;
+ c = tolower(*p);
+ p++;
+ q = NULL;
+ if (c >= 'a' && c <= 'z') {
+ // we have a mark
+ c = c - 'a';
+ q = mark[(unsigned char) c];
+ }
+ if (q == NULL) // is mark valid
+ return NULL;
+ new_addr = count_lines(text, q);
}
- if (q == NULL) // is mark valid
- return NULL;
- *addr = count_lines(text, q);
- }
# endif
# if ENABLE_FEATURE_VI_SEARCH
- else if (*p == '/' || *p == '?') { // a search pattern
- c = *p;
- q = strchrnul(p + 1, c);
- if (p + 1 != q) {
- // save copy of new pattern
- free(last_search_pattern);
- last_search_pattern = xstrndup(p, q - p);
+ else if (*p == '/' || *p == '?') { // a search pattern
+ c = *p;
+ q = strchrnul(p + 1, c);
+ if (p + 1 != q) {
+ // save copy of new pattern
+ free(last_search_pattern);
+ last_search_pattern = xstrndup(p, q - p);
+ }
+ p = q;
+ if (*p == c)
+ p++;
+ if (c == '/') {
+ q = next_line(dot);
+ dir = (FORWARD << 1) | FULL;
+ } else {
+ q = begin_line(dot);
+ dir = ((unsigned)BACK << 1) | FULL;
+ }
+ q = char_search(q, last_search_pattern + 1, dir);
+ if (q == NULL)
+ return NULL;
+ new_addr = count_lines(text, q);
}
- p = q;
- if (*p == c)
+# endif
+ else if (*p == '$') { // the last line in file
p++;
- if (c == '/') {
- q = next_line(dot);
- dir = (FORWARD << 1) | FULL;
+ new_addr = count_lines(text, end - 1);
+ } else if (isdigit(*p)) {
+ sscanf(p, "%d%n", &num, &st);
+ p += st;
+ if (addr < 0) { // specific line number
+ addr = num;
+ } else { // offset from current addr
+ addr += sign >= 0 ? num : -num;
+ }
+ sign = 0;
+ } else if (*p == '-' || *p == '+') {
+ sign = *p++ == '-' ? -1 : 1;
+ if (addr < 0) { // default address is dot
+ addr = count_lines(text, dot);
+ }
} else {
- q = begin_line(dot);
- dir = ((unsigned)BACK << 1) | FULL;
+ addr += sign; // consume unused trailing sign
+ break;
+ }
+ if (new_addr >= 0) {
+ if (addr >= 0) // only one new address per expression
+ return NULL;
+ addr = new_addr;
}
- q = char_search(q, last_search_pattern + 1, dir);
- if (q == NULL)
- return NULL;
- *addr = count_lines(text, q);
- }
-# endif
- else if (*p == '$') { // the last line in file
- p++;
- *addr = count_lines(text, end - 1);
- } else if (isdigit(*p)) { // specific line number
- sscanf(p, "%d%n", addr, &st);
- p += st;
}
+ *result = addr;
return p;
}