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.