aboutsummaryrefslogtreecommitdiff
path: root/coreutils/sort.c
diff options
context:
space:
mode:
Diffstat (limited to 'coreutils/sort.c')
-rw-r--r--coreutils/sort.c97
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:"
98enum { 89enum {
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
114static 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
123static char key_separator; 124static 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
131static char *get_key(char *str, struct sort_key *key, int flags) 136static 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)
368int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 398int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
369int sort_main(int argc UNUSED_PARAM, char **argv) 399int 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]);