diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2022-01-05 12:05:55 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2022-01-05 12:05:55 +0100 |
commit | 34e0bb3931b595e7a48061255692ec4ff29499c5 (patch) | |
tree | 7d4095eac56be1bf6d824180a7c102a4e31e680b | |
parent | cc7d2e21780c28608b00a4faf0fed297527bcbf4 (diff) | |
download | busybox-w32-34e0bb3931b595e7a48061255692ec4ff29499c5.tar.gz busybox-w32-34e0bb3931b595e7a48061255692ec4ff29499c5.tar.bz2 busybox-w32-34e0bb3931b595e7a48061255692ec4ff29499c5.zip |
sort: support -h
function old new delta
static.scale_suffix - 62 +62
.rodata 104304 104336 +32
compare_keys 820 848 +28
packed_usage 34159 34184 +25
static.suffix - 9 +9
sort_opt_str 37 38 +1
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 4/0 up/down: 157/0) Total: 157 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | coreutils/sort.c | 95 | ||||
-rwxr-xr-x | testsuite/sort.tests | 29 |
2 files changed, 93 insertions, 31 deletions
diff --git a/coreutils/sort.c b/coreutils/sort.c index 9ff777851..9aac656fe 100644 --- a/coreutils/sort.c +++ b/coreutils/sort.c | |||
@@ -18,7 +18,7 @@ | |||
18 | //config: sort is used to sort lines of text in specified files. | 18 | //config: sort is used to sort lines of text in specified files. |
19 | //config: | 19 | //config: |
20 | //config:config FEATURE_SORT_BIG | 20 | //config:config FEATURE_SORT_BIG |
21 | //config: bool "Full SuSv3 compliant sort (support -ktcbdfiogM)" | 21 | //config: bool "Full SuSv3 compliant sort (support -ktcbdfioghM)" |
22 | //config: default y | 22 | //config: default y |
23 | //config: depends on SORT | 23 | //config: depends on SORT |
24 | //config: help | 24 | //config: help |
@@ -43,7 +43,7 @@ | |||
43 | 43 | ||
44 | //usage:#define sort_trivial_usage | 44 | //usage:#define sort_trivial_usage |
45 | //usage: "[-nru" | 45 | //usage: "[-nru" |
46 | //usage: IF_FEATURE_SORT_BIG("gMcszbdfiokt] [-o FILE] [-k START[.OFS][OPTS][,END[.OFS][OPTS]] [-t CHAR") | 46 | //usage: IF_FEATURE_SORT_BIG("ghMcszbdfiokt] [-o FILE] [-k START[.OFS][OPTS][,END[.OFS][OPTS]] [-t CHAR") |
47 | //usage: "] [FILE]..." | 47 | //usage: "] [FILE]..." |
48 | //usage:#define sort_full_usage "\n\n" | 48 | //usage:#define sort_full_usage "\n\n" |
49 | //usage: "Sort lines of text\n" | 49 | //usage: "Sort lines of text\n" |
@@ -59,6 +59,7 @@ | |||
59 | //usage: "\n -n Sort numbers" | 59 | //usage: "\n -n Sort numbers" |
60 | //usage: IF_FEATURE_SORT_BIG( | 60 | //usage: IF_FEATURE_SORT_BIG( |
61 | //usage: "\n -g General numerical sort" | 61 | //usage: "\n -g General numerical sort" |
62 | //usage: "\n -h Sort human readable numbers (2K 1G)" | ||
62 | //usage: "\n -M Sort month" | 63 | //usage: "\n -M Sort month" |
63 | //usage: "\n -V Sort version" | 64 | //usage: "\n -V Sort version" |
64 | //usage: "\n -t CHAR Field separator" | 65 | //usage: "\n -t CHAR Field separator" |
@@ -94,31 +95,32 @@ | |||
94 | enum { | 95 | enum { |
95 | FLAG_n = 1 << 0, /* Numeric sort */ | 96 | FLAG_n = 1 << 0, /* Numeric sort */ |
96 | FLAG_g = 1 << 1, /* Sort using strtod() */ | 97 | FLAG_g = 1 << 1, /* Sort using strtod() */ |
97 | FLAG_M = 1 << 2, /* Sort date */ | 98 | FLAG_h = 1 << 2, /* Sort using strtod(), plus KMGT suffixes */ |
98 | FLAG_V = 1 << 3, /* Sort version */ | 99 | FLAG_M = 1 << 3, /* Sort date */ |
100 | FLAG_V = 1 << 4, /* Sort version */ | ||
99 | /* ucsz apply to root level only, not keys. b at root level implies bb */ | 101 | /* ucsz apply to root level only, not keys. b at root level implies bb */ |
100 | FLAG_u = 1 << 4, /* Unique */ | 102 | FLAG_u = 1 << 5, /* Unique */ |
101 | FLAG_c = 1 << 5, /* Check: no output, exit(!ordered) */ | 103 | FLAG_c = 1 << 6, /* Check: no output, exit(!ordered) */ |
102 | FLAG_s = 1 << 6, /* Stable sort, no ascii fallback at end */ | 104 | FLAG_s = 1 << 7, /* Stable sort, no ascii fallback at end */ |
103 | FLAG_z = 1 << 7, /* Input and output is NUL terminated, not \n */ | 105 | FLAG_z = 1 << 8, /* Input and output is NUL terminated, not \n */ |
104 | /* These can be applied to search keys, the previous four can't */ | 106 | /* These can be applied to search keys, the previous four can't */ |
105 | FLAG_b = 1 << 8, /* Ignore leading blanks */ | 107 | FLAG_b = 1 << 9, /* Ignore leading blanks */ |
106 | FLAG_r = 1 << 9, /* Reverse */ | 108 | FLAG_r = 1 << 10, /* Reverse */ |
107 | FLAG_d = 1 << 10, /* Ignore !(isalnum()|isspace()) */ | 109 | FLAG_d = 1 << 11, /* Ignore !(isalnum()|isspace()) */ |
108 | FLAG_f = 1 << 11, /* Force uppercase */ | 110 | FLAG_f = 1 << 12, /* Force uppercase */ |
109 | FLAG_i = 1 << 12, /* Ignore !isprint() */ | 111 | FLAG_i = 1 << 13, /* Ignore !isprint() */ |
110 | FLAG_m = 1 << 13, /* ignored: merge already sorted files; do not sort */ | 112 | FLAG_m = 1 << 14, /* ignored: merge already sorted files; do not sort */ |
111 | FLAG_S = 1 << 14, /* ignored: -S, --buffer-size=SIZE */ | 113 | FLAG_S = 1 << 15, /* ignored: -S, --buffer-size=SIZE */ |
112 | FLAG_T = 1 << 15, /* ignored: -T, --temporary-directory=DIR */ | 114 | FLAG_T = 1 << 16, /* ignored: -T, --temporary-directory=DIR */ |
113 | FLAG_o = 1 << 16, | 115 | FLAG_o = 1 << 17, |
114 | FLAG_k = 1 << 17, | 116 | FLAG_k = 1 << 18, |
115 | FLAG_t = 1 << 18, | 117 | FLAG_t = 1 << 19, |
116 | FLAG_bb = 0x80000000, /* Ignore trailing blanks */ | 118 | FLAG_bb = 0x80000000, /* Ignore trailing blanks */ |
117 | FLAG_no_tie_break = 0x40000000, | 119 | FLAG_no_tie_break = 0x40000000, |
118 | }; | 120 | }; |
119 | 121 | ||
120 | static const char sort_opt_str[] ALIGN1 = "^" | 122 | static const char sort_opt_str[] ALIGN1 = "^" |
121 | "ngMVucszbrdfimS:T:o:k:*t:" | 123 | "nghMVucszbrdfimS:T:o:k:*t:" |
122 | "\0" "o--o:t--t"/*-t, -o: at most one of each*/; | 124 | "\0" "o--o:t--t"/*-t, -o: at most one of each*/; |
123 | /* | 125 | /* |
124 | * OPT_STR must not be string literal, needs to have stable address: | 126 | * OPT_STR must not be string literal, needs to have stable address: |
@@ -253,6 +255,25 @@ static struct sort_key *add_key(void) | |||
253 | #define GET_LINE(fp) xmalloc_fgetline(fp) | 255 | #define GET_LINE(fp) xmalloc_fgetline(fp) |
254 | #endif | 256 | #endif |
255 | 257 | ||
258 | #if ENABLE_FEATURE_SORT_BIG | ||
259 | static int scale_suffix(const char *tail) | ||
260 | { | ||
261 | static const char suffix[] ALIGN1 = "kmgtpezy"; | ||
262 | const char *s; | ||
263 | int n; | ||
264 | |||
265 | if (!tail[0]) | ||
266 | return -1; | ||
267 | s = strchr(suffix, tail[0] | 0x20); | ||
268 | if (!s) | ||
269 | return -1; | ||
270 | n = s - suffix; | ||
271 | if (n != 0 && tail[0] >= 'a') | ||
272 | return -1; /* mg... not accepted, only MG... */ | ||
273 | return n; | ||
274 | } | ||
275 | #endif | ||
276 | |||
256 | /* Iterate through keys list and perform comparisons */ | 277 | /* Iterate through keys list and perform comparisons */ |
257 | static int compare_keys(const void *xarg, const void *yarg) | 278 | static int compare_keys(const void *xarg, const void *yarg) |
258 | { | 279 | { |
@@ -275,7 +296,7 @@ static int compare_keys(const void *xarg, const void *yarg) | |||
275 | y = *(char **)yarg; | 296 | y = *(char **)yarg; |
276 | #endif | 297 | #endif |
277 | /* Perform actual comparison */ | 298 | /* Perform actual comparison */ |
278 | switch (flags & (FLAG_n | FLAG_g | FLAG_M | FLAG_V)) { | 299 | switch (flags & (FLAG_n | FLAG_g | FLAG_h | FLAG_M | FLAG_V)) { |
279 | default: | 300 | default: |
280 | bb_simple_error_msg_and_die("unknown sort type"); | 301 | bb_simple_error_msg_and_die("unknown sort type"); |
281 | break; | 302 | break; |
@@ -293,7 +314,8 @@ static int compare_keys(const void *xarg, const void *yarg) | |||
293 | #endif | 314 | #endif |
294 | break; | 315 | break; |
295 | #if ENABLE_FEATURE_SORT_BIG | 316 | #if ENABLE_FEATURE_SORT_BIG |
296 | case FLAG_g: { | 317 | case FLAG_g: |
318 | case FLAG_h: { | ||
297 | char *xx, *yy; | 319 | char *xx, *yy; |
298 | //TODO: needs setlocale(LC_NUMERIC, "C")? | 320 | //TODO: needs setlocale(LC_NUMERIC, "C")? |
299 | double dx = strtod(x, &xx); | 321 | double dx = strtod(x, &xx); |
@@ -308,16 +330,26 @@ static int compare_keys(const void *xarg, const void *yarg) | |||
308 | retval = (dy != dy) ? 0 : -1; | 330 | retval = (dy != dy) ? 0 : -1; |
309 | else if (dy != dy) | 331 | else if (dy != dy) |
310 | retval = 1; | 332 | retval = 1; |
311 | /* Check for infinity. Could underflow, but it avoids libm. */ | 333 | else { |
312 | else if (1.0 / dx == 0.0) { | 334 | if (flags & FLAG_h) { |
313 | if (dx < 0) | 335 | int xs = scale_suffix(xx); |
314 | retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1; | 336 | int ys = scale_suffix(yy); |
337 | if (xs != ys) { | ||
338 | retval = xs - ys; | ||
339 | break; | ||
340 | } | ||
341 | } | ||
342 | /* Check for infinity. Could underflow, but it avoids libm. */ | ||
343 | if (1.0 / dx == 0.0) { | ||
344 | if (dx < 0) | ||
345 | retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1; | ||
346 | else | ||
347 | retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1; | ||
348 | } else if (1.0 / dy == 0.0) | ||
349 | retval = (dy < 0) ? 1 : -1; | ||
315 | else | 350 | else |
316 | retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1; | 351 | retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); |
317 | } else if (1.0 / dy == 0.0) | 352 | } |
318 | retval = (dy < 0) ? 1 : -1; | ||
319 | else | ||
320 | retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); | ||
321 | break; | 353 | break; |
322 | } | 354 | } |
323 | case FLAG_M: { | 355 | case FLAG_M: { |
@@ -476,6 +508,7 @@ int sort_main(int argc UNUSED_PARAM, char **argv) | |||
476 | FLAG_allowed_for_k = | 508 | FLAG_allowed_for_k = |
477 | FLAG_n | /* Numeric sort */ | 509 | FLAG_n | /* Numeric sort */ |
478 | FLAG_g | /* Sort using strtod() */ | 510 | FLAG_g | /* Sort using strtod() */ |
511 | FLAG_h | /* Sort using strtod(), plus KMGT suffixes */ | ||
479 | FLAG_M | /* Sort date */ | 512 | FLAG_M | /* Sort date */ |
480 | FLAG_b | /* Ignore leading blanks */ | 513 | FLAG_b | /* Ignore leading blanks */ |
481 | FLAG_r | /* Reverse */ | 514 | FLAG_r | /* Reverse */ |
diff --git a/testsuite/sort.tests b/testsuite/sort.tests index 5375f93de..ff33e21b4 100755 --- a/testsuite/sort.tests +++ b/testsuite/sort.tests | |||
@@ -188,6 +188,35 @@ c 1 | |||
188 | d 2 | 188 | d 2 |
189 | " "" | 189 | " "" |
190 | 190 | ||
191 | testing "sort -h" \ | ||
192 | "sort -h input" "\ | ||
193 | 3e | ||
194 | 4m | ||
195 | 5y | ||
196 | 1023 | ||
197 | 1024 | ||
198 | 1025 | ||
199 | 3000 | ||
200 | 2K | ||
201 | 3k | ||
202 | 1M | ||
203 | 2E | ||
204 | 1Y | ||
205 | " "\ | ||
206 | 1Y | ||
207 | 5y | ||
208 | 1M | ||
209 | 2E | ||
210 | 3k | ||
211 | 3e | ||
212 | 2K | ||
213 | 4m | ||
214 | 1023 | ||
215 | 1025 | ||
216 | 3000 | ||
217 | 1024 | ||
218 | " "" | ||
219 | |||
191 | # testing "description" "command(s)" "result" "infile" "stdin" | 220 | # testing "description" "command(s)" "result" "infile" "stdin" |
192 | 221 | ||
193 | exit $FAILCOUNT | 222 | exit $FAILCOUNT |