2 * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
3 * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * $FreeBSD: head/usr.bin/sort/sort.c 281182 2015-04-07 01:17:49Z pfg $
32 #include <sys/sysctl.h>
33 #include <sys/types.h>
49 #if defined(SORT_RANDOM)
50 #include <openssl/md5.h>
62 #if defined(SORT_RANDOM)
63 #define OPTIONS "bcCdfghik:Mmno:RrsS:t:T:uVz"
65 #define OPTIONS "bcCdfghik:Mmno:rsS:t:T:uVz"
68 #if defined(SORT_RANDOM)
69 #define DEFAULT_RANDOM_SORT_SEED_FILE ("/dev/random")
70 #define MAX_DEFAULT_RANDOM_SEED_DATA_SIZE (1024)
72 static bool need_random
;
73 static const char *random_source
= DEFAULT_RANDOM_SORT_SEED_FILE
;
74 static const void *random_seed
;
75 static size_t random_seed_size
;
81 * Default messages to use when NLS is disabled or no catalogue
84 const char *nlsstr
[] = { "",
85 /* 1*/"mutually exclusive flags",
86 /* 2*/"extra argument not allowed with -c",
87 /* 3*/"Unknown feature",
88 /* 4*/"Wrong memory buffer specification",
89 /* 5*/"0 field in key specs",
90 /* 6*/"0 column in key specs",
91 /* 7*/"Wrong file mode",
92 /* 8*/"Cannot open file for reading",
93 /* 9*/"Radix sort cannot be used with these sort options",
94 /*10*/"The chosen sort method cannot be used with stable and/or unique sort",
95 /*11*/"Invalid key position",
96 /*12*/"Usage: %s [-bcCdfigMmnrsuz] [-kPOS1[,POS2] ... ] "
97 "[+POS1 [-POS2]] [-S memsize] [-T tmpdir] [-t separator] "
98 "[-o outfile] [--batch-size size] [--files0-from file] "
99 "[--heapsort] [--mergesort] [--radixsort] [--qsort] "
101 #if defined(SORT_THREADS)
102 "[--parallel thread_no] "
104 "[--human-numeric-sort] "
105 #if defined(SORT_RANDOM)
106 "[--version-sort] [--random-sort [--random-source file]] "
110 "[--compress-program program] [file ...]\n" };
112 struct sort_opts sort_opts_vals
;
117 #if defined(SORT_THREADS)
118 unsigned int ncpu
= 1;
122 static bool gnusort_numeric_compatibility
;
124 static struct sort_mods default_sort_mods_object
;
125 struct sort_mods
* const default_sort_mods
= &default_sort_mods_object
;
127 static bool print_symbols_on_debug
;
130 * Arguments from file (when file0-from option is used:
132 static size_t argc_from_file0
= (size_t)-1;
133 static char **argv_from_file0
;
136 * Placeholder symbols for options which have no single-character equivalent
140 SORT_OPT
= CHAR_MAX
+ 1,
146 #if defined(SORT_THREADS)
149 #if defined(SORT_RANDOM)
160 #define NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS 6
161 static const char mutually_exclusive_flags
[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS
] = { 'M', 'n', 'g', 'R', 'h', 'V' };
163 static struct option long_options
[] = {
164 { "batch-size", required_argument
, NULL
, BS_OPT
},
165 { "buffer-size", required_argument
, NULL
, 'S' },
166 { "check", optional_argument
, NULL
, 'c' },
167 { "check=silent|quiet", optional_argument
, NULL
, 'C' },
168 { "compress-program", required_argument
, NULL
, COMPRESSPROGRAM_OPT
},
169 { "debug", no_argument
, NULL
, DEBUG_OPT
},
170 { "dictionary-order", no_argument
, NULL
, 'd' },
171 { "field-separator", required_argument
, NULL
, 't' },
172 { "files0-from", required_argument
, NULL
, FF_OPT
},
173 { "general-numeric-sort", no_argument
, NULL
, 'g' },
174 { "heapsort", no_argument
, NULL
, HEAPSORT_OPT
},
175 { "help",no_argument
, NULL
, HELP_OPT
},
176 { "human-numeric-sort", no_argument
, NULL
, 'h' },
177 { "ignore-leading-blanks", no_argument
, NULL
, 'b' },
178 { "ignore-case", no_argument
, NULL
, 'f' },
179 { "ignore-nonprinting", no_argument
, NULL
, 'i' },
180 { "key", required_argument
, NULL
, 'k' },
181 { "merge", no_argument
, NULL
, 'm' },
182 { "mergesort", no_argument
, NULL
, MERGESORT_OPT
},
183 { "mmap", no_argument
, NULL
, MMAP_OPT
},
184 { "month-sort", no_argument
, NULL
, 'M' },
185 { "numeric-sort", no_argument
, NULL
, 'n' },
186 { "output", required_argument
, NULL
, 'o' },
187 #if defined(SORT_THREADS)
188 { "parallel", required_argument
, NULL
, PARALLEL_OPT
},
190 { "qsort", no_argument
, NULL
, QSORT_OPT
},
191 { "radixsort", no_argument
, NULL
, RADIXSORT_OPT
},
192 #if defined(SORT_RANDOM)
193 { "random-sort", no_argument
, NULL
, 'R' },
194 { "random-source", required_argument
, NULL
, RANDOMSOURCE_OPT
},
196 { "reverse", no_argument
, NULL
, 'r' },
197 { "sort", required_argument
, NULL
, SORT_OPT
},
198 { "stable", no_argument
, NULL
, 's' },
199 { "temporary-directory",required_argument
, NULL
, 'T' },
200 { "unique", no_argument
, NULL
, 'u' },
201 { "version", no_argument
, NULL
, VERSION_OPT
},
202 { "version-sort",no_argument
, NULL
, 'V' },
203 { "zero-terminated", no_argument
, NULL
, 'z' },
204 { NULL
, no_argument
, NULL
, 0 }
207 static void fix_obsolete_keys(int *argc
, char **argv
);
210 * Check where sort modifier is present
213 sort_modifier_empty(struct sort_mods
*sm
)
218 return (!(sm
->Mflag
|| sm
->Vflag
|| sm
->nflag
|| sm
->gflag
||
222 sm
->rflag
|| sm
->hflag
|| sm
->dflag
|| sm
->fflag
));
226 * Print out usage text.
233 out
= opt_err
? stderr
: stdout
;
235 fprintf(out
, getstr(12), getprogname());
242 * Read input file names from a file (file0-from option).
245 read_fns_from_file0(const char *fn
)
259 while ((linelen
= getdelim(&line
, &linesize
, '\0', f
)) != -1) {
261 if (argc_from_file0
== (size_t) - 1)
264 argv_from_file0
= sort_realloc(argv_from_file0
,
265 argc_from_file0
* sizeof(char *));
266 if (argv_from_file0
== NULL
)
268 argv_from_file0
[argc_from_file0
- 1] = line
;
276 err(2, "%s: getdelim", fn
);
282 * Check how much RAM is available for the sort.
291 #if defined(SORT_THREADS)
295 pages
= sysconf(_SC_PHYS_PAGES
);
297 perror("sysconf pages");
300 psize
= sysconf(_SC_PAGESIZE
);
302 perror("sysconf psize");
305 #if defined(SORT_THREADS)
306 ncpu
= (unsigned int)sysconf(_SC_NPROCESSORS_ONLN
);
315 free_memory
= (unsigned long long) pages
* (unsigned long long) psize
;
316 available_free_memory
= free_memory
/ 2;
318 if (available_free_memory
< 1024)
319 available_free_memory
= 1024;
323 * Convert "plain" symbol to wide symbol, with default value.
326 conv_mbtowc(wchar_t *wc
, const char *c
, const wchar_t def
)
332 res
= mbtowc(wc
, c
, MB_CUR_MAX
);
339 * Set current locale symbols.
347 setlocale(LC_ALL
, "");
352 /* obtain LC_NUMERIC info */
353 /* Convert to wide char form */
354 conv_mbtowc(&symbol_decimal_point
, lc
->decimal_point
,
355 symbol_decimal_point
);
356 conv_mbtowc(&symbol_thousands_sep
, lc
->thousands_sep
,
357 symbol_thousands_sep
);
358 conv_mbtowc(&symbol_positive_sign
, lc
->positive_sign
,
359 symbol_positive_sign
);
360 conv_mbtowc(&symbol_negative_sign
, lc
->negative_sign
,
361 symbol_negative_sign
);
364 if (getenv("GNUSORT_NUMERIC_COMPATIBILITY"))
365 gnusort_numeric_compatibility
= true;
367 locale
= setlocale(LC_COLLATE
, NULL
);
371 const char *cclocale
;
373 tmpl
= sort_strdup(locale
);
374 cclocale
= setlocale(LC_COLLATE
, "C");
375 if (cclocale
&& !strcmp(cclocale
, tmpl
))
378 const char *pclocale
;
380 pclocale
= setlocale(LC_COLLATE
, "POSIX");
381 if (pclocale
&& !strcmp(pclocale
, tmpl
))
384 setlocale(LC_COLLATE
, tmpl
);
390 * Set directory temporary files.
397 td
= getenv("TMPDIR");
399 tmpdir
= sort_strdup(td
);
405 static unsigned long long
406 parse_memory_buffer_value(const char *value
)
410 return (available_free_memory
);
413 unsigned long long membuf
;
417 membuf
= strtoll(value
, &endptr
, 10);
420 warn("%s",getstr(4));
421 membuf
= available_free_memory
;
452 membuf
= (available_free_memory
* membuf
) /
456 warnc(EINVAL
, "%s", optarg
);
457 membuf
= available_free_memory
;
465 * Signal handler that clears the temporary files.
468 sig_handler(int sig __unused
, siginfo_t
*siginfo __unused
,
469 void *context __unused
)
477 * Set signal handler on panic signals.
480 set_signal_handler(void)
484 memset(&sa
, 0, sizeof(sa
));
485 sa
.sa_sigaction
= &sig_handler
;
486 sa
.sa_flags
= SA_SIGINFO
;
488 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
492 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
496 if (sigaction(SIGINT
, &sa
, NULL
) < 0) {
500 if (sigaction(SIGQUIT
, &sa
, NULL
) < 0) {
504 if (sigaction(SIGABRT
, &sa
, NULL
) < 0) {
508 if (sigaction(SIGBUS
, &sa
, NULL
) < 0) {
512 if (sigaction(SIGSEGV
, &sa
, NULL
) < 0) {
516 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
520 if (sigaction(SIGUSR2
, &sa
, NULL
) < 0) {
527 * Print "unknown" message and exit with status 2.
530 unknown(const char *what
)
533 errx(2, "%s: %s", getstr(3), what
);
537 * Check whether contradictory input options are used.
540 check_mutually_exclusive_flags(char c
, bool *mef_flags
)
543 bool found_others
, found_this
;
545 found_others
= found_this
= false;
548 for (int i
= 0; i
< NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS
; i
++) {
549 mec
= mutually_exclusive_flags
[i
];
554 errx(1, "%c:%c: %s", c
, mec
, getstr(1));
560 errx(1, "%c:%c: %s", c
, mutually_exclusive_flags
[fo_index
], getstr(1));
568 * Initialise sort opts data.
574 memset(&default_sort_mods_object
, 0,
575 sizeof(default_sort_mods_object
));
576 memset(&sort_opts_vals
, 0, sizeof(sort_opts_vals
));
577 default_sort_mods_object
.func
=
578 get_sort_func(&default_sort_mods_object
);
582 * Set a sort modifier on a sort modifiers object.
585 set_sort_modifier(struct sort_mods
*sm
, int c
)
620 print_symbols_on_debug
= true;
631 print_symbols_on_debug
= true;
636 sort_opts_vals
.complex_sort
= true;
637 sm
->func
= get_sort_func(sm
);
643 * Parse POS in -k option.
646 parse_pos(const char *s
, struct key_specs
*ks
, bool *mef_flags
, bool second
)
648 regmatch_t pmatch
[4];
651 const char *sregexp
= "^([0-9]+)(\\.[0-9]+)?([bdfirMngRhV]+)?$";
659 if (regcomp(&re
, sregexp
, REG_EXTENDED
) != 0)
662 if (regexec(&re
, s
, nmatch
, pmatch
, 0) != 0)
665 if (pmatch
[0].rm_eo
<= pmatch
[0].rm_so
)
668 if (pmatch
[1].rm_eo
<= pmatch
[1].rm_so
)
671 len
= pmatch
[1].rm_eo
- pmatch
[1].rm_so
;
672 f
= sort_malloc((len
+ 1) * sizeof(char));
674 strncpy(f
, s
+ pmatch
[1].rm_so
, len
);
679 ks
->f2
= (size_t) strtoul(f
, NULL
, 10);
683 warn("%s",getstr(5));
688 ks
->f1
= (size_t) strtoul(f
, NULL
, 10);
692 warn("%s",getstr(5));
697 if (pmatch
[2].rm_eo
> pmatch
[2].rm_so
) {
698 len
= pmatch
[2].rm_eo
- pmatch
[2].rm_so
- 1;
699 c
= sort_malloc((len
+ 1) * sizeof(char));
701 strncpy(c
, s
+ pmatch
[2].rm_so
+ 1, len
);
706 ks
->c2
= (size_t) strtoul(c
, NULL
, 10);
711 ks
->c1
= (size_t) strtoul(c
, NULL
, 10);
715 warn("%s",getstr(6));
726 if (pmatch
[3].rm_eo
> pmatch
[3].rm_so
) {
729 for (i
= pmatch
[3].rm_so
; i
< pmatch
[3].rm_eo
; i
++) {
730 check_mutually_exclusive_flags(s
[i
], mef_flags
);
736 } else if (!set_sort_modifier(&(ks
->sm
), s
[i
]))
755 * Parse -k option value.
758 parse_k(const char *s
, struct key_specs
*ks
)
761 bool mef_flags
[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS
] =
762 { false, false, false, false, false, false };
767 sptr
= strchr(s
, ',');
776 pos1
= sort_malloc((size1
+ 1) * sizeof(char));
778 strncpy(pos1
, s
, size1
);
781 ret
= parse_pos(pos1
, ks
, mef_flags
, false);
787 pos2
= sort_strdup(sptr
+ 1);
788 ret
= parse_pos(pos2
, ks
, mef_flags
, true);
791 ret
= parse_pos(s
, ks
, mef_flags
, false);
798 * Parse POS in +POS -POS option.
801 parse_pos_obs(const char *s
, int *nf
, int *nc
, char* sopts
)
804 regmatch_t pmatch
[4];
806 const char *sregexp
= "^([0-9]+)(\\.[0-9]+)?([A-Za-z]+)?$";
815 if (regcomp(&re
, sregexp
, REG_EXTENDED
) != 0)
818 if (regexec(&re
, s
, nmatch
, pmatch
, 0) != 0)
821 if (pmatch
[0].rm_eo
<= pmatch
[0].rm_so
)
824 if (pmatch
[1].rm_eo
<= pmatch
[1].rm_so
)
827 len
= pmatch
[1].rm_eo
- pmatch
[1].rm_so
;
828 f
= sort_malloc((len
+ 1) * sizeof(char));
830 strncpy(f
, s
+ pmatch
[1].rm_so
, len
);
834 *nf
= (size_t) strtoul(f
, NULL
, 10);
836 errx(2, "%s", getstr(11));
838 if (pmatch
[2].rm_eo
> pmatch
[2].rm_so
) {
839 len
= pmatch
[2].rm_eo
- pmatch
[2].rm_so
- 1;
840 c
= sort_malloc((len
+ 1) * sizeof(char));
842 strncpy(c
, s
+ pmatch
[2].rm_so
+ 1, len
);
846 *nc
= (size_t) strtoul(c
, NULL
, 10);
848 errx(2, "%s", getstr(11));
851 if (pmatch
[3].rm_eo
> pmatch
[3].rm_so
) {
853 len
= pmatch
[3].rm_eo
- pmatch
[3].rm_so
;
855 strncpy(sopts
, s
+ pmatch
[3].rm_so
, len
);
872 * "Translate" obsolete +POS1 -POS2 syntax into new -kPOS1,POS2 syntax
875 fix_obsolete_keys(int *argc
, char **argv
)
879 for (int i
= 1; i
< *argc
; i
++) {
884 if (strlen(arg1
) > 1 && arg1
[0] == '+') {
891 if (parse_pos_obs(arg1
+ 1, &f1
, &c1
, sopts1
) < 0)
897 char *arg2
= argv
[i
+ 1];
899 if (strlen(arg2
) > 1 &&
907 if (parse_pos_obs(arg2
+ 1,
908 &f2
, &c2
, sopts2
) >= 0) {
914 f2
, c2
, sopts2
)== -1)
917 for (int j
= i
+ 1; j
+ 1 < *argc
; j
++)
918 argv
[j
] = argv
[j
+ 1];
924 asprintf(&snew
, "-k%d.%d%s", f1
, c1
, sopts1
);
934 #if defined(SORT_RANDOM)
936 random_md5end(MD5_CTX
*ctx
)
938 unsigned char digest
[MD5_DIGEST_LENGTH
];
939 static const char hex
[]="0123456789abcdef";
943 buf
= malloc(MD5_DIGEST_LENGTH
* 2 + 1);
946 MD5_Final(digest
, ctx
);
947 for (i
= 0; i
< MD5_DIGEST_LENGTH
; i
++) {
948 buf
[2*i
] = hex
[digest
[i
] >> 4];
949 buf
[2*i
+1] = hex
[digest
[i
] & 0x0f];
951 buf
[MD5_DIGEST_LENGTH
* 2] = '\0';
956 random_fromfile(const char *filename
)
960 unsigned char buffer
[4096];
965 fp
= openfile(filename
, "r");
968 if (fstat(fileno(fp
), &st
) < 0) {
976 while (size
> 0 && (bytes
= fread(buffer
, 1, sizeof(buffer
), fp
)) > 0) {
977 MD5_Update(&ctx
, buffer
, bytes
);
986 return (random_md5end(&ctx
));
990 set_random_seed(void)
994 if (strcmp(random_source
, DEFAULT_RANDOM_SORT_SEED_FILE
) == 0) {
997 char rsd
[MAX_DEFAULT_RANDOM_SEED_DATA_SIZE
];
1000 fseed
= openfile(random_source
, "r");
1001 while (!feof(fseed
)) {
1008 rsd
[sz
++] = (char) cr
;
1010 if (sz
>= MAX_DEFAULT_RANDOM_SEED_DATA_SIZE
)
1014 closefile(fseed
, random_source
);
1017 MD5_Update(&ctx
, rsd
, sz
);
1019 random_seed
= random_md5end(&ctx
);
1020 random_seed_size
= strlen(random_seed
);
1027 b
= random_fromfile(random_source
);
1032 random_seed_size
= strlen(b
);
1036 if(random_seed_size
>0) {
1037 MD5_Update(&md5_ctx
, random_seed
, random_seed_size
);
1047 main(int argc
, char **argv
)
1049 char *outfile
, *real_outfile
;
1051 bool mef_flags
[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS
] =
1052 { false, false, false, false, false, false };
1055 outfile
= sort_strdup("-");
1056 real_outfile
= NULL
;
1058 struct sort_mods
*sm
= &default_sort_mods_object
;
1062 set_signal_handler();
1069 fix_obsolete_keys(&argc
, argv
);
1071 while (((c
= getopt_long(argc
, argv
, OPTIONS
, long_options
, NULL
))
1074 check_mutually_exclusive_flags(c
, mef_flags
);
1076 if (!set_sort_modifier(sm
, c
)) {
1080 sort_opts_vals
.cflag
= true;
1082 if (!strcmp(optarg
, "diagnose-first"))
1084 else if (!strcmp(optarg
, "silent") ||
1085 !strcmp(optarg
, "quiet"))
1086 sort_opts_vals
.csilentflag
= true;
1092 sort_opts_vals
.cflag
= true;
1093 sort_opts_vals
.csilentflag
= true;
1097 sort_opts_vals
.complex_sort
= true;
1098 sort_opts_vals
.kflag
= true;
1101 keys
= sort_realloc(keys
, keys_num
*
1102 sizeof(struct key_specs
));
1103 memset(&(keys
[keys_num
- 1]), 0,
1104 sizeof(struct key_specs
));
1106 if (parse_k(optarg
, &(keys
[keys_num
- 1]))
1108 errc(2, EINVAL
, "-k %s", optarg
);
1114 sort_opts_vals
.mflag
= true;
1117 outfile
= sort_realloc(outfile
, (strlen(optarg
) + 1));
1118 strcpy(outfile
, optarg
);
1121 sort_opts_vals
.sflag
= true;
1124 available_free_memory
=
1125 parse_memory_buffer_value(optarg
);
1128 tmpdir
= sort_strdup(optarg
);
1131 while (strlen(optarg
) > 1) {
1132 if (optarg
[0] != '\\') {
1133 errc(2, EINVAL
, "%s", optarg
);
1136 if (*optarg
== '0') {
1141 sort_opts_vals
.tflag
= true;
1142 sort_opts_vals
.field_sep
= btowc(optarg
[0]);
1143 if (sort_opts_vals
.field_sep
== WEOF
) {
1147 if (!gnusort_numeric_compatibility
) {
1148 if (symbol_decimal_point
== sort_opts_vals
.field_sep
)
1149 symbol_decimal_point
= WEOF
;
1150 if (symbol_thousands_sep
== sort_opts_vals
.field_sep
)
1151 symbol_thousands_sep
= WEOF
;
1152 if (symbol_negative_sign
== sort_opts_vals
.field_sep
)
1153 symbol_negative_sign
= WEOF
;
1154 if (symbol_positive_sign
== sort_opts_vals
.field_sep
)
1155 symbol_positive_sign
= WEOF
;
1159 sort_opts_vals
.uflag
= true;
1160 /* stable sort for the correct unique val */
1161 sort_opts_vals
.sflag
= true;
1164 sort_opts_vals
.zflag
= true;
1168 if (!strcmp(optarg
, "general-numeric"))
1169 set_sort_modifier(sm
, 'g');
1170 else if (!strcmp(optarg
, "human-numeric"))
1171 set_sort_modifier(sm
, 'h');
1172 else if (!strcmp(optarg
, "numeric"))
1173 set_sort_modifier(sm
, 'n');
1174 else if (!strcmp(optarg
, "month"))
1175 set_sort_modifier(sm
, 'M');
1176 #if defined(SORT_RANDOM)
1177 else if (!strcmp(optarg
, "random"))
1178 set_sort_modifier(sm
, 'R');
1184 #if defined(SORT_THREADS)
1186 nthreads
= (size_t)(atoi(optarg
));
1189 if (nthreads
> 1024)
1194 sort_opts_vals
.sort_method
= SORT_QSORT
;
1197 sort_opts_vals
.sort_method
= SORT_MERGESORT
;
1203 sort_opts_vals
.sort_method
= SORT_HEAPSORT
;
1206 sort_opts_vals
.sort_method
= SORT_RADIXSORT
;
1208 #if defined(SORT_RANDOM)
1209 case RANDOMSOURCE_OPT
:
1210 random_source
= strdup(optarg
);
1213 case COMPRESSPROGRAM_OPT
:
1214 compress_program
= strdup(optarg
);
1217 read_fns_from_file0(optarg
);
1222 long mof
= strtol(optarg
, NULL
, 10);
1224 err(2, "--batch-size");
1226 max_open_files
= (size_t) mof
+ 1;
1230 printf("%s\n", VERSION
);
1252 catalog
= catopen("sort", NL_CAT_LOCALE
);
1255 if (sort_opts_vals
.cflag
&& sort_opts_vals
.mflag
)
1256 errx(1, "%c:%c: %s", 'm', 'c', getstr(1));
1262 if (keys_num
== 0) {
1264 keys
= sort_realloc(keys
, sizeof(struct key_specs
));
1265 memset(&(keys
[0]), 0, sizeof(struct key_specs
));
1267 keys
[0].pos1b
= default_sort_mods
->bflag
;
1268 keys
[0].pos2b
= default_sort_mods
->bflag
;
1269 memcpy(&(keys
[0].sm
), default_sort_mods
,
1270 sizeof(struct sort_mods
));
1273 for (size_t i
= 0; i
< keys_num
; i
++) {
1274 struct key_specs
*ks
;
1278 if (sort_modifier_empty(&(ks
->sm
)) && !(ks
->pos1b
) &&
1280 ks
->pos1b
= sm
->bflag
;
1281 ks
->pos2b
= sm
->bflag
;
1282 memcpy(&(ks
->sm
), sm
, sizeof(struct sort_mods
));
1285 ks
->sm
.func
= get_sort_func(&(ks
->sm
));
1288 if (argv_from_file0
) {
1289 argc
= argc_from_file0
;
1290 argv
= argv_from_file0
;
1294 printf("Memory to be used for sorting: %llu\n",available_free_memory
);
1295 #if defined(SORT_THREADS)
1296 printf("Number of CPUs: %d\n",(int)ncpu
);
1299 printf("Using collate rules of %s locale\n",
1300 setlocale(LC_COLLATE
, NULL
));
1302 printf("Byte sort is used\n");
1303 if (print_symbols_on_debug
) {
1304 printf("Decimal Point: <%lc>\n", symbol_decimal_point
);
1305 if (symbol_thousands_sep
)
1306 printf("Thousands separator: <%lc>\n",
1307 symbol_thousands_sep
);
1308 printf("Positive sign: <%lc>\n", symbol_positive_sign
);
1309 printf("Negative sign: <%lc>\n", symbol_negative_sign
);
1313 #if defined(SORT_RANDOM)
1317 /* Case when the outfile equals one of the input files: */
1318 if (strcmp(outfile
, "-")) {
1320 for(int i
= 0; i
< argc
; ++i
) {
1321 if (strcmp(argv
[i
], outfile
) == 0) {
1322 real_outfile
= sort_strdup(outfile
);
1324 char* tmp
= sort_malloc(strlen(outfile
) +
1325 strlen(".tmp") + 1);
1327 strcpy(tmp
, outfile
);
1328 strcpy(tmp
+ strlen(tmp
), ".tmp");
1331 if (access(outfile
, F_OK
) < 0)
1334 tmp_file_atexit(outfile
);
1339 #if defined(SORT_THREADS)
1340 if ((argc
< 1) || (strcmp(outfile
, "-") == 0) || (*outfile
== 0))
1344 if (!sort_opts_vals
.cflag
&& !sort_opts_vals
.mflag
) {
1345 struct file_list fl
;
1346 struct sort_list list
;
1348 sort_list_init(&list
);
1349 file_list_init(&fl
, true);
1352 procfile("-", &list
, &fl
);
1355 procfile(*argv
, &list
, &fl
);
1362 sort_list_to_file(&list
, outfile
);
1364 if (list
.count
> 0) {
1365 char *flast
= new_tmp_file_name();
1367 sort_list_to_file(&list
, flast
);
1368 file_list_add(&fl
, flast
, false);
1370 merge_files(&fl
, outfile
);
1373 file_list_clean(&fl
);
1376 * We are about to exit the program, so we can ignore
1377 * the clean-up for speed
1379 * sort_list_clean(&list);
1382 } else if (sort_opts_vals
.cflag
) {
1383 result
= (argc
== 0) ? (check("-")) : (check(*argv
));
1384 } else if (sort_opts_vals
.mflag
) {
1385 struct file_list fl
;
1387 file_list_init(&fl
, false);
1388 file_list_populate(&fl
, argc
, argv
, true);
1389 merge_files(&fl
, outfile
);
1390 file_list_clean(&fl
);
1394 unlink(real_outfile
);
1395 if (rename(outfile
, real_outfile
) < 0)
1397 sort_free(real_outfile
);