So, with the tzset(3) restriction in place I'd like to fix grdc, because what we currently have is wrong:
There are time zones that have minute offsets, display those correctly. Pointed out by pjanzen@. To display the offset, use ISO 8601, as suggested by David Goerger. Take a guess if tzset(3) will accept the time zone specified in TZ as a relative path and only display it if that is true and it's not too long. All time zones currently in /usr/share/zoneinfo fit. Lastly check if tm->tm_gmtoff changed which probably means that we moved in or out of daylight savings time. OK? p.s. I don't know what to do about wintoosmall at this time, the diff is big enough as it is. And quite frankly I don't care about that. diff --git grdc.6 grdc.6 index 16e1758c6d2..5aa6e84a2d2 100644 --- grdc.6 +++ grdc.6 @@ -34,8 +34,11 @@ key exits the program. .Bl -tag -width Ds .It Ev TZ The time zone to use for displaying the time. -It is specified as a pathname relative to -.Pa /usr/share/zoneinfo . +It is normally specified as a pathname relative to +.Pa /usr/share/zoneinfo , +though see +.Xr tzset 3 +for more information. If this variable is not set, the time zone is determined based on .Pa /etc/localtime . .El diff --git grdc.c grdc.c index 66e5eee79e6..4bb7fa1b1af 100644 --- grdc.c +++ grdc.c @@ -12,6 +12,7 @@ */ #include <sys/ioctl.h> +#include <sys/stat.h> #include <curses.h> #include <err.h> @@ -20,6 +21,7 @@ #include <signal.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <time.h> #include <unistd.h> @@ -45,6 +47,7 @@ void getwinsize(int *, int *); void set(int, int); void standt(int); void __dead usage(void); +int check_tz(const char *); void sigalrm(int signo) @@ -64,6 +67,36 @@ sigresize(int signo) sigwinched = signo; } +/* Take a guess if tzset(3) will accept TZ as a relative path */ +int +check_tz(const char *tz) +{ + struct stat sb; + char fullname[PATH_MAX]; + int i; + + if (tz == NULL) + return 0; + + if (tz[0] == ':') + tz++; + + if (tz[0] == '/' || strstr(tz, "../") != NULL) + return 0; + + i = snprintf(fullname, sizeof(fullname), "/usr/share/zoneinfo/%s", tz); + if (i < 0 || i >= sizeof(fullname)) + return 0; + + if (stat(fullname, &sb) == -1) + return 0; + + if (!S_ISREG(sb.st_mode)) + return 0; + + return 1; +} + int main(int argc, char *argv[]) { @@ -78,9 +111,19 @@ main(int argc, char *argv[]) int xbase; int ybase; int wintoosmall; + int tz_valid; + int tz_len = 0; + int prev_tm_gmtoff; char *tz; tz = getenv("TZ"); + tz_valid = check_tz(tz); + + if (tz_valid) { + if (tz[0] == ':') + tz++; + tz_len = strlen(tz); + } scrol = wintoosmall = 0; while ((i = getopt(argc, argv, "sh")) != -1) { @@ -135,6 +178,7 @@ main(int argc, char *argv[]) curs_set(0); sigwinched = 1; /* force initial sizing */ + prev_tm_gmtoff = 24 * 3600; /* force initial header printing */ clock_gettime(CLOCK_REALTIME, &now); if (n) { @@ -152,9 +196,11 @@ main(int argc, char *argv[]) set(tm->tm_hour / 10, 24); set(10, 7); set(10, 17); - if (sigwinched) { + /* force repaint if window size changed or DST changed */ + if (sigwinched || prev_tm_gmtoff != tm->tm_gmtoff) { sigwinched = 0; wintoosmall = 0; + prev_tm_gmtoff = tm->tm_gmtoff; getwinsize(&i, &j); if (i >= XLENGTH + 2) xbase = (i - XLENGTH) / 2; @@ -185,9 +231,20 @@ main(int argc, char *argv[]) vline(ACS_VLINE, YDEPTH); if (tz != NULL) { + int h, m; + h = tm->tm_gmtoff / 3600; + m = abs((int)tm->tm_gmtoff % 3600 / 60); + if (tz_valid && tz_len > XLENGTH - + strlen("[ () +0000 ]") - + strlen(tm->tm_zone)) + tz_valid = 0; move(ybase - 1, xbase); - printw("[ %s %+d ]", tz, - tm->tm_gmtoff / 60 / 60 ); + if (tz_valid) + printw("[ %s (%s) %+2.2d%02d ]", + tz, tm->tm_zone, h, m); + else + printw("[ %s %+2.2d%02d ]", + tm->tm_zone, h, m); } attrset(COLOR_PAIR(2)); -- I'm not entirely sure you are real.