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