[PATCH 1/1] vi: further changes to colon addresses
Denys Vlasenko
vda.linux at googlemail.com
Sun Aug 29 18:19:29 UTC 2021
Applied, thank you.
On Sun, Aug 29, 2021 at 3:56 PM Ron Yorston <rmy at pobox.com> wrote:
>
> 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 at pobox.com>
> ---
> editors/vi.c | 132 ++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 83 insertions(+), 49 deletions(-)
>
> diff --git a/editors/vi.c b/editors/vi.c
> index e4ba2b2b0..3dbe5b471 100644
> --- a/editors/vi.c
> +++ b/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 (!got_addr && *p == '.') { // the current line
> p++;
> - } else if (*p == '.') { // the current line
> + //addr = count_lines(text, dot);
> + got_addr = TRUE;
> + } else if (!got_addr && *p == '$') { // the last line in file
> p++;
> - new_addr = count_lines(text, dot);
> + 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);
> }
> --
> 2.31.1
>
> _______________________________________________
> busybox mailing list
> busybox at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox
More information about the busybox
mailing list