aboutsummaryrefslogblamecommitdiffstats
path: root/mongotimer.c
blob: 692d7a216b2cb2f0f0dbb2ba958cb8da170f84fb (plain) (tree)





















































































































































































































































































































































                                                                                            
/* See LICENSE file for copyright and license details. */
#include <sys/ioctl.h>
#include <sys/timerfd.h>
#include <errno.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "arg.h"

#define DX 16
#define DY 12
#define DC 16
#define DM 16

#include "mongo_0.h"
#include "mongo_1.h"
#include "mongo_2.h"
#include "mongo_3.h"
#include "mongo_4.h"
#include "mongo_5.h"
#include "mongo_6.h"
#include "mongo_7.h"
#include "mongo_8.h"
#include "mongo_9.h"
#include "mongo_c.h"
#include "mongo_m.h"

static const char **mongo_ds[] = {
	mongo_0, mongo_1, mongo_2, mongo_3, mongo_4,
	mongo_5, mongo_6, mongo_7, mongo_8, mongo_9
};

static volatile sig_atomic_t caught_sigterm = 0;
static volatile sig_atomic_t caught_sigwinch = 1;

char *argv0;

static void
usage(void)
{
	fprintf(stderr, "usage: %s [[-e] [[hh]:mm:]ss]\n", argv0);
	exit(1);
}

static void
sigterm(int signo)
{
	caught_sigterm = 1;
	(void) signo;
}

static void
sigwinch(int signo)
{
	caught_sigwinch = 1;
	(void) signo;
}

static void
print_time(const char ***str, size_t y, size_t x)
{
	size_t r, c;

	fprintf(stdout, "\033[%zu;1H\033[1J", y + 1);

	for (r = 0; r < DY; r++) {
		fprintf(stdout, "\033[%zu;%zuH\033[1K", y + r + 1, x + 1);
		for (c = 0; str[c]; c++)
			fprintf(stdout, "%s", str[c][r]);
		fprintf(stdout, "\033[0K");
	}

	fprintf(stdout, "\033[0J");
	fflush(stdout);
}

static int
display_stopwatch(int timerfd)
{
	uint64_t overrun, total_overrun = 0;
	struct winsize winsize;
	size_t x = 0, y = 0, width, i;
	const char **digits[23];
	uint64_t h, m, s, h_div = 1, th;
	size_t h_digits = 0;
	int small = 0;

	/* TODO pause (yellow text) on <space> */

	while (!caught_sigterm) {
		if (caught_sigwinch) {
			if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) {
				if (errno == EINTR)
					continue;
				goto fail;
			}
			caught_sigwinch = 0;
		recenter:
			y = winsize.ws_row;
			x = winsize.ws_col;
			width = 4 * DX + DC;
			if (h_digits) {
				width += DC;
				width += h_digits * DX;
			}
			if      (y < DY)    small = 1;
			else if (x < width) small = 1;
			else                small = 0;
			y -= DY;
			x -= width;
			y /= 2;
			x /= 2;
		}

		if (small) {
			fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small");
			fflush(stdout);
			pause();
			continue;
		}

		s = total_overrun % 60;
		m = total_overrun / 60 % 60;
		h = total_overrun / 60 / 60;
		if (h / h_div) {
			h_div *= 10;
			h_digits++;
			goto recenter;
		}

		i = 0;
		if (h_digits) {
			for (th = h; th; th /= 10)
				digits[h_digits - ++i] = mongo_ds[th % 10];
			digits[i++] = mongo_c;
		}
		digits[i++] = mongo_ds[m / 10];
		digits[i++] = mongo_ds[m % 10];
		digits[i++] = mongo_c;
		digits[i++] = mongo_ds[s / 10];
		digits[i++] = mongo_ds[s % 10];
		digits[i++] = NULL;

		print_time(digits, y, x);

		if (read(timerfd, &overrun, sizeof(overrun)) < 0) {
			if (errno != EINTR)
				goto fail;
		} else {
			total_overrun += overrun;
		}
	}

	return 0;

fail:
	return -1;
}

