summaryrefslogtreecommitdiffstats
path: root/clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'clock.c')
-rw-r--r--clock.c354
1 files changed, 354 insertions, 0 deletions
diff --git a/clock.c b/clock.c
new file mode 100644
index 0000000..317f517
--- /dev/null
+++ b/clock.c
@@ -0,0 +1,354 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#include <sys/timex.h>
+
+USAGE("[-u | -v | -w] [-s2]");
+
+
+#include "large.h"
+
+
+static volatile sig_atomic_t caught_sigterm = 0;
+static volatile sig_atomic_t caught_sigwinch = 1;
+static volatile sig_atomic_t caught_sigio = 1;
+static int with_small = 0;
+static int with_quad = 0;
+static const char *week_fmt = "%V";
+
+static const char **large_digits[] = {
+ large_0, large_1, large_2, large_3, large_4,
+ large_5, large_6, large_7, large_8, large_9
+};
+
+static const char **small_digits[] = {
+ small_0, small_1, small_2, small_3, small_4,
+ small_5, small_6, small_7, small_8, small_9
+};
+
+
+
+static void
+sigterm(int signo)
+{
+ caught_sigterm = 1;
+ (void) signo;
+}
+
+static void
+sigwinch(int signo)
+{
+ caught_sigwinch = 1;
+ (void) signo;
+}
+
+static void
+sigio(int signo)
+{
+ caught_sigio = 1;
+ (void) signo;
+}
+
+
+static void
+print_time(const char ***str, int left, size_t height)
+{
+ size_t r, c;
+
+ for (r = 0; r < height; r++) {
+ printf("%*.s", left, "");
+ for (c = 0; str[c]; c++)
+ fprintf(stdout, "%s", str[c][r]);
+ fprintf(stdout, "\033[K\n");
+ }
+}
+
+
+static int
+display_clock(int timerfd)
+{
+ const char *months = "JanFebMarAprMajJunJulAugSepOctNovDec";
+ const char *weekdays = "SunMonTueWedThuFriSat";
+ const char **digits[9];
+ int r, leap_insert, x = 0, y = 0, was_quad = 0, len1, len2, len3, width;
+ uint64_t _overrun;
+ struct winsize winsize;
+ struct tm *now;
+ struct timex timex;
+ char line1[sizeof(int) * 3 + sizeof("+-(00)aaa-00")];
+ char line2[sizeof("aaa, week , ") + sizeof(int) * 3 + 32];
+ char line3[sizeof("@(+1)") + sizeof(uintmax_t) * 3];
+ char week[sizeof(int) * 3], c;
+ ssize_t rd;
+
+ memset(&timex, 0, sizeof(timex));
+ digits[8] = NULL;
+
+ while (!caught_sigterm) {
+ if (caught_sigwinch) {
+ if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) {
+ if (errno == EINTR)
+ continue;
+ goto fail;
+ }
+ caught_sigwinch = 0;
+ y = winsize.ws_row;
+ x = winsize.ws_col;
+ if (was_quad) {
+ was_quad = 0;
+ for (r = 0; r < y; r++)
+ printf("%s\033#5", r ? "\n" : "\033[H");
+ }
+ }
+
+ if (caught_sigio) {
+ caught_sigio = 0;
+ for (;;) {
+ rd = read(STDIN_FILENO, &c, 1);
+ if (rd <= 0) {
+ if (!rd)
+ goto out;
+ if (errno == EAGAIN)
+ break;
+ if (errno == EINTR)
+ continue;
+ goto fail;
+ }
+ if (c == 'q')
+ goto out;
+ }
+ }
+
+ leap_insert = 0;
+ r = adjtimex(&timex);
+ if (r == -1)
+ goto fail;
+ if (timex.time.tv_sec % (24 * 60 * 60) == 0) {
+ if (r == TIME_INS) {
+ timex.time.tv_sec -= 1;
+ now = localtime(&timex.time.tv_sec);
+ if (!now)
+ goto fail;
+ now->tm_sec += leap_insert = 1;
+ } else {
+ timex.time.tv_sec += (r == TIME_DEL);
+ now = localtime(&timex.time.tv_sec);
+ if (!now)
+ goto fail;
+ }
+ } else {
+ now = localtime(&timex.time.tv_sec);
+ if (!now)
+ goto fail;
+ if (r == TIME_OOP)
+ now->tm_sec += leap_insert = 1;
+ }
+
+ strftime(week, sizeof(week), week_fmt, now);
+ len1 = snprintf(line1, sizeof(line1), "%i-(%02i)%.3s-%02i", now->tm_year + 1900,
+ now->tm_mon + 1, &months[(size_t)now->tm_mon % 12 * 3], now->tm_mday);
+ len2 = snprintf(line2, sizeof(line2), "%.3s, week %s, %s",
+ &weekdays[(size_t)now->tm_mday % 7 * 3], week, tzname[now->tm_isdst]);
+ len3 = snprintf(line3, sizeof(line3), "(@%ji%s)", (intmax_t)timex.time.tv_sec, leap_insert ? "+1" : "");
+
+ printf("\033[H");
+
+ if (x >= 6 * LARGE_XD + 2 * LARGE_XC && y > LARGE_Y + 1) {
+ if (y > LARGE_Y + 6)
+ printf("\033[K\n");
+ digits[0] = large_digits[now->tm_hour / 10];
+ digits[1] = large_digits[now->tm_hour % 10];
+ digits[2] = large_colon;
+ digits[3] = large_digits[now->tm_min / 10];
+ digits[4] = large_digits[now->tm_min % 10];
+ digits[5] = large_colon;
+ digits[6] = large_digits[now->tm_sec / 10];
+ digits[7] = large_digits[now->tm_sec % 10];
+ width = 6 * LARGE_XD + 2 * LARGE_XC;
+ print_time(digits, (x - width) / 2, LARGE_Y);
+ printf("\033[K\n");
+ r = LARGE_Y + 1;
+ } else if (with_small && x >= 6 * SMALL_XD + 2 * SMALL_XC && y > SMALL_Y + 1) {
+ if (y > SMALL_Y + 6)
+ printf("\033[K\n");
+ digits[0] = small_digits[now->tm_hour / 10];
+ digits[1] = small_digits[now->tm_hour % 10];
+ digits[2] = small_colon;
+ digits[3] = small_digits[now->tm_min / 10];
+ digits[4] = small_digits[now->tm_min % 10];
+ digits[5] = small_colon;
+ digits[6] = small_digits[now->tm_sec / 10];
+ digits[7] = small_digits[now->tm_sec % 10];
+ width = 6 * SMALL_XD + 2 * SMALL_XC;
+ print_time(digits, (x - width) / 2, SMALL_Y);
+ printf("\033[K\n");
+ r = SMALL_Y + 1;
+ } else if (with_quad && x > 8 * 2 && y > 2) {
+ if (y > 6)
+ printf("\033[K\n");
+ printf("\033#3%*.s%02i:%02i:%02i\033[K\n", (x / 2 - 8) / 2, "", now->tm_hour, now->tm_min, now->tm_sec);
+ printf("\033#4%*.s%02i:%02i:%02i\033[K\n", (x / 2 - 8) / 2, "", now->tm_hour, now->tm_min, now->tm_sec);
+ printf("\033#5");
+ was_quad = 1;
+ width = 8 * 2;
+ r = 2;
+ } else {
+ printf("%*.s%02i:%02i:%02i\033[K", (x - 8) / 2, "", now->tm_hour, now->tm_min, now->tm_sec);
+ if (y > 1)
+ printf("\n");
+ width = 8;
+ r = 1;
+ }
+
+ if (len1 + len2 + 2 < width && len3 < width && r + 1 < y) {
+ printf("%*.s%s, %s\033[K\n", (x - len1 - len2 - 2) / 2, "", line1, line2);
+ printf("%*.s%s\033[K", (x - len3) / 2, "", line3);
+ } else if (len1 + len2 + len3 + 3 < width && r < y) {
+ printf("%*.s%s, %s %s\033[K", (x - len1 - len2 - len3 - 3) / 2, "", line1, line2, line3);
+ } else if (len1 + len2 + 2 < width && len3 < width && r + 1 == y) {
+ printf("%*.s%s, %s\033[K", (x - len1 - len2 - 2) / 2, "", line1, line2);
+ } else {
+ if (len1 < x && r < y) {
+ printf("%*.s%s\033[K", (x - len1) / 2, "", line1);
+ r += 1;
+ if (r < y)
+ printf("\n");
+ }
+ if (len2 < x && r < y) {
+ printf("%*.s%s\033[K", (x - len2) / 2, "", line2);
+ r += 1;
+ if (r < y)
+ printf("\n");
+ }
+ if (len3 < x && r < y) {
+ printf("%*.s%s\033[K", (x - len3) / 2, "", line3);
+ r += 1;
+ }
+ }
+
+ printf("\033[J");
+ fflush(stdout);
+
+ if (read(timerfd, &_overrun, sizeof(_overrun)) < 0)
+ if (errno != EINTR)
+ goto fail;
+ }
+
+out:
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int timerfd = -1, tcset = 0, old_flags = -1, old_sig = 0, owner_set = 0;
+ struct itimerspec itimerspec;
+ struct sigaction sigact;
+ struct termios stty, saved_stty;
+ struct f_owner_ex old_owner, new_owner;
+
+ ARGBEGIN {
+ case '2':
+ with_quad = 1;
+ break;
+ case 's':
+ with_small = 1;
+ break;
+ case 'u':
+ week_fmt = "%U";
+ break;
+ case 'v':
+ week_fmt = "%V";
+ break;
+ case 'w':
+ week_fmt = "%W";
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc)
+ usage();
+
+ tzset();
+
+ fprintf(stdout, "\033[?1049h\033[?25l");
+
+ if (clock_gettime(CLOCK_REALTIME, &itimerspec.it_value))
+ goto fail;
+ 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_REALTIME, 0);
+ if (timerfd < 0)
+ goto fail;
+ if (timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &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);
+
+ 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_clock(timerfd))
+ goto fail;
+
+ fprintf(stdout, "\033[?25h\n\033[?1049l");
+ fflush(stdout);
+ close(timerfd);
+ 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);
+ return 0;
+
+fail:
+ perror(argv0 ? argv0 : "clock");
+ fprintf(stdout, "\033[?25h\n\033[?1049l");
+ 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;
+}