1#include <err.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <signal.h>
6#include <unistd.h>
7
8#include <sys/types.h>
9
10static char **lines;
11static size_t nlines;
12static volatile sig_atomic_t sortdone;
13
14enum {
15 LINESTEP = 16,
16};
17
18static int
19compar(const void *s1, const void *s2)
20{
21 unsigned long long u1, u2;
22
23 u1 = strtoull(*(char**)s1, NULL, 10);
24 u2 = strtoull(*(char**)s2, NULL, 10);
25
26 if (u1 > u2) {
27 return 1;
28 } else if (u1 < u2) {
29 return -1;
30 } else {
31 return 0;
32 }
33}
34
35static void
36sortprint(void)
37{
38 size_t i;
39
40 qsort(lines, nlines, sizeof(char*), compar);
41 for (i = 0; i < nlines; i++)
42 printf("%s", lines[i]);
43 fflush(stdout);
44
45 for (i = 0; i < nlines; i++)
46 free(lines[i]);
47 if (lines)
48 free(lines);
49 lines = NULL;
50 nlines = 0;
51}
52
53static void
54sigalarm(int num)
55{
56 (void)num;
57 sortdone = 1;
58 sortprint();
59}
60
61static void
62bufferline(char *line)
63{
64 size_t newsiz;
65
66 if (nlines && nlines % LINESTEP == 0) {
67 newsiz = (nlines + LINESTEP) * sizeof(char*);
68 if (!(lines = realloc(lines, newsiz)))
69 err(EXIT_FAILURE, "realloc failed");
70 }
71
72 if (!(lines[nlines++] = strdup(line)))
73 err(EXIT_FAILURE, "strdup failed");
74}
75
76static void
77sethandler(void)
78{
79 struct sigaction act;
80
81 act.sa_flags = SA_RESTART;
82 act.sa_handler = sigalarm;
83 if (sigemptyset(&act.sa_mask) == -1)
84 err(EXIT_FAILURE, "sigemptyset failed");
85 if (sigaction(SIGALRM, &act, NULL))
86 err(EXIT_FAILURE, "sigaction failed");
87}
88
89static void
90inloop(sigset_t *blockset)
91{
92 static char *line;
93 static size_t llen;
94
95 while (getline(&line, &llen, stdin) != -1) {
96 if (sortdone) { // Just behave like tee(1) from now on.
97 printf("%s", line);
98 fflush(stdout);
99 continue;
100 }
101
102 if (sigprocmask(SIG_BLOCK, blockset, NULL))
103 err(EXIT_FAILURE, "signal blocking failed");
104 bufferline(line);
105 if (sigprocmask(SIG_UNBLOCK, blockset, NULL))
106 err(EXIT_FAILURE, "signal unblocking failed");
107 }
108 if (ferror(stdin))
109 err(EXIT_FAILURE, "ferror failed");
110}
111
112int
113main(int argc, char **argv)
114{
115 unsigned int delay;
116 sigset_t blockset;
117
118#ifdef __OpenBSD__
119 if (pledge("stdio", NULL) == -1)
120 err(EXIT_FAILURE, "pledge failed");
121#endif
122
123 if (argc <= 1) {
124 fprintf(stderr, "USAGE: %s DELAY\n", argv[0]);
125 return EXIT_FAILURE;
126 }
127
128 if (!(delay = strtoul(argv[1], NULL, 10)))
129 errx(EXIT_FAILURE, "delay must be a uint greater zero");
130 if (!(lines = malloc(LINESTEP * sizeof(char*))))
131 err(EXIT_FAILURE, "malloc failed");
132
133 sethandler();
134 if (sigemptyset(&blockset) == -1)
135 err(EXIT_FAILURE, "sigemptyset failed");
136 sigaddset(&blockset, SIGALRM);
137 alarm(delay);
138
139 inloop(&blockset);
140 alarm(0); // Cancel alarm to prevent sortprint()-race
141 sortprint();
142
143 return EXIT_SUCCESS;
144}