1#include <err.h>2#include <stdio.h>3#include <stdlib.h>4#include <string.h>5#include <signal.h>6#include <unistd.h>78#include <sys/types.h>910static char **lines;11static size_t nlines;12static volatile sig_atomic_t sortdone;1314enum {15 LINESTEP = 16,16};1718static int19compar(const void *s1, const void *s2)20{21 unsigned long long u1, u2;2223 u1 = strtoull(*(char**)s1, NULL, 10);24 u2 = strtoull(*(char**)s2, NULL, 10);2526 if (u1 > u2) {27 return 1;28 } else if (u1 < u2) {29 return -1;30 } else {31 return 0;32 }33}3435static void36sortprint(void)37{38 size_t i;3940 qsort(lines, nlines, sizeof(char*), compar);41 for (i = 0; i < nlines; i++)42 printf("%s", lines[i]);43 fflush(stdout);4445 for (i = 0; i < nlines; i++)46 free(lines[i]);47 if (lines)48 free(lines);49 lines = NULL;50 nlines = 0;51}5253static void54sigalarm(int num)55{56 (void)num;57 sortdone = 1;58 sortprint();59}6061static void62bufferline(char *line)63{64 size_t newsiz;6566 if (nlines && nlines % LINESTEP == 0) {67 newsiz = (nlines + LINESTEP) * sizeof(char*);68 if (!(lines = realloc(lines, newsiz)))69 err(EXIT_FAILURE, "realloc failed");70 }7172 if (!(lines[nlines++] = strdup(line)))73 err(EXIT_FAILURE, "strdup failed");74}7576static void77sethandler(void)78{79 struct sigaction act;8081 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}8889static void90inloop(sigset_t *blockset)91{92 static char *line;93 static size_t llen;9495 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 }101102 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}111112int113main(int argc, char **argv)114{115 unsigned int delay;116 sigset_t blockset;117118#ifdef __OpenBSD__119 if (pledge("stdio", NULL) == -1)120 err(EXIT_FAILURE, "pledge failed");121#endif122123 if (argc <= 1) {124 fprintf(stderr, "USAGE: %s DELAY\n", argv[0]);125 return EXIT_FAILURE;126 }127128 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");132133 sethandler();134 if (sigemptyset(&blockset) == -1)135 err(EXIT_FAILURE, "sigemptyset failed");136 sigaddset(&blockset, SIGALRM);137 alarm(delay);138139 inloop(&blockset);140 alarm(0); // Cancel alarm to prevent sortprint()-race141 sortprint();142143 return EXIT_SUCCESS;144}