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:
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 -------------------------------------
|
//----- The Colon commands -------------------------------------
|
||||||
#if ENABLE_FEATURE_VI_COLON
|
#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
|
# if ENABLE_FEATURE_VI_YANKMARK || ENABLE_FEATURE_VI_SEARCH
|
||||||
char *q, c;
|
char *q, c;
|
||||||
# endif
|
# endif
|
||||||
IF_FEATURE_VI_SEARCH(int dir;)
|
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;
|
sign = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
new_addr = -1;
|
|
||||||
if (isblank(*p)) {
|
if (isblank(*p)) {
|
||||||
|
if (got_addr) {
|
||||||
|
addr += sign;
|
||||||
|
sign = 0;
|
||||||
|
}
|
||||||
p++;
|
p++;
|
||||||
} else if (*p == '.') { // the current line
|
} else if (!got_addr && *p == '.') { // the current line
|
||||||
p++;
|
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
|
# if ENABLE_FEATURE_VI_YANKMARK
|
||||||
else if (*p == '\'') { // is this a mark addr
|
else if (!got_addr && *p == '\'') { // is this a mark addr
|
||||||
p++;
|
p++;
|
||||||
c = tolower(*p);
|
c = tolower(*p);
|
||||||
p++;
|
p++;
|
||||||
@ -2487,13 +2499,16 @@ static char *get_one_address(char *p, int *result) // get colon addr, if present
|
|||||||
c = c - 'a';
|
c = c - 'a';
|
||||||
q = mark[(unsigned char) c];
|
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;
|
return NULL;
|
||||||
new_addr = count_lines(text, q);
|
}
|
||||||
|
addr = count_lines(text, q);
|
||||||
|
got_addr = TRUE;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
# if ENABLE_FEATURE_VI_SEARCH
|
# if ENABLE_FEATURE_VI_SEARCH
|
||||||
else if (*p == '/' || *p == '?') { // a search pattern
|
else if (!got_addr && (*p == '/' || *p == '?')) { // a search pattern
|
||||||
c = *p;
|
c = *p;
|
||||||
q = strchrnul(p + 1, c);
|
q = strchrnul(p + 1, c);
|
||||||
if (p + 1 != q) {
|
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
|
// no match, continue from other end of file
|
||||||
q = char_search(dir > 0 ? text : end - 1,
|
q = char_search(dir > 0 ? text : end - 1,
|
||||||
last_search_pattern + 1, dir);
|
last_search_pattern + 1, dir);
|
||||||
if (q == NULL)
|
if (q == NULL) {
|
||||||
|
status_line_bold("Pattern not found");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
new_addr = count_lines(text, q);
|
addr = count_lines(text, q);
|
||||||
|
got_addr = TRUE;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
else if (*p == '$') { // the last line in file
|
else if (isdigit(*p)) {
|
||||||
p++;
|
num = 0;
|
||||||
new_addr = count_lines(text, end - 1);
|
while (isdigit(*p))
|
||||||
} else if (isdigit(*p)) {
|
num = num * 10 + *p++ -'0';
|
||||||
sscanf(p, "%d%n", &num, &st);
|
if (!got_addr) { // specific line number
|
||||||
p += st;
|
|
||||||
if (addr < 0) { // specific line number
|
|
||||||
addr = num;
|
addr = num;
|
||||||
|
got_addr = TRUE;
|
||||||
} else { // offset from current addr
|
} else { // offset from current addr
|
||||||
addr += sign >= 0 ? num : -num;
|
addr += sign >= 0 ? num : -num;
|
||||||
}
|
}
|
||||||
sign = 0;
|
sign = 0;
|
||||||
} else if (*p == '-' || *p == '+') {
|
} else if (*p == '-' || *p == '+') {
|
||||||
sign = *p++ == '-' ? -1 : 1;
|
if (!got_addr) { // default address is dot
|
||||||
if (addr < 0) { // default address is dot
|
//addr = count_lines(text, dot);
|
||||||
addr = count_lines(text, dot);
|
got_addr = TRUE;
|
||||||
|
} else {
|
||||||
|
addr += sign;
|
||||||
}
|
}
|
||||||
|
sign = *p++ == '-' ? -1 : 1;
|
||||||
} else {
|
} else {
|
||||||
addr += sign; // consume unused trailing sign
|
addr += sign; // consume unused trailing sign
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (new_addr >= 0) {
|
|
||||||
if (addr >= 0) // only one new address per expression
|
|
||||||
return NULL;
|
|
||||||
addr = new_addr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*result = addr;
|
*result = addr;
|
||||||
|
*valid = got_addr;
|
||||||
return p;
|
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
|
// Read line addresses for a colon command. The user can enter as
|
||||||
// many as they like but only the last two will be used.
|
// 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 state = GET_ADDRESS;
|
||||||
|
int valid;
|
||||||
|
int addr;
|
||||||
char *save_dot = dot;
|
char *save_dot = dot;
|
||||||
|
|
||||||
//----- get the address' i.e., 1,3 'a,'b -----
|
//----- get the address' i.e., 1,3 'a,'b -----
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (isblank(*p)) {
|
if (isblank(*p)) {
|
||||||
p++;
|
p++;
|
||||||
} else if (*p == '%' && state == GET_ADDRESS) { // alias for 1,$
|
} else if (state == GET_ADDRESS && *p == '%') { // alias for 1,$
|
||||||
p++;
|
p++;
|
||||||
*b = 1;
|
*b = 1;
|
||||||
*e = count_lines(text, end-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;
|
state = GET_SEPARATOR;
|
||||||
} else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
|
} else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
|
||||||
if (*p == ';')
|
if (*p == ';')
|
||||||
dot = find_line(*e);
|
dot = find_line(*e);
|
||||||
p++;
|
p++;
|
||||||
*b = *e;
|
|
||||||
state = GET_ADDRESS;
|
state = GET_ADDRESS;
|
||||||
} else if (state == GET_ADDRESS) {
|
|
||||||
p = get_one_address(p, e);
|
|
||||||
if (p == NULL)
|
|
||||||
break;
|
|
||||||
state = GET_SEPARATOR;
|
|
||||||
} else {
|
} else {
|
||||||
if (state == GET_SEPARATOR && *b >= 0 && *e < 0)
|
|
||||||
*e = count_lines(text, dot);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2799,9 +2821,14 @@ static void colon(char *buf)
|
|||||||
not_implemented(p);
|
not_implemented(p);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
// check how many addresses we got
|
||||||
|
# define GOT_ADDRESS (got & 1)
|
||||||
|
# define GOT_RANGE ((got & 3) == 3)
|
||||||
|
|
||||||
char c, *buf1, *q, *r;
|
char c, *buf1, *q, *r;
|
||||||
char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
|
char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
|
||||||
int i, l, li, b, e;
|
int i, l, li, b, e;
|
||||||
|
unsigned int got;
|
||||||
int useforce;
|
int useforce;
|
||||||
|
|
||||||
// :3154 // if (-e line 3154) goto it else stay put
|
// :3154 // if (-e line 3154) goto it else stay put
|
||||||
@ -2828,14 +2855,13 @@ static void colon(char *buf)
|
|||||||
|
|
||||||
li = i = 0;
|
li = i = 0;
|
||||||
b = e = -1;
|
b = e = -1;
|
||||||
|
got = 0;
|
||||||
li = count_lines(text, end - 1);
|
li = count_lines(text, end - 1);
|
||||||
fn = current_filename;
|
fn = current_filename;
|
||||||
|
|
||||||
// look for optional address(es) :. :1 :1,9 :'q,'a :%
|
// look for optional address(es) :. :1 :1,9 :'q,'a :%
|
||||||
buf1 = buf;
|
buf = get_address(buf, &b, &e, &got);
|
||||||
buf = get_address(buf, &b, &e);
|
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
status_line_bold("Bad address: %s", buf1);
|
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2858,13 +2884,17 @@ static void colon(char *buf)
|
|||||||
}
|
}
|
||||||
// assume the command will want a range, certain commands
|
// assume the command will want a range, certain commands
|
||||||
// (read, substitute) need to adjust these assumptions
|
// (read, substitute) need to adjust these assumptions
|
||||||
if (e < 0) {
|
if (!GOT_ADDRESS) {
|
||||||
q = text; // no addr, use 1,$ for the range
|
q = text; // no addr, use 1,$ for the range
|
||||||
r = end - 1;
|
r = end - 1;
|
||||||
} else {
|
} else {
|
||||||
// at least one addr was given, get its details
|
// 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);
|
q = r = find_line(e);
|
||||||
if (b < 0) {
|
if (!GOT_RANGE) {
|
||||||
// if there is only one addr, then it's the line
|
// if there is only one addr, then it's the line
|
||||||
// number of the single line the user wants.
|
// number of the single line the user wants.
|
||||||
// Reset the end pointer to the end of that line.
|
// Reset the end pointer to the end of that line.
|
||||||
@ -2873,6 +2903,10 @@ static void colon(char *buf)
|
|||||||
} else {
|
} else {
|
||||||
// we were given two addrs. change the
|
// we were given two addrs. change the
|
||||||
// start pointer to the addr given by user.
|
// 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
|
q = find_line(b); // what line is #b
|
||||||
r = end_line(r);
|
r = end_line(r);
|
||||||
li = e - b + 1;
|
li = e - b + 1;
|
||||||
@ -2903,12 +2937,12 @@ static void colon(char *buf)
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
else if (cmd[0] == '=' && !cmd[1]) { // where is the address
|
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);
|
e = count_lines(text, dot);
|
||||||
}
|
}
|
||||||
status_line("%d", e);
|
status_line("%d", e);
|
||||||
} else if (strncmp(cmd, "delete", i) == 0) { // delete lines
|
} 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
|
q = begin_line(dot); // assume .,. for the range
|
||||||
r = end_line(dot);
|
r = end_line(dot);
|
||||||
}
|
}
|
||||||
@ -2980,7 +3014,7 @@ static void colon(char *buf)
|
|||||||
rawmode();
|
rawmode();
|
||||||
Hit_Return();
|
Hit_Return();
|
||||||
} else if (strncmp(cmd, "list", i) == 0) { // literal print line
|
} 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
|
q = begin_line(dot); // assume .,. for the range
|
||||||
r = end_line(dot);
|
r = end_line(dot);
|
||||||
}
|
}
|
||||||
@ -3063,7 +3097,7 @@ static void colon(char *buf)
|
|||||||
if (e == 0) { // user said ":0r foo"
|
if (e == 0) { // user said ":0r foo"
|
||||||
q = text;
|
q = text;
|
||||||
} else { // read after given line or current line if none given
|
} 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
|
// read after last line
|
||||||
if (q == end-1)
|
if (q == end-1)
|
||||||
++q;
|
++q;
|
||||||
@ -3184,11 +3218,11 @@ static void colon(char *buf)
|
|||||||
len_F = strlen(F);
|
len_F = strlen(F);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e < 0) { // no addr given
|
if (!GOT_ADDRESS) { // no addr given
|
||||||
q = begin_line(dot); // start with cur line
|
q = begin_line(dot); // start with cur line
|
||||||
r = end_line(dot);
|
r = end_line(dot);
|
||||||
b = e = count_lines(text, q); // cur line number
|
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;
|
b = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3359,7 +3393,7 @@ static void colon(char *buf)
|
|||||||
}
|
}
|
||||||
# if ENABLE_FEATURE_VI_YANKMARK
|
# if ENABLE_FEATURE_VI_YANKMARK
|
||||||
} else if (strncmp(cmd, "yank", i) == 0) { // yank lines
|
} 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
|
q = begin_line(dot); // assume .,. for the range
|
||||||
r = end_line(dot);
|
r = end_line(dot);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user