insomnia

A frontend for the hii IRC client

git clone https://git.8pit.net/insomnia.git

  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}