vi: further changes to colon addresses
Improved error messages: - specify when a search fails or a mark isn't set; - warn when line addresses are out of range or when a range of lines is reversed. Addresses are limited to the number of lines in the file so a command like ':2000000000' (go to the two billionth line) no longer causes a long pause. Improved vi compatibility of '+' and '-' operators that aren't followed immediately by a number: :4+++= 7 :3-2= 1 :3 - 2= 4 (yes, really!) In a command like ':,$' the empty address before the separator now correctly refers to the current line. (The similar case ':1,' was already being handled.) And all with a tidy reduction in bloat (32-bit build): function old new delta colon 4029 4069 +40 .rodata 99348 99253 -95 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 40/-95) Total: -55 bytes Signed-off-by: Ron Yorston <rmy@pobox.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
		
				
					committed by
					
						
						Denys Vlasenko
					
				
			
			
				
	
			
			
			
						parent
						
							6d3da732a5
						
					
				
				
					commit
					a51d953b95
				
			
							
								
								
									
										132
									
								
								editors/vi.c
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								editors/vi.c
									
									
									
									
									
								
							@@ -2458,26 +2458,38 @@ 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 *result)	// get colon addr, if present
 | 
			
		||||
// Evaluate colon address expression.  Returns a pointer to the
 | 
			
		||||
// next character or NULL on error.  If 'result' contains a valid
 | 
			
		||||
// address 'valid' is TRUE.
 | 
			
		||||