static int
display_timer(int timerfd, int64_t time, int exit_on_zero)
{
	uint64_t overrun, abstime;
	struct winsize winsize;
	size_t x = 0, y = 0, width;
	const char **digits[24];
	uint64_t h, m, s, th, h_div;
	size_t h_digits, i;
	int small = 0;

	/* TODO pause (yellow text) on <space> */

	while (!caught_sigterm) {
		if (caught_sigwinch) {
			if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) {
				if (errno == EINTR)
					continue;
				goto fail;
			}
			caught_sigwinch = 0;
		}

		abstime = time < 0 ? (uint64_t)-time : (uint64_t)time;
		s = abstime % 60;
		m = abstime / 60 % 60;
		h = abstime / 60 / 60;

		y = winsize.ws_row;
		x = winsize.ws_col;
		for (h_div = 1, h_digits = 0; h / h_div; h_div *= 10, h_digits += 1);
		width = 4 * DX + DC;
		if (time < 0)
			width += DM;
		if (h_digits)
			width += h_digits * DX + DC;
		if      (y < DY)    small = 1;
		else if (x < width) small = 1;
		else                small = 0;
		y -= DY;
		x -= width;
		y /= 2;
		x /= 2;

		if (small) {
			fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small");
			fflush(stdout);
			pause();
			continue;
		}

		i = 0;
		if (time < 0)
			digits[i++] = mongo_m;
		if (h_digits) {
			for (th = h; th; th /= 10)
				digits[h_digits - ++i] = mongo_ds[th % 10];
			digits[i++] = mongo_c;
		}
		digits[i++] = mongo_ds[m / 10];
		digits[i++] = mongo_ds[m % 10];
		digits[i++] = mongo_c;
		digits[i++] = mongo_ds[s / 10];
		digits[i++] = mongo_ds[s % 10];
		digits[i++] = NULL;

		if (time >= 0) {
			print_time(digits, y, x);
		} else {
			fprintf(stdout, "\033[31m\n");
			print_time(digits, y, x);
			fprintf(stdout, "\033[39m\n");
		}

		if (read(timerfd, &overrun, sizeof(overrun)) < 0) {
			if (errno != EINTR)
				goto fail;
		} else {
			time -= (int64_t)overrun;
			if (time <= 0 && exit_on_zero)
				break;
		}
	}

	if (caught_sigterm && exit_on_zero)
		return -1;

	return 0;

fail:
	return -1;
}

int
main(int argc, char *argv[])
{
	int timerfd = -1;
	int exit_on_zero = 0;
	struct itimerspec itimerspec;
	struct sigaction sigact;
	int64_t time = 0, t = 0;
	size_t colons = 0;
	char *s;

	ARGBEGIN {
	case 'e':
		exit_on_zero = 1;
		break;
	default:
		usage();
	} ARGEND;

	if (argc > 1 || (exit_on_zero && !argc))
		usage();

	fprintf(stdout, "\033[?1049h\033[?25l");

	itimerspec.it_interval.tv_sec = 1;
	itimerspec.it_interval.tv_nsec = 0;
	itimerspec.it_value.tv_sec = 1;
	itimerspec.it_value.tv_nsec = 0;
	timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
	if (timerfd < 0)
		goto fail;
	if (timerfd_settime(timerfd, 0, &itimerspec, NULL))
		goto fail;

	memset(&sigact, 0, sizeof(sigact));

	sigact.sa_handler = sigterm;
	sigaction(SIGTERM, &sigact, NULL);
	sigaction(SIGQUIT, &sigact, NULL);
	sigaction(SIGINT, &sigact, NULL);

	sigact.sa_handler = sigwinch;
	sigaction(SIGWINCH, &sigact, NULL);

	if (argc) {
		for (s = argv[0]; *s; s++) {
			if ('0' <= *s && *s <= '9') {
				t = t * 10 + (*s & 15);
			} else if (*s == ':' && colons++ < 2) {
				if (s == argv[0] || s[-1] == ':')
					goto fail_usage;
				time = (time + t) * 60;
				t = 0;
			} else {
				goto fail_usage;
			}
		}
		if (s == argv[0] || s[-1] == ':')
			goto fail_usage;
		time += t;
		if (display_timer(timerfd, time, exit_on_zero))
			goto fail;
	} else {
		if (display_stopwatch(timerfd))
			goto fail;
	}

	fprintf(stdout, "\033[?25h\n\033[?1049l");
	fflush(stdout);
	close(timerfd);
	return 0;

fail:
	perror(argv0 ? argv0 : "mongotimer");
	fprintf(stdout, "\033[?25h\n\033[?1049l");
	fflush(stdout);
	if (timerfd >= 0)
		close(timerfd);
	return 1;

fail_usage:
	fprintf(stdout, "\033[?25h\n\033[?1049l");
	fflush(stdout);
	usage();
}