/* See LICENSE file for copyright and license details. */ #include "common.h" USAGE("[-2]"); static volatile sig_atomic_t caught_sigterm = 0; static volatile sig_atomic_t caught_sigio = 0; static struct itimerspec orig_time; static int quadsize = 0; static void sigterm(int signo) { caught_sigterm = 1; (void) signo; } static void sigio(int signo) { caught_sigio = 1; (void) signo; } static int display_stopwatch(int timerfd) { uint64_t overrun = 0; uintmax_t total_overrun = 0, h, m, s, d, first_overrun = 0; int paused = 0, have_first = 0; struct itimerspec old_time, zero_time; size_t lap = 1; ssize_t r; char c; memset(&zero_time, 0, sizeof(zero_time)); while (!caught_sigterm) { if (caught_sigio) { caught_sigio = 0; for (;;) { r = read(STDIN_FILENO, &c, 1); if (r <= 0) { if (!r) goto out; if (errno == EAGAIN) break; if (errno == EINTR) continue; goto fail; } if (c == 'q') { goto out; } else if (c == ' ' || c == 'p') { paused ^= 1; if (paused) { if (timerfd_settime(timerfd, 0, &zero_time, &old_time)) goto fail; } else { if (timerfd_settime(timerfd, 0, &old_time, NULL)) goto fail; } } else if (c == '\n') { if (timerfd_settime(timerfd, 0, &orig_time, NULL)) goto fail; d = total_overrun % 10; s = total_overrun / 10 % 60; m = total_overrun / 10 / 60 % 60; h = total_overrun / 10 / 60 / 60; if (quadsize) printf("\033#5"); printf("Lap %2zu: %ju:%02ju:%02ju.%ju", lap, h, m, s, d); lap++; if (!have_first) { have_first = 1; first_overrun = total_overrun; } else if (total_overrun > first_overrun) { total_overrun = total_overrun - first_overrun; d = total_overrun % 10; s = total_overrun / 10 % 60; m = total_overrun / 10 / 60 % 60; h = total_overrun / 10 / 60 / 60; printf(" \033[31m(+%ju:%02ju:%02ju.%ju)\033[m", h, m, s, d); } else if (total_overrun < first_overrun) { total_overrun = first_overrun - total_overrun; d = total_overrun % 10; s = total_overrun / 10 % 60; m = total_overrun / 10 / 60 % 60; h = total_overrun / 10 / 60 / 60; printf(" \033[34m(-%ju:%02ju:%02ju.%ju)\033[m", h, m, s, d); } printf("\033[K\n"); total_overrun = 0; } } } d = total_overrun % 10; s = total_overrun / 10 % 60; m = total_overrun / 10 / 60 % 60; h = total_overrun / 10 / 60 / 60; if (!paused) { if (quadsize) { printf("\033""7\033#3%ju:%02ju:%02ju.%ju\033[K\n", h, m, s, d); printf("\033#4%ju:%02ju:%02ju.%ju\033[K\033""8", h, m, s, d); } else { printf("\033""7%ju:%02ju:%02ju.%ju\033[K\033""8", h, m, s, d); } fflush(stdout); } else { if (quadsize) { printf("\033""7\033#3\033[33m%ju:%02ju:%02ju.%ju (paused)\033[m\033[K\n", h, m, s, d); printf("\033#4\033[33m%ju:%02ju:%02ju.%ju (paused)\033[m\033[K\033""8", h, m, s, d); } else { printf("\033""7\033[33m%ju:%02ju:%02ju.%ju (paused)\033[m\033[K\033""8", h, m, s, d); } fflush(stdout); pause(); continue; } if (read(timerfd, &overrun, sizeof(overrun)) < 0) { if (errno != EINTR) goto fail; } else { total_overrun += (uintmax_t)overrun; } } out: return 0; fail: return -1; } int main(int argc, char *argv[]) { int timerfd = -1, old_flags = -1, tcset = 0, old_sig = 0; struct sigaction sigact; int64_t owner_set = 0; struct f_owner_ex old_owner, new_owner; struct termios stty, saved_stty; ARGBEGIN { case '2': quadsize = 1; break; default: usage(); } ARGEND; if (argc) usage(); printf("\033[?25l\033[m"); orig_time.it_interval.tv_sec = 0; orig_time.it_interval.tv_nsec = 100000000L; orig_time.it_value.tv_sec = 0; orig_time.it_value.tv_nsec = 100000000L; timerfd = timerfd_create(CLOCK_BOOTTIME, 0); if (timerfd < 0) goto fail; if (timerfd_settime(timerfd, 0, &orig_time, 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 = sigio; sigaction(SIGIO, &sigact, NULL); sigaction(SIGURG, &sigact, NULL); if (fcntl(STDIN_FILENO, F_GETOWN_EX, &old_owner)) goto fail; memset(&new_owner, 0, sizeof(new_owner)); new_owner.type = F_OWNER_PID; new_owner.pid = getpid(); if (fcntl(STDIN_FILENO, F_SETOWN_EX, &new_owner)) goto fail; owner_set = 1; old_flags = fcntl(STDIN_FILENO, F_GETFL); fcntl(STDIN_FILENO, F_SETFL, old_flags | FASYNC | O_NONBLOCK); fcntl(STDIN_FILENO, F_GETSIG, &old_sig); if (old_sig) fcntl(STDIN_FILENO, F_SETSIG, 0); if (!tcgetattr(STDIN_FILENO, &stty)) { saved_stty = stty; stty.c_lflag &= (tcflag_t)~(ECHO | ICANON); tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty); tcset = 1; } if (display_stopwatch(timerfd)) goto fail; if (quadsize) printf("\n\n\033#5\033[?25h"); else printf("\033[?25h\n"); fflush(stdout); fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner); fcntl(STDIN_FILENO, F_SETFL, old_flags); fcntl(STDIN_FILENO, F_SETSIG, old_sig); tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); close(timerfd); return 0; fail: printf("\033[?25h\n"); perror(argv0 ? argv0 : "stopwatch"); fflush(stdout); if (owner_set) fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner); if (old_flags != -1) fcntl(STDIN_FILENO, F_SETFL, old_flags); if (old_sig) fcntl(STDIN_FILENO, F_SETSIG, old_sig); if (tcset) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); if (timerfd >= 0) close(timerfd); return 1; }