diff options
Diffstat (limited to 'coreutils/sort.c')
-rw-r--r-- | coreutils/sort.c | 97 |
1 files changed, 73 insertions, 24 deletions
diff --git a/coreutils/sort.c b/coreutils/sort.c index ceea24491..b39297a26 100644 --- a/coreutils/sort.c +++ b/coreutils/sort.c | |||
@@ -62,9 +62,9 @@ | |||
62 | //usage: "\n -u Suppress duplicate lines" | 62 | //usage: "\n -u Suppress duplicate lines" |
63 | //usage: IF_FEATURE_SORT_BIG( | 63 | //usage: IF_FEATURE_SORT_BIG( |
64 | //usage: "\n -z Lines are terminated by NUL, not newline" | 64 | //usage: "\n -z Lines are terminated by NUL, not newline" |
65 | ////usage: "\n -m Ignored for GNU compatibility" | 65 | ///////: "\n -m Ignored for GNU compatibility" |
66 | ////usage: "\n -S BUFSZ Ignored for GNU compatibility" | 66 | ///////: "\n -S BUFSZ Ignored for GNU compatibility" |
67 | ////usage: "\n -T TMPDIR Ignored for GNU compatibility" | 67 | ///////: "\n -T TMPDIR Ignored for GNU compatibility" |
68 | //usage: ) | 68 | //usage: ) |
69 | //usage: | 69 | //usage: |
70 | //usage:#define sort_example_usage | 70 | //usage:#define sort_example_usage |
@@ -85,16 +85,7 @@ | |||
85 | 85 | ||
86 | #include "libbb.h" | 86 | #include "libbb.h" |
87 | 87 | ||
88 | /* This is a NOEXEC applet. Be very careful! */ | ||
89 | |||
90 | |||
91 | /* | ||
92 | sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...] | ||
93 | sort -c [-bdfinru][-t char][-k keydef][file] | ||
94 | */ | ||
95 | |||
96 | /* These are sort types */ | 88 | /* These are sort types */ |
97 | #define OPT_STR "ngMucszbrdfimS:T:o:k:*t:" | ||
98 | enum { | 89 | enum { |
99 | FLAG_n = 1, /* Numeric sort */ | 90 | FLAG_n = 1, /* Numeric sort */ |
100 | FLAG_g = 2, /* Sort using strtod() */ | 91 | FLAG_g = 2, /* Sort using strtod() */ |
@@ -117,8 +108,18 @@ enum { | |||
117 | FLAG_k = 0x10000, | 108 | FLAG_k = 0x10000, |
118 | FLAG_t = 0x20000, | 109 | FLAG_t = 0x20000, |
119 | FLAG_bb = 0x80000000, /* Ignore trailing blanks */ | 110 | FLAG_bb = 0x80000000, /* Ignore trailing blanks */ |
111 | FLAG_no_tie_break = 0x40000000, | ||
120 | }; | 112 | }; |
121 | 113 | ||
114 | static const char sort_opt_str[] ALIGN1 = "^" | ||
115 | "ngMucszbrdfimS:T:o:k:*t:" | ||
116 | "\0" "o--o:t--t"/*-t, -o: at most one of each*/; | ||
117 | /* | ||
118 | * OPT_STR must not be string literal, needs to have stable address: | ||
119 | * code uses "strchr(OPT_STR,c) - OPT_STR" idiom. | ||
120 | */ | ||
121 | #define OPT_STR (sort_opt_str + 1) | ||
122 | |||
122 | #if ENABLE_FEATURE_SORT_BIG | 123 | #if ENABLE_FEATURE_SORT_BIG |
123 | static char key_separator; | 124 | static char key_separator; |
124 | 125 | ||
@@ -128,6 +129,10 @@ static struct sort_key { | |||
128 | unsigned flags; | 129 | unsigned flags; |
129 | } *key_list; | 130 | } *key_list; |
130 | 131 | ||
132 | |||
133 | /* This is a NOEXEC applet. Be very careful! */ | ||
134 | |||
135 | |||
131 | static char *get_key(char *str, struct sort_key *key, int flags) | 136 | static char *get_key(char *str, struct sort_key *key, int flags) |
132 | { | 137 | { |
133 | int start = start; /* for compiler */ | 138 | int start = start; /* for compiler */ |
@@ -340,10 +345,35 @@ static int compare_keys(const void *xarg, const void *yarg) | |||
340 | #endif | 345 | #endif |
341 | } /* for */ | 346 | } /* for */ |
342 | 347 | ||
343 | /* Perform fallback sort if necessary */ | 348 | if (retval == 0) { |
344 | if (!retval && !(option_mask32 & FLAG_s)) { | 349 | /* So far lines are "the same" */ |
345 | flags = option_mask32; | 350 | |
346 | retval = strcmp(*(char **)xarg, *(char **)yarg); | 351 | if (option_mask32 & FLAG_s) { |
352 | /* "Stable sort": later line is "greater than", | ||
353 | * IOW: do not allow qsort() to swap equal lines. | ||
354 | */ | ||
355 | uint32_t *p32; | ||
356 | uint32_t x32, y32; | ||
357 | char *line; | ||
358 | unsigned len; | ||
359 | |||
360 | line = *(char**)xarg; | ||
361 | len = (strlen(line) + 4) & (~3u); | ||
362 | p32 = (void*)(line + len); | ||
363 | x32 = *p32; | ||
364 | line = *(char**)yarg; | ||
365 | len = (strlen(line) + 4) & (~3u); | ||
366 | p32 = (void*)(line + len); | ||
367 | y32 = *p32; | ||
368 | |||
369 | /* If x > y, 1, else -1 */ | ||
370 | retval = (x32 > y32) * 2 - 1; | ||
371 | } else | ||
372 | if (!(option_mask32 & FLAG_no_tie_break)) { | ||
373 | /* fallback sort */ | ||
374 | flags = option_mask32; | ||
375 | retval = strcmp(*(char **)xarg, *(char **)yarg); | ||
376 | } | ||
347 | } | 377 | } |
348 | 378 | ||
349 | if (flags & FLAG_r) | 379 | if (flags & FLAG_r) |
@@ -368,7 +398,7 @@ static unsigned str2u(char **str) | |||
368 | int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 398 | int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
369 | int sort_main(int argc UNUSED_PARAM, char **argv) | 399 | int sort_main(int argc UNUSED_PARAM, char **argv) |
370 | { | 400 | { |
371 | char *line, **lines; | 401 | char **lines; |
372 | char *str_ignored, *str_o, *str_t; | 402 | char *str_ignored, *str_o, *str_t; |
373 | llist_t *lst_k = NULL; | 403 | llist_t *lst_k = NULL; |
374 | int i; | 404 | int i; |
@@ -378,9 +408,8 @@ int sort_main(int argc UNUSED_PARAM, char **argv) | |||
378 | xfunc_error_retval = 2; | 408 | xfunc_error_retval = 2; |
379 | 409 | ||
380 | /* Parse command line options */ | 410 | /* Parse command line options */ |
381 | opts = getopt32(argv, "^" | 411 | opts = getopt32(argv, |
382 | OPT_STR | 412 | sort_opt_str, |
383 | "\0" "o--o:t--t"/*-t, -o: at most one of each*/, | ||
384 | &str_ignored, &str_ignored, &str_o, &lst_k, &str_t | 413 | &str_ignored, &str_ignored, &str_o, &lst_k, &str_t |
385 | ); | 414 | ); |
386 | /* global b strips leading and trailing spaces */ | 415 | /* global b strips leading and trailing spaces */ |
@@ -457,7 +486,7 @@ int sort_main(int argc UNUSED_PARAM, char **argv) | |||
457 | * do not continue to next file: */ | 486 | * do not continue to next file: */ |
458 | FILE *fp = xfopen_stdin(*argv); | 487 | FILE *fp = xfopen_stdin(*argv); |
459 | for (;;) { | 488 | for (;;) { |
460 | line = GET_LINE(fp); | 489 | char *line = GET_LINE(fp); |
461 | if (!line) | 490 | if (!line) |
462 | break; | 491 | break; |
463 | lines = xrealloc_vector(lines, 6, linecount); | 492 | lines = xrealloc_vector(lines, 6, linecount); |
@@ -482,15 +511,35 @@ int sort_main(int argc UNUSED_PARAM, char **argv) | |||
482 | return EXIT_SUCCESS; | 511 | return EXIT_SUCCESS; |
483 | } | 512 | } |
484 | #endif | 513 | #endif |
514 | |||
515 | /* For stable sort, store original line position beyond terminating NUL */ | ||
516 | if (option_mask32 & FLAG_s) { | ||
517 | for (i = 0; i < linecount; i++) { | ||
518 | uint32_t *p32; | ||
519 | char *line; | ||
520 | unsigned len; | ||
521 | |||
522 | line = lines[i]; | ||
523 | len = (strlen(line) + 4) & (~3u); | ||
524 | lines[i] = line = xrealloc(line, len + 4); | ||
525 | p32 = (void*)(line + len); | ||
526 | *p32 = i; | ||
527 | } | ||
528 | /*option_mask32 |= FLAG_no_tie_break;*/ | ||
529 | /* ^^^redundant: if FLAG_s, compare_keys() does no tie break */ | ||
530 | } | ||
531 | |||
485 | /* Perform the actual sort */ | 532 | /* Perform the actual sort */ |
486 | qsort(lines, linecount, sizeof(lines[0]), compare_keys); | 533 | qsort(lines, linecount, sizeof(lines[0]), compare_keys); |
487 | 534 | ||
488 | /* Handle -u */ | 535 | /* Handle -u */ |
489 | if (option_mask32 & FLAG_u) { | 536 | if (option_mask32 & FLAG_u) { |
490 | int j = 0; | 537 | int j = 0; |
491 | /* coreutils 6.3 drop lines for which only key is the same */ | 538 | /* coreutils 6.3 drop lines for which only key is the same |
492 | /* -- disabling last-resort compare... */ | 539 | * -- disabling last-resort compare, or else compare_keys() |
493 | option_mask32 |= FLAG_s; | 540 | * will be the same only for completely identical lines. |
541 | */ | ||
542 | option_mask32 |= FLAG_no_tie_break; | ||
494 | for (i = 1; i < linecount; i++) { | 543 | for (i = 1; i < linecount; i++) { |
495 | if (compare_keys(&lines[j], &lines[i]) == 0) | 544 | if (compare_keys(&lines[j], &lines[i]) == 0) |
496 | free(lines[i]); | 545 | free(lines[i]); |