1*e9980296Sderaadt /* $OpenBSD: grdc.c,v 1.37 2022/09/27 03:01:42 deraadt Exp $ */
24acc410cSdownsj /*
3bf56a031Spjanzen *
4bf56a031Spjanzen * Copyright 2002 Amos Shapir. Public domain.
5bf56a031Spjanzen *
64acc410cSdownsj * Grand digital clock for curses compatible terminals
74acc410cSdownsj * Usage: grdc [-s] [n] -- run for n seconds (default infinity)
84acc410cSdownsj * Flags: -s: scroll
94acc410cSdownsj *
104acc410cSdownsj * modified 10-18-89 for curses (jrl)
114acc410cSdownsj * 10-18-89 added signal handling
124acc410cSdownsj */
134acc410cSdownsj
141b2da923Stedu #include <sys/ioctl.h>
159ea8ec70Smestre
164acc410cSdownsj #include <curses.h>
172010f3c8Smestre #include <err.h>
18a20cec71Spjanzen #include <limits.h>
198ca82b00Stedu #include <poll.h>
206222ba34Scheloha #include <signal.h>
216222ba34Scheloha #include <stdio.h>
226222ba34Scheloha #include <stdlib.h>
2391219502Sflorian #include <string.h>
246222ba34Scheloha #include <time.h>
254acc410cSdownsj #include <unistd.h>
264acc410cSdownsj
274acc410cSdownsj #define XLENGTH 58
284acc410cSdownsj #define YDEPTH 7
294acc410cSdownsj
30a20cec71Spjanzen struct timespec now;
314acc410cSdownsj struct tm *tm;
324acc410cSdownsj
334acc410cSdownsj short disp[11] = {
344acc410cSdownsj 075557, 011111, 071747, 071717, 055711,
354acc410cSdownsj 074717, 074757, 071111, 075757, 075717, 002020
364acc410cSdownsj };
374acc410cSdownsj long old[6], next[6], new[6], mask;
384acc410cSdownsj
39fb669f89Scheloha volatile sig_atomic_t sigalrmed = 0;
40c48e110cSjsg volatile sig_atomic_t sigtermed = 0;
411b2da923Stedu volatile sig_atomic_t sigwinched = 0;
424acc410cSdownsj
434acc410cSdownsj int hascolor = 0;
444acc410cSdownsj
456222ba34Scheloha void getwinsize(int *, int *);
46c72b5b24Smillert void set(int, int);
47c72b5b24Smillert void standt(int);
486222ba34Scheloha void __dead usage(void);
494acc410cSdownsj
5091764506Sderaadt void
sigalrm(int signo)51fb669f89Scheloha sigalrm(int signo)
52fb669f89Scheloha {
53fb669f89Scheloha sigalrmed = signo;
54fb669f89Scheloha }
55fb669f89Scheloha
56fb669f89Scheloha void
sighndl(int signo)573eb8c9edSjsg sighndl(int signo)
584acc410cSdownsj {
594acc410cSdownsj sigtermed = signo;
604acc410cSdownsj }
614acc410cSdownsj
621b2da923Stedu void
sigresize(int signo)631b2da923Stedu sigresize(int signo)
641b2da923Stedu {
651b2da923Stedu sigwinched = signo;
661b2da923Stedu }
671b2da923Stedu
684acc410cSdownsj int
main(int argc,char * argv[])693eb8c9edSjsg main(int argc, char *argv[])
704acc410cSdownsj {
714acc410cSdownsj long t, a;
728ca82b00Stedu int i, j, s, k, rv;
7335b20aedSschwarze int scrol;
74fb669f89Scheloha unsigned int n = 0;
75fb669f89Scheloha struct timespec delay;
768ca82b00Stedu struct pollfd pfd;
7735b20aedSschwarze const char *errstr;
7835b20aedSschwarze long scroldelay = 50000000;
797f2aa82dSschwarze int xbase;
807f2aa82dSschwarze int ybase;
811b2da923Stedu int wintoosmall;
8291219502Sflorian int tz_len = 0;
8391219502Sflorian int h, m;
8491219502Sflorian int prev_tm_gmtoff;
8594089c8aSflorian char *tz;
8694089c8aSflorian
8794089c8aSflorian tz = getenv("TZ");
8891219502Sflorian if (tz != NULL)
8991219502Sflorian tz_len = strlen(tz);
904acc410cSdownsj
911b2da923Stedu scrol = wintoosmall = 0;
926222ba34Scheloha while ((i = getopt(argc, argv, "sh")) != -1) {
93a20cec71Spjanzen switch (i) {
94a20cec71Spjanzen case 's':
95a20cec71Spjanzen scrol = 1;
96a20cec71Spjanzen break;
97a20cec71Spjanzen case 'h':
98a20cec71Spjanzen default:
99a20cec71Spjanzen usage();
100a20cec71Spjanzen }
1016222ba34Scheloha }
102a20cec71Spjanzen argv += optind;
103a20cec71Spjanzen argc -= optind;
104a20cec71Spjanzen
105a20cec71Spjanzen if (argc > 1)
106a20cec71Spjanzen usage();
107a20cec71Spjanzen if (argc == 1) {
108fb669f89Scheloha n = strtonum(*argv, 1, UINT_MAX, &errstr);
10935b20aedSschwarze if (errstr) {
1109ab6e3d4Stb warnx("number of seconds is %s", errstr);
111a20cec71Spjanzen usage();
112a20cec71Spjanzen }
113a20cec71Spjanzen }
114a20cec71Spjanzen
1151b37764bSpjanzen initscr();
1167f2aa82dSschwarze
117c5ef424aSmestre if (pledge("stdio tty", NULL) == -1)
118c5ef424aSmestre err(1, "pledge");
119c5ef424aSmestre
1204acc410cSdownsj signal(SIGINT, sighndl);
1214acc410cSdownsj signal(SIGTERM, sighndl);
1224acc410cSdownsj signal(SIGHUP, sighndl);
1231b2da923Stedu signal(SIGWINCH, sigresize);
1241b2da923Stedu signal(SIGCONT, sigresize); /* for resizes during suspend */
1254acc410cSdownsj
1268ca82b00Stedu pfd.fd = STDIN_FILENO;
1278ca82b00Stedu pfd.events = POLLIN;
1288ca82b00Stedu
1294acc410cSdownsj cbreak();
1304acc410cSdownsj noecho();
1314acc410cSdownsj
1324acc410cSdownsj hascolor = has_colors();
1334acc410cSdownsj
1344acc410cSdownsj if (hascolor) {
1354acc410cSdownsj start_color();
1364acc410cSdownsj init_pair(1, COLOR_BLACK, COLOR_RED);
1374acc410cSdownsj init_pair(2, COLOR_RED, COLOR_BLACK);
1384acc410cSdownsj init_pair(3, COLOR_WHITE, COLOR_BLACK);
1394acc410cSdownsj attrset(COLOR_PAIR(2));
1404acc410cSdownsj }
1414acc410cSdownsj
142a20cec71Spjanzen curs_set(0);
1431b2da923Stedu sigwinched = 1; /* force initial sizing */
14491219502Sflorian prev_tm_gmtoff = 24 * 3600; /* force initial header printing */
1451b2da923Stedu
14677b728daStb clock_gettime(CLOCK_REALTIME, &now);
147fb669f89Scheloha if (n) {
148fb669f89Scheloha signal(SIGALRM, sigalrm);
149fb669f89Scheloha alarm(n);
150fb669f89Scheloha }
1511b2da923Stedu do {
15294089c8aSflorian mask = 0;
15394089c8aSflorian tm = localtime(&now.tv_sec);
15494089c8aSflorian set(tm->tm_sec % 10, 0);
15594089c8aSflorian set(tm->tm_sec / 10, 4);
15694089c8aSflorian set(tm->tm_min % 10, 10);
15794089c8aSflorian set(tm->tm_min / 10, 14);
15894089c8aSflorian set(tm->tm_hour % 10, 20);
15994089c8aSflorian set(tm->tm_hour / 10, 24);
16094089c8aSflorian set(10, 7);
16194089c8aSflorian set(10, 17);
16291219502Sflorian /* force repaint if window size changed or DST changed */
16391219502Sflorian if (sigwinched || prev_tm_gmtoff != tm->tm_gmtoff) {
1641b2da923Stedu sigwinched = 0;
1651b2da923Stedu wintoosmall = 0;
16691219502Sflorian prev_tm_gmtoff = tm->tm_gmtoff;
1671b2da923Stedu getwinsize(&i, &j);
1681b2da923Stedu if (i >= XLENGTH + 2)
1691b2da923Stedu xbase = (i - XLENGTH) / 2;
1701b2da923Stedu else
1711b2da923Stedu wintoosmall = 1;
1721b2da923Stedu if (j >= YDEPTH + 2)
1731b2da923Stedu ybase = (j - YDEPTH) / 2;
1741b2da923Stedu else
1751b2da923Stedu wintoosmall = 1;
1761b2da923Stedu resizeterm(j, i);
1774acc410cSdownsj clear();
1784acc410cSdownsj refresh();
1791b2da923Stedu if (hascolor && !wintoosmall) {
1804acc410cSdownsj attrset(COLOR_PAIR(3));
1814acc410cSdownsj
1827f2aa82dSschwarze mvaddch(ybase - 1, xbase - 1, ACS_ULCORNER);
1834acc410cSdownsj hline(ACS_HLINE, XLENGTH);
1847f2aa82dSschwarze mvaddch(ybase - 1, xbase + XLENGTH, ACS_URCORNER);
1854acc410cSdownsj
1867f2aa82dSschwarze mvaddch(ybase + YDEPTH, xbase - 1, ACS_LLCORNER);
1874acc410cSdownsj hline(ACS_HLINE, XLENGTH);
1887f2aa82dSschwarze mvaddch(ybase + YDEPTH, xbase + XLENGTH, ACS_LRCORNER);
1894acc410cSdownsj
1907f2aa82dSschwarze move(ybase, xbase - 1);
1914acc410cSdownsj vline(ACS_VLINE, YDEPTH);
1924acc410cSdownsj
1937f2aa82dSschwarze move(ybase, xbase + XLENGTH);
1944acc410cSdownsj vline(ACS_VLINE, YDEPTH);
1954acc410cSdownsj
19694089c8aSflorian move(ybase - 1, xbase);
19791219502Sflorian
19891219502Sflorian h = tm->tm_gmtoff / 3600;
19991219502Sflorian m = abs((int)tm->tm_gmtoff % 3600 / 60);
20091219502Sflorian
20191219502Sflorian if (tz_len > 0 && tz_len <= XLENGTH -
20291219502Sflorian strlen("[ () +0000 ]") -
20391219502Sflorian strlen(tm->tm_zone))
20491219502Sflorian printw("[ %s (%s) %+2.2d%02d ]", tz,
20591219502Sflorian tm->tm_zone, h, m);
20691219502Sflorian else
20791219502Sflorian printw("[ %s %+2.2d%02d ]",
20891219502Sflorian tm->tm_zone, h, m);
20994089c8aSflorian
2104acc410cSdownsj attrset(COLOR_PAIR(2));
2114acc410cSdownsj }
2121b2da923Stedu for (k = 0; k < 6; k++)
2131b2da923Stedu old[k] = 0;
2141b2da923Stedu }
2151b2da923Stedu if (wintoosmall) {
2161b2da923Stedu move(0, 0);
2171b2da923Stedu printw("%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
2181b2da923Stedu tm->tm_sec);
2191b2da923Stedu } else for (k = 0; k < 6; k++) {
2204acc410cSdownsj if (scrol) {
2214acc410cSdownsj for(i = 0; i < 5; i++)
2224acc410cSdownsj new[i] = (new[i] & ~mask) | (new[i + 1] & mask);
2234acc410cSdownsj new[5] = (new[5] & ~mask) | (next[k] & mask);
2244acc410cSdownsj } else
2254acc410cSdownsj new[k] = (new[k] & ~mask) | (next[k] & mask);
2264acc410cSdownsj next[k] = 0;
2274acc410cSdownsj for (s = 1; s >= 0; s--) {
2284acc410cSdownsj standt(s);
2294acc410cSdownsj for (i = 0; i < 6; i++) {
2304acc410cSdownsj if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) {
2314acc410cSdownsj for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
2324acc410cSdownsj if (a & t) {
2334acc410cSdownsj if (!(a & (t << 1))) {
2341b2da923Stedu move(ybase + i + 1, xbase + 2 * (j + 1));
2354acc410cSdownsj }
2364acc410cSdownsj addstr(" ");
2374acc410cSdownsj }
2384acc410cSdownsj }
2394acc410cSdownsj }
2404acc410cSdownsj if (!s) {
2414acc410cSdownsj old[i] = new[i];
2424acc410cSdownsj }
2434acc410cSdownsj }
2444acc410cSdownsj if (!s) {
2454acc410cSdownsj refresh();
2464acc410cSdownsj }
2474acc410cSdownsj }
24835b20aedSschwarze if (scrol && k <= 4) {
24977b728daStb clock_gettime(CLOCK_REALTIME, &now);
25035b20aedSschwarze delay.tv_sec = 0;
25135b20aedSschwarze delay.tv_nsec = 1000000000 - now.tv_nsec
25235b20aedSschwarze - (4 - k) * scroldelay;
25335b20aedSschwarze if (delay.tv_nsec <= scroldelay &&
25435b20aedSschwarze delay.tv_nsec > 0)
25535b20aedSschwarze nanosleep(&delay, NULL);
25635b20aedSschwarze }
2574acc410cSdownsj }
2581b2da923Stedu move(6, 0);
2594acc410cSdownsj refresh();
26077b728daStb clock_gettime(CLOCK_REALTIME, &now);
261a3963358Sd delay.tv_sec = 0;
262a3963358Sd delay.tv_nsec = (1000000000 - now.tv_nsec);
26335b20aedSschwarze /* want scrolling to END on the second */
2641b2da923Stedu if (scrol && !wintoosmall)
26535b20aedSschwarze delay.tv_nsec -= 5 * scroldelay;
2668ca82b00Stedu rv = ppoll(&pfd, 1, &delay, NULL);
2678ca82b00Stedu if (rv == 1) {
2688ca82b00Stedu char q = 0;
2698ca82b00Stedu read(STDIN_FILENO, &q, 1);
270fb669f89Scheloha if (q == 'q')
271fb669f89Scheloha sigalrmed = 1;
2728ca82b00Stedu }
273a3963358Sd now.tv_sec++;
274a3963358Sd
2754acc410cSdownsj if (sigtermed) {
2764acc410cSdownsj standend();
2774acc410cSdownsj clear();
2784acc410cSdownsj refresh();
2794acc410cSdownsj endwin();
280*e9980296Sderaadt exit(0);
2814acc410cSdownsj }
282fb669f89Scheloha } while (!sigalrmed);
2834acc410cSdownsj standend();
2844acc410cSdownsj clear();
2854acc410cSdownsj refresh();
2864acc410cSdownsj endwin();
28717641e31Stb return 0;
2884acc410cSdownsj }
2894acc410cSdownsj
2904acc410cSdownsj void
set(int t,int n)2914acc410cSdownsj set(int t, int n)
2924acc410cSdownsj {
2934acc410cSdownsj int i, m;
2944acc410cSdownsj
2954acc410cSdownsj m = 7 << n;
2964acc410cSdownsj for (i = 0; i < 5; i++) {
2974acc410cSdownsj next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n;
2984acc410cSdownsj mask |= (next[i] ^ old[i]) & m;
2994acc410cSdownsj }
3004acc410cSdownsj if (mask & m)
3014acc410cSdownsj mask |= m;
3024acc410cSdownsj }
3034acc410cSdownsj
3044acc410cSdownsj void
standt(int on)3054acc410cSdownsj standt(int on)
3064acc410cSdownsj {
3074acc410cSdownsj if (on) {
3084acc410cSdownsj if (hascolor) {
3094acc410cSdownsj attron(COLOR_PAIR(1));
3104acc410cSdownsj } else {
3114acc410cSdownsj attron(A_STANDOUT);
3124acc410cSdownsj }
3134acc410cSdownsj } else {
3144acc410cSdownsj if (hascolor) {
3154acc410cSdownsj attron(COLOR_PAIR(2));
3164acc410cSdownsj } else {
3174acc410cSdownsj attroff(A_STANDOUT);
3184acc410cSdownsj }
3194acc410cSdownsj }
3204acc410cSdownsj }
3214acc410cSdownsj
3224acc410cSdownsj void
getwinsize(int * wid,int * ht)3231b2da923Stedu getwinsize(int *wid, int *ht)
3244acc410cSdownsj {
3251b2da923Stedu struct winsize size;
3261b2da923Stedu
327df69c215Sderaadt if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) {
3281b2da923Stedu *wid = 80; /* Default */
3291b2da923Stedu *ht = 24;
3301b2da923Stedu } else {
3311b2da923Stedu *wid = size.ws_col;
3321b2da923Stedu *ht = size.ws_row;
3331b2da923Stedu }
3344acc410cSdownsj }
335a20cec71Spjanzen
3366222ba34Scheloha void __dead
usage(void)3373eb8c9edSjsg usage(void)
338a20cec71Spjanzen {
3396222ba34Scheloha fprintf(stderr, "usage: %s [-s] [number]\n", getprogname());
340a20cec71Spjanzen exit(1);
341a20cec71Spjanzen }
342