xref: /openbsd-src/usr.bin/sort/file.c (revision 479c151d3429b7cfa6228ee428d945620629789d)
1*479c151dSjsg /*	$OpenBSD: file.c,v 1.24 2024/09/20 02:00:46 jsg Exp $	*/
279428148Smillert 
379428148Smillert /*-
479428148Smillert  * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
579428148Smillert  * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
679428148Smillert  * All rights reserved.
779428148Smillert  *
879428148Smillert  * Redistribution and use in source and binary forms, with or without
979428148Smillert  * modification, are permitted provided that the following conditions
1079428148Smillert  * are met:
1179428148Smillert  * 1. Redistributions of source code must retain the above copyright
1279428148Smillert  *    notice, this list of conditions and the following disclaimer.
1379428148Smillert  * 2. Redistributions in binary form must reproduce the above copyright
1479428148Smillert  *    notice, this list of conditions and the following disclaimer in the
1579428148Smillert  *    documentation and/or other materials provided with the distribution.
1679428148Smillert  *
1779428148Smillert  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1879428148Smillert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1979428148Smillert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2079428148Smillert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2179428148Smillert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2279428148Smillert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2379428148Smillert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2479428148Smillert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2579428148Smillert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2679428148Smillert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2779428148Smillert  * SUCH DAMAGE.
2879428148Smillert  */
2979428148Smillert 
3079428148Smillert #include <sys/mman.h>
3179428148Smillert #include <sys/stat.h>
3279428148Smillert #include <sys/types.h>
3379428148Smillert #include <sys/queue.h>
3479428148Smillert 
3579428148Smillert #include <err.h>
3679428148Smillert #include <fcntl.h>
3779428148Smillert #include <stdio.h>
3879428148Smillert #include <stdlib.h>
39df61954fSderaadt #include <signal.h>
4079428148Smillert #include <string.h>
4179428148Smillert #include <unistd.h>
4279428148Smillert #include <wchar.h>
4379428148Smillert #include <wctype.h>
4479428148Smillert 
4579428148Smillert #include "coll.h"
4679428148Smillert #include "file.h"
4779428148Smillert #include "radixsort.h"
4879428148Smillert 
4979428148Smillert unsigned long long available_free_memory = 1000000;
5079428148Smillert 
5179428148Smillert bool use_mmap;
5279428148Smillert 
53afc06763Slteo const char *tmpdir = "/tmp";
5479428148Smillert const char *compress_program;
5579428148Smillert 
5679428148Smillert size_t max_open_files = 16;
5779428148Smillert 
5879428148Smillert /*
5979428148Smillert  * How much space we read from file at once
6079428148Smillert  */
6179428148Smillert #define READ_CHUNK 4096
6279428148Smillert 
6379428148Smillert /*
6479428148Smillert  * File reader structure
6579428148Smillert  */
6679428148Smillert struct file_reader {
6779428148Smillert 	struct reader_buffer	 rb;
6879428148Smillert 	FILE			*file;
6979428148Smillert 	char			*fname;
7079428148Smillert 	unsigned char		*buffer;
7179428148Smillert 	unsigned char		*mmapaddr;
7279428148Smillert 	unsigned char		*mmapptr;
7379428148Smillert 	size_t			 bsz;
7479428148Smillert 	size_t			 cbsz;
7579428148Smillert 	size_t			 mmapsize;
7679428148Smillert 	size_t			 strbeg;
7779428148Smillert 	char			 elsymb;
7879428148Smillert };
7979428148Smillert 
8079428148Smillert /*
8179428148Smillert  * Structure to be used in file merge process.
8279428148Smillert  */
8379428148Smillert struct file_header {
8479428148Smillert 	struct file_reader		*fr;
8579428148Smillert 	struct sort_list_item		*si; /* current top line */
8679428148Smillert 	size_t				 file_pos;
8779428148Smillert };
8879428148Smillert 
8979428148Smillert /*
9079428148Smillert  * List elements of "cleanable" files list.
9179428148Smillert  */
9279428148Smillert struct CLEANABLE_FILE {
9379428148Smillert 	char				*fn;
9479428148Smillert 	LIST_ENTRY(CLEANABLE_FILE)	 files;
9579428148Smillert };
9679428148Smillert 
9779428148Smillert /*
9879428148Smillert  * List header of "cleanable" files list.
9979428148Smillert  */
10079428148Smillert static LIST_HEAD(CLEANABLE_FILES, CLEANABLE_FILE) tmp_files;
10179428148Smillert 
10279428148Smillert /*
10379428148Smillert  * Init tmp files list
10479428148Smillert  */
10579428148Smillert void
10679428148Smillert init_tmp_files(void)
10779428148Smillert {
10879428148Smillert 	LIST_INIT(&tmp_files);
10979428148Smillert }
11079428148Smillert 
11179428148Smillert /*
11279428148Smillert  * Save name of a tmp file for signal cleanup
11379428148Smillert  */
11479428148Smillert void
11579428148Smillert tmp_file_atexit(const char *tmp_file)
11679428148Smillert {
117120aa463Smillert 	struct CLEANABLE_FILE *item;
118df61954fSderaadt 	sigset_t mask, oldmask;
119120aa463Smillert 
120120aa463Smillert 	item = sort_malloc(sizeof(struct CLEANABLE_FILE));
12179428148Smillert 	item->fn = sort_strdup(tmp_file);
122df61954fSderaadt 
123df61954fSderaadt 	sigfillset(&mask);
124df61954fSderaadt 	sigprocmask(SIG_BLOCK, &mask, &oldmask);
12579428148Smillert 	LIST_INSERT_HEAD(&tmp_files, item, files);
126df61954fSderaadt 	sigprocmask(SIG_SETMASK, &oldmask, NULL);
12779428148Smillert }
12879428148Smillert 
12979428148Smillert /*
13079428148Smillert  * Clear tmp files
13179428148Smillert  */
13279428148Smillert void
13379428148Smillert clear_tmp_files(void)
13479428148Smillert {
13579428148Smillert 	struct CLEANABLE_FILE *item;
13679428148Smillert 
13779428148Smillert 	LIST_FOREACH(item, &tmp_files, files) {
13879428148Smillert 		if (item != NULL && item->fn != NULL)
13979428148Smillert 			unlink(item->fn);
14079428148Smillert 	}
14179428148Smillert }
14279428148Smillert 
14379428148Smillert /*
14479428148Smillert  * Check whether a file is a temporary file
14579428148Smillert  */
14679428148Smillert static bool
14779428148Smillert file_is_tmp(const char *fn)
14879428148Smillert {
14979428148Smillert 	struct CLEANABLE_FILE *item;
15079428148Smillert 
15179428148Smillert 	LIST_FOREACH(item, &tmp_files, files) {
152120aa463Smillert 		if (item->fn != NULL && strcmp(item->fn, fn) == 0)
153120aa463Smillert 			return true;
15479428148Smillert 	}
15579428148Smillert 
156120aa463Smillert 	return false;
15779428148Smillert }
15879428148Smillert 
15979428148Smillert /*
16079428148Smillert  * Generate new temporary file name
16179428148Smillert  */
16279428148Smillert char *
16379428148Smillert new_tmp_file_name(void)
16479428148Smillert {
16579428148Smillert 	char *ret;
1666183c83dStobias 	int fd;
16779428148Smillert 
1686183c83dStobias 	sort_asprintf(&ret, "%s/.bsdsort.XXXXXXXXXX", tmpdir);
1696183c83dStobias 	if ((fd = mkstemp(ret)) == -1)
1706183c83dStobias 		err(2, "%s", ret);
1716183c83dStobias 	close(fd);
17279428148Smillert 	tmp_file_atexit(ret);
17379428148Smillert 	return ret;
17479428148Smillert }
17579428148Smillert 
17679428148Smillert /*
17779428148Smillert  * Initialize file list
17879428148Smillert  */
17979428148Smillert void
18079428148Smillert file_list_init(struct file_list *fl, bool tmp)
18179428148Smillert {
18279428148Smillert 	fl->count = 0;
18379428148Smillert 	fl->sz = 0;
18479428148Smillert 	fl->fns = NULL;
18579428148Smillert 	fl->tmp = tmp;
18679428148Smillert }
18779428148Smillert 
18879428148Smillert /*
18979428148Smillert  * Add a file name to the list
19079428148Smillert  */
19179428148Smillert void
19279428148Smillert file_list_add(struct file_list *fl, char *fn, bool allocate)
19379428148Smillert {
194120aa463Smillert 	if (fl->count >= fl->sz) {
195d20d9662Smillert 		fl->fns = sort_reallocarray(fl->fns,
196d20d9662Smillert 		    fl->sz ? fl->sz : (fl->sz = 1), 2 * sizeof(char *));
197d20d9662Smillert 		fl->sz *= 2;
19879428148Smillert 	}
19979428148Smillert 	fl->fns[fl->count] = allocate ? sort_strdup(fn) : fn;
20079428148Smillert 	fl->count += 1;
20179428148Smillert }
20279428148Smillert 
20379428148Smillert /*
20479428148Smillert  * Populate file list from array of file names
20579428148Smillert  */
20679428148Smillert void
20779428148Smillert file_list_populate(struct file_list *fl, int argc, char **argv, bool allocate)
20879428148Smillert {
20979428148Smillert 	int i;
21079428148Smillert 
21179428148Smillert 	for (i = 0; i < argc; i++)
21279428148Smillert 		file_list_add(fl, argv[i], allocate);
21379428148Smillert }
21479428148Smillert 
21579428148Smillert /*
21679428148Smillert  * Clean file list data and delete the files,
21779428148Smillert  * if this is a list of temporary files
21879428148Smillert  */
21979428148Smillert void
22079428148Smillert file_list_clean(struct file_list *fl)
22179428148Smillert {
22279428148Smillert 	if (fl->fns) {
22379428148Smillert 		size_t i;
22479428148Smillert 
22579428148Smillert 		for (i = 0; i < fl->count; i++) {
22679428148Smillert 			if (fl->fns[i]) {
22779428148Smillert 				if (fl->tmp)
22879428148Smillert 					unlink(fl->fns[i]);
22979428148Smillert 				sort_free(fl->fns[i]);
230120aa463Smillert 				fl->fns[i] = NULL;
23179428148Smillert 			}
23279428148Smillert 		}
23379428148Smillert 		sort_free(fl->fns);
23479428148Smillert 		fl->fns = NULL;
23579428148Smillert 	}
23679428148Smillert 	fl->sz = 0;
23779428148Smillert 	fl->count = 0;
23879428148Smillert 	fl->tmp = false;
23979428148Smillert }
24079428148Smillert 
24179428148Smillert /*
24279428148Smillert  * Init sort list
24379428148Smillert  */
24479428148Smillert void
24579428148Smillert sort_list_init(struct sort_list *l)
24679428148Smillert {
24779428148Smillert 	l->count = 0;
24879428148Smillert 	l->size = 0;
24979428148Smillert 	l->memsize = sizeof(struct sort_list);
25079428148Smillert 	l->list = NULL;
25179428148Smillert }
25279428148Smillert 
25379428148Smillert /*
25479428148Smillert  * Add string to sort list
25579428148Smillert  */
25679428148Smillert void
25779428148Smillert sort_list_add(struct sort_list *l, struct bwstring *str)
25879428148Smillert {
25979428148Smillert 	size_t indx = l->count;
26079428148Smillert 
26179428148Smillert 	if ((l->list == NULL) || (indx >= l->size)) {
26279428148Smillert 		size_t newsize = (l->size + 1) + 1024;
26379428148Smillert 
26479428148Smillert 		l->list = sort_reallocarray(l->list, newsize,
26579428148Smillert 		    sizeof(struct sort_list_item *));
26679428148Smillert 		l->memsize += (newsize - l->size) *
26779428148Smillert 		    sizeof(struct sort_list_item *);
26879428148Smillert 		l->size = newsize;
26979428148Smillert 	}
27079428148Smillert 	l->list[indx] = sort_list_item_alloc();
27179428148Smillert 	sort_list_item_set(l->list[indx], str);
27279428148Smillert 	l->memsize += sort_list_item_size(l->list[indx]);
27379428148Smillert 	l->count += 1;
27479428148Smillert }
27579428148Smillert 
27679428148Smillert /*
27779428148Smillert  * Clean sort list data
27879428148Smillert  */
27979428148Smillert void
28079428148Smillert sort_list_clean(struct sort_list *l)
28179428148Smillert {
28279428148Smillert 	if (l->list) {
28379428148Smillert 		size_t i;
28479428148Smillert 
28579428148Smillert 		for (i = 0; i < l->count; i++) {
28679428148Smillert 			struct sort_list_item *item;
28779428148Smillert 
28879428148Smillert 			item = l->list[i];
28979428148Smillert 
29079428148Smillert 			if (item) {
29179428148Smillert 				sort_list_item_clean(item);
29279428148Smillert 				sort_free(item);
29379428148Smillert 				l->list[i] = NULL;
29479428148Smillert 			}
29579428148Smillert 		}
29679428148Smillert 		sort_free(l->list);
29779428148Smillert 		l->list = NULL;
29879428148Smillert 	}
29979428148Smillert 	l->count = 0;
30079428148Smillert 	l->size = 0;
30179428148Smillert 	l->memsize = sizeof(struct sort_list);
30279428148Smillert }
30379428148Smillert 
30479428148Smillert /*
30579428148Smillert  * Write sort list to file
30679428148Smillert  */
30779428148Smillert void
30879428148Smillert sort_list_dump(struct sort_list *l, const char *fn)
30979428148Smillert {
31079428148Smillert 	FILE *f;
31179428148Smillert 
31279428148Smillert 	f = openfile(fn, "w");
31379428148Smillert 	if (f == NULL)
31479428148Smillert 		err(2, "%s", fn);
31579428148Smillert 
31679428148Smillert 	if (l->list) {
31779428148Smillert 		size_t i;
318120aa463Smillert 
31979428148Smillert 		if (!sort_opts_vals.uflag) {
32079428148Smillert 			for (i = 0; i < l->count; ++i)
32179428148Smillert 				bwsfwrite(l->list[i]->str, f,
32279428148Smillert 				    sort_opts_vals.zflag);
32379428148Smillert 		} else {
32479428148Smillert 			struct sort_list_item *last_printed_item = NULL;
32579428148Smillert 			struct sort_list_item *item;
32679428148Smillert 			for (i = 0; i < l->count; ++i) {
32779428148Smillert 				item = l->list[i];
32879428148Smillert 				if ((last_printed_item == NULL) ||
32979428148Smillert 				    list_coll(&last_printed_item, &item)) {
33079428148Smillert 					bwsfwrite(item->str, f, sort_opts_vals.zflag);
33179428148Smillert 					last_printed_item = item;
33279428148Smillert 				}
33379428148Smillert 			}
33479428148Smillert 		}
33579428148Smillert 	}
33679428148Smillert 
33779428148Smillert 	closefile(f, fn);
33879428148Smillert }
33979428148Smillert 
34079428148Smillert /*
34179428148Smillert  * Checks if the given file is sorted.  Stops at the first disorder,
34279428148Smillert  * prints the disordered line and returns 1.
34379428148Smillert  */
34479428148Smillert int
34579428148Smillert check(const char *fn)
34679428148Smillert {
347df33caa0Stobias 	struct bwstring *s1, *s2;
34879428148Smillert 	struct file_reader *fr;
34979428148Smillert 	struct keys_array *ka1, *ka2;
35079428148Smillert 	int res;
35179428148Smillert 	size_t pos, posdisorder;
35279428148Smillert 
353df33caa0Stobias 	s1 = s2 = NULL;
35479428148Smillert 	ka1 = ka2 = NULL;
35579428148Smillert 
35679428148Smillert 	fr = file_reader_init(fn);
35779428148Smillert 
35879428148Smillert 	res = 0;
35979428148Smillert 	pos = 1;
36079428148Smillert 	posdisorder = 1;
36179428148Smillert 
362df33caa0Stobias 	if (fr == NULL)
36379428148Smillert 		err(2, "%s", fn);
36479428148Smillert 
36579428148Smillert 	s1 = file_reader_readline(fr);
36679428148Smillert 	if (s1 == NULL)
36779428148Smillert 		goto end;
36879428148Smillert 
36979428148Smillert 	ka1 = keys_array_alloc();
37079428148Smillert 	preproc(s1, ka1);
37179428148Smillert 
37279428148Smillert 	s2 = file_reader_readline(fr);
37379428148Smillert 	if (s2 == NULL)
37479428148Smillert 		goto end;
37579428148Smillert 
37679428148Smillert 	ka2 = keys_array_alloc();
37779428148Smillert 	preproc(s2, ka2);
37879428148Smillert 
37979428148Smillert 	for (;;) {
38079428148Smillert 
38179428148Smillert 		if (debug_sort) {
38279428148Smillert 			bwsprintf(stdout, s2, "s1=<", ">");
38379428148Smillert 			bwsprintf(stdout, s1, "s2=<", ">");
38479428148Smillert 		}
38579428148Smillert 		int cmp = key_coll(ka2, ka1, 0);
38679428148Smillert 		if (debug_sort)
38779428148Smillert 			printf("; cmp1=%d", cmp);
38879428148Smillert 
38979428148Smillert 		if (!cmp && sort_opts_vals.complex_sort &&
390aee7016fSmillert 		    !(sort_opts_vals.uflag) && !(sort_opts_vals.sflag) &&
391aee7016fSmillert 		    !(sort_opts_vals.kflag)) {
39279428148Smillert 			cmp = top_level_str_coll(s2, s1);
39379428148Smillert 			if (debug_sort)
39479428148Smillert 				printf("; cmp2=%d", cmp);
39579428148Smillert 		}
39679428148Smillert 		if (debug_sort)
39779428148Smillert 			printf("\n");
39879428148Smillert 
39979428148Smillert 		if ((sort_opts_vals.uflag && (cmp <= 0)) || (cmp < 0)) {
40079428148Smillert 			if (!(sort_opts_vals.csilentflag)) {
401df33caa0Stobias 				bws_disorder_warnx(s2, fn, posdisorder);
40279428148Smillert 				posdisorder = pos;
40379428148Smillert 				if (debug_sort)
404df33caa0Stobias 					bws_disorder_warnx(s1, fn, posdisorder);
40579428148Smillert 			}
40679428148Smillert 			res = 1;
40779428148Smillert 			goto end;
40879428148Smillert 		}
40979428148Smillert 
41079428148Smillert 		pos++;
41179428148Smillert 
41279428148Smillert 		clean_keys_array(s1, ka1);
41379428148Smillert 		sort_free(ka1);
41479428148Smillert 		ka1 = ka2;
41579428148Smillert 		ka2 = NULL;
41679428148Smillert 
41779428148Smillert 		bwsfree(s1);
41879428148Smillert 		s1 = s2;
41979428148Smillert 
42079428148Smillert 		s2 = file_reader_readline(fr);
42179428148Smillert 		if (s2 == NULL)
42279428148Smillert 			goto end;
42379428148Smillert 
42479428148Smillert 		ka2 = keys_array_alloc();
42579428148Smillert 		preproc(s2, ka2);
42679428148Smillert 	}
42779428148Smillert 
42879428148Smillert end:
42979428148Smillert 	if (ka1) {
43079428148Smillert 		clean_keys_array(s1, ka1);
43179428148Smillert 		sort_free(ka1);
43279428148Smillert 	}
43379428148Smillert 
43479428148Smillert 	bwsfree(s1);
43579428148Smillert 
43679428148Smillert 	if (ka2) {
43779428148Smillert 		clean_keys_array(s2, ka2);
43879428148Smillert 		sort_free(ka2);
43979428148Smillert 	}
44079428148Smillert 
44179428148Smillert 	bwsfree(s2);
44279428148Smillert 
44379428148Smillert 	if (fn == NULL || *fn == 0 || strcmp(fn, "-") == 0) {
44479428148Smillert 		for (;;) {
44579428148Smillert 			s2 = file_reader_readline(fr);
44679428148Smillert 			if (s2 == NULL)
44779428148Smillert 				break;
44879428148Smillert 			bwsfree(s2);
44979428148Smillert 		}
45079428148Smillert 	}
45179428148Smillert 
45279428148Smillert 	file_reader_free(fr);
45379428148Smillert 
454df33caa0Stobias 	return res;
45579428148Smillert }
45679428148Smillert 
45779428148Smillert /*
45879428148Smillert  * Opens a file.  If the given filename is "-", stdout will be
45979428148Smillert  * opened.
46079428148Smillert  */
46179428148Smillert FILE *
46279428148Smillert openfile(const char *fn, const char *mode)
46379428148Smillert {
46479428148Smillert 	FILE *file;
46579428148Smillert 
4668d51f3e1Smillert 	if (strcmp(fn, "-") == 0)
4678d51f3e1Smillert 		return (mode[0] == 'r') ? stdin : stdout;
46879428148Smillert 
4698d51f3e1Smillert 	if (file_is_tmp(fn) && (compress_program != NULL)) {
47079428148Smillert 		char *cmd;
47179428148Smillert 
47279428148Smillert 		fflush(stdout);
47379428148Smillert 
47479428148Smillert 		if (mode[0] == 'r')
475db9923beSmillert 			sort_asprintf(&cmd, "%s -d < %s",
476e461c5e5Stobias 			    compress_program, fn);
47779428148Smillert 		else if (mode[0] == 'w')
478db9923beSmillert 			sort_asprintf(&cmd, "%s > %s",
47979428148Smillert 			    compress_program, fn);
48079428148Smillert 		else
4818d51f3e1Smillert 			err(2, "invalid file mode");
48279428148Smillert 
48379428148Smillert 		if ((file = popen(cmd, mode)) == NULL)
4848d51f3e1Smillert 			err(2, "%s", compress_program);
48579428148Smillert 
48679428148Smillert 		sort_free(cmd);
48779428148Smillert 
48879428148Smillert 	} else if ((file = fopen(fn, mode)) == NULL)
48979428148Smillert 		err(2, "%s", fn);
49079428148Smillert 
49179428148Smillert 	return file;
49279428148Smillert }
49379428148Smillert 
49479428148Smillert /*
49579428148Smillert  * Close file
49679428148Smillert  */
49779428148Smillert void
49879428148Smillert closefile(FILE *f, const char *fn)
49979428148Smillert {
50079428148Smillert 	if (f == NULL) {
50179428148Smillert 		;
50279428148Smillert 	} else if (f == stdin) {
50379428148Smillert 		;
50479428148Smillert 	} else if (f == stdout) {
50579428148Smillert 		fflush(f);
50679428148Smillert 	} else {
50779428148Smillert 		if (file_is_tmp(fn) && compress_program != NULL) {
50879428148Smillert 			if (pclose(f) < 0)
50979428148Smillert 				err(2, NULL);
51079428148Smillert 		} else
51179428148Smillert 			fclose(f);
51279428148Smillert 	}
51379428148Smillert }
51479428148Smillert 
51579428148Smillert /*
51679428148Smillert  * Reads a file into the internal buffer.
51779428148Smillert  */
51879428148Smillert struct file_reader *
51979428148Smillert file_reader_init(const char *fsrc)
52079428148Smillert {
52179428148Smillert 	struct file_reader *ret;
52279428148Smillert 
52379428148Smillert 	if (fsrc == NULL)
52479428148Smillert 		fsrc = "-";
52579428148Smillert 
5268fd2d339Smillert 	ret = sort_calloc(1, sizeof(struct file_reader));
52779428148Smillert 
52879428148Smillert 	ret->elsymb = '\n';
52979428148Smillert 	if (sort_opts_vals.zflag)
53079428148Smillert 		ret->elsymb = 0;
53179428148Smillert 
53279428148Smillert 	ret->fname = sort_strdup(fsrc);
53379428148Smillert 
53479428148Smillert 	if (strcmp(fsrc, "-") && (compress_program == NULL) && use_mmap) {
53579428148Smillert 		struct stat stat_buf;
53679428148Smillert 		void *addr;
53779428148Smillert 		size_t sz = 0;
53879428148Smillert 		int fd;
53979428148Smillert 
54079428148Smillert 		fd = open(fsrc, O_RDONLY);
54179428148Smillert 		if (fd < 0)
54279428148Smillert 			err(2, "%s", fsrc);
54379428148Smillert 
54479428148Smillert 		if (fstat(fd, &stat_buf) < 0)
54579428148Smillert 			err(2, "%s", fsrc);
54679428148Smillert 		sz = stat_buf.st_size;
54779428148Smillert 
54879428148Smillert 		addr = mmap(NULL, sz, PROT_READ, 0, fd, 0);
54979428148Smillert 		close(fd);
55036cffe14Smillert 		if (addr != MAP_FAILED) {
55179428148Smillert 			ret->mmapaddr = addr;
55279428148Smillert 			ret->mmapsize = sz;
55379428148Smillert 			ret->mmapptr = ret->mmapaddr;
55479428148Smillert 			posix_madvise(addr, sz, POSIX_MADV_SEQUENTIAL);
55579428148Smillert 		}
55679428148Smillert 	}
55779428148Smillert 
55879428148Smillert 	if (ret->mmapaddr == NULL) {
55979428148Smillert 		ret->file = openfile(fsrc, "r");
56079428148Smillert 		if (ret->file == NULL)
56179428148Smillert 			err(2, "%s", fsrc);
56279428148Smillert 
56379428148Smillert 		if (strcmp(fsrc, "-")) {
56479428148Smillert 			ret->cbsz = READ_CHUNK;
56579428148Smillert 			ret->buffer = sort_malloc(ret->cbsz);
56679428148Smillert 			ret->bsz = 0;
56779428148Smillert 			ret->strbeg = 0;
56879428148Smillert 
56979428148Smillert 			ret->bsz = fread(ret->buffer, 1, ret->cbsz, ret->file);
57079428148Smillert 			if (ret->bsz == 0) {
57179428148Smillert 				if (ferror(ret->file))
57279428148Smillert 					err(2, NULL);
57379428148Smillert 			}
57479428148Smillert 		}
57579428148Smillert 	}
57679428148Smillert 
57779428148Smillert 	return ret;
57879428148Smillert }
57979428148Smillert 
58079428148Smillert struct bwstring *
58179428148Smillert file_reader_readline(struct file_reader *fr)
58279428148Smillert {
58379428148Smillert 	struct bwstring *ret = NULL;
58479428148Smillert 
58579428148Smillert 	if (fr->mmapaddr) {
58679428148Smillert 		unsigned char *mmapend;
58779428148Smillert 
58879428148Smillert 		mmapend = fr->mmapaddr + fr->mmapsize;
58979428148Smillert 		if (fr->mmapptr >= mmapend)
59079428148Smillert 			return NULL;
59179428148Smillert 		else {
59279428148Smillert 			unsigned char *strend;
59379428148Smillert 			size_t sz;
59479428148Smillert 
59579428148Smillert 			sz = mmapend - fr->mmapptr;
59679428148Smillert 			strend = memchr(fr->mmapptr, fr->elsymb, sz);
59779428148Smillert 
59879428148Smillert 			if (strend == NULL) {
59979428148Smillert 				ret = bwscsbdup(fr->mmapptr, sz);
60079428148Smillert 				fr->mmapptr = mmapend;
60179428148Smillert 			} else {
60279428148Smillert 				ret = bwscsbdup(fr->mmapptr, strend -
60379428148Smillert 				    fr->mmapptr);
60479428148Smillert 				fr->mmapptr = strend + 1;
60579428148Smillert 			}
60679428148Smillert 		}
60779428148Smillert 
60879428148Smillert 	} else if (fr->file != stdin) {
60979428148Smillert 		unsigned char *strend;
61079428148Smillert 		size_t bsz1, remsz, search_start;
61179428148Smillert 
61279428148Smillert 		search_start = 0;
61379428148Smillert 		remsz = 0;
61479428148Smillert 		strend = NULL;
61579428148Smillert 
61679428148Smillert 		if (fr->bsz > fr->strbeg)
61779428148Smillert 			remsz = fr->bsz - fr->strbeg;
61879428148Smillert 
61979428148Smillert 		/* line read cycle */
62079428148Smillert 		for (;;) {
62179428148Smillert 			if (remsz > search_start)
62279428148Smillert 				strend = memchr(fr->buffer + fr->strbeg +
62379428148Smillert 				    search_start, fr->elsymb, remsz -
62479428148Smillert 				    search_start);
62579428148Smillert 			else
62679428148Smillert 				strend = NULL;
62779428148Smillert 
62879428148Smillert 			if (strend)
62979428148Smillert 				break;
63079428148Smillert 			if (feof(fr->file))
63179428148Smillert 				break;
63279428148Smillert 
63379428148Smillert 			if (fr->bsz != fr->cbsz)
63479428148Smillert 				/* NOTREACHED */
63579428148Smillert 				err(2, "File read software error 1");
63679428148Smillert 
63779428148Smillert 			if (remsz > (READ_CHUNK >> 1)) {
63879428148Smillert 				search_start = fr->cbsz - fr->strbeg;
63979428148Smillert 				fr->cbsz += READ_CHUNK;
640de2b9554Smillert 				fr->buffer = sort_reallocarray(fr->buffer,
641de2b9554Smillert 				    1, fr->cbsz);
64279428148Smillert 				bsz1 = fread(fr->buffer + fr->bsz, 1,
64379428148Smillert 				    READ_CHUNK, fr->file);
64479428148Smillert 				if (bsz1 == 0) {
64579428148Smillert 					if (ferror(fr->file))
64679428148Smillert 						err(2, NULL);
64779428148Smillert 					break;
64879428148Smillert 				}
64979428148Smillert 				fr->bsz += bsz1;
65079428148Smillert 				remsz += bsz1;
65179428148Smillert 			} else {
652120aa463Smillert 				if (remsz > 0 && fr->strbeg > 0) {
653120aa463Smillert 					memmove(fr->buffer,
654120aa463Smillert 					    fr->buffer + fr->strbeg, remsz);
655120aa463Smillert 				}
65679428148Smillert 				fr->strbeg = 0;
65779428148Smillert 				search_start = remsz;
65879428148Smillert 				bsz1 = fread(fr->buffer + remsz, 1,
65979428148Smillert 				    fr->cbsz - remsz, fr->file);
66079428148Smillert 				if (bsz1 == 0) {
66179428148Smillert 					if (ferror(fr->file))
66279428148Smillert 						err(2, NULL);
66379428148Smillert 					break;
66479428148Smillert 				}
66579428148Smillert 				fr->bsz = remsz + bsz1;
66679428148Smillert 				remsz = fr->bsz;
66779428148Smillert 			}
66879428148Smillert 		}
66979428148Smillert 
67079428148Smillert 		if (strend == NULL)
67179428148Smillert 			strend = fr->buffer + fr->bsz;
67279428148Smillert 
67379428148Smillert 		if ((fr->buffer + fr->strbeg <= strend) &&
67479428148Smillert 		    (fr->strbeg < fr->bsz) && (remsz>0))
67579428148Smillert 			ret = bwscsbdup(fr->buffer + fr->strbeg, strend -
67679428148Smillert 			    fr->buffer - fr->strbeg);
67779428148Smillert 
67879428148Smillert 		fr->strbeg = (strend - fr->buffer) + 1;
67979428148Smillert 	} else {
68079428148Smillert 		size_t len = 0;
68179428148Smillert 
68279428148Smillert 		ret = bwsfgetln(fr->file, &len, sort_opts_vals.zflag,
68379428148Smillert 		    &(fr->rb));
68479428148Smillert 	}
68579428148Smillert 
68679428148Smillert 	return ret;
68779428148Smillert }
68879428148Smillert 
68979428148Smillert static void
69079428148Smillert file_reader_clean(struct file_reader *fr)
69179428148Smillert {
69279428148Smillert 	if (fr->mmapaddr)
69379428148Smillert 		munmap(fr->mmapaddr, fr->mmapsize);
69479428148Smillert 
69579428148Smillert 	sort_free(fr->buffer);
69679428148Smillert 
69779428148Smillert 	if (fr->file)
69879428148Smillert 		closefile(fr->file, fr->fname);
69979428148Smillert 
70079428148Smillert 	sort_free(fr->fname);
70179428148Smillert 
70279428148Smillert 	memset(fr, 0, sizeof(struct file_reader));
70379428148Smillert }
70479428148Smillert 
70579428148Smillert void
70679428148Smillert file_reader_free(struct file_reader *fr)
70779428148Smillert {
70879428148Smillert 	file_reader_clean(fr);
70979428148Smillert 	sort_free(fr);
71079428148Smillert }
71179428148Smillert 
71279428148Smillert int
71379428148Smillert procfile(const char *fsrc, struct sort_list *list, struct file_list *fl)
71479428148Smillert {
71579428148Smillert 	struct file_reader *fr;
71679428148Smillert 
71779428148Smillert 	fr = file_reader_init(fsrc);
71879428148Smillert 	if (fr == NULL)
71979428148Smillert 		err(2, "%s", fsrc);
72079428148Smillert 
72179428148Smillert 	/* file browse cycle */
72279428148Smillert 	for (;;) {
72379428148Smillert 		struct bwstring *bws;
72479428148Smillert 
72579428148Smillert 		bws = file_reader_readline(fr);
72679428148Smillert 
72779428148Smillert 		if (bws == NULL)
72879428148Smillert 			break;
72979428148Smillert 
73079428148Smillert 		sort_list_add(list, bws);
73179428148Smillert 
73279428148Smillert 		if (list->memsize >= available_free_memory) {
73379428148Smillert 			char *fn;
73479428148Smillert 
73579428148Smillert 			fn = new_tmp_file_name();
73679428148Smillert 			sort_list_to_file(list, fn);
73779428148Smillert 			file_list_add(fl, fn, false);
73879428148Smillert 			sort_list_clean(list);
73979428148Smillert 		}
74079428148Smillert 	}
74179428148Smillert 
74279428148Smillert 	file_reader_free(fr);
74379428148Smillert 
74479428148Smillert 	return 0;
74579428148Smillert }
74679428148Smillert 
74779428148Smillert /*
74879428148Smillert  * Compare file headers. Files with EOF always go to the end of the list.
74979428148Smillert  */
75079428148Smillert static int
75179428148Smillert file_header_cmp(struct file_header *f1, struct file_header *f2)
75279428148Smillert {
753120aa463Smillert 	int ret;
754120aa463Smillert 
75579428148Smillert 	if (f1 == f2)
75679428148Smillert 		return 0;
757120aa463Smillert 	if (f1->fr == NULL)
75879428148Smillert 		return (f2->fr == NULL) ? 0 : 1;
759120aa463Smillert 	if (f2->fr == NULL)
76079428148Smillert 		return -1;
76179428148Smillert 
76279428148Smillert 	ret = list_coll(&(f1->si), &(f2->si));
76379428148Smillert 	if (!ret)
76479428148Smillert 		return (f1->file_pos < f2->file_pos) ? -1 : 1;
76579428148Smillert 	return ret;
76679428148Smillert }
76779428148Smillert 
76879428148Smillert /*
76979428148Smillert  * Allocate and init file header structure
77079428148Smillert  */
77179428148Smillert static void
77279428148Smillert file_header_init(struct file_header **fh, const char *fn, size_t file_pos)
77379428148Smillert {
77479428148Smillert 	struct bwstring *line;
77579428148Smillert 
77679428148Smillert 	*fh = sort_malloc(sizeof(struct file_header));
77779428148Smillert 	(*fh)->file_pos = file_pos;
77879428148Smillert 	(*fh)->fr = file_reader_init(fn);
77979428148Smillert 	if ((*fh)->fr == NULL) {
78079428148Smillert 		err(2, "Cannot open %s for reading",
78179428148Smillert 		    strcmp(fn, "-") == 0 ? "stdin" : fn);
78279428148Smillert 	}
78379428148Smillert 	line = file_reader_readline((*fh)->fr);
78479428148Smillert 	if (line == NULL) {
78579428148Smillert 		file_reader_free((*fh)->fr);
78679428148Smillert 		(*fh)->fr = NULL;
78779428148Smillert 		(*fh)->si = NULL;
78879428148Smillert 	} else {
78979428148Smillert 		(*fh)->si = sort_list_item_alloc();
79079428148Smillert 		sort_list_item_set((*fh)->si, line);
79179428148Smillert 	}
79279428148Smillert }
79379428148Smillert 
79479428148Smillert /*
79579428148Smillert  * Close file
79679428148Smillert  */
79779428148Smillert static void
79879428148Smillert file_header_close(struct file_header **fh)
79979428148Smillert {
80079428148Smillert 	if ((*fh)->fr) {
80179428148Smillert 		file_reader_free((*fh)->fr);
80279428148Smillert 		(*fh)->fr = NULL;
80379428148Smillert 	}
80479428148Smillert 	if ((*fh)->si) {
80579428148Smillert 		sort_list_item_clean((*fh)->si);
80679428148Smillert 		sort_free((*fh)->si);
80779428148Smillert 		(*fh)->si = NULL;
80879428148Smillert 	}
80979428148Smillert 	sort_free(*fh);
81079428148Smillert 	*fh = NULL;
81179428148Smillert }
81279428148Smillert 
81379428148Smillert /*
81479428148Smillert  * Swap two array elements
81579428148Smillert  */
81679428148Smillert static void
81779428148Smillert file_header_swap(struct file_header **fh, size_t i1, size_t i2)
81879428148Smillert {
81979428148Smillert 	struct file_header *tmp;
82079428148Smillert 
82179428148Smillert 	tmp = fh[i1];
82279428148Smillert 	fh[i1] = fh[i2];
82379428148Smillert 	fh[i2] = tmp;
82479428148Smillert }
82579428148Smillert 
82679428148Smillert /* heap algorithm ==>> */
82779428148Smillert 
82879428148Smillert /*
82979428148Smillert  * See heap sort algorithm
83079428148Smillert  * "Raises" last element to its right place
83179428148Smillert  */
83279428148Smillert static void
83379428148Smillert file_header_heap_swim(struct file_header **fh, size_t indx)
83479428148Smillert {
83579428148Smillert 	if (indx > 0) {
83679428148Smillert 		size_t parent_index;
83779428148Smillert 
83879428148Smillert 		parent_index = (indx - 1) >> 1;
83979428148Smillert 
84079428148Smillert 		if (file_header_cmp(fh[indx], fh[parent_index]) < 0) {
84179428148Smillert 			/* swap child and parent and continue */
84279428148Smillert 			file_header_swap(fh, indx, parent_index);
84379428148Smillert 			file_header_heap_swim(fh, parent_index);
84479428148Smillert 		}
84579428148Smillert 	}
84679428148Smillert }
84779428148Smillert 
84879428148Smillert /*
84979428148Smillert  * Sink the top element to its correct position
85079428148Smillert  */
85179428148Smillert static void
85279428148Smillert file_header_heap_sink(struct file_header **fh, size_t indx, size_t size)
85379428148Smillert {
85479428148Smillert 	size_t left_child_index;
85579428148Smillert 	size_t right_child_index;
85679428148Smillert 
85779428148Smillert 	left_child_index = indx + indx + 1;
85879428148Smillert 	right_child_index = left_child_index + 1;
85979428148Smillert 
86079428148Smillert 	if (left_child_index < size) {
86179428148Smillert 		size_t min_child_index;
86279428148Smillert 
86379428148Smillert 		min_child_index = left_child_index;
86479428148Smillert 
86579428148Smillert 		if ((right_child_index < size) &&
86679428148Smillert 		    (file_header_cmp(fh[left_child_index],
86779428148Smillert 		    fh[right_child_index]) > 0))
86879428148Smillert 			min_child_index = right_child_index;
86979428148Smillert 		if (file_header_cmp(fh[indx], fh[min_child_index]) > 0) {
87079428148Smillert 			file_header_swap(fh, indx, min_child_index);
87179428148Smillert 			file_header_heap_sink(fh, min_child_index, size);
87279428148Smillert 		}
87379428148Smillert 	}
87479428148Smillert }
87579428148Smillert 
87679428148Smillert /* <<== heap algorithm */
87779428148Smillert 
87879428148Smillert /*
87979428148Smillert  * Adds element to the "left" end
88079428148Smillert  */
88179428148Smillert static void
88279428148Smillert file_header_list_rearrange_from_header(struct file_header **fh, size_t size)
88379428148Smillert {
88479428148Smillert 	file_header_heap_sink(fh, 0, size);
88579428148Smillert }
88679428148Smillert 
88779428148Smillert /*
88879428148Smillert  * Adds element to the "right" end
88979428148Smillert  */
89079428148Smillert static void
89179428148Smillert file_header_list_push(struct file_header *f, struct file_header **fh, size_t size)
89279428148Smillert {
89379428148Smillert 	fh[size++] = f;
89479428148Smillert 	file_header_heap_swim(fh, size - 1);
89579428148Smillert }
89679428148Smillert 
89779428148Smillert struct last_printed
89879428148Smillert {
89979428148Smillert 	struct bwstring *str;
90079428148Smillert };
90179428148Smillert 
90279428148Smillert /*
90379428148Smillert  * Prints the current line of the file
90479428148Smillert  */
90579428148Smillert static void
90679428148Smillert file_header_print(struct file_header *fh, FILE *f_out, struct last_printed *lp)
90779428148Smillert {
90879428148Smillert 	if (sort_opts_vals.uflag) {
90979428148Smillert 		if ((lp->str == NULL) || (str_list_coll(lp->str, &(fh->si)))) {
91079428148Smillert 			bwsfwrite(fh->si->str, f_out, sort_opts_vals.zflag);
91179428148Smillert 			if (lp->str)
91279428148Smillert 				bwsfree(lp->str);
91379428148Smillert 			lp->str = bwsdup(fh->si->str);
91479428148Smillert 		}
91579428148Smillert 	} else
91679428148Smillert 		bwsfwrite(fh->si->str, f_out, sort_opts_vals.zflag);
91779428148Smillert }
91879428148Smillert 
91979428148Smillert /*
92079428148Smillert  * Read next line
92179428148Smillert  */
92279428148Smillert static void
92379428148Smillert file_header_read_next(struct file_header *fh)
92479428148Smillert {
92579428148Smillert 	struct bwstring *tmp;
92679428148Smillert 
92779428148Smillert 	tmp = file_reader_readline(fh->fr);
92879428148Smillert 	if (tmp == NULL) {
92979428148Smillert 		file_reader_free(fh->fr);
93079428148Smillert 		fh->fr = NULL;
93179428148Smillert 		if (fh->si) {
93279428148Smillert 			sort_list_item_clean(fh->si);
93379428148Smillert 			sort_free(fh->si);
93479428148Smillert 			fh->si = NULL;
93579428148Smillert 		}
93679428148Smillert 	} else {
93779428148Smillert 		if (fh->si == NULL)
93879428148Smillert 			fh->si = sort_list_item_alloc();
93979428148Smillert 		sort_list_item_set(fh->si, tmp);
94079428148Smillert 	}
94179428148Smillert }
94279428148Smillert 
94379428148Smillert /*
94479428148Smillert  * Merge array of "files headers"
94579428148Smillert  */
94679428148Smillert static void
94779428148Smillert file_headers_merge(size_t fnum, struct file_header **fh, FILE *f_out)
94879428148Smillert {
94979428148Smillert 	struct last_printed lp;
95079428148Smillert 	size_t i;
95179428148Smillert 
95279428148Smillert 	memset(&lp, 0, sizeof(lp));
95379428148Smillert 
95479428148Smillert 	/*
95579428148Smillert 	 * construct the initial sort structure
95679428148Smillert 	 */
95779428148Smillert 	for (i = 0; i < fnum; i++)
95879428148Smillert 		file_header_list_push(fh[i], fh, i);
95979428148Smillert 
96079428148Smillert 	while (fh[0]->fr) { /* unfinished files are always in front */
96179428148Smillert 		/* output the smallest line: */
96279428148Smillert 		file_header_print(fh[0], f_out, &lp);
96379428148Smillert 		/* read a new line, if possible: */
96479428148Smillert 		file_header_read_next(fh[0]);
96579428148Smillert 		/* re-arrange the list: */
96679428148Smillert 		file_header_list_rearrange_from_header(fh, fnum);
96779428148Smillert 	}
96879428148Smillert 
96979428148Smillert 	if (lp.str)
97079428148Smillert 		bwsfree(lp.str);
97179428148Smillert }
97279428148Smillert 
97379428148Smillert /*
97479428148Smillert  * Merges the given files into the output file, which can be
97579428148Smillert  * stdout.
97679428148Smillert  */
97779428148Smillert static void
97879428148Smillert merge_files_array(size_t argc, char **argv, const char *fn_out)
97979428148Smillert {
98079428148Smillert 	struct file_header **fh;
98179428148Smillert 	FILE *f_out;
98279428148Smillert 	size_t i;
98379428148Smillert 
98479428148Smillert 	f_out = openfile(fn_out, "w");
98579428148Smillert 
98679428148Smillert 	if (f_out == NULL)
98779428148Smillert 		err(2, "%s", fn_out);
98879428148Smillert 
989120aa463Smillert 	fh = sort_reallocarray(NULL, argc + 1, sizeof(struct file_header *));
99079428148Smillert 
99179428148Smillert 	for (i = 0; i < argc; i++)
992120aa463Smillert 		file_header_init(fh + i, argv[i], i);
99379428148Smillert 
99479428148Smillert 	file_headers_merge(argc, fh, f_out);
99579428148Smillert 
99679428148Smillert 	for (i = 0; i < argc; i++)
99779428148Smillert 		file_header_close(fh + i);
99879428148Smillert 
99979428148Smillert 	sort_free(fh);
100079428148Smillert 
100179428148Smillert 	closefile(f_out, fn_out);
100279428148Smillert }
100379428148Smillert 
100479428148Smillert /*
100579428148Smillert  * Shrinks the file list until its size smaller than max number of opened files
100679428148Smillert  */
100779428148Smillert static int
100879428148Smillert shrink_file_list(struct file_list *fl)
100979428148Smillert {
101079428148Smillert 	struct file_list new_fl;
101179428148Smillert 	size_t indx = 0;
101279428148Smillert 
1013120aa463Smillert 	if (fl->count < max_open_files)
1014120aa463Smillert 		return 0;
1015120aa463Smillert 
101679428148Smillert 	file_list_init(&new_fl, true);
101779428148Smillert 	while (indx < fl->count) {
101879428148Smillert 		char *fnew;
101979428148Smillert 		size_t num;
102079428148Smillert 
102179428148Smillert 		num = fl->count - indx;
102279428148Smillert 		fnew = new_tmp_file_name();
102379428148Smillert 
1024120aa463Smillert 		if (num >= max_open_files)
102579428148Smillert 			num = max_open_files - 1;
102679428148Smillert 		merge_files_array(num, fl->fns + indx, fnew);
102779428148Smillert 		if (fl->tmp) {
102879428148Smillert 			size_t i;
102979428148Smillert 
103079428148Smillert 			for (i = 0; i < num; i++)
103179428148Smillert 				unlink(fl->fns[indx + i]);
103279428148Smillert 		}
103379428148Smillert 		file_list_add(&new_fl, fnew, false);
103479428148Smillert 		indx += num;
103579428148Smillert 	}
103679428148Smillert 	fl->tmp = false; /* already taken care of */
103779428148Smillert 	file_list_clean(fl);
103879428148Smillert 
103979428148Smillert 	fl->count = new_fl.count;
104079428148Smillert 	fl->fns = new_fl.fns;
104179428148Smillert 	fl->sz = new_fl.sz;
104279428148Smillert 	fl->tmp = new_fl.tmp;
104379428148Smillert 
104479428148Smillert 	return 1;
104579428148Smillert }
104679428148Smillert 
104779428148Smillert /*
104879428148Smillert  * Merge list of files
104979428148Smillert  */
105079428148Smillert void
105179428148Smillert merge_files(struct file_list *fl, const char *fn_out)
105279428148Smillert {
1053120aa463Smillert 	while (shrink_file_list(fl))
1054120aa463Smillert 		;
105579428148Smillert 
105679428148Smillert 	merge_files_array(fl->count, fl->fns, fn_out);
105779428148Smillert }
105879428148Smillert 
105979428148Smillert static const char *
106079428148Smillert get_sort_method_name(int sm)
106179428148Smillert {
106279428148Smillert 	if (sm == SORT_MERGESORT)
106379428148Smillert 		return "mergesort";
106479428148Smillert 	else if (sort_opts_vals.sort_method == SORT_RADIXSORT)
106579428148Smillert 		return "radixsort";
106679428148Smillert 	else if (sort_opts_vals.sort_method == SORT_HEAPSORT)
106779428148Smillert 		return "heapsort";
106879428148Smillert 	else
106979428148Smillert 		return "quicksort";
107079428148Smillert }
107179428148Smillert 
107279428148Smillert /*
107379428148Smillert  * Sort list of lines and writes it to the file
107479428148Smillert  */
107579428148Smillert void
107679428148Smillert sort_list_to_file(struct sort_list *list, const char *outfile)
107779428148Smillert {
107879428148Smillert 	struct sort_mods *sm = &(keys[0].sm);
107979428148Smillert 
108079428148Smillert 	if (!sm->Mflag && !sm->Rflag && !sm->Vflag &&
108179428148Smillert 	    !sm->gflag && !sm->hflag && !sm->nflag) {
108252e4174eSschwarze 		if (sort_opts_vals.sort_method == SORT_DEFAULT &&
108352e4174eSschwarze 		    sort_mb_cur_max == 1)
108479428148Smillert 			sort_opts_vals.sort_method = SORT_RADIXSORT;
108579428148Smillert 
108679428148Smillert 	} else if (sort_opts_vals.sort_method == SORT_RADIXSORT)
108779428148Smillert 		err(2, "Radix sort cannot be used with these sort options");
108879428148Smillert 
108979428148Smillert 	/*
1090120aa463Smillert 	 * To handle stable sort and the unique cases in the
1091120aa463Smillert 	 * right order, we need to use a stable algorithm.
109279428148Smillert 	 */
109379428148Smillert 	if (sort_opts_vals.sflag) {
109479428148Smillert 		switch (sort_opts_vals.sort_method){
109579428148Smillert 		case SORT_MERGESORT:
109679428148Smillert 			break;
109779428148Smillert 		case SORT_RADIXSORT:
109879428148Smillert 			break;
109979428148Smillert 		case SORT_DEFAULT:
110079428148Smillert 			sort_opts_vals.sort_method = SORT_MERGESORT;
110179428148Smillert 			break;
110279428148Smillert 		default:
110379428148Smillert 			errx(2, "The chosen sort method cannot be used with "
110479428148Smillert 			    "stable and/or unique sort");
1105*479c151dSjsg 		}
110679428148Smillert 	}
110779428148Smillert 
110879428148Smillert 	if (sort_opts_vals.sort_method == SORT_DEFAULT)
110979428148Smillert 		sort_opts_vals.sort_method = DEFAULT_SORT_ALGORITHM;
111079428148Smillert 
111179428148Smillert 	if (debug_sort)
111279428148Smillert 		printf("sort_method=%s\n",
111379428148Smillert 		    get_sort_method_name(sort_opts_vals.sort_method));
111479428148Smillert 
111579428148Smillert 	switch (sort_opts_vals.sort_method){
111679428148Smillert 	case SORT_RADIXSORT:
111779428148Smillert 		rxsort(list->list, list->count);
111879428148Smillert 		break;
111979428148Smillert 	case SORT_MERGESORT:
112079428148Smillert 		mergesort(list->list, list->count,
112179428148Smillert 		    sizeof(struct sort_list_item *), list_coll);
112279428148Smillert 		break;
112379428148Smillert 	case SORT_HEAPSORT:
112479428148Smillert 		heapsort(list->list, list->count,
112579428148Smillert 		    sizeof(struct sort_list_item *), list_coll);
112679428148Smillert 		break;
112779428148Smillert 	case SORT_QSORT:
112879428148Smillert 		qsort(list->list, list->count,
112979428148Smillert 		    sizeof(struct sort_list_item *), list_coll);
113079428148Smillert 		break;
113179428148Smillert 	default:
113279428148Smillert 		DEFAULT_SORT_FUNC(list->list, list->count,
113379428148Smillert 		    sizeof(struct sort_list_item *), list_coll);
113479428148Smillert 		break;
113579428148Smillert 	}
113679428148Smillert 	sort_list_dump(list, outfile);
113779428148Smillert }
1138