From 8817e285b7ce071a27c366a2a602d3ca162b08ba Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sat, 7 Aug 2021 09:41:49 +0100 Subject: shuf: speed-up when limited output is requested A user noted that the following command was slower than they expected: busybox shuf -i "1500000000-$(date +%s)" -n 5 At time of writing the range contains 128 million values. On my system this takes 7.7s whereas 'shuf' from coreutils takes a handful of milliseconds. Optimise BusyBox 'shuf' for cases where -n is specified by stopping shuffling once the required number of lines have been processed. On my system the time for the example is reduced to 0.4s. function old new delta shuf_main 520 540 +20 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 20/0) Total: 20 bytes v2: Code shrink. Since outlines <= numlines: - the loop in shuffle_lines() only needs to test the value of outlines; - shuffle_lines() can be called unconditionally. Update timing to allow for the 13 million seconds elapsed since v1. Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- coreutils/shuf.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/coreutils/shuf.c b/coreutils/shuf.c index fdbd3e9b2..3b2ba93cf 100644 --- a/coreutils/shuf.c +++ b/coreutils/shuf.c @@ -24,7 +24,7 @@ //usage: "\n -i L-H Treat numbers L-H as lines" //usage: "\n -n NUM Output at most NUM lines" //usage: "\n -o FILE Write to FILE, not standard output" -//usage: "\n -z End lines with zero byte, not newline" +//usage: "\n -z NUL terminated output" #include "libbb.h" @@ -39,8 +39,10 @@ /* * Use the Fisher-Yates shuffle algorithm on an array of lines. + * If the required number of output lines is less than the total + * we can stop shuffling early. */ -static void shuffle_lines(char **lines, unsigned numlines) +static void shuffle_lines(char **lines, unsigned numlines, unsigned outlines) { unsigned i; unsigned r; @@ -48,7 +50,7 @@ static void shuffle_lines(char **lines, unsigned numlines) srand(monotonic_us()); - for (i = numlines-1; i > 0; i--) { + for (i = numlines-1; outlines > 0; i--, outlines--) { r = rand(); /* RAND_MAX can be as small as 32767 */ if (i > RAND_MAX) @@ -67,7 +69,7 @@ int shuf_main(int argc, char **argv) char *opt_i_str, *opt_n_str, *opt_o_str; unsigned i; char **lines; - unsigned numlines; + unsigned numlines, outlines; char eol; opts = getopt32(argv, "^" @@ -128,24 +130,23 @@ int shuf_main(int argc, char **argv) fclose_if_not_stdin(fp); } - if (numlines != 0) - shuffle_lines(lines, numlines); + outlines = numlines; + if (opts & OPT_n) { + outlines = xatou(opt_n_str); + if (outlines > numlines) + outlines = numlines; + } + + shuffle_lines(lines, numlines, outlines); if (opts & OPT_o) xmove_fd(xopen(opt_o_str, O_WRONLY|O_CREAT|O_TRUNC), STDOUT_FILENO); - if (opts & OPT_n) { - unsigned maxlines; - maxlines = xatou(opt_n_str); - if (numlines > maxlines) - numlines = maxlines; - } - eol = '\n'; if (opts & OPT_z) eol = '\0'; - for (i = 0; i < numlines; i++) { + for (i = numlines - outlines; i < numlines; i++) { if (opts & OPT_i) printf("%u%c", (unsigned)(uintptr_t)lines[i], eol); else -- cgit v1.2.3-55-g6feb From 1e7ca1859112333e3fb8d31dc715a4e4d5af5788 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 22 Aug 2021 15:43:29 +0200 Subject: Regularize "NUL terminated input" --help texts Signed-off-by: Denys Vlasenko --- archival/cpio.c | 2 +- findutils/grep.c | 2 +- findutils/xargs.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/archival/cpio.c b/archival/cpio.c index d84f6937d..f525419b8 100644 --- a/archival/cpio.c +++ b/archival/cpio.c @@ -74,7 +74,7 @@ //usage: "\n -F FILE Input (-t,-i,-p) or output (-o) file" //usage: "\n -R USER[:GRP] Set owner of created files" //usage: "\n -L Dereference symlinks" -//usage: "\n -0 Input is separated by NULs" +//usage: "\n -0 NUL terminated input" /* GNU cpio 2.9 --help (abridged): diff --git a/findutils/grep.c b/findutils/grep.c index be4362ed0..8600d72fa 100644 --- a/findutils/grep.c +++ b/findutils/grep.c @@ -83,7 +83,7 @@ //usage: "\n -F PATTERN is a literal (not regexp)" //usage: "\n -E PATTERN is an extended regexp" //usage: IF_EXTRA_COMPAT( -//usage: "\n -z Input is NUL terminated" +//usage: "\n -z NUL terminated input" //usage: ) //usage: "\n -m N Match up to N times per file" //usage: IF_FEATURE_GREP_CONTEXT( diff --git a/findutils/xargs.c b/findutils/xargs.c index e2b3527f3..90ff05986 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c @@ -537,7 +537,7 @@ static int xargs_ask_confirmation(void) //usage:#define xargs_full_usage "\n\n" //usage: "Run PROG on every item given by stdin\n" //usage: IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( -//usage: "\n -0 Input is separated by NULs" +//usage: "\n -0 NUL terminated input" //usage: ) //usage: IF_FEATURE_XARGS_SUPPORT_ARGS_FILE( //usage: "\n -a FILE Read from FILE instead of stdin" -- cgit v1.2.3-55-g6feb From 922b58b3e4a26377b5b65c56eec0ac93d80a6fc7 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sun, 22 Aug 2021 11:24:46 +0100 Subject: tar,smemcap: silence compiler warning gcc 11.2.1 complains that the tar header checksum might overflow the checksum field. It won't and using an unsigned int for the calculation seems to convince the compiler too. Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- archival/chksum_and_xwrite_tar_header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archival/chksum_and_xwrite_tar_header.c b/archival/chksum_and_xwrite_tar_header.c index 25934f898..f2d46b9ef 100644 --- a/archival/chksum_and_xwrite_tar_header.c +++ b/archival/chksum_and_xwrite_tar_header.c @@ -15,7 +15,7 @@ void FAST_FUNC chksum_and_xwrite_tar_header(int fd, struct tar_header_t *hp) * (Sun and HP-UX gets it wrong... more details in * GNU tar source) */ const unsigned char *cp; - int chksum, size; + unsigned int chksum, size; strcpy(hp->magic, "ustar "); -- cgit v1.2.3-55-g6feb From dbdf9e0ab1bfe65b75ba20cdf26c8fded2174c60 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 23 Aug 2021 02:30:13 +0200 Subject: mount: with -w, do not fall back to read-only mounts function old new delta mount_it_now 364 358 -6 Signed-off-by: Denys Vlasenko --- util-linux/mount.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/util-linux/mount.c b/util-linux/mount.c index 831dab9e2..5bc60de59 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -713,10 +713,12 @@ static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filtero errno = 0; rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, vfsflags, filteropts); + if (rc == 0) + goto mtab; // success - // If mount failed, try - // helper program mount. - if (HELPERS_ALLOWED && rc && mp->mnt_type) { + // mount failed, try helper program + // mount. + if (HELPERS_ALLOWED && mp->mnt_type) { char *args[8]; int errno_save = errno; args[0] = xasprintf("mount.%s", mp->mnt_type); @@ -734,13 +736,19 @@ static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filtero args[rc] = NULL; rc = spawn_and_wait(args); free(args[0]); - if (!rc) - break; + if (rc == 0) + goto mtab; // success errno = errno_save; } - if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS)) - break; + // Should we retry read-only mount? + if (vfsflags & MS_RDONLY) + break; // no, already was tried + if (option_mask32 & OPT_w) + break; // no, "mount -w" never falls back to RO + if (errno != EACCES && errno != EROFS) + break; // no, error isn't hinting that RO may work + if (!(vfsflags & MS_SILENT)) bb_error_msg("%s is write-protected, mounting read-only", mp->mnt_fsname); -- cgit v1.2.3-55-g6feb From 1b661122a8bf00e9fb493c8fc144d7822ce05816 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 23 Aug 2021 02:31:26 +0200 Subject: mount: code shrink function old new delta append_mount_options 174 157 -17 Signed-off-by: Denys Vlasenko --- util-linux/mount.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util-linux/mount.c b/util-linux/mount.c index 5bc60de59..44afdbcff 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -562,9 +562,9 @@ static void append_mount_options(char **oldopts, const char *newopts) // Do not insert options which are already there while (newopts[0]) { char *p; - int len = strlen(newopts); - p = strchr(newopts, ','); - if (p) len = p - newopts; + int len; + + len = strchrnul(newopts, ',') - newopts; p = *oldopts; while (1) { if (!strncmp(p, newopts, len) @@ -579,7 +579,7 @@ static void append_mount_options(char **oldopts, const char *newopts) *oldopts = p; skip: newopts += len; - while (newopts[0] == ',') newopts++; + while (*newopts == ',') newopts++; } } else { if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts); -- cgit v1.2.3-55-g6feb From d59f539d577ebf6100f1292e27560514e8a18195 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 23 Aug 2021 15:48:22 +0200 Subject: shuf: tweak --help text Signed-off-by: Denys Vlasenko --- coreutils/shuf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coreutils/shuf.c b/coreutils/shuf.c index 3b2ba93cf..4b41d5e71 100644 --- a/coreutils/shuf.c +++ b/coreutils/shuf.c @@ -17,14 +17,14 @@ //kbuild:lib-$(CONFIG_SHUF) += shuf.o //usage:#define shuf_trivial_usage -//usage: "[-e|-i L-H] [-n NUM] [-o FILE] [-z] [FILE|ARG...]" +//usage: "[-n NUM] [-o FILE] [-z] [FILE | -e [ARG...] | -i L-H]" //usage:#define shuf_full_usage "\n\n" //usage: "Randomly permute lines\n" -//usage: "\n -e Treat ARGs as lines" -//usage: "\n -i L-H Treat numbers L-H as lines" //usage: "\n -n NUM Output at most NUM lines" //usage: "\n -o FILE Write to FILE, not standard output" //usage: "\n -z NUL terminated output" +//usage: "\n -e Treat ARGs as lines" +//usage: "\n -i L-H Treat numbers L-H as lines" #include "libbb.h" @@ -50,7 +50,7 @@ static void shuffle_lines(char **lines, unsigned numlines, unsigned outlines) srand(monotonic_us()); - for (i = numlines-1; outlines > 0; i--, outlines--) { + for (i = numlines - 1; outlines > 0; i--, outlines--) { r = rand(); /* RAND_MAX can be as small as 32767 */ if (i > RAND_MAX) -- cgit v1.2.3-55-g6feb From 60f4843468213324cc348af9d8ec09648b6f6784 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 23 Aug 2021 15:52:34 +0200 Subject: shuf: with -i LOW-HIGH, do not allow any argv's function old new delta shuf_main 436 441 +5 Signed-off-by: Denys Vlasenko --- coreutils/shuf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coreutils/shuf.c b/coreutils/shuf.c index 4b41d5e71..fc9635147 100644 --- a/coreutils/shuf.c +++ b/coreutils/shuf.c @@ -92,6 +92,9 @@ int shuf_main(int argc, char **argv) char *dash; unsigned lo, hi; + if (argv[0]) + bb_show_usage(); + dash = strchr(opt_i_str, '-'); if (!dash) { bb_error_msg_and_die("bad range '%s'", opt_i_str); -- cgit v1.2.3-55-g6feb From 49a2e484b5bd3f6343e55bfed823d3ca6bd5d45a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 29 Aug 2021 14:39:01 +0200 Subject: shuf: in -i RANGE, accept numbers up to width of pointers function old new delta .rodata 108468 108474 +6 shuf_main 555 542 -13 Signed-off-by: Denys Vlasenko --- coreutils/shuf.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/coreutils/shuf.c b/coreutils/shuf.c index fc9635147..77f8a8ff9 100644 --- a/coreutils/shuf.c +++ b/coreutils/shuf.c @@ -90,7 +90,7 @@ int shuf_main(int argc, char **argv) if (opts & OPT_i) { /* create a range of numbers */ char *dash; - unsigned lo, hi; + uintptr_t lo, hi; if (argv[0]) bb_show_usage(); @@ -100,8 +100,17 @@ int shuf_main(int argc, char **argv) bb_error_msg_and_die("bad range '%s'", opt_i_str); } *dash = '\0'; - lo = xatou(opt_i_str); - hi = xatou(dash + 1); + if (sizeof(lo) == sizeof(int)) { + lo = xatou(opt_i_str); + hi = xatou(dash + 1); + } else + if (sizeof(lo) == sizeof(long)) { + lo = xatoul(opt_i_str); + hi = xatoul(dash + 1); + } else { + lo = xatoull(opt_i_str); + hi = xatoull(dash + 1); + } *dash = '-'; if (hi < lo) { bb_error_msg_and_die("bad range '%s'", opt_i_str); @@ -110,17 +119,21 @@ int shuf_main(int argc, char **argv) numlines = (hi+1) - lo; lines = xmalloc(numlines * sizeof(lines[0])); for (i = 0; i < numlines; i++) { - lines[i] = (char*)(uintptr_t)lo; + lines[i] = (char*)lo; lo++; } } else { /* default - read lines from stdin or the input file */ FILE *fp; + const char *fname = "-"; - if (argc > 1) - bb_show_usage(); + if (argv[0]) { + if (argv[1]) + bb_show_usage(); + fname = argv[0]; + } - fp = xfopen_stdin(argv[0] ? argv[0] : "-"); + fp = xfopen_stdin(fname); lines = NULL; numlines = 0; for (;;) { @@ -150,9 +163,14 @@ int shuf_main(int argc, char **argv) eol = '\0'; for (i = numlines - outlines; i < numlines; i++) { - if (opts & OPT_i) - printf("%u%c", (unsigned)(uintptr_t)lines[i], eol); - else + if (opts & OPT_i) { + if (sizeof(lines[0]) == sizeof(int)) + printf("%u%c", (unsigned)(uintptr_t)lines[i], eol); + else if (sizeof(lines[0]) == sizeof(long)) + printf("%lu%c", (unsigned long)(uintptr_t)lines[i], eol); + else + printf("%llu%c", (unsigned long long)(uintptr_t)lines[i], eol); + } else printf("%s%c", lines[i], eol); } -- cgit v1.2.3-55-g6feb From 6d3da732a5d07b4c2f05f4f96df57b7618d0448f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 29 Aug 2021 15:32:42 +0200 Subject: bzip: make ftab[] and crc32table[] member arrays of EState, do not allocate function old new delta mainSort 941 986 +45 fallbackSort 1471 1469 -2 add_pair_to_block 194 188 -6 compressStream 543 515 -28 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/3 up/down: 45/-36) Total: 9 bytes Signed-off-by: Denys Vlasenko --- archival/libarchive/bz/bzlib.c | 7 +++---- archival/libarchive/bz/bzlib_private.h | 11 +++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/archival/libarchive/bz/bzlib.c b/archival/libarchive/bz/bzlib.c index 9af2f026d..ef19ae165 100644 --- a/archival/libarchive/bz/bzlib.c +++ b/archival/libarchive/bz/bzlib.c @@ -99,9 +99,8 @@ void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k) s->ptr = (uint32_t*)s->arr1; s->arr2 = xmalloc((n + BZ_N_OVERSHOOT) * sizeof(uint32_t)); s->block = (uint8_t*)s->arr2; - s->ftab = xmalloc(65537 * sizeof(uint32_t)); - s->crc32table = crc32_filltable(NULL, 1); + crc32_filltable(s->crc32table, 1); s->state = BZ_S_INPUT; s->mode = BZ_M_RUNNING; @@ -369,8 +368,8 @@ void BZ2_bzCompressEnd(bz_stream *strm) s = strm->state; free(s->arr1); free(s->arr2); - free(s->ftab); - free(s->crc32table); + //free(s->ftab); // made it array member of s + //free(s->crc32table); // ditto free(s); } diff --git a/archival/libarchive/bz/bzlib_private.h b/archival/libarchive/bz/bzlib_private.h index ea0f29b7c..650444a5c 100644 --- a/archival/libarchive/bz/bzlib_private.h +++ b/archival/libarchive/bz/bzlib_private.h @@ -134,7 +134,7 @@ typedef struct EState { /* for doing the block sorting */ uint32_t *arr1; uint32_t *arr2; - uint32_t *ftab; + //uint32_t *ftab; //moved into this struct, see below uint16_t *quadrant; int32_t budget; @@ -160,9 +160,6 @@ typedef struct EState { uint32_t bsBuff; int32_t bsLive; - /* guess what */ - uint32_t *crc32table; - /* block and combined CRCs */ uint32_t blockCRC; uint32_t combinedCRC; @@ -185,6 +182,12 @@ typedef struct EState { uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* guess what */ + uint32_t crc32table[256]; + + /* for doing the block sorting */ + uint32_t ftab[65537]; + /* stack-saving measures: these can be local, but they are too big */ int32_t sendMTFValues__code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; int32_t sendMTFValues__rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; -- cgit v1.2.3-55-g6feb From a51d953b95a7cc6b40a6b3a5bfd95f3154acf5e2 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sun, 29 Aug 2021 14:56:02 +0100 Subject: 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 Signed-off-by: Denys Vlasenko --- 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); } -- cgit v1.2.3-55-g6feb