static char *get_one_address(char *p, int *result, int *valid)
 | 
			
		||||
{
 | 
			
		||||
	int st, num, sign, addr, new_addr;
 | 
			
		||||
	int num, sign, addr, got_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
 | 
			
		||||
	got_addr = FALSE;
 | 
			
		||||
	addr = count_lines(text, dot);	// default to current line
 | 
			
		||||
	sign = 0;
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		new_addr = -1;
 | 
			
		||||
		if (isblank(*p)) {
 | 
			
		||||
			if (got_addr) {
 | 
			
		||||
				addr += sign;
 | 
			
		||||
				sign = 0;
 | 
			
		||||
			}
 | 
			
		||||
			p++;
 | 
			
		||||
		} else if (*p == '.') {	// the current line
 | 
			
		||||
		} else if (!got_addr && *p == '.') {	// the current line
 | 
			
		||||
			p++;
 | 
			
		||||
			new_addr = count_lines(text, dot);
 | 
			
		||||
			//addr = count_lines(text, dot);
 | 
			
		||||
			got_addr = TRUE;
 | 
			
		||||
		} else if (!got_addr && *p == '$') {	// the last line in file
 | 
			
		||||
			p++;
 | 
			
		||||
			addr = count_lines(text, end - 1);
 | 
			
		||||
			got_addr = TRUE;
 | 
			
		||||
		}
 | 
			
		||||
# if ENABLE_FEATURE_VI_YANKMARK
 | 
			
		||||
		else if (*p == '\'') {	// is this a mark addr
 | 
			
		||||
		else if (!got_addr && *p == '\'') {	// is this a mark addr
 | 
			
		||||
			p++;
 | 
			
		||||
			c = tolower(*p);
 | 
			
		||||
			p++;
 | 
			
		||||
@@ -2487,13 +2499,16 @@ static char *get_one_address(char *p, int *result)	// get colon addr, if present
 | 
			
		||||
				c = c - 'a';
 | 
			
		||||
				q = mark[(unsigned char) c];
 | 
			
		||||
			}
 | 
			
		||||
			if (q == NULL)	// is mark valid
 | 
			
		||||
			if (q == NULL) {	// is mark valid
 | 
			
		||||
				status_line_bold("Mark not set");
 | 
			
		||||
				return NULL;
 | 
			
		||||
			new_addr = count_lines(text, q);
 | 
			
		||||
			}
 | 
			
		||||
			addr = count_lines(text, q);
 | 
			
		||||
			got_addr = TRUE;
 | 
			
		||||
		}
 | 
			
		||||
# endif
 | 
			
		||||
# if ENABLE_FEATURE_VI_SEARCH
 | 
			
		||||
		else if (*p == '/' || *p == '?') {	// a search pattern
 | 
			
		||||
		else if (!got_addr && (*p == '/' || *p == '?')) {	// a search pattern
 | 
			
		||||
			c = *p;
 | 
			
		||||
			q = strchrnul(p + 1, c);
 | 
			
		||||
			if (p + 1 != q) {
 | 
			
		||||
@@ -2516,40 +2531,41 @@ static char *get_one_address(char *p, int *result)	// get colon addr, if present
 | 
			
		||||
				// no match, continue from other end of file
 | 
			
		||||
				q = char_search(dir > 0 ? text : end - 1,
 | 
			
		||||
								last_search_pattern + 1, dir);
 | 
			
		||||
				if (q == NULL)
 | 
			
		||||
				if (q == NULL) {
 | 
			
		||||
					status_line_bold("Pattern not found");
 | 
			
		||||
					return NULL;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			new_addr = count_lines(text, q);
 | 
			
		||||
			addr = count_lines(text, q);
 | 
			
		||||
			got_addr = TRUE;
 | 
			
		||||
		}
 | 
			
		||||
# endif
 | 
			
		||||
		else if (*p == '$') {	// the last line in file
 | 
			
		||||
			p++;
 | 
			
		||||
			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
 | 
			
		||||
		else if (isdigit(*p)) {
 | 
			
		||||
			num = 0;
 | 
			
		||||
			while (isdigit(*p))
 | 
			
		||||
				num = num * 10 + *p++ -'0';
 | 
			
		||||
			if (!got_addr) {	// specific line number
 | 
			
		||||
				addr = num;
 | 
			
		||||
				got_addr = TRUE;
 | 
			
		||||
			} 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);
 | 
			
		||||
			if (!got_addr) {	// default address is dot
 | 
			
		||||
				//addr = count_lines(text, dot);
 | 
			
		||||
				got_addr = TRUE;
 | 
			
		||||
			} else {
 | 
			
		||||
				addr += sign;
 | 
			
		||||
			}
 | 
			
		||||
			sign = *p++ == '-' ? -1 : 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*result = addr;
 | 
			
		||||
	*valid = got_addr;
 | 
			
		||||
	return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2558,34 +2574,40 @@ static char *get_one_address(char *p, int *result)	// get colon addr, if present
 | 
			
		||||
 | 
			
		||||
// Read line addresses for a colon command.  The user can enter as
 | 
			
		||||
// many as they like but only the last two will be used.
 | 
			
		||||
static char *get_address(char *p, int *b, int *e)
 | 
			
		||||
static char *get_address(char *p, int *b, int *e, unsigned int *got)
 | 
			
		||||
{
 | 
			
		||||
	int state = GET_ADDRESS;
 | 
			
		||||
	int valid;
 | 
			
		||||
	int addr;
 | 
			
		||||
	char *save_dot = dot;
 | 
			
		||||
 | 
			
		||||
	//----- get the address' i.e., 1,3   'a,'b  -----
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		if (isblank(*p)) {
 | 
			
		||||
			p++;
 | 
			
		||||
		} else if (*p == '%' && state == GET_ADDRESS) {	// alias for 1,$
 | 
			
		||||
		} else if (state == GET_ADDRESS && *p == '%') {	// alias for 1,$
 | 
			
		||||
			p++;
 | 
			
		||||
			*b = 1;
 | 
			
		||||
			*e = count_lines(text, end-1);
 | 
			
		||||
			*got = 3;
 | 
			
		||||
			state = GET_SEPARATOR;
 | 
			
		||||
		} else if (state == GET_ADDRESS) {
 | 
			
		||||
			valid = FALSE;
 | 
			
		||||
			p = get_one_address(p, &addr, &valid);
 | 
			
		||||
			// Quit on error or if the address is invalid and isn't of
 | 
			
		||||
			// the form ',$' or '1,' (in which case it defaults to dot).
 | 
			
		||||
			if (p == NULL || !(valid || *p == ',' || *p == ';' || *got & 1))
 | 
			
		||||
				break;
 | 
			
		||||
			*b = *e;
 | 
			
		||||
			*e = addr;
 | 
			
		||||
			*got = (*got << 1) | 1;
 | 
			
		||||
			state = GET_SEPARATOR;
 | 
			
		||||
		} else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
 | 
			
		||||
			if (*p == ';')
 | 
			
		||||
				dot = find_line(*e);
 | 
			
		||||
			p++;
 | 
			
		||||
			*b = *e;
 | 
			
		||||
			state = GET_ADDRESS;
 | 
			
		||||
		} else if (state == GET_ADDRESS) {
 | 
			
		||||
			p = get_one_address(p, e);
 | 
			
		||||
			if (p == NULL)
 | 
			
		||||
				break;
 | 
			
		||||
			state = GET_SEPARATOR;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (state == GET_SEPARATOR && *b >= 0 && *e < 0)
 | 
			
		||||
				*e = count_lines(text, dot);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -2799,9 +2821,14 @@ static void colon(char *buf)
 | 
			
		||||
	not_implemented(p);
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
// check how many addresses we got
 | 
			
		||||
# define GOT_ADDRESS (got & 1)
 | 
			
		||||
# define GOT_RANGE ((got & 3) == 3)
 | 
			
		||||
 | 
			
		||||
	char c, *buf1, *q, *r;
 | 
			
		||||
	char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
 | 
			
		||||
	int i, l, li, b, e;
 | 
			
		||||
	unsigned int got;
 | 
			
		||||
	int useforce;
 | 
			
		||||
 | 
			
		||||
	// :3154	// if (-e line 3154) goto it  else stay put
 | 
			
		||||
@@ -2828,14 +2855,13 @@ static void colon(char *buf)
 | 
			
		||||
 | 
			
		||||
	li = i = 0;
 | 
			
		||||
	b = e = -1;
 | 
			
		||||
	got = 0;
 | 
			
		||||
	li = count_lines(text, end - 1);
 | 
			
		||||
	fn = current_filename;
 | 
			
		||||
 | 
			
		||||
	// look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
 | 
			
		||||
	buf1 = buf;
 | 
			
		||||
	buf = get_address(buf, &b, &e);
 | 
			
		||||
	buf = get_address(buf, &b, &e, &got);
 | 
			
		||||
	if (buf == NULL) {
 | 
			
		||||
		status_line_bold("Bad address: %s", buf1);
 | 
			
		||||
		goto ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2858,13 +2884,17 @@ static void colon(char *buf)
 | 
			
		||||
	}
 | 
			
		||||
	// assume the command will want a range, certain commands
 | 
			
		||||
	// (read, substitute) need to adjust these assumptions
 | 
			
		||||
	if (e < 0) {
 | 
			
		||||
	if (!GOT_ADDRESS) {
 | 
			
		||||
		q = text;			// no addr, use 1,$ for the range
 | 
			
		||||
		r = end - 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		// at least one addr was given, get its details
 | 
			
		||||
		if (e < 0 || e > li) {
 | 
			
		||||
			status_line_bold("Invalid range");
 | 
			
		||||
			goto ret;
 | 
			
		||||
		}
 | 
			
		||||
		q = r = find_line(e);
 | 
			
		||||
		if (b < 0) {
 | 
			
		||||
		if (!GOT_RANGE) {
 | 
			
		||||
			// if there is only one addr, then it's the line
 | 
			
		||||
			// number of the single line the user wants.
 | 
			
		||||
			// Reset the end pointer to the end of that line.
 | 
			
		||||
@@ -2873,6 +2903,10 @@ static void colon(char *buf)
 | 
			
		||||
		} else {
 | 
			
		||||
			// we were given two addrs.  change the
 | 
			
		||||
			// start pointer to the addr given by user.
 | 
			
		||||
			if (b < 0 || b > li || b > e) {
 | 
			
		||||
				status_line_bold("Invalid range");
 | 
			
		||||
				goto ret;
 | 
			
		||||
			}
 | 
			
		||||
			q = find_line(b);	// what line is #b
 | 
			
		||||
			r = end_line(r);
 | 
			
		||||
			li = e - b + 1;
 | 
			
		||||
@@ -2903,12 +2937,12 @@ static void colon(char *buf)
 | 
			
		||||
	}
 | 
			
		||||
# endif
 | 
			
		||||
	else if (cmd[0] == '=' && !cmd[1]) {	// where is the address
 | 
			
		||||
		if (e < 0) {	// no addr given- use defaults
 | 
			
		||||
		if (!GOT_ADDRESS) {	// no addr given- use defaults
 | 
			
		||||
			e = count_lines(text, dot);
 | 
			
		||||
		}
 | 
			
		||||
		status_line("%d", e);
 | 
			
		||||
	} else if (strncmp(cmd, "delete", i) == 0) {	// delete lines
 | 
			
		||||
		if (e < 0) {	// no addr given- use defaults
 | 
			
		||||
		if (!GOT_ADDRESS) {	// no addr given- use defaults
 | 
			
		||||
			q = begin_line(dot);	// assume .,. for the range
 | 
			
		||||
			r = end_line(dot);
 | 
			
		||||
		}
 | 
			
		||||
@@ -2980,7 +3014,7 @@ static void colon(char *buf)
 | 
			
		||||
		rawmode();
 | 
			
		||||
		Hit_Return();
 | 
			
		||||
	} else if (strncmp(cmd, "list", i) == 0) {	// literal print line
 | 
			
		||||
		if (e < 0) {	// no addr given- use defaults
 | 
			
		||||
		if (!GOT_ADDRESS) {	// no addr given- use defaults
 | 
			
		||||
			q = begin_line(dot);	// assume .,. for the range
 | 
			
		||||
			r = end_line(dot);
 | 
			
		||||
		}
 | 
			
		||||
@@ -3063,7 +3097,7 @@ static void colon(char *buf)
 | 
			
		||||
		if (e == 0) {	// user said ":0r foo"
 | 
			
		||||
			q = text;
 | 
			
		||||
		} else {	// read after given line or current line if none given
 | 
			
		||||
			q = next_line(e > 0 ? find_line(e) : dot);
 | 
			
		||||
			q = next_line(GOT_ADDRESS ? find_line(e) : dot);
 | 
			
		||||
			// read after last line
 | 
			
		||||
			if (q == end-1)
 | 
			
		||||
				++q;
 | 
			
		||||
@@ -3184,11 +3218,11 @@ static void colon(char *buf)
 | 
			
		||||
			len_F = strlen(F);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (e < 0) {	// no addr given
 | 
			
		||||
		if (!GOT_ADDRESS) {	// no addr given
 | 
			
		||||
			q = begin_line(dot);      // start with cur line
 | 
			
		||||
			r = end_line(dot);
 | 
			
		||||
			b = e = count_lines(text, q); // cur line number
 | 
			
		||||
		} else if (b < 0) {	// one addr given
 | 
			
		||||
		} else if (!GOT_RANGE) {	// one addr given
 | 
			
		||||
			b = e;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -3359,7 +3393,7 @@ static void colon(char *buf)
 | 
			
		||||
		}
 | 
			
		||||
# if ENABLE_FEATURE_VI_YANKMARK
 | 
			
		||||
	} else if (strncmp(cmd, "yank", i) == 0) {	// yank lines
 | 
			
		||||
		if (b < 0) {	// no addr given- use defaults
 | 
			
		||||
		if (!GOT_ADDRESS) {	// no addr given- use defaults
 | 
			
		||||
			q = begin_line(dot);	// assume .,. for the range
 | 
			
		||||
			r = end_line(dot);
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user