aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2022-01-05 12:05:55 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2022-01-05 12:05:55 +0100
commit34e0bb3931b595e7a48061255692ec4ff29499c5 (patch)
tree7d4095eac56be1bf6d824180a7c102a4e31e680b
parentcc7d2e21780c28608b00a4faf0fed297527bcbf4 (diff)
downloadbusybox-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.c95
-rwxr-xr-xtestsuite/sort.tests29
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 @@
94enum { 95enum {
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
120static const char sort_opt_str[] ALIGN1 = "^" 122static 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
259static 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 */
257static int compare_keys(const void *xarg, const void *yarg) 278static 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
188d 2 188d 2
189" "" 189" ""
190 190
191testing "sort -h" \
192"sort -h input" "\
1933e
1944m
1955y
1961023
1971024
1981025
1993000
2002K
2013k
2021M
2032E
2041Y
205" "\
2061Y
2075y
2081M
2092E
2103k
2113e
2122K
2134m
2141023
2151025
2163000
2171024
218" ""
219
191# testing "description" "command(s)" "result" "infile" "stdin" 220# testing "description" "command(s)" "result" "infile" "stdin"
192 221
193exit $FAILCOUNT 222exit $FAILCOUNT