/* See LICENSE file for copyright and license details. */ #include "common.h" #include 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; }