vi: changes to line addresses for colon commands

Make line addresses behave more like vi:

- Vi allows the user to enter an arbitrary number of addresses,
  though only the last two are used.  This simplifies get_address()
  by reducing the amount of state that needs to be carried.

- When a command requires a single address the last one entered is
  used.

- If addresses are separated by a ';' instead of a ',' the current
  line is updated to the left address.  This may be useful when a
  search is used to specify a range, e.g. ':/first/;/last/d'.

- When the last address is empty it should refer to the current line.

function                                             old     new   delta
colon                                               3855    3834     -21
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-21)             Total: -21 bytes

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2021-04-25 11:51:55 +01:00 committed by Denys Vlasenko
parent 81f9a0035b
commit 7a8ceb4eb2

View File

@ -2473,37 +2473,43 @@ static char *get_one_address(char *p, int *result) // get colon addr, if present
return p; return p;
} }
# define GET_FIRST 0 # define GET_ADDRESS 0
# define GET_SECOND 1 # define GET_SEPARATOR 1
# define GOT_FIRST 2
# define GOT_SECOND 3
# define GOT 2
static char *get_address(char *p, int *b, int *e) // get two colon addrs, 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)
{ {
int state = GET_FIRST; int state = GET_ADDRESS;
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_FIRST) { // alias for 1,$ } else if (*p == '%' && state == GET_ADDRESS) { // alias for 1,$
p++; p++;
*b = 1; *b = 1;
*e = count_lines(text, end-1); *e = count_lines(text, end-1);
state = GOT_SECOND; state = GET_SEPARATOR;
} else if (*p == ',' && state == GOT_FIRST) { } else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
if (*p == ';')
dot = find_line(*e);
p++; p++;
state = GET_SECOND; *b = *e;
} else if (state == GET_FIRST || state == GET_SECOND) { state = GET_ADDRESS;
p = get_one_address(p, state == GET_FIRST ? b : e); } else if (state == GET_ADDRESS) {
p = get_one_address(p, e);
if (p == NULL) if (p == NULL)
break; break;
state |= GOT; state = GET_SEPARATOR;
} else { } else {
if (state == GET_SEPARATOR && *e < 0)
*e = count_lines(text, dot);
break; break;
} }
} }
dot = save_dot;
return p; return p;
} }
@ -2637,8 +2643,6 @@ static void colon(char *buf)
li = i = 0; li = i = 0;
b = e = -1; b = e = -1;
q = text; // assume 1,$ for the range
r = end - 1;
li = count_lines(text, end - 1); li = count_lines(text, end - 1);
fn = current_filename; fn = current_filename;
@ -2673,27 +2677,33 @@ static void colon(char *buf)
useforce = TRUE; useforce = TRUE;
*buf1 = '\0'; // get rid of ! *buf1 = '\0'; // get rid of !
} }
if (b >= 0) { // assume the command will want a range, certain commands
// if there is only one addr, then the addr // (read, substitute) need to adjust these assumptions
// is the line number of the single line the if (e < 0) {
// user wants. So, reset the end q = text; // no addr, use 1,$ for the range
// pointer to point at end of the "b" line r = end - 1;
q = find_line(b); // what line is #b } else {
// at least one addr was given, get its details
q = r = find_line(e);
if (b < 0) {
// 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.
r = end_line(q); r = end_line(q);
li = 1; li = 1;
} } else {
if (e >= 0) {
// we were given two addrs. change the // we were given two addrs. change the
// end pointer to the addr given by user. // start pointer to the addr given by user.
r = find_line(e); // what line is #e q = find_line(b); // what line is #b
r = end_line(r); r = end_line(r);
li = e - b + 1; li = e - b + 1;
} }
}
// ------------ now look for the command ------------ // ------------ now look for the command ------------
i = strlen(cmd); i = strlen(cmd);
if (i == 0) { // :123CR goto line #123 if (i == 0) { // :123CR goto line #123
if (b >= 0) { if (e >= 0) {
dot = find_line(b); // what line is #b dot = find_line(e); // what line is #e
dot_skip_over_ws(); dot_skip_over_ws();
} }
} }
@ -2711,12 +2721,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 (b < 0) { // no addr given- use defaults if (e < 0) { // no addr given- use defaults
b = e = count_lines(text, dot); e = count_lines(text, dot);
} }
status_line("%d", b); status_line("%d", e);
} else if (strncmp(cmd, "delete", i) == 0) { // delete lines } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
if (b < 0) { // no addr given- use defaults if (e < 0) { // 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);
} }
@ -2767,7 +2777,7 @@ static void colon(char *buf)
li, (int)(end - text) li, (int)(end - text)
); );
} else if (strncmp(cmd, "file", i) == 0) { // what File is this } else if (strncmp(cmd, "file", i) == 0) { // what File is this
if (b != -1 || e != -1) { if (e >= 0) {
status_line_bold("No address allowed on this command"); status_line_bold("No address allowed on this command");
goto ret; goto ret;
} }
@ -2787,7 +2797,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 (b < 0) { // no addr given- use defaults if (e < 0) { // 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);
} }
@ -2861,12 +2871,12 @@ static void colon(char *buf)
status_line_bold("No filename given"); status_line_bold("No filename given");
goto ret; goto ret;
} }
if (b < 0) { // no addr given- use defaults if (e < 0) { // no addr given- read after current line
q = begin_line(dot); // assume "dot" q = begin_line(dot);
} } else if (e == 0) { // user said ":0r foo"
// read after current line- unless user said ":0r foo" q = text;
if (b != 0) { } else { // addr given- read after that line
q = next_line(q); q = next_line(find_line(e));
// read after last line // read after last line
if (q == end-1) if (q == end-1)
++q; ++q;
@ -2969,13 +2979,13 @@ static void colon(char *buf)
} }
len_R = strlen(R); len_R = strlen(R);
q = begin_line(q); if (e < 0) { // no addr given
if (b < 0) { // maybe :s/foo/bar/
q = begin_line(dot); // start with cur line q = begin_line(dot); // start with cur line
b = count_lines(text, q); // cur line number r = end_line(dot);
b = e = count_lines(text, q); // cur line number
} else if (b < 0) { // one addr given
b = e;
} }
if (e < 0)
e = b; // maybe :.s/foo/bar/
for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
char *ls = q; // orig line start char *ls = q; // orig line start