1 /* $OpenBSD: grdc.c,v 1.37 2022/09/27 03:01:42 deraadt Exp $ */
2 /*
3 *
4 * Copyright 2002 Amos Shapir. Public domain.
5 *
6 * Grand digital clock for curses compatible terminals
7 * Usage: grdc [-s] [n] -- run for n seconds (default infinity)
8 * Flags: -s: scroll
9 *
10 * modified 10-18-89 for curses (jrl)
11 * 10-18-89 added signal handling
12 */
13
14 #include <sys/ioctl.h>
15
16 #include <curses.h>
17 #include <err.h>
18 #include <limits.h>
19 #include <poll.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 #include <unistd.h>
26
27 #define XLENGTH 58
28 #define YDEPTH 7
29
30 struct timespec now;
31 struct tm *tm;
32
33 short disp[11] = {
34 075557, 011111, 071747, 071717, 055711,
35 074717, 074757, 071111, 075757, 075717, 002020
36 };
37 long old[6], next[6], new[6], mask;
38
39 volatile sig_atomic_t sigalrmed = 0;
40 volatile sig_atomic_t sigtermed = 0;
41 volatile sig_atomic_t sigwinched = 0;
42
43 int hascolor = 0;
44
45 void getwinsize(int *, int *);
46 void set(int, int);
47 void standt(int);
48 void __dead usage(void);
49
50 void
sigalrm(int signo)51 sigalrm(int signo)
52 {
53 sigalrmed = signo;
54 }
55
56 void
sighndl(int signo)57 sighndl(int signo)
58 {
59 sigtermed = signo;
60 }
61
62 void
sigresize(int signo)63 sigresize(int signo)
64 {
65 sigwinched = signo;
66 }
67
68 int
main(int argc,char * argv[])69 main(int argc, char *argv[])
70 {
71 long t, a;
72 int i, j, s, k, rv;
73 int scrol;
74 unsigned int n = 0;
75 struct timespec delay;
76 struct pollfd pfd;
77 const char *errstr;
78 long scroldelay = 50000000;
79 int xbase;
80 int ybase;
81 int wintoosmall;
82 int tz_len = 0;
83 int h, m;
84 int prev_tm_gmtoff;
85 char *tz;
86
87 tz = getenv("TZ");
88 if (tz != NULL)
89 tz_len = strlen(tz);
90
91 scrol = wintoosmall = 0;
92 while ((i = getopt(argc, argv, "sh")) != -1) {
93 switch (i) {
94 case 's':
95 scrol = 1;
96 break;
97 case 'h':
98 default:
99 usage();
100 }
101 }
102 argv += optind;
103 argc -= optind;
104
105 if (argc > 1)
106 usage();
107 if (argc == 1) {
108 n = strtonum(*argv, 1, UINT_MAX, &errstr);
109 if (errstr) {
110 warnx("number of seconds is %s", errstr);
111 usage();
112 }
113 }
114
115 initscr();
116
117 if (pledge("stdio tty", NULL) == -1)
118 err(1, "pledge");
119
120 signal(SIGINT, sighndl);
121 signal(SIGTERM, sighndl);
122 signal(SIGHUP, sighndl);
123 signal(SIGWINCH, sigresize);
124 signal(SIGCONT, sigresize); /* for resizes during suspend */
125
126 pfd.fd = STDIN_FILENO;
127 pfd.events = POLLIN;
128
129 cbreak();
130 noecho();
131
132 hascolor = has_colors();
133
134 if (hascolor) {
135 start_color();
136 init_pair(1, COLOR_BLACK, COLOR_RED);
137 init_pair(2, COLOR_RED, COLOR_BLACK);
138 init_pair(3, COLOR_WHITE, COLOR_BLACK);
139 attrset(COLOR_PAIR(2));
140 }
141
142 curs_set(0);
143 sigwinched = 1; /* force initial sizing */
144 prev_tm_gmtoff = 24 * 3600; /* force initial header printing */
145
146 clock_gettime(CLOCK_REALTIME, &now);
147 if (n) {
148 signal(SIGALRM, sigalrm);
149 alarm(n);
150 }
151 do {
152 mask = 0;
153 tm = localtime(&now.tv_sec);
154 set(tm->tm_sec % 10, 0);
155 set(tm->tm_sec / 10, 4);
156 set(tm->tm_min % 10, 10);
157 set(tm->tm_min / 10, 14);
158 set(tm->tm_hour % 10, 20);
159 set(tm->tm_hour / 10, 24);
160 set(10, 7);
161 set(10, 17);
162 /* force repaint if window size changed or DST changed */
163 if (sigwinched || prev_tm_gmtoff != tm->tm_gmtoff) {
164 sigwinched = 0;
165 wintoosmall = 0;
166 prev_tm_gmtoff = tm->tm_gmtoff;
167 getwinsize(&i, &j);
168 if (i >= XLENGTH + 2)
169 xbase = (i - XLENGTH) / 2;
170 else
171 wintoosmall = 1;
172 if (j >= YDEPTH + 2)
173 ybase = (j - YDEPTH) / 2;
174 else
175 wintoosmall = 1;
176 resizeterm(j, i);
177 clear();
178 refresh();
179 if (hascolor && !wintoosmall) {
180 attrset(COLOR_PAIR(3));
181
182 mvaddch(ybase - 1, xbase - 1, ACS_ULCORNER);
183 hline(ACS_HLINE, XLENGTH);
184 mvaddch(ybase - 1, xbase + XLENGTH, ACS_URCORNER);
185
186 mvaddch(ybase + YDEPTH, xbase - 1, ACS_LLCORNER);
187 hline(ACS_HLINE, XLENGTH);
188 mvaddch(ybase + YDEPTH, xbase + XLENGTH, ACS_LRCORNER);
189
190 move(ybase, xbase - 1);
191 vline(ACS_VLINE, YDEPTH);
192
193 move(ybase, xbase + XLENGTH);
194 vline(ACS_VLINE, YDEPTH);
195
196 move(ybase - 1, xbase);
197
198 h = tm->tm_gmtoff / 3600;
199 m = abs((int)tm->tm_gmtoff % 3600 / 60);
200
201 if (tz_len > 0 && tz_len <= XLENGTH -
202 strlen("[ () +0000 ]") -
203 strlen(tm->tm_zone))
204 printw("[ %s (%s) %+2.2d%02d ]", tz,
205 tm->tm_zone, h, m);
206 else
207 printw("[ %s %+2.2d%02d ]",
208 tm->tm_zone, h, m);
209
210 attrset(COLOR_PAIR(2));
211 }
212 for (k = 0; k < 6; k++)
213 old[k] = 0;
214 }
215 if (wintoosmall) {
216 move(0, 0);
217 printw("%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
218 tm->tm_sec);
219 } else for (k = 0; k < 6; k++) {
220 if (scrol) {
221 for(i = 0; i < 5; i++)
222 new[i] = (new[i] & ~mask) | (new[i + 1] & mask);
223 new[5] = (new[5] & ~mask) | (next[k] & mask);
224 } else
225 new[k] = (new[k] & ~mask) | (next[k] & mask);
226 next[k] = 0;
227 for (s = 1; s >= 0; s--) {
228 standt(s);
229 for (i = 0; i < 6; i++) {
230 if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) {
231 for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
232 if (a & t) {
233 if (!(a & (t << 1))) {
234 move(ybase + i + 1, xbase + 2 * (j + 1));
235 }
236 addstr(" ");
237 }
238 }
239 }
240 if (!s) {
241 old[i] = new[i];
242 }
243 }
244 if (!s) {
245 refresh();
246 }
247 }
248 if (scrol && k <= 4) {
249 clock_gettime(CLOCK_REALTIME, &now);
250 delay.tv_sec = 0;
251 delay.tv_nsec = 1000000000 - now.tv_nsec
252 - (4 - k) * scroldelay;
253 if (delay.tv_nsec <= scroldelay &&
254 delay.tv_nsec > 0)
255 nanosleep(&delay, NULL);
256 }
257 }
258 move(6, 0);
259 refresh();
260 clock_gettime(CLOCK_REALTIME, &now);
261 delay.tv_sec = 0;
262 delay.tv_nsec = (1000000000 - now.tv_nsec);
263 /* want scrolling to END on the second */
264 if (scrol && !wintoosmall)
265 delay.tv_nsec -= 5 * scroldelay;
266 rv = ppoll(&pfd, 1, &delay, NULL);
267 if (rv == 1) {
268 char q = 0;
269 read(STDIN_FILENO, &q, 1);
270 if (q == 'q')
271 sigalrmed = 1;
272 }
273 now.tv_sec++;
274
275 if (sigtermed) {
276 standend();
277 clear();
278 refresh();
279 endwin();
280 exit(0);
281 }
282 } while (!sigalrmed);
283 standend();
284 clear();
285 refresh();
286 endwin();
287 return 0;
288 }
289
290 void
set(int t,int n)291 set(int t, int n)
292 {
293 int i, m;
294
295 m = 7 << n;
296 for (i = 0; i < 5; i++) {
297 next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n;
298 mask |= (next[i] ^ old[i]) & m;
299 }
300 if (mask & m)
301 mask |= m;
302 }
303
304 void
standt(int on)305 standt(int on)
306 {
307 if (on) {
308 if (hascolor) {
309 attron(COLOR_PAIR(1));
310 } else {
311 attron(A_STANDOUT);
312 }
313 } else {
314 if (hascolor) {
315 attron(COLOR_PAIR(2));
316 } else {
317 attroff(A_STANDOUT);
318 }
319 }
320 }
321
322 void
getwinsize(int * wid,int * ht)323 getwinsize(int *wid, int *ht)
324 {
325 struct winsize size;
326
327 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) {
328 *wid = 80; /* Default */
329 *ht = 24;
330 } else {
331 *wid = size.ws_col;
332 *ht = size.ws_row;
333 }
334 }
335
336 void __dead
usage(void)337 usage(void)
338 {
339 fprintf(stderr, "usage: %s [-s] [number]\n", getprogname());
340 exit(1);
341 }
342