1 1.3 riastrad /* $NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $ */ 2 1.1 riastrad 3 1.1 riastrad /*- 4 1.1 riastrad * Copyright (c) 2025 The NetBSD Foundation, Inc. 5 1.1 riastrad * All rights reserved. 6 1.1 riastrad * 7 1.1 riastrad * Redistribution and use in source and binary forms, with or without 8 1.1 riastrad * modification, are permitted provided that the following conditions 9 1.1 riastrad * are met: 10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright 11 1.1 riastrad * notice, this list of conditions and the following disclaimer. 12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the 14 1.1 riastrad * documentation and/or other materials provided with the distribution. 15 1.1 riastrad * 16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE. 27 1.1 riastrad */ 28 1.1 riastrad 29 1.1 riastrad #include <sys/cdefs.h> 30 1.3 riastrad __RCSID("$NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $"); 31 1.1 riastrad 32 1.2 riastrad #include <assert.h> 33 1.1 riastrad #include <err.h> 34 1.2 riastrad #include <getopt.h> 35 1.1 riastrad #include <stdio.h> 36 1.1 riastrad #include <stdlib.h> 37 1.1 riastrad #include <string.h> 38 1.1 riastrad #include <unistd.h> 39 1.1 riastrad 40 1.1 riastrad static void 41 1.1 riastrad heapsort_r_wrapper(void *a, size_t n, size_t sz, 42 1.1 riastrad int (*cmp)(const void *, const void *, void *), void *cookie) 43 1.1 riastrad { 44 1.1 riastrad 45 1.1 riastrad if (heapsort_r(a, n, sz, cmp, cookie) == -1) 46 1.1 riastrad err(1, "heapsort_r"); 47 1.1 riastrad } 48 1.1 riastrad 49 1.1 riastrad static void 50 1.1 riastrad mergesort_r_wrapper(void *a, size_t n, size_t sz, 51 1.1 riastrad int (*cmp)(const void *, const void *, void *), void *cookie) 52 1.1 riastrad { 53 1.1 riastrad 54 1.1 riastrad if (mergesort_r(a, n, sz, cmp, cookie) == -1) 55 1.1 riastrad err(1, "mergesort_r"); 56 1.1 riastrad } 57 1.1 riastrad 58 1.2 riastrad struct context { 59 1.2 riastrad const char *buf; 60 1.2 riastrad const size_t *linepos; 61 1.2 riastrad }; 62 1.2 riastrad 63 1.1 riastrad static int 64 1.1 riastrad cmp(const void *va, const void *vb, void *cookie) 65 1.1 riastrad { 66 1.2 riastrad const struct context *C = cookie; 67 1.1 riastrad const size_t *a = va; 68 1.1 riastrad const size_t *b = vb; 69 1.1 riastrad 70 1.2 riastrad return strcmp(C->buf + C->linepos[*a], C->buf + C->linepos[*b]); 71 1.2 riastrad } 72 1.2 riastrad 73 1.2 riastrad static void __dead 74 1.2 riastrad usage(void) 75 1.2 riastrad { 76 1.2 riastrad 77 1.2 riastrad fprintf(stderr, "Usage: %s [-n] <sortfn>\n", getprogname()); 78 1.2 riastrad exit(1); 79 1.1 riastrad } 80 1.1 riastrad 81 1.1 riastrad int 82 1.1 riastrad main(int argc, char **argv) 83 1.1 riastrad { 84 1.2 riastrad int ch; 85 1.2 riastrad int nflag = 0; 86 1.1 riastrad void (*sortfn)(void *, size_t, size_t, 87 1.1 riastrad int (*)(const void *, const void *, void *), void *); 88 1.1 riastrad char *buf = NULL; 89 1.1 riastrad size_t nbuf; 90 1.2 riastrad size_t *linepos = NULL; 91 1.1 riastrad size_t nlines; 92 1.2 riastrad size_t *permutation = NULL; 93 1.1 riastrad size_t off; 94 1.1 riastrad ssize_t nread; 95 1.1 riastrad char *p; 96 1.1 riastrad size_t i; 97 1.1 riastrad int error; 98 1.1 riastrad 99 1.1 riastrad /* 100 1.1 riastrad * Parse arguments. 101 1.1 riastrad */ 102 1.1 riastrad setprogname(argv[0]); 103 1.2 riastrad while ((ch = getopt(argc, argv, "hn")) != -1) { 104 1.2 riastrad switch (ch) { 105 1.2 riastrad case 'n': 106 1.2 riastrad nflag = 1; 107 1.2 riastrad break; 108 1.2 riastrad case '?': 109 1.2 riastrad case 'h': 110 1.2 riastrad default: 111 1.2 riastrad usage(); 112 1.2 riastrad } 113 1.2 riastrad } 114 1.2 riastrad argc -= optind; 115 1.2 riastrad argv += optind; 116 1.2 riastrad if (argc != 1) 117 1.2 riastrad usage(); 118 1.2 riastrad if (strcmp(argv[0], "heapsort_r") == 0) 119 1.1 riastrad sortfn = &heapsort_r_wrapper; 120 1.2 riastrad else if (strcmp(argv[0], "mergesort_r") == 0) 121 1.1 riastrad sortfn = &mergesort_r_wrapper; 122 1.2 riastrad else if (strcmp(argv[0], "qsort_r") == 0) 123 1.1 riastrad sortfn = &qsort_r; 124 1.1 riastrad else 125 1.2 riastrad errx(1, "unknown sort: %s", argv[0]); 126 1.1 riastrad 127 1.1 riastrad /* 128 1.1 riastrad * Allocate an initial 4K buffer. 129 1.1 riastrad */ 130 1.1 riastrad nbuf = 0x1000; 131 1.1 riastrad error = reallocarr(&buf, nbuf, 1); 132 1.1 riastrad if (error) 133 1.2 riastrad errc(1, error, "reallocarr"); 134 1.1 riastrad 135 1.1 riastrad /* 136 1.1 riastrad * Read the input into a contiguous buffer. Reject input with 137 1.1 riastrad * embedded NULs so we can use strcmp(3) to compare lines. 138 1.1 riastrad */ 139 1.1 riastrad off = 0; 140 1.3 riastrad while ((nread = read(STDIN_FILENO, buf + off, nbuf - off - 1)) != 0) { 141 1.1 riastrad if (nread == -1) 142 1.1 riastrad err(1, "read"); 143 1.3 riastrad if ((size_t)nread > nbuf - off - 1) 144 1.1 riastrad errx(1, "overlong read: %zu", (size_t)nread); 145 1.1 riastrad if (memchr(buf + off, '\0', (size_t)nread) != NULL) 146 1.1 riastrad errx(1, "NUL byte in input"); 147 1.1 riastrad off += (size_t)nread; 148 1.1 riastrad 149 1.1 riastrad /* 150 1.1 riastrad * If we filled the buffer, reallocate it with double 151 1.1 riastrad * the size. Bail if that would overflow. 152 1.1 riastrad */ 153 1.3 riastrad if (nbuf - off == 1) { 154 1.1 riastrad if (nbuf > SIZE_MAX/2) 155 1.1 riastrad errx(1, "input overflow"); 156 1.1 riastrad nbuf *= 2; 157 1.1 riastrad error = reallocarr(&buf, nbuf, 1); 158 1.1 riastrad if (error) 159 1.2 riastrad errc(1, error, "reallocarr"); 160 1.1 riastrad } 161 1.1 riastrad } 162 1.1 riastrad 163 1.1 riastrad /* 164 1.1 riastrad * If the input was empty, nothing to do. 165 1.1 riastrad */ 166 1.1 riastrad if (off == 0) 167 1.1 riastrad return 0; 168 1.1 riastrad 169 1.1 riastrad /* 170 1.1 riastrad * NUL-terminate the input and count the lines. The last line 171 1.1 riastrad * may have no trailing \n. 172 1.1 riastrad */ 173 1.1 riastrad buf[off] = '\0'; 174 1.1 riastrad nlines = 1; 175 1.1 riastrad for (p = buf; (p = strchr(p, '\n')) != NULL;) { 176 1.1 riastrad if (*++p == '\0') 177 1.1 riastrad break; 178 1.1 riastrad nlines++; 179 1.1 riastrad } 180 1.1 riastrad 181 1.1 riastrad /* 182 1.2 riastrad * Create an array of line positions to sort. NUL-terminate 183 1.2 riastrad * each line so we can use strcmp(3). 184 1.1 riastrad */ 185 1.2 riastrad error = reallocarr(&linepos, nlines, sizeof(linepos[0])); 186 1.2 riastrad if (error) 187 1.2 riastrad errc(1, error, "reallocarr"); 188 1.1 riastrad i = 0; 189 1.2 riastrad for (p = buf; linepos[i++] = p - buf, (p = strchr(p, '\n')) != NULL;) { 190 1.1 riastrad *p = '\0'; 191 1.1 riastrad if (*++p == '\0') 192 1.1 riastrad break; 193 1.1 riastrad } 194 1.2 riastrad assert(i == nlines); 195 1.2 riastrad 196 1.2 riastrad /* 197 1.2 riastrad * Create an array of permuted line numbers. 198 1.2 riastrad */ 199 1.2 riastrad error = reallocarr(&permutation, nlines, sizeof(permutation[0])); 200 1.2 riastrad if (error) 201 1.2 riastrad errc(1, error, "reallocarr"); 202 1.2 riastrad for (i = 0; i < nlines; i++) 203 1.2 riastrad permutation[i] = i; 204 1.1 riastrad 205 1.1 riastrad /* 206 1.2 riastrad * Sort the lines via comparison function that consults the 207 1.2 riastrad * buffer as a cookie. 208 1.1 riastrad */ 209 1.2 riastrad (*sortfn)(permutation, nlines, sizeof(permutation[0]), &cmp, 210 1.2 riastrad &(struct context) { .buf = buf, .linepos = linepos }); 211 1.1 riastrad 212 1.1 riastrad /* 213 1.2 riastrad * Print the lines in sorted order with the original line 214 1.2 riastrad * numbers. 215 1.1 riastrad */ 216 1.2 riastrad for (i = 0; i < nlines; i++) { 217 1.2 riastrad const size_t j = permutation[i]; 218 1.2 riastrad if (nflag) 219 1.2 riastrad printf("%zu %s\n", j, buf + linepos[j]); 220 1.2 riastrad else 221 1.2 riastrad printf("%s\n", buf + linepos[j]); 222 1.2 riastrad } 223 1.1 riastrad fflush(stdout); 224 1.1 riastrad return ferror(stdout); 225 1.1 riastrad } 226