1235267Sgabor/*-
2235267Sgabor * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
3251245Sgabor * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
4235267Sgabor * All rights reserved.
5235267Sgabor *
6235267Sgabor * Redistribution and use in source and binary forms, with or without
7235267Sgabor * modification, are permitted provided that the following conditions
8235267Sgabor * are met:
9235267Sgabor * 1. Redistributions of source code must retain the above copyright
10235267Sgabor *    notice, this list of conditions and the following disclaimer.
11235267Sgabor * 2. Redistributions in binary form must reproduce the above copyright
12235267Sgabor *    notice, this list of conditions and the following disclaimer in the
13235267Sgabor *    documentation and/or other materials provided with the distribution.
14235267Sgabor *
15235267Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16235267Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17235267Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18235267Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19235267Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20235267Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21235267Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22235267Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23235267Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24235267Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25235267Sgabor * SUCH DAMAGE.
26235267Sgabor */
27235267Sgabor
28235267Sgabor#include <sys/cdefs.h>
29235267Sgabor__FBSDID("$FreeBSD: stable/10/usr.bin/sort/sort.c 309862 2016-12-12 00:47:12Z delphij $");
30235267Sgabor
31235267Sgabor#include <sys/stat.h>
32235267Sgabor#include <sys/sysctl.h>
33235267Sgabor#include <sys/types.h>
34235267Sgabor
35235267Sgabor#include <err.h>
36235267Sgabor#include <errno.h>
37235267Sgabor#include <getopt.h>
38235267Sgabor#include <limits.h>
39235267Sgabor#include <locale.h>
40235267Sgabor#include <md5.h>
41235267Sgabor#include <regex.h>
42235267Sgabor#include <signal.h>
43235267Sgabor#include <stdbool.h>
44235267Sgabor#include <stdio.h>
45235267Sgabor#include <stdlib.h>
46235267Sgabor#include <string.h>
47235267Sgabor#include <unistd.h>
48235267Sgabor#include <wchar.h>
49235267Sgabor#include <wctype.h>
50235267Sgabor
51235267Sgabor#include "coll.h"
52235267Sgabor#include "file.h"
53235267Sgabor#include "sort.h"
54235267Sgabor
55235267Sgabor#ifndef WITHOUT_NLS
56235267Sgabor#include <nl_types.h>
57235267Sgabornl_catd catalog;
58235267Sgabor#endif
59235267Sgabor
60235267Sgabor#define	OPTIONS	"bcCdfghik:Mmno:RrsS:t:T:uVz"
61235267Sgabor
62235267Sgabor#define DEFAULT_RANDOM_SORT_SEED_FILE ("/dev/random")
63235267Sgabor#define MAX_DEFAULT_RANDOM_SEED_DATA_SIZE (1024)
64235267Sgabor
65235435Sgaborstatic bool need_random;
66235435Sgaborstatic const char *random_source = DEFAULT_RANDOM_SORT_SEED_FILE;
67235435Sgaborstatic const void *random_seed;
68235435Sgaborstatic size_t random_seed_size;
69235267Sgabor
70235267SgaborMD5_CTX md5_ctx;
71235267Sgabor
72235267Sgabor/*
73235267Sgabor * Default messages to use when NLS is disabled or no catalogue
74235267Sgabor * is found.
75235267Sgabor */
76235267Sgaborconst char *nlsstr[] = { "",
77235432Sgabor/* 1*/"mutually exclusive flags",
78235267Sgabor/* 2*/"extra argument not allowed with -c",
79235432Sgabor/* 3*/"Unknown feature",
80235267Sgabor/* 4*/"Wrong memory buffer specification",
81235267Sgabor/* 5*/"0 field in key specs",
82235267Sgabor/* 6*/"0 column in key specs",
83235267Sgabor/* 7*/"Wrong file mode",
84235267Sgabor/* 8*/"Cannot open file for reading",
85235267Sgabor/* 9*/"Radix sort cannot be used with these sort options",
86235267Sgabor/*10*/"The chosen sort method cannot be used with stable and/or unique sort",
87235267Sgabor/*11*/"Invalid key position",
88235267Sgabor/*12*/"Usage: %s [-bcCdfigMmnrsuz] [-kPOS1[,POS2] ... ] "
89235267Sgabor      "[+POS1 [-POS2]] [-S memsize] [-T tmpdir] [-t separator] "
90235267Sgabor      "[-o outfile] [--batch-size size] [--files0-from file] "
91235267Sgabor      "[--heapsort] [--mergesort] [--radixsort] [--qsort] "
92235987Sgabor      "[--mmap] "
93235267Sgabor#if defined(SORT_THREADS)
94238108Sgabor      "[--parallel thread_no] "
95235267Sgabor#endif
96235267Sgabor      "[--human-numeric-sort] "
97235267Sgabor      "[--version-sort] [--random-sort [--random-source file]] "
98235267Sgabor      "[--compress-program program] [file ...]\n" };
99235267Sgabor
100235267Sgaborstruct sort_opts sort_opts_vals;
101235267Sgabor
102235435Sgaborbool debug_sort;
103235435Sgaborbool need_hint;
104235267Sgabor
105235267Sgabor#if defined(SORT_THREADS)
106244346Sgaborunsigned int ncpu = 1;
107235267Sgaborsize_t nthreads = 1;
108235267Sgabor#endif
109235267Sgabor
110235435Sgaborstatic bool gnusort_numeric_compatibility;
111235267Sgabor
112235267Sgaborstatic struct sort_mods default_sort_mods_object;
113235267Sgaborstruct sort_mods * const default_sort_mods = &default_sort_mods_object;
114235267Sgabor
115235435Sgaborstatic bool print_symbols_on_debug;
116235267Sgabor
117235267Sgabor/*
118235267Sgabor * Arguments from file (when file0-from option is used:
119235267Sgabor */
120242430Sgaborstatic size_t argc_from_file0 = (size_t)-1;
121235435Sgaborstatic char **argv_from_file0;
122235267Sgabor
123235267Sgabor/*
124235267Sgabor * Placeholder symbols for options which have no single-character equivalent
125235267Sgabor */
126235267Sgaborenum
127235267Sgabor{
128235267Sgabor	SORT_OPT = CHAR_MAX + 1,
129235267Sgabor	HELP_OPT,
130235267Sgabor	FF_OPT,
131235267Sgabor	BS_OPT,
132235267Sgabor	VERSION_OPT,
133235267Sgabor	DEBUG_OPT,
134235267Sgabor#if defined(SORT_THREADS)
135238108Sgabor	PARALLEL_OPT,
136235267Sgabor#endif
137235267Sgabor	RANDOMSOURCE_OPT,
138235267Sgabor	COMPRESSPROGRAM_OPT,
139235267Sgabor	QSORT_OPT,
140235267Sgabor	MERGESORT_OPT,
141235267Sgabor	HEAPSORT_OPT,
142235987Sgabor	RADIXSORT_OPT,
143235987Sgabor	MMAP_OPT
144235267Sgabor};
145235267Sgabor
146235267Sgabor#define	NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS 6
147235267Sgaborstatic const char mutually_exclusive_flags[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS] = { 'M', 'n', 'g', 'R', 'h', 'V' };
148235267Sgabor
149241737Sedstatic struct option long_options[] = {
150235267Sgabor				{ "batch-size", required_argument, NULL, BS_OPT },
151235267Sgabor				{ "buffer-size", required_argument, NULL, 'S' },
152235267Sgabor				{ "check", optional_argument, NULL, 'c' },
153235267Sgabor				{ "check=silent|quiet", optional_argument, NULL, 'C' },
154235267Sgabor				{ "compress-program", required_argument, NULL, COMPRESSPROGRAM_OPT },
155235267Sgabor				{ "debug", no_argument, NULL, DEBUG_OPT },
156235267Sgabor				{ "dictionary-order", no_argument, NULL, 'd' },
157235267Sgabor				{ "field-separator", required_argument, NULL, 't' },
158235267Sgabor				{ "files0-from", required_argument, NULL, FF_OPT },
159235267Sgabor				{ "general-numeric-sort", no_argument, NULL, 'g' },
160235267Sgabor				{ "heapsort", no_argument, NULL, HEAPSORT_OPT },
161235267Sgabor				{ "help",no_argument, NULL, HELP_OPT },
162235267Sgabor				{ "human-numeric-sort", no_argument, NULL, 'h' },
163235267Sgabor				{ "ignore-leading-blanks", no_argument, NULL, 'b' },
164235267Sgabor				{ "ignore-case", no_argument, NULL, 'f' },
165235267Sgabor				{ "ignore-nonprinting", no_argument, NULL, 'i' },
166235267Sgabor				{ "key", required_argument, NULL, 'k' },
167235267Sgabor				{ "merge", no_argument, NULL, 'm' },
168235267Sgabor				{ "mergesort", no_argument, NULL, MERGESORT_OPT },
169235987Sgabor				{ "mmap", no_argument, NULL, MMAP_OPT },
170235267Sgabor				{ "month-sort", no_argument, NULL, 'M' },
171235267Sgabor				{ "numeric-sort", no_argument, NULL, 'n' },
172235267Sgabor				{ "output", required_argument, NULL, 'o' },
173235267Sgabor#if defined(SORT_THREADS)
174238108Sgabor				{ "parallel", required_argument, NULL, PARALLEL_OPT },
175235267Sgabor#endif
176235267Sgabor				{ "qsort", no_argument, NULL, QSORT_OPT },
177235267Sgabor				{ "radixsort", no_argument, NULL, RADIXSORT_OPT },
178235267Sgabor				{ "random-sort", no_argument, NULL, 'R' },
179235267Sgabor				{ "random-source", required_argument, NULL, RANDOMSOURCE_OPT },
180235267Sgabor				{ "reverse", no_argument, NULL, 'r' },
181235267Sgabor				{ "sort", required_argument, NULL, SORT_OPT },
182235267Sgabor				{ "stable", no_argument, NULL, 's' },
183235267Sgabor				{ "temporary-directory",required_argument, NULL, 'T' },
184235267Sgabor				{ "unique", no_argument, NULL, 'u' },
185235267Sgabor				{ "version", no_argument, NULL, VERSION_OPT },
186235267Sgabor				{ "version-sort",no_argument, NULL, 'V' },
187235267Sgabor				{ "zero-terminated", no_argument, NULL, 'z' },
188235267Sgabor				{ NULL, no_argument, NULL, 0 }
189235267Sgabor};
190235267Sgabor
191235267Sgaborvoid fix_obsolete_keys(int *argc, char **argv);
192235267Sgabor
193235267Sgabor/*
194235267Sgabor * Check where sort modifier is present
195235267Sgabor */
196235267Sgaborstatic bool
197235267Sgaborsort_modifier_empty(struct sort_mods *sm)
198235267Sgabor{
199235267Sgabor
200235267Sgabor	if (sm == NULL)
201235267Sgabor		return (true);
202235267Sgabor	return (!(sm->Mflag || sm->Vflag || sm->nflag || sm->gflag ||
203235267Sgabor	    sm->rflag || sm->Rflag || sm->hflag || sm->dflag || sm->fflag));
204235267Sgabor}
205235267Sgabor
206235267Sgabor/*
207235267Sgabor * Print out usage text.
208235267Sgabor */
209235267Sgaborstatic void
210235267Sgaborusage(bool opt_err)
211235267Sgabor{
212235267Sgabor	FILE *out;
213235267Sgabor
214309862Sdelphij	out = opt_err ? stderr : stdout;
215235267Sgabor
216235267Sgabor	fprintf(out, getstr(12), getprogname());
217235267Sgabor	if (opt_err)
218235267Sgabor		exit(2);
219235267Sgabor	exit(0);
220235267Sgabor}
221235267Sgabor
222235267Sgabor/*
223235267Sgabor * Read input file names from a file (file0-from option).
224235267Sgabor */
225235267Sgaborstatic void
226235267Sgaborread_fns_from_file0(const char *fn)
227235267Sgabor{
228281535Spfg	FILE *f;
229281535Spfg	char *line = NULL;
230281535Spfg	size_t linesize = 0;
231281535Spfg	ssize_t linelen;
232235267Sgabor
233281535Spfg	if (fn == NULL)
234281535Spfg		return;
235235267Sgabor
236281535Spfg	f = fopen(fn, "r");
237281535Spfg	if (f == NULL)
238281535Spfg		err(2, "%s", fn);
239235267Sgabor
240281535Spfg	while ((linelen = getdelim(&line, &linesize, '\0', f)) != -1) {
241281535Spfg		if (*line != '\0') {
242281535Spfg			if (argc_from_file0 == (size_t) - 1)
243281535Spfg				argc_from_file0 = 0;
244281535Spfg			++argc_from_file0;
245281535Spfg			argv_from_file0 = sort_realloc(argv_from_file0,
246281535Spfg			    argc_from_file0 * sizeof(char *));
247281535Spfg			if (argv_from_file0 == NULL)
248281535Spfg				err(2, NULL);
249281535Spfg			argv_from_file0[argc_from_file0 - 1] = line;
250281535Spfg		} else {
251281535Spfg			free(line);
252235267Sgabor		}
253281535Spfg		line = NULL;
254281535Spfg		linesize = 0;
255235267Sgabor	}
256281535Spfg	if (ferror(f))
257281535Spfg		err(2, "%s: getdelim", fn);
258281535Spfg
259281535Spfg	closefile(f, fn);
260235267Sgabor}
261235267Sgabor
262235267Sgabor/*
263235267Sgabor * Check how much RAM is available for the sort.
264235267Sgabor */
265235267Sgaborstatic void
266235267Sgaborset_hw_params(void)
267235267Sgabor{
268244515Sgabor	long pages, psize;
269235267Sgabor
270235267Sgabor#if defined(SORT_THREADS)
271235267Sgabor	ncpu = 1;
272235267Sgabor#endif
273235267Sgabor
274244515Sgabor	pages = sysconf(_SC_PHYS_PAGES);
275244515Sgabor	if (pages < 1) {
276244515Sgabor		perror("sysconf pages");
277309862Sdelphij		pages = 1;
278235267Sgabor	}
279244515Sgabor	psize = sysconf(_SC_PAGESIZE);
280244515Sgabor	if (psize < 1) {
281244515Sgabor		perror("sysconf psize");
282244515Sgabor		psize = 4096;
283235267Sgabor	}
284235267Sgabor#if defined(SORT_THREADS)
285244515Sgabor	ncpu = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
286244515Sgabor	if (ncpu < 1)
287235267Sgabor		ncpu = 1;
288235267Sgabor	else if(ncpu > 32)
289235267Sgabor		ncpu = 32;
290235267Sgabor
291235267Sgabor	nthreads = ncpu;
292235267Sgabor#endif
293235267Sgabor
294235267Sgabor	free_memory = (unsigned long long) pages * (unsigned long long) psize;
295244515Sgabor	available_free_memory = free_memory / 2;
296244346Sgabor
297244346Sgabor	if (available_free_memory < 1024)
298244346Sgabor		available_free_memory = 1024;
299235267Sgabor}
300235267Sgabor
301235267Sgabor/*
302235267Sgabor * Convert "plain" symbol to wide symbol, with default value.
303235267Sgabor */
304235267Sgaborstatic void
305235267Sgaborconv_mbtowc(wchar_t *wc, const char *c, const wchar_t def)
306235267Sgabor{
307235267Sgabor
308235267Sgabor	if (wc && c) {
309235267Sgabor		int res;
310235267Sgabor
311235267Sgabor		res = mbtowc(wc, c, MB_CUR_MAX);
312235267Sgabor		if (res < 1)
313235267Sgabor			*wc = def;
314235267Sgabor	}
315235267Sgabor}
316235267Sgabor
317235267Sgabor/*
318235267Sgabor * Set current locale symbols.
319235267Sgabor */
320235267Sgaborstatic void
321235267Sgaborset_locale(void)
322235267Sgabor{
323235267Sgabor	struct lconv *lc;
324235267Sgabor	const char *locale;
325235267Sgabor
326235267Sgabor	setlocale(LC_ALL, "");
327235267Sgabor
328235267Sgabor	lc = localeconv();
329235267Sgabor
330235267Sgabor	if (lc) {
331235267Sgabor		/* obtain LC_NUMERIC info */
332235267Sgabor		/* Convert to wide char form */
333235267Sgabor		conv_mbtowc(&symbol_decimal_point, lc->decimal_point,
334235267Sgabor		    symbol_decimal_point);
335235267Sgabor		conv_mbtowc(&symbol_thousands_sep, lc->thousands_sep,
336235267Sgabor		    symbol_thousands_sep);
337235267Sgabor		conv_mbtowc(&symbol_positive_sign, lc->positive_sign,
338235267Sgabor		    symbol_positive_sign);
339235267Sgabor		conv_mbtowc(&symbol_negative_sign, lc->negative_sign,
340235267Sgabor		    symbol_negative_sign);
341235267Sgabor	}
342235267Sgabor
343235267Sgabor	if (getenv("GNUSORT_NUMERIC_COMPATIBILITY"))
344235267Sgabor		gnusort_numeric_compatibility = true;
345235267Sgabor
346235267Sgabor	locale = setlocale(LC_COLLATE, NULL);
347235267Sgabor
348235267Sgabor	if (locale) {
349235267Sgabor		char *tmpl;
350235267Sgabor		const char *cclocale;
351235267Sgabor
352235267Sgabor		tmpl = sort_strdup(locale);
353235267Sgabor		cclocale = setlocale(LC_COLLATE, "C");
354235267Sgabor		if (cclocale && !strcmp(cclocale, tmpl))
355235267Sgabor			byte_sort = true;
356235267Sgabor		else {
357235267Sgabor			const char *pclocale;
358235267Sgabor
359235267Sgabor			pclocale = setlocale(LC_COLLATE, "POSIX");
360235267Sgabor			if (pclocale && !strcmp(pclocale, tmpl))
361235267Sgabor				byte_sort = true;
362235267Sgabor		}
363235267Sgabor		setlocale(LC_COLLATE, tmpl);
364235267Sgabor		sort_free(tmpl);
365235267Sgabor	}
366235267Sgabor}
367235267Sgabor
368235267Sgabor/*
369235267Sgabor * Set directory temporary files.
370235267Sgabor */
371235267Sgaborstatic void
372235267Sgaborset_tmpdir(void)
373235267Sgabor{
374235267Sgabor	char *td;
375235267Sgabor
376235267Sgabor	td = getenv("TMPDIR");
377235267Sgabor	if (td != NULL)
378235267Sgabor		tmpdir = sort_strdup(td);
379235267Sgabor}
380235267Sgabor
381235267Sgabor/*
382235267Sgabor * Parse -S option.
383235267Sgabor */
384235267Sgaborstatic unsigned long long
385235267Sgaborparse_memory_buffer_value(const char *value)
386235267Sgabor{
387235267Sgabor
388235267Sgabor	if (value == NULL)
389235267Sgabor		return (available_free_memory);
390235267Sgabor	else {
391235267Sgabor		char *endptr;
392235267Sgabor		unsigned long long membuf;
393235267Sgabor
394235267Sgabor		endptr = NULL;
395235267Sgabor		errno = 0;
396235267Sgabor		membuf = strtoll(value, &endptr, 10);
397235267Sgabor
398235267Sgabor		if (errno != 0) {
399235432Sgabor			warn("%s",getstr(4));
400235267Sgabor			membuf = available_free_memory;
401235267Sgabor		} else {
402235267Sgabor			switch (*endptr){
403235267Sgabor			case 'Y':
404235267Sgabor				membuf *= 1024;
405235267Sgabor				/* FALLTHROUGH */
406235267Sgabor			case 'Z':
407235267Sgabor				membuf *= 1024;
408235267Sgabor				/* FALLTHROUGH */
409235267Sgabor			case 'E':
410235267Sgabor				membuf *= 1024;
411235267Sgabor				/* FALLTHROUGH */
412235267Sgabor			case 'P':
413235267Sgabor				membuf *= 1024;
414235267Sgabor				/* FALLTHROUGH */
415235267Sgabor			case 'T':
416235267Sgabor				membuf *= 1024;
417235267Sgabor				/* FALLTHROUGH */
418235267Sgabor			case 'G':
419235267Sgabor				membuf *= 1024;
420235267Sgabor				/* FALLTHROUGH */
421235267Sgabor			case 'M':
422235267Sgabor				membuf *= 1024;
423235267Sgabor				/* FALLTHROUGH */
424235267Sgabor			case '\0':
425235267Sgabor			case 'K':
426235267Sgabor				membuf *= 1024;
427235267Sgabor				/* FALLTHROUGH */
428235267Sgabor			case 'b':
429235267Sgabor				break;
430235267Sgabor			case '%':
431235267Sgabor				membuf = (available_free_memory * membuf) /
432235267Sgabor				    100;
433235267Sgabor				break;
434235267Sgabor			default:
435245997Sgabor				warnc(EINVAL, "%s", optarg);
436235267Sgabor				membuf = available_free_memory;
437235267Sgabor			}
438235267Sgabor		}
439235267Sgabor		return (membuf);
440235267Sgabor	}
441235267Sgabor}
442235267Sgabor
443235267Sgabor/*
444235267Sgabor * Signal handler that clears the temporary files.
445235267Sgabor */
446235267Sgaborstatic void
447235432Sgaborsig_handler(int sig __unused, siginfo_t *siginfo __unused,
448235432Sgabor    void *context __unused)
449235267Sgabor{
450235267Sgabor
451235267Sgabor	clear_tmp_files();
452235267Sgabor	exit(-1);
453235267Sgabor}
454235267Sgabor
455235267Sgabor/*
456235267Sgabor * Set signal handler on panic signals.
457235267Sgabor */
458235267Sgaborstatic void
459235267Sgaborset_signal_handler(void)
460235267Sgabor{
461235267Sgabor	struct sigaction sa;
462235267Sgabor
463235267Sgabor	memset(&sa, 0, sizeof(sa));
464235267Sgabor	sa.sa_sigaction = &sig_handler;
465235267Sgabor	sa.sa_flags = SA_SIGINFO;
466235267Sgabor
467235267Sgabor	if (sigaction(SIGTERM, &sa, NULL) < 0) {
468235267Sgabor		perror("sigaction");
469235267Sgabor		return;
470235267Sgabor	}
471235267Sgabor	if (sigaction(SIGHUP, &sa, NULL) < 0) {
472235267Sgabor		perror("sigaction");
473235267Sgabor		return;
474235267Sgabor	}
475235267Sgabor	if (sigaction(SIGINT, &sa, NULL) < 0) {
476235267Sgabor		perror("sigaction");
477235267Sgabor		return;
478235267Sgabor	}
479235267Sgabor	if (sigaction(SIGQUIT, &sa, NULL) < 0) {
480235267Sgabor		perror("sigaction");
481235267Sgabor		return;
482235267Sgabor	}
483235267Sgabor	if (sigaction(SIGABRT, &sa, NULL) < 0) {
484235267Sgabor		perror("sigaction");
485235267Sgabor		return;
486235267Sgabor	}
487235267Sgabor	if (sigaction(SIGBUS, &sa, NULL) < 0) {
488235267Sgabor		perror("sigaction");
489235267Sgabor		return;
490235267Sgabor	}
491235267Sgabor	if (sigaction(SIGSEGV, &sa, NULL) < 0) {
492235267Sgabor		perror("sigaction");
493235267Sgabor		return;
494235267Sgabor	}
495235267Sgabor	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
496235267Sgabor		perror("sigaction");
497235267Sgabor		return;
498235267Sgabor	}
499235267Sgabor	if (sigaction(SIGUSR2, &sa, NULL) < 0) {
500235267Sgabor		perror("sigaction");
501235267Sgabor		return;
502235267Sgabor	}
503235267Sgabor}
504235267Sgabor
505235267Sgabor/*
506235267Sgabor * Print "unknown" message and exit with status 2.
507235267Sgabor */
508235267Sgaborstatic void
509235267Sgaborunknown(const char *what)
510235267Sgabor{
511235267Sgabor
512235432Sgabor	errx(2, "%s: %s", getstr(3), what);
513235267Sgabor}
514235267Sgabor
515235267Sgabor/*
516235267Sgabor * Check whether contradictory input options are used.
517235267Sgabor */
518235267Sgaborstatic void
519235267Sgaborcheck_mutually_exclusive_flags(char c, bool *mef_flags)
520235267Sgabor{
521235267Sgabor	int fo_index, mec;
522235267Sgabor	bool found_others, found_this;
523235267Sgabor
524235267Sgabor	found_others = found_this =false;
525235267Sgabor	fo_index = 0;
526235267Sgabor
527235267Sgabor	for (int i = 0; i < NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS; i++) {
528235267Sgabor		mec = mutually_exclusive_flags[i];
529235267Sgabor
530235267Sgabor		if (mec != c) {
531235267Sgabor			if (mef_flags[i]) {
532235267Sgabor				if (found_this)
533235432Sgabor					errx(1, "%c:%c: %s", c, mec, getstr(1));
534235267Sgabor				found_others = true;
535235267Sgabor				fo_index = i;
536235267Sgabor			}
537235267Sgabor		} else {
538235267Sgabor			if (found_others)
539235432Sgabor				errx(1, "%c:%c: %s", c, mutually_exclusive_flags[fo_index], getstr(1));
540235267Sgabor			mef_flags[i] = true;
541235267Sgabor			found_this = true;
542235267Sgabor		}
543235267Sgabor	}
544235267Sgabor}
545235267Sgabor
546235267Sgabor/*
547235267Sgabor * Initialise sort opts data.
548235267Sgabor */
549235267Sgaborstatic void
550235267Sgaborset_sort_opts(void)
551235267Sgabor{
552235267Sgabor
553235267Sgabor	memset(&default_sort_mods_object, 0,
554235267Sgabor	    sizeof(default_sort_mods_object));
555235267Sgabor	memset(&sort_opts_vals, 0, sizeof(sort_opts_vals));
556235267Sgabor	default_sort_mods_object.func =
557235267Sgabor	    get_sort_func(&default_sort_mods_object);
558235267Sgabor}
559235267Sgabor
560235267Sgabor/*
561235267Sgabor * Set a sort modifier on a sort modifiers object.
562235267Sgabor */
563235267Sgaborstatic bool
564235267Sgaborset_sort_modifier(struct sort_mods *sm, int c)
565235267Sgabor{
566235267Sgabor
567235267Sgabor	if (sm) {
568235267Sgabor		switch (c){
569235267Sgabor		case 'b':
570235267Sgabor			sm->bflag = true;
571235267Sgabor			break;
572235267Sgabor		case 'd':
573235267Sgabor			sm->dflag = true;
574235267Sgabor			break;
575235267Sgabor		case 'f':
576235267Sgabor			sm->fflag = true;
577235267Sgabor			break;
578235267Sgabor		case 'g':
579235267Sgabor			sm->gflag = true;
580235267Sgabor			need_hint = true;
581235267Sgabor			break;
582235267Sgabor		case 'i':
583235267Sgabor			sm->iflag = true;
584235267Sgabor			break;
585235267Sgabor		case 'R':
586235267Sgabor			sm->Rflag = true;
587235267Sgabor			need_random = true;
588235267Sgabor			break;
589235267Sgabor		case 'M':
590235267Sgabor			initialise_months();
591235267Sgabor			sm->Mflag = true;
592235267Sgabor			need_hint = true;
593235267Sgabor			break;
594235267Sgabor		case 'n':
595235267Sgabor			sm->nflag = true;
596235267Sgabor			need_hint = true;
597235267Sgabor			print_symbols_on_debug = true;
598235267Sgabor			break;
599235267Sgabor		case 'r':
600235267Sgabor			sm->rflag = true;
601235267Sgabor			break;
602235267Sgabor		case 'V':
603235267Sgabor			sm->Vflag = true;
604235267Sgabor			break;
605235267Sgabor		case 'h':
606235267Sgabor			sm->hflag = true;
607235267Sgabor			need_hint = true;
608235267Sgabor			print_symbols_on_debug = true;
609235267Sgabor			break;
610235267Sgabor		default:
611235267Sgabor			return false;
612235267Sgabor		}
613235267Sgabor		sort_opts_vals.complex_sort = true;
614235267Sgabor		sm->func = get_sort_func(sm);
615235267Sgabor	}
616235267Sgabor	return (true);
617235267Sgabor}
618235267Sgabor
619235267Sgabor/*
620235267Sgabor * Parse POS in -k option.
621235267Sgabor */
622235267Sgaborstatic int
623235267Sgaborparse_pos(const char *s, struct key_specs *ks, bool *mef_flags, bool second)
624235267Sgabor{
625235267Sgabor	regmatch_t pmatch[4];
626235267Sgabor	regex_t re;
627235267Sgabor	char *c, *f;
628235267Sgabor	const char *sregexp = "^([0-9]+)(\\.[0-9]+)?([bdfirMngRhV]+)?$";
629235267Sgabor	size_t len, nmatch;
630235267Sgabor	int ret;
631235267Sgabor
632235267Sgabor	ret = -1;
633235267Sgabor	nmatch = 4;
634235267Sgabor	c = f = NULL;
635235267Sgabor
636235267Sgabor	if (regcomp(&re, sregexp, REG_EXTENDED) != 0)
637235267Sgabor		return (-1);
638235267Sgabor
639235267Sgabor	if (regexec(&re, s, nmatch, pmatch, 0) != 0)
640235267Sgabor		goto end;
641235267Sgabor
642235267Sgabor	if (pmatch[0].rm_eo <= pmatch[0].rm_so)
643235267Sgabor		goto end;
644235267Sgabor
645235267Sgabor	if (pmatch[1].rm_eo <= pmatch[1].rm_so)
646235267Sgabor		goto end;
647235267Sgabor
648235267Sgabor	len = pmatch[1].rm_eo - pmatch[1].rm_so;
649235267Sgabor	f = sort_malloc((len + 1) * sizeof(char));
650235267Sgabor
651235267Sgabor	strncpy(f, s + pmatch[1].rm_so, len);
652235267Sgabor	f[len] = '\0';
653235267Sgabor
654235267Sgabor	if (second) {
655235267Sgabor		errno = 0;
656235267Sgabor		ks->f2 = (size_t) strtoul(f, NULL, 10);
657235267Sgabor		if (errno != 0)
658245997Sgabor			err(2, "-k");
659235267Sgabor		if (ks->f2 == 0) {
660235432Sgabor			warn("%s",getstr(5));
661235267Sgabor			goto end;
662235267Sgabor		}
663235267Sgabor	} else {
664235267Sgabor		errno = 0;
665235267Sgabor		ks->f1 = (size_t) strtoul(f, NULL, 10);
666235267Sgabor		if (errno != 0)
667245997Sgabor			err(2, "-k");
668235267Sgabor		if (ks->f1 == 0) {
669235432Sgabor			warn("%s",getstr(5));
670235267Sgabor			goto end;
671235267Sgabor		}
672235267Sgabor	}
673235267Sgabor
674235267Sgabor	if (pmatch[2].rm_eo > pmatch[2].rm_so) {
675235267Sgabor		len = pmatch[2].rm_eo - pmatch[2].rm_so - 1;
676235267Sgabor		c = sort_malloc((len + 1) * sizeof(char));
677235267Sgabor
678235267Sgabor		strncpy(c, s + pmatch[2].rm_so + 1, len);
679235267Sgabor		c[len] = '\0';
680235267Sgabor
681235267Sgabor		if (second) {
682235267Sgabor			errno = 0;
683235267Sgabor			ks->c2 = (size_t) strtoul(c, NULL, 10);
684235267Sgabor			if (errno != 0)
685245997Sgabor				err(2, "-k");
686235267Sgabor		} else {
687235267Sgabor			errno = 0;
688235267Sgabor			ks->c1 = (size_t) strtoul(c, NULL, 10);
689235267Sgabor			if (errno != 0)
690245997Sgabor				err(2, "-k");
691235267Sgabor			if (ks->c1 == 0) {
692235432Sgabor				warn("%s",getstr(6));
693235267Sgabor				goto end;
694235267Sgabor			}
695235267Sgabor		}
696235267Sgabor	} else {
697235267Sgabor		if (second)
698235267Sgabor			ks->c2 = 0;
699235267Sgabor		else
700235267Sgabor			ks->c1 = 1;
701235267Sgabor	}
702235267Sgabor
703235267Sgabor	if (pmatch[3].rm_eo > pmatch[3].rm_so) {
704235267Sgabor		regoff_t i = 0;
705235267Sgabor
706235267Sgabor		for (i = pmatch[3].rm_so; i < pmatch[3].rm_eo; i++) {
707235267Sgabor			check_mutually_exclusive_flags(s[i], mef_flags);
708235267Sgabor			if (s[i] == 'b') {
709235267Sgabor				if (second)
710235267Sgabor					ks->pos2b = true;
711235267Sgabor				else
712235267Sgabor					ks->pos1b = true;
713235267Sgabor			} else if (!set_sort_modifier(&(ks->sm), s[i]))
714235267Sgabor				goto end;
715235267Sgabor		}
716235267Sgabor	}
717235267Sgabor
718235267Sgabor	ret = 0;
719235267Sgabor
720235267Sgaborend:
721235267Sgabor
722235267Sgabor	if (c)
723235267Sgabor		sort_free(c);
724235267Sgabor	if (f)
725235267Sgabor		sort_free(f);
726235267Sgabor	regfree(&re);
727235267Sgabor
728235267Sgabor	return (ret);
729235267Sgabor}
730235267Sgabor
731235267Sgabor/*
732235267Sgabor * Parse -k option value.
733235267Sgabor */
734235267Sgaborstatic int
735235267Sgaborparse_k(const char *s, struct key_specs *ks)
736235267Sgabor{
737235267Sgabor	int ret = -1;
738235267Sgabor	bool mef_flags[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS] =
739235267Sgabor	    { false, false, false, false, false, false };
740235267Sgabor
741235267Sgabor	if (s && *s) {
742235267Sgabor		char *sptr;
743235267Sgabor
744235267Sgabor		sptr = strchr(s, ',');
745235267Sgabor		if (sptr) {
746235267Sgabor			size_t size1;
747235267Sgabor			char *pos1, *pos2;
748235267Sgabor
749235267Sgabor			size1 = sptr - s;
750235267Sgabor
751235267Sgabor			if (size1 < 1)
752235267Sgabor				return (-1);
753235267Sgabor			pos1 = sort_malloc((size1 + 1) * sizeof(char));
754235267Sgabor
755235267Sgabor			strncpy(pos1, s, size1);
756235267Sgabor			pos1[size1] = '\0';
757235267Sgabor
758235267Sgabor			ret = parse_pos(pos1, ks, mef_flags, false);
759235267Sgabor
760235267Sgabor			sort_free(pos1);
761235267Sgabor			if (ret < 0)
762235267Sgabor				return (ret);
763235267Sgabor
764235267Sgabor			pos2 = sort_strdup(sptr + 1);
765235267Sgabor			ret = parse_pos(pos2, ks, mef_flags, true);
766235267Sgabor			sort_free(pos2);
767235267Sgabor		} else
768235267Sgabor			ret = parse_pos(s, ks, mef_flags, false);
769235267Sgabor	}
770235267Sgabor
771235267Sgabor	return (ret);
772235267Sgabor}
773235267Sgabor
774235267Sgabor/*
775235267Sgabor * Parse POS in +POS -POS option.
776235267Sgabor */
777235267Sgaborstatic int
778235267Sgaborparse_pos_obs(const char *s, int *nf, int *nc, char* sopts)
779235267Sgabor{
780235267Sgabor	regex_t re;
781235267Sgabor	regmatch_t pmatch[4];
782235267Sgabor	char *c, *f;
783235267Sgabor	const char *sregexp = "^([0-9]+)(\\.[0-9]+)?([A-Za-z]+)?$";
784235267Sgabor	int ret;
785235267Sgabor	size_t len, nmatch;
786235267Sgabor
787235267Sgabor	ret = -1;
788235267Sgabor	nmatch = 4;
789235267Sgabor	c = f = NULL;
790235267Sgabor	*nc = *nf = 0;
791235267Sgabor
792235267Sgabor	if (regcomp(&re, sregexp, REG_EXTENDED) != 0)
793235267Sgabor		return (-1);
794235267Sgabor
795235267Sgabor	if (regexec(&re, s, nmatch, pmatch, 0) != 0)
796235267Sgabor		goto end;
797235267Sgabor
798235267Sgabor	if (pmatch[0].rm_eo <= pmatch[0].rm_so)
799235267Sgabor		goto end;
800235267Sgabor
801235267Sgabor	if (pmatch[1].rm_eo <= pmatch[1].rm_so)
802235267Sgabor		goto end;
803235267Sgabor
804235267Sgabor	len = pmatch[1].rm_eo - pmatch[1].rm_so;
805235267Sgabor	f = sort_malloc((len + 1) * sizeof(char));
806235267Sgabor
807235267Sgabor	strncpy(f, s + pmatch[1].rm_so, len);
808235267Sgabor	f[len] = '\0';
809235267Sgabor
810235267Sgabor	errno = 0;
811235267Sgabor	*nf = (size_t) strtoul(f, NULL, 10);
812235267Sgabor	if (errno != 0)
813235432Sgabor		errx(2, "%s", getstr(11));
814235267Sgabor
815235267Sgabor	if (pmatch[2].rm_eo > pmatch[2].rm_so) {
816235267Sgabor		len = pmatch[2].rm_eo - pmatch[2].rm_so - 1;
817235267Sgabor		c = sort_malloc((len + 1) * sizeof(char));
818235267Sgabor
819235267Sgabor		strncpy(c, s + pmatch[2].rm_so + 1, len);
820235267Sgabor		c[len] = '\0';
821235267Sgabor
822235267Sgabor		errno = 0;
823235267Sgabor		*nc = (size_t) strtoul(c, NULL, 10);
824235267Sgabor		if (errno != 0)
825235432Sgabor			errx(2, "%s", getstr(11));
826235267Sgabor	}
827235267Sgabor
828235267Sgabor	if (pmatch[3].rm_eo > pmatch[3].rm_so) {
829235267Sgabor
830235267Sgabor		len = pmatch[3].rm_eo - pmatch[3].rm_so;
831235267Sgabor
832235267Sgabor		strncpy(sopts, s + pmatch[3].rm_so, len);
833235267Sgabor		sopts[len] = '\0';
834235267Sgabor	}
835235267Sgabor
836235267Sgabor	ret = 0;
837235267Sgabor
838235267Sgaborend:
839235267Sgabor	if (c)
840235267Sgabor		sort_free(c);
841235267Sgabor	if (f)
842235267Sgabor		sort_free(f);
843235267Sgabor	regfree(&re);
844235267Sgabor
845235267Sgabor	return (ret);
846235267Sgabor}
847235267Sgabor
848235267Sgabor/*
849235267Sgabor * "Translate" obsolete +POS1 -POS2 syntax into new -kPOS1,POS2 syntax
850235267Sgabor */
851235267Sgaborvoid
852235267Sgaborfix_obsolete_keys(int *argc, char **argv)
853235267Sgabor{
854235267Sgabor	char sopt[129];
855235267Sgabor
856235267Sgabor	for (int i = 1; i < *argc; i++) {
857235267Sgabor		char *arg1;
858235267Sgabor
859235267Sgabor		arg1 = argv[i];
860235267Sgabor
861235267Sgabor		if (strlen(arg1) > 1 && arg1[0] == '+') {
862235267Sgabor			int c1, f1;
863235267Sgabor			char sopts1[128];
864235267Sgabor
865235267Sgabor			sopts1[0] = 0;
866235267Sgabor			c1 = f1 = 0;
867235267Sgabor
868235267Sgabor			if (parse_pos_obs(arg1 + 1, &f1, &c1, sopts1) < 0)
869235267Sgabor				continue;
870235267Sgabor			else {
871235267Sgabor				f1 += 1;
872235267Sgabor				c1 += 1;
873235267Sgabor				if (i + 1 < *argc) {
874235267Sgabor					char *arg2 = argv[i + 1];
875235267Sgabor
876235267Sgabor					if (strlen(arg2) > 1 &&
877235267Sgabor					    arg2[0] == '-') {
878235267Sgabor						int c2, f2;
879235267Sgabor						char sopts2[128];
880235267Sgabor
881235267Sgabor						sopts2[0] = 0;
882235267Sgabor						c2 = f2 = 0;
883235267Sgabor
884235267Sgabor						if (parse_pos_obs(arg2 + 1,
885235267Sgabor						    &f2, &c2, sopts2) >= 0) {
886235267Sgabor							if (c2 > 0)
887235267Sgabor								f2 += 1;
888235267Sgabor							sprintf(sopt, "-k%d.%d%s,%d.%d%s",
889235267Sgabor							    f1, c1, sopts1, f2, c2, sopts2);
890235267Sgabor							argv[i] = sort_strdup(sopt);
891235267Sgabor							for (int j = i + 1; j + 1 < *argc; j++)
892235267Sgabor								argv[j] = argv[j + 1];
893235267Sgabor							*argc -= 1;
894235267Sgabor							continue;
895235267Sgabor						}
896235267Sgabor					}
897235267Sgabor				}
898272603Sbapt				sprintf(sopt, "-k%d.%d%s", f1, c1, sopts1);
899235267Sgabor				argv[i] = sort_strdup(sopt);
900235267Sgabor			}
901235267Sgabor		}
902235267Sgabor	}
903235267Sgabor}
904235267Sgabor
905235267Sgabor/*
906235267Sgabor * Set random seed
907235267Sgabor */
908235267Sgaborstatic void
909235267Sgaborset_random_seed(void)
910235267Sgabor{
911235267Sgabor	if (need_random) {
912235267Sgabor
913235267Sgabor		if (strcmp(random_source, DEFAULT_RANDOM_SORT_SEED_FILE) == 0) {
914235267Sgabor			FILE* fseed;
915235267Sgabor			MD5_CTX ctx;
916235267Sgabor			char rsd[MAX_DEFAULT_RANDOM_SEED_DATA_SIZE];
917235267Sgabor			size_t sz = 0;
918235267Sgabor
919235267Sgabor			fseed = openfile(random_source, "r");
920235267Sgabor			while (!feof(fseed)) {
921235267Sgabor				int cr;
922235267Sgabor
923235267Sgabor				cr = fgetc(fseed);
924235267Sgabor				if (cr == EOF)
925235267Sgabor					break;
926235267Sgabor
927235267Sgabor				rsd[sz++] = (char) cr;
928235267Sgabor
929235267Sgabor				if (sz >= MAX_DEFAULT_RANDOM_SEED_DATA_SIZE)
930235267Sgabor					break;
931235267Sgabor			}
932235267Sgabor
933235267Sgabor			closefile(fseed, random_source);
934235267Sgabor
935235267Sgabor			MD5Init(&ctx);
936235267Sgabor			MD5Update(&ctx, rsd, sz);
937235267Sgabor
938235267Sgabor			random_seed = MD5End(&ctx, NULL);
939235267Sgabor			random_seed_size = strlen(random_seed);
940235267Sgabor
941235267Sgabor		} else {
942235267Sgabor			MD5_CTX ctx;
943235267Sgabor			char *b;
944235267Sgabor
945235267Sgabor			MD5Init(&ctx);
946235267Sgabor			b = MD5File(random_source, NULL);
947235267Sgabor			if (b == NULL)
948235267Sgabor				err(2, NULL);
949235267Sgabor
950235267Sgabor			random_seed = b;
951235267Sgabor			random_seed_size = strlen(b);
952235267Sgabor		}
953235267Sgabor
954235267Sgabor		MD5Init(&md5_ctx);
955235267Sgabor		if(random_seed_size>0) {
956235267Sgabor			MD5Update(&md5_ctx, random_seed, random_seed_size);
957235267Sgabor		}
958235267Sgabor	}
959235267Sgabor}
960235267Sgabor
961235267Sgabor/*
962235267Sgabor * Main function.
963235267Sgabor */
964235267Sgaborint
965235267Sgabormain(int argc, char **argv)
966235267Sgabor{
967235267Sgabor	char *outfile, *real_outfile;
968235267Sgabor	int c, result;
969235267Sgabor	bool mef_flags[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS] =
970235267Sgabor	    { false, false, false, false, false, false };
971235267Sgabor
972235267Sgabor	result = 0;
973235267Sgabor	outfile = sort_strdup("-");
974235267Sgabor	real_outfile = NULL;
975235267Sgabor
976235267Sgabor	struct sort_mods *sm = &default_sort_mods_object;
977235267Sgabor
978235267Sgabor	init_tmp_files();
979235267Sgabor
980235267Sgabor	set_signal_handler();
981235267Sgabor
982235267Sgabor	set_hw_params();
983235267Sgabor	set_locale();
984235267Sgabor	set_tmpdir();
985235267Sgabor	set_sort_opts();
986235267Sgabor
987235267Sgabor	fix_obsolete_keys(&argc, argv);
988235267Sgabor
989235267Sgabor	while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL))
990235267Sgabor	    != -1)) {
991235267Sgabor
992235267Sgabor		check_mutually_exclusive_flags(c, mef_flags);
993235267Sgabor
994235267Sgabor		if (!set_sort_modifier(sm, c)) {
995235267Sgabor
996235267Sgabor			switch (c) {
997235267Sgabor			case 'c':
998235267Sgabor				sort_opts_vals.cflag = true;
999235267Sgabor				if (optarg) {
1000235267Sgabor					if (!strcmp(optarg, "diagnose-first"))
1001235267Sgabor						;
1002235267Sgabor					else if (!strcmp(optarg, "silent") ||
1003235267Sgabor					    !strcmp(optarg, "quiet"))
1004235267Sgabor						sort_opts_vals.csilentflag = true;
1005235267Sgabor					else if (*optarg)
1006235267Sgabor						unknown(optarg);
1007235267Sgabor				}
1008235267Sgabor				break;
1009235267Sgabor			case 'C':
1010235267Sgabor				sort_opts_vals.cflag = true;
1011235267Sgabor				sort_opts_vals.csilentflag = true;
1012235267Sgabor				break;
1013235267Sgabor			case 'k':
1014235267Sgabor			{
1015235267Sgabor				sort_opts_vals.complex_sort = true;
1016235267Sgabor				sort_opts_vals.kflag = true;
1017235267Sgabor
1018235267Sgabor				keys_num++;
1019235267Sgabor				keys = sort_realloc(keys, keys_num *
1020235267Sgabor				    sizeof(struct key_specs));
1021235267Sgabor				memset(&(keys[keys_num - 1]), 0,
1022235267Sgabor				    sizeof(struct key_specs));
1023235267Sgabor
1024235267Sgabor				if (parse_k(optarg, &(keys[keys_num - 1]))
1025235267Sgabor				    < 0) {
1026245997Sgabor					errc(2, EINVAL, "-k %s", optarg);
1027235267Sgabor				}
1028235267Sgabor
1029235267Sgabor				break;
1030235267Sgabor			}
1031235267Sgabor			case 'm':
1032235267Sgabor				sort_opts_vals.mflag = true;
1033235267Sgabor				break;
1034235267Sgabor			case 'o':
1035235546Sgabor				outfile = sort_realloc(outfile, (strlen(optarg) + 1));
1036235546Sgabor				strcpy(outfile, optarg);
1037235267Sgabor				break;
1038235267Sgabor			case 's':
1039235267Sgabor				sort_opts_vals.sflag = true;
1040235267Sgabor				break;
1041235267Sgabor			case 'S':
1042235267Sgabor				available_free_memory =
1043235267Sgabor				    parse_memory_buffer_value(optarg);
1044235267Sgabor				break;
1045235267Sgabor			case 'T':
1046235267Sgabor				tmpdir = sort_strdup(optarg);
1047235267Sgabor				break;
1048235267Sgabor			case 't':
1049235987Sgabor				while (strlen(optarg) > 1) {
1050235987Sgabor					if (optarg[0] != '\\') {
1051245997Sgabor						errc(2, EINVAL, "%s", optarg);
1052235267Sgabor					}
1053235987Sgabor					optarg += 1;
1054235987Sgabor					if (*optarg == '0') {
1055235987Sgabor						*optarg = 0;
1056235987Sgabor						break;
1057235987Sgabor					}
1058235267Sgabor				}
1059235267Sgabor				sort_opts_vals.tflag = true;
1060235267Sgabor				sort_opts_vals.field_sep = btowc(optarg[0]);
1061235267Sgabor				if (sort_opts_vals.field_sep == WEOF) {
1062235267Sgabor					errno = EINVAL;
1063235267Sgabor					err(2, NULL);
1064235267Sgabor				}
1065235267Sgabor				if (!gnusort_numeric_compatibility) {
1066235267Sgabor					if (symbol_decimal_point == sort_opts_vals.field_sep)
1067235267Sgabor						symbol_decimal_point = WEOF;
1068235267Sgabor					if (symbol_thousands_sep == sort_opts_vals.field_sep)
1069235267Sgabor						symbol_thousands_sep = WEOF;
1070235267Sgabor					if (symbol_negative_sign == sort_opts_vals.field_sep)
1071235267Sgabor						symbol_negative_sign = WEOF;
1072235267Sgabor					if (symbol_positive_sign == sort_opts_vals.field_sep)
1073235267Sgabor						symbol_positive_sign = WEOF;
1074235267Sgabor				}
1075235267Sgabor				break;
1076235267Sgabor			case 'u':
1077235267Sgabor				sort_opts_vals.uflag = true;
1078235267Sgabor				/* stable sort for the correct unique val */
1079235267Sgabor				sort_opts_vals.sflag = true;
1080235267Sgabor				break;
1081235267Sgabor			case 'z':
1082235267Sgabor				sort_opts_vals.zflag = true;
1083235267Sgabor				break;
1084235267Sgabor			case SORT_OPT:
1085235267Sgabor				if (optarg) {
1086235267Sgabor					if (!strcmp(optarg, "general-numeric"))
1087235267Sgabor						set_sort_modifier(sm, 'g');
1088235267Sgabor					else if (!strcmp(optarg, "human-numeric"))
1089235267Sgabor						set_sort_modifier(sm, 'h');
1090235267Sgabor					else if (!strcmp(optarg, "numeric"))
1091235267Sgabor						set_sort_modifier(sm, 'n');
1092235267Sgabor					else if (!strcmp(optarg, "month"))
1093235267Sgabor						set_sort_modifier(sm, 'M');
1094235267Sgabor					else if (!strcmp(optarg, "random"))
1095235267Sgabor						set_sort_modifier(sm, 'R');
1096235267Sgabor					else
1097235267Sgabor						unknown(optarg);
1098235267Sgabor				}
1099235267Sgabor				break;
1100235267Sgabor#if defined(SORT_THREADS)
1101238108Sgabor			case PARALLEL_OPT:
1102235267Sgabor				nthreads = (size_t)(atoi(optarg));
1103235267Sgabor				if (nthreads < 1)
1104235267Sgabor					nthreads = 1;
1105235267Sgabor				if (nthreads > 1024)
1106235267Sgabor					nthreads = 1024;
1107235267Sgabor				break;
1108235267Sgabor#endif
1109235267Sgabor			case QSORT_OPT:
1110235267Sgabor				sort_opts_vals.sort_method = SORT_QSORT;
1111235267Sgabor				break;
1112235267Sgabor			case MERGESORT_OPT:
1113235267Sgabor				sort_opts_vals.sort_method = SORT_MERGESORT;
1114235267Sgabor				break;
1115235987Sgabor			case MMAP_OPT:
1116235987Sgabor				use_mmap = true;
1117235987Sgabor				break;
1118235267Sgabor			case HEAPSORT_OPT:
1119235267Sgabor				sort_opts_vals.sort_method = SORT_HEAPSORT;
1120235267Sgabor				break;
1121235267Sgabor			case RADIXSORT_OPT:
1122235267Sgabor				sort_opts_vals.sort_method = SORT_RADIXSORT;
1123235267Sgabor				break;
1124235267Sgabor			case RANDOMSOURCE_OPT:
1125235267Sgabor				random_source = strdup(optarg);
1126235267Sgabor				break;
1127235267Sgabor			case COMPRESSPROGRAM_OPT:
1128235267Sgabor				compress_program = strdup(optarg);
1129235267Sgabor				break;
1130235267Sgabor			case FF_OPT:
1131235267Sgabor				read_fns_from_file0(optarg);
1132235267Sgabor				break;
1133235267Sgabor			case BS_OPT:
1134235267Sgabor			{
1135235267Sgabor				errno = 0;
1136235267Sgabor				long mof = strtol(optarg, NULL, 10);
1137235267Sgabor				if (errno != 0)
1138245997Sgabor					err(2, "--batch-size");
1139235267Sgabor				if (mof >= 2)
1140235267Sgabor					max_open_files = (size_t) mof + 1;
1141235267Sgabor			}
1142235267Sgabor				break;
1143235267Sgabor			case VERSION_OPT:
1144235267Sgabor				printf("%s\n", VERSION);
1145235267Sgabor				exit(EXIT_SUCCESS);
1146235267Sgabor				/* NOTREACHED */
1147235267Sgabor				break;
1148235267Sgabor			case DEBUG_OPT:
1149235267Sgabor				debug_sort = true;
1150235267Sgabor				break;
1151235267Sgabor			case HELP_OPT:
1152235267Sgabor				usage(false);
1153235267Sgabor				/* NOTREACHED */
1154235267Sgabor				break;
1155235267Sgabor			default:
1156235267Sgabor				usage(true);
1157235267Sgabor				/* NOTREACHED */
1158235267Sgabor			}
1159235267Sgabor		}
1160235267Sgabor	}
1161235267Sgabor
1162235267Sgabor	argc -= optind;
1163235267Sgabor	argv += optind;
1164235267Sgabor
1165235267Sgabor#ifndef WITHOUT_NLS
1166235267Sgabor	catalog = catopen("sort", NL_CAT_LOCALE);
1167235267Sgabor#endif
1168235267Sgabor
1169235267Sgabor	if (sort_opts_vals.cflag && sort_opts_vals.mflag)
1170235432Sgabor		errx(1, "%c:%c: %s", 'm', 'c', getstr(1));
1171235267Sgabor
1172235267Sgabor#ifndef WITHOUT_NLS
1173235267Sgabor	catclose(catalog);
1174235267Sgabor#endif
1175235267Sgabor
1176235267Sgabor	if (keys_num == 0) {
1177235267Sgabor		keys_num = 1;
1178235267Sgabor		keys = sort_realloc(keys, sizeof(struct key_specs));
1179235267Sgabor		memset(&(keys[0]), 0, sizeof(struct key_specs));
1180235267Sgabor		keys[0].c1 = 1;
1181235267Sgabor		keys[0].pos1b = default_sort_mods->bflag;
1182235267Sgabor		keys[0].pos2b = default_sort_mods->bflag;
1183235267Sgabor		memcpy(&(keys[0].sm), default_sort_mods,
1184235267Sgabor		    sizeof(struct sort_mods));
1185235267Sgabor	}
1186235267Sgabor
1187235267Sgabor	for (size_t i = 0; i < keys_num; i++) {
1188235267Sgabor		struct key_specs *ks;
1189235267Sgabor
1190235267Sgabor		ks = &(keys[i]);
1191235267Sgabor
1192235267Sgabor		if (sort_modifier_empty(&(ks->sm)) && !(ks->pos1b) &&
1193235267Sgabor		    !(ks->pos2b)) {
1194235267Sgabor			ks->pos1b = sm->bflag;
1195235267Sgabor			ks->pos2b = sm->bflag;
1196235267Sgabor			memcpy(&(ks->sm), sm, sizeof(struct sort_mods));
1197235267Sgabor		}
1198235267Sgabor
1199235267Sgabor		ks->sm.func = get_sort_func(&(ks->sm));
1200235267Sgabor	}
1201235267Sgabor
1202242430Sgabor	if (argv_from_file0) {
1203235267Sgabor		argc = argc_from_file0;
1204235267Sgabor		argv = argv_from_file0;
1205235267Sgabor	}
1206235267Sgabor
1207235267Sgabor	if (debug_sort) {
1208244515Sgabor		printf("Memory to be used for sorting: %llu\n",available_free_memory);
1209235267Sgabor#if defined(SORT_THREADS)
1210244515Sgabor		printf("Number of CPUs: %d\n",(int)ncpu);
1211235267Sgabor		nthreads = 1;
1212235267Sgabor#endif
1213235267Sgabor		printf("Using collate rules of %s locale\n",
1214235267Sgabor		    setlocale(LC_COLLATE, NULL));
1215235267Sgabor		if (byte_sort)
1216235267Sgabor			printf("Byte sort is used\n");
1217235267Sgabor		if (print_symbols_on_debug) {
1218235267Sgabor			printf("Decimal Point: <%lc>\n", symbol_decimal_point);
1219235267Sgabor			if (symbol_thousands_sep)
1220235267Sgabor				printf("Thousands separator: <%lc>\n",
1221235267Sgabor				    symbol_thousands_sep);
1222235267Sgabor			printf("Positive sign: <%lc>\n", symbol_positive_sign);
1223235267Sgabor			printf("Negative sign: <%lc>\n", symbol_negative_sign);
1224235267Sgabor		}
1225235267Sgabor	}
1226235267Sgabor
1227235267Sgabor	set_random_seed();
1228235267Sgabor
1229235267Sgabor	/* Case when the outfile equals one of the input files: */
1230235267Sgabor	if (strcmp(outfile, "-")) {
1231235267Sgabor
1232235267Sgabor		for(int i = 0; i < argc; ++i) {
1233235267Sgabor			if (strcmp(argv[i], outfile) == 0) {
1234235267Sgabor				real_outfile = sort_strdup(outfile);
1235235267Sgabor				for(;;) {
1236235267Sgabor					char* tmp = sort_malloc(strlen(outfile) +
1237235267Sgabor					    strlen(".tmp") + 1);
1238235267Sgabor
1239235267Sgabor					strcpy(tmp, outfile);
1240235267Sgabor					strcpy(tmp + strlen(tmp), ".tmp");
1241235267Sgabor					sort_free(outfile);
1242235267Sgabor					outfile = tmp;
1243235267Sgabor					if (access(outfile, F_OK) < 0)
1244235267Sgabor						break;
1245235267Sgabor				}
1246235267Sgabor				tmp_file_atexit(outfile);
1247235267Sgabor			}
1248235267Sgabor		}
1249235267Sgabor	}
1250235267Sgabor
1251235987Sgabor#if defined(SORT_THREADS)
1252235987Sgabor	if ((argc < 1) || (strcmp(outfile, "-") == 0) || (*outfile == 0))
1253235987Sgabor		nthreads = 1;
1254235987Sgabor#endif
1255235987Sgabor
1256235267Sgabor	if (!sort_opts_vals.cflag && !sort_opts_vals.mflag) {
1257235267Sgabor		struct file_list fl;
1258235267Sgabor		struct sort_list list;
1259235267Sgabor
1260235267Sgabor		sort_list_init(&list);
1261235267Sgabor		file_list_init(&fl, true);
1262235267Sgabor
1263235267Sgabor		if (argc < 1)
1264235267Sgabor			procfile("-", &list, &fl);
1265235267Sgabor		else {
1266235267Sgabor			while (argc > 0) {
1267235267Sgabor				procfile(*argv, &list, &fl);
1268235267Sgabor				--argc;
1269235267Sgabor				++argv;
1270235267Sgabor			}
1271235267Sgabor		}
1272235267Sgabor
1273235267Sgabor		if (fl.count < 1)
1274235267Sgabor			sort_list_to_file(&list, outfile);
1275235267Sgabor		else {
1276235267Sgabor			if (list.count > 0) {
1277235267Sgabor				char *flast = new_tmp_file_name();
1278235267Sgabor
1279235267Sgabor				sort_list_to_file(&list, flast);
1280235267Sgabor				file_list_add(&fl, flast, false);
1281235267Sgabor			}
1282235267Sgabor			merge_files(&fl, outfile);
1283235267Sgabor		}
1284235267Sgabor
1285235267Sgabor		file_list_clean(&fl);
1286235267Sgabor
1287235267Sgabor		/*
1288235267Sgabor		 * We are about to exit the program, so we can ignore
1289235267Sgabor		 * the clean-up for speed
1290235267Sgabor		 *
1291235267Sgabor		 * sort_list_clean(&list);
1292235267Sgabor		 */
1293235267Sgabor
1294235267Sgabor	} else if (sort_opts_vals.cflag) {
1295235267Sgabor		result = (argc == 0) ? (check("-")) : (check(*argv));
1296235267Sgabor	} else if (sort_opts_vals.mflag) {
1297235267Sgabor		struct file_list fl;
1298235267Sgabor
1299235267Sgabor		file_list_init(&fl, false);
1300235267Sgabor		file_list_populate(&fl, argc, argv, true);
1301235267Sgabor		merge_files(&fl, outfile);
1302235267Sgabor		file_list_clean(&fl);
1303235267Sgabor	}
1304235267Sgabor
1305235267Sgabor	if (real_outfile) {
1306235267Sgabor		unlink(real_outfile);
1307235267Sgabor		if (rename(outfile, real_outfile) < 0)
1308235267Sgabor			err(2, NULL);
1309235267Sgabor		sort_free(real_outfile);
1310235267Sgabor	}
1311235267Sgabor
1312235267Sgabor	sort_free(outfile);
1313235267Sgabor
1314235267Sgabor	return (result);
1315235267Sgabor}
1316