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