[git commit] vi: allow line addresses to have an offset

Denys Vlasenko vda.linux at googlemail.com
Thu Apr 15 11:09:12 UTC 2021


commit: https://git.busybox.net/busybox/commit/?id=f2277268384d47fbcaba081f19cebc68de819836
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 editors/vi.c | 118 ++++++++++++++++++++++++++++++++++++-----------------------
 1 file 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;
 }
 


More information about the busybox-cvs mailing list