aboutsummaryrefslogtreecommitdiffstats
path: root/mongotimer.c
diff options
context:
space:
mode:
Diffstat (limited to 'mongotimer.c')
-rw-r--r--mongotimer.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/mongotimer.c b/mongotimer.c
new file mode 100644
index 0000000..692d7a2
--- /dev/null
+++ b/mongotimer.c
@@ -0,0 +1,342 @@
+/* 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();
+}