aboutsummaryrefslogtreecommitdiff
path: root/zdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'zdump.c')
-rw-r--r--zdump.c76
1 files changed, 42 insertions, 34 deletions
diff --git a/zdump.c b/zdump.c
index a63a3438f571..ffb321a0369e 100644
--- a/zdump.c
+++ b/zdump.c
@@ -56,21 +56,6 @@
*/
enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
-#if !defined HAVE_GENERIC && defined __has_extension
-# if __has_extension(c_generic_selections)
-# define HAVE_GENERIC 1
-# else
-# define HAVE_GENERIC 0
-# endif
-#endif
-/* _Generic is buggy in pre-4.9 GCC. */
-#if !defined HAVE_GENERIC && defined __GNUC__
-# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
-#endif
-#ifndef HAVE_GENERIC
-# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
-#endif
-
#if HAVE_GETTEXT
# include <locale.h> /* for setlocale */
#endif /* HAVE_GETTEXT */
@@ -243,33 +228,56 @@ mktime_z(timezone_t tz, struct tm *tmp)
static timezone_t
tzalloc(char const *val)
{
+# if HAVE_SETENV
+ if (setenv("TZ", val, 1) != 0) {
+ perror("setenv");
+ exit(EXIT_FAILURE);
+ }
+ tzset();
+ return NULL;
+# else
+ enum { TZeqlen = 3 };
+ static char const TZeq[TZeqlen] = "TZ=";
static char **fakeenv;
- char **env = fakeenv;
- char *env0;
- if (! env) {
- char **e = environ;
- int to;
+ static size_t fakeenv0size;
+ void *freeable = NULL;
+ char **env = fakeenv, **initial_environ;
+ size_t valsize = strlen(val) + 1;
+ if (fakeenv0size < valsize) {
+ char **e = environ, **to;
+ ptrdiff_t initial_nenvptrs; /* Counting the trailing NULL pointer. */
while (*e++)
continue;
- env = xmalloc(sumsize(sizeof *environ,
- (e - environ) * sizeof *environ));
- to = 1;
- for (e = environ; (env[to] = *e); e++)
- to += strncmp(*e, "TZ=", 3) != 0;
+ initial_nenvptrs = e - environ;
+ fakeenv0size = sumsize(valsize, valsize);
+ fakeenv0size = max(fakeenv0size, 64);
+ freeable = env;
+ fakeenv = env =
+ xmalloc(sumsize(sumsize(sizeof *environ,
+ initial_nenvptrs * sizeof *environ),
+ sumsize(TZeqlen, fakeenv0size)));
+ to = env + 1;
+ for (e = environ; (*to = *e); e++)
+ to += strncmp(*e, TZeq, TZeqlen) != 0;
+ env[0] = memcpy(to + 1, TZeq, TZeqlen);
}
- env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
- env[0] = strcat(strcpy(env0, "TZ="), val);
- environ = fakeenv = env;
+ memcpy(env[0] + TZeqlen, val, valsize);
+ initial_environ = environ;
+ environ = env;
tzset();
- return env;
+ free(freeable);
+ return initial_environ;
+# endif
}
static void
-tzfree(timezone_t env)
+tzfree(timezone_t initial_environ)
{
- environ = env + 1;
- free(env[0]);
+# if !HAVE_SETENV
+ environ = initial_environ;
+ tzset();
+# endif
}
#endif /* ! USE_LOCALTIME_RZ */
@@ -281,9 +289,9 @@ gmtzinit(void)
if (USE_LOCALTIME_RZ) {
/* Try "GMT" first to find out whether this is one of the rare
platforms where time_t counts leap seconds; this works due to
- the "Link Etc/GMT GMT" line in the "etcetera" file. If "GMT"
+ the "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT"
fails, fall back on "GMT0" which might be similar due to the
- "Link Etc/GMT GMT0" line in the "backward" file, and which
+ "Link GMT GMT0" line in the "backward" file, and which
should work on all POSIX platforms. The rest of zdump does not
use the "GMT" abbreviation that comes from this setting, so it
is OK to use "GMT" here rather than the more-modern "UTC" which