Blame time/tzfile.c

Packit 6c4009
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdio_ext.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
Packit 6c4009
#include <timezone/tzfile.h>
Packit 6c4009
Packit 6c4009
int __use_tzfile;
Packit 6c4009
static dev_t tzfile_dev;
Packit 6c4009
static ino64_t tzfile_ino;
Packit 6c4009
static time_t tzfile_mtime;
Packit 6c4009
Packit 6c4009
struct ttinfo
Packit 6c4009
  {
Packit 6c4009
    long int offset;		/* Seconds east of GMT.  */
Packit 6c4009
    unsigned char isdst;	/* Used to set tm_isdst.  */
Packit 6c4009
    unsigned char idx;		/* Index into `zone_names'.  */
Packit 6c4009
    unsigned char isstd;	/* Transition times are in standard time.  */
Packit 6c4009
    unsigned char isgmt;	/* Transition times are in GMT.  */
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
struct leap
Packit 6c4009
  {
Packit 6c4009
    internal_time_t transition;	/* Time the transition takes effect.  */
Packit 6c4009
    long int change;		/* Seconds of correction to apply.  */
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
static size_t num_transitions;
Packit 6c4009
libc_freeres_ptr (static internal_time_t *transitions);
Packit 6c4009
static unsigned char *type_idxs;
Packit 6c4009
static size_t num_types;
Packit 6c4009
static struct ttinfo *types;
Packit 6c4009
static char *zone_names;
Packit 6c4009
static long int rule_stdoff;
Packit 6c4009
static long int rule_dstoff;
Packit 6c4009
static size_t num_leaps;
Packit 6c4009
static struct leap *leaps;
Packit 6c4009
static char *tzspec;
Packit 6c4009
Packit 6c4009
#include <endian.h>
Packit 6c4009
#include <byteswap.h>
Packit 6c4009
Packit 6c4009
/* Decode the four bytes at PTR as a signed integer in network byte order.  */
Packit 6c4009
static inline int
Packit 6c4009
__attribute ((always_inline))
Packit 6c4009
decode (const void *ptr)
Packit 6c4009
{
Packit 6c4009
  if (BYTE_ORDER == BIG_ENDIAN && sizeof (int) == 4)
Packit 6c4009
    return *(const int *) ptr;
Packit 6c4009
  if (sizeof (int) == 4)
Packit 6c4009
    return bswap_32 (*(const int *) ptr);
Packit 6c4009
Packit 6c4009
  const unsigned char *p = ptr;
Packit 6c4009
  int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
Packit 6c4009
Packit 6c4009
  result = (result << 8) | *p++;
Packit 6c4009
  result = (result << 8) | *p++;
Packit 6c4009
  result = (result << 8) | *p++;
Packit 6c4009
  result = (result << 8) | *p++;
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static inline int64_t
Packit 6c4009
__attribute ((always_inline))
Packit 6c4009
decode64 (const void *ptr)
Packit 6c4009
{
Packit 6c4009
  if ((BYTE_ORDER == BIG_ENDIAN))
Packit 6c4009
    return *(const int64_t *) ptr;
Packit 6c4009
Packit 6c4009
  return bswap_64 (*(const int64_t *) ptr);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__tzfile_read (const char *file, size_t extra, char **extrap)
Packit 6c4009
{
Packit 6c4009
  static const char default_tzdir[] = TZDIR;
Packit 6c4009
  size_t num_isstd, num_isgmt;
Packit 6c4009
  FILE *f;
Packit 6c4009
  struct tzhead tzhead;
Packit 6c4009
  size_t chars;
Packit 6c4009
  size_t i;
Packit 6c4009
  size_t total_size;
Packit 6c4009
  size_t types_idx;
Packit 6c4009
  size_t leaps_idx;
Packit 6c4009
  int was_using_tzfile = __use_tzfile;
Packit 6c4009
  int trans_width = 4;
Packit 6c4009
  size_t tzspec_len;
Packit 6c4009
  char *new = NULL;
Packit 6c4009
Packit 6c4009
  _Static_assert (sizeof (internal_time_t) == 8,
Packit 6c4009
		  "internal_time_t must be eight bytes");
Packit 6c4009
Packit 6c4009
  __use_tzfile = 0;
Packit 6c4009
Packit 6c4009
  if (file == NULL)
Packit 6c4009
    /* No user specification; use the site-wide default.  */
Packit 6c4009
    file = TZDEFAULT;
Packit 6c4009
  else if (*file == '\0')
Packit 6c4009
    /* User specified the empty string; use UTC with no leap seconds.  */
Packit 6c4009
    goto ret_free_transitions;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We must not allow to read an arbitrary file in a setuid
Packit 6c4009
	 program.  So we fail for any file which is not in the
Packit 6c4009
	 directory hierachy starting at TZDIR
Packit 6c4009
	 and which is not the system wide default TZDEFAULT.  */
Packit 6c4009
      if (__libc_enable_secure
Packit 6c4009
	  && ((*file == '/'
Packit 6c4009
	       && memcmp (file, TZDEFAULT, sizeof TZDEFAULT)
Packit 6c4009
	       && memcmp (file, default_tzdir, sizeof (default_tzdir) - 1))
Packit 6c4009
	      || strstr (file, "../") != NULL))
Packit 6c4009
	/* This test is certainly a bit too restrictive but it should
Packit 6c4009
	   catch all critical cases.  */
Packit 6c4009
	goto ret_free_transitions;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (*file != '/')
Packit 6c4009
    {
Packit 6c4009
      const char *tzdir;
Packit 6c4009
Packit 6c4009
      tzdir = getenv ("TZDIR");
Packit 6c4009
      if (tzdir == NULL || *tzdir == '\0')
Packit 6c4009
	tzdir = default_tzdir;
Packit 6c4009
      if (__asprintf (&new, "%s/%s", tzdir, file) == -1)
Packit 6c4009
	goto ret_free_transitions;
Packit 6c4009
      file = new;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If we were already using tzfile, check whether the file changed.  */
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  if (was_using_tzfile
Packit 6c4009
      && stat64 (file, &st) == 0
Packit 6c4009
      && tzfile_ino == st.st_ino && tzfile_dev == st.st_dev
Packit 6c4009
      && tzfile_mtime == st.st_mtime)
Packit 6c4009
    goto done;  /* Nothing to do.  */
Packit 6c4009
Packit 6c4009
  /* Note the file is opened with cancellation in the I/O functions
Packit 6c4009
     disabled and if available FD_CLOEXEC set.  */
Packit 6c4009
  f = fopen (file, "rce");
Packit 6c4009
  if (f == NULL)
Packit 6c4009
    goto ret_free_transitions;
Packit 6c4009
Packit 6c4009
  /* Get information about the file we are actually using.  */
Packit 6c4009
  if (fstat64 (__fileno (f), &st) != 0)
Packit 6c4009
    {
Packit 6c4009
      fclose (f);
Packit 6c4009
      goto ret_free_transitions;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  free ((void *) transitions);
Packit 6c4009
  transitions = NULL;
Packit 6c4009
Packit 6c4009
  /* Remember the inode and device number and modification time.  */
Packit 6c4009
  tzfile_dev = st.st_dev;
Packit 6c4009
  tzfile_ino = st.st_ino;
Packit 6c4009
  tzfile_mtime = st.st_mtime;
Packit 6c4009
Packit 6c4009
  /* No threads reading this stream.  */
Packit 6c4009
  __fsetlocking (f, FSETLOCKING_BYCALLER);
Packit 6c4009
Packit 6c4009
 read_again:
Packit 6c4009
  if (__builtin_expect (__fread_unlocked ((void *) &tzhead, sizeof (tzhead),
Packit 6c4009
					  1, f) != 1, 0)
Packit 6c4009
      || memcmp (tzhead.tzh_magic, TZ_MAGIC, sizeof (tzhead.tzh_magic)) != 0)
Packit 6c4009
    goto lose;
Packit 6c4009
Packit 6c4009
  num_transitions = (size_t) decode (tzhead.tzh_timecnt);
Packit 6c4009
  num_types = (size_t) decode (tzhead.tzh_typecnt);
Packit 6c4009
  chars = (size_t) decode (tzhead.tzh_charcnt);
Packit 6c4009
  num_leaps = (size_t) decode (tzhead.tzh_leapcnt);
Packit 6c4009
  num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
Packit 6c4009
  num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (num_isstd > num_types || num_isgmt > num_types))
Packit 6c4009
    goto lose;
Packit 6c4009
Packit 6c4009
  if (trans_width == 4 && tzhead.tzh_version[0] != '\0')
Packit 6c4009
    {
Packit 6c4009
      /* We use the 8-byte format.  */
Packit 6c4009
      trans_width = 8;
Packit 6c4009
Packit 6c4009
      /* Position the stream before the second header.  */
Packit 6c4009
      size_t to_skip = (num_transitions * (4 + 1)
Packit 6c4009
			+ num_types * 6
Packit 6c4009
			+ chars
Packit 6c4009
			+ num_leaps * 8
Packit 6c4009
			+ num_isstd
Packit 6c4009
			+ num_isgmt);
Packit 6c4009
      if (fseek (f, to_skip, SEEK_CUR) != 0)
Packit 6c4009
	goto lose;
Packit 6c4009
Packit 6c4009
      goto read_again;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (num_transitions
Packit 6c4009
			> ((SIZE_MAX - (__alignof__ (struct ttinfo) - 1))
Packit 6c4009
			   / (sizeof (internal_time_t) + 1)), 0))
Packit 6c4009
    goto lose;
Packit 6c4009
  total_size = num_transitions * (sizeof (internal_time_t) + 1);
Packit 6c4009
  total_size = ((total_size + __alignof__ (struct ttinfo) - 1)
Packit 6c4009
		& ~(__alignof__ (struct ttinfo) - 1));
Packit 6c4009
  types_idx = total_size;
Packit 6c4009
  if (__builtin_expect (num_types
Packit 6c4009
			> (SIZE_MAX - total_size) / sizeof (struct ttinfo), 0))
Packit 6c4009
    goto lose;
Packit 6c4009
  total_size += num_types * sizeof (struct ttinfo);
Packit 6c4009
  if (__glibc_unlikely (chars > SIZE_MAX - total_size))
Packit 6c4009
    goto lose;
Packit 6c4009
  total_size += chars;
Packit 6c4009
  if (__builtin_expect (__alignof__ (struct leap) - 1
Packit 6c4009
			> SIZE_MAX - total_size, 0))
Packit 6c4009
    goto lose;
Packit 6c4009
  total_size = ((total_size + __alignof__ (struct leap) - 1)
Packit 6c4009
		& ~(__alignof__ (struct leap) - 1));
Packit 6c4009
  leaps_idx = total_size;
Packit 6c4009
  if (__builtin_expect (num_leaps
Packit 6c4009
			> (SIZE_MAX - total_size) / sizeof (struct leap), 0))
Packit 6c4009
    goto lose;
Packit 6c4009
  total_size += num_leaps * sizeof (struct leap);
Packit 6c4009
  tzspec_len = 0;
Packit 6c4009
  if (trans_width == 8)
Packit 6c4009
    {
Packit 6c4009
      off_t rem = st.st_size - __ftello (f);
Packit 6c4009
      if (__builtin_expect (rem < 0
Packit 6c4009
			    || (size_t) rem < (num_transitions * (8 + 1)
Packit 6c4009
					       + num_types * 6
Packit 6c4009
					       + chars), 0))
Packit 6c4009
	goto lose;
Packit 6c4009
      tzspec_len = (size_t) rem - (num_transitions * (8 + 1)
Packit 6c4009
				   + num_types * 6
Packit 6c4009
				   + chars);
Packit 6c4009
      if (__builtin_expect (num_leaps > SIZE_MAX / 12
Packit 6c4009
			    || tzspec_len < num_leaps * 12, 0))
Packit 6c4009
	goto lose;
Packit 6c4009
      tzspec_len -= num_leaps * 12;
Packit 6c4009
      if (__glibc_unlikely (tzspec_len < num_isstd))
Packit 6c4009
	goto lose;
Packit 6c4009
      tzspec_len -= num_isstd;
Packit 6c4009
      if (__glibc_unlikely (tzspec_len == 0 || tzspec_len - 1 < num_isgmt))
Packit 6c4009
	goto lose;
Packit 6c4009
      tzspec_len -= num_isgmt + 1;
Packit 6c4009
      if (__glibc_unlikely (tzspec_len == 0
Packit 6c4009
			    || SIZE_MAX - total_size < tzspec_len))
Packit 6c4009
	goto lose;
Packit 6c4009
    }
Packit 6c4009
  if (__glibc_unlikely (SIZE_MAX - total_size - tzspec_len < extra))
Packit 6c4009
    goto lose;
Packit 6c4009
Packit 6c4009
  /* Allocate enough memory including the extra block requested by the
Packit 6c4009
     caller.  */
Packit 6c4009
  transitions = malloc (total_size + tzspec_len + extra);
Packit 6c4009
  if (transitions == NULL)
Packit 6c4009
    goto lose;
Packit 6c4009
Packit 6c4009
  type_idxs = (unsigned char *) transitions + (num_transitions
Packit 6c4009
					       * sizeof (internal_time_t));
Packit 6c4009
  types = (struct ttinfo *) ((char *) transitions + types_idx);
Packit 6c4009
  zone_names = (char *) types + num_types * sizeof (struct ttinfo);
Packit 6c4009
  leaps = (struct leap *) ((char *) transitions + leaps_idx);
Packit 6c4009
  if (trans_width == 8)
Packit 6c4009
    tzspec = (char *) leaps + num_leaps * sizeof (struct leap) + extra;
Packit 6c4009
  else
Packit 6c4009
    tzspec = NULL;
Packit 6c4009
  if (extra > 0)
Packit 6c4009
    *extrap = (char *) &leaps[num_leaps];
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (trans_width == 8, 1))
Packit 6c4009
    {
Packit 6c4009
      if (__builtin_expect (__fread_unlocked (transitions, trans_width + 1,
Packit 6c4009
					      num_transitions, f)
Packit 6c4009
			    != num_transitions, 0))
Packit 6c4009
	goto lose;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      if (__builtin_expect (__fread_unlocked (transitions, 4,
Packit 6c4009
					      num_transitions, f)
Packit 6c4009
			    != num_transitions, 0)
Packit 6c4009
	  || __builtin_expect (__fread_unlocked (type_idxs, 1, num_transitions,
Packit 6c4009
						 f) != num_transitions, 0))
Packit 6c4009
	goto lose;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Check for bogus indices in the data file, so we can hereafter
Packit 6c4009
     safely use type_idxs[T] as indices into `types' and never crash.  */
Packit 6c4009
  for (i = 0; i < num_transitions; ++i)
Packit 6c4009
    if (__glibc_unlikely (type_idxs[i] >= num_types))
Packit 6c4009
      goto lose;
Packit 6c4009
Packit 6c4009
  if (trans_width == 4)
Packit 6c4009
    {
Packit 6c4009
      /* Decode the transition times, stored as 4-byte integers in
Packit 6c4009
	 network (big-endian) byte order.  We work from the end of the
Packit 6c4009
	 array so as not to clobber the next element to be
Packit 6c4009
	 processed.  */
Packit 6c4009
      i = num_transitions;
Packit 6c4009
      while (i-- > 0)
Packit 6c4009
	transitions[i] = decode ((char *) transitions + i * 4);
Packit 6c4009
    }
Packit 6c4009
  else if (BYTE_ORDER != BIG_ENDIAN)
Packit 6c4009
    {
Packit 6c4009
      /* Decode the transition times, stored as 8-byte integers in
Packit 6c4009
	 network (big-endian) byte order.  */
Packit 6c4009
      for (i = 0; i < num_transitions; ++i)
Packit 6c4009
	transitions[i] = decode64 ((char *) transitions + i * 8);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  for (i = 0; i < num_types; ++i)
Packit 6c4009
    {
Packit 6c4009
      unsigned char x[4];
Packit 6c4009
      int c;
Packit 6c4009
      if (__builtin_expect (__fread_unlocked (x, 1,
Packit 6c4009
					      sizeof (x), f) != sizeof (x),
Packit 6c4009
			    0))
Packit 6c4009
	goto lose;
Packit 6c4009
      c = __getc_unlocked (f);
Packit 6c4009
      if (__glibc_unlikely ((unsigned int) c > 1u))
Packit 6c4009
	goto lose;
Packit 6c4009
      types[i].isdst = c;
Packit 6c4009
      c = __getc_unlocked (f);
Packit 6c4009
      if (__glibc_unlikely ((size_t) c > chars))
Packit 6c4009
	/* Bogus index in data file.  */
Packit 6c4009
	goto lose;
Packit 6c4009
      types[i].idx = c;
Packit 6c4009
      types[i].offset = (long int) decode (x);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (__fread_unlocked (zone_names, 1, chars, f) != chars))
Packit 6c4009
    goto lose;
Packit 6c4009
Packit 6c4009
  for (i = 0; i < num_leaps; ++i)
Packit 6c4009
    {
Packit 6c4009
      unsigned char x[8];
Packit 6c4009
      if (__builtin_expect (__fread_unlocked (x, 1, trans_width, f)
Packit 6c4009
			    != trans_width, 0))
Packit 6c4009
	goto lose;
Packit 6c4009
      if (trans_width == 4)
Packit 6c4009
	leaps[i].transition = decode (x);
Packit 6c4009
      else
Packit 6c4009
	leaps[i].transition = decode64 (x);
Packit 6c4009
Packit 6c4009
      if (__glibc_unlikely (__fread_unlocked (x, 1, 4, f) != 4))
Packit 6c4009
	goto lose;
Packit 6c4009
      leaps[i].change = (long int) decode (x);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  for (i = 0; i < num_isstd; ++i)
Packit 6c4009
    {
Packit 6c4009
      int c = __getc_unlocked (f);
Packit 6c4009
      if (__glibc_unlikely (c == EOF))
Packit 6c4009
	goto lose;
Packit 6c4009
      types[i].isstd = c != 0;
Packit 6c4009
    }
Packit 6c4009
  while (i < num_types)
Packit 6c4009
    types[i++].isstd = 0;
Packit 6c4009
Packit 6c4009
  for (i = 0; i < num_isgmt; ++i)
Packit 6c4009
    {
Packit 6c4009
      int c = __getc_unlocked (f);
Packit 6c4009
      if (__glibc_unlikely (c == EOF))
Packit 6c4009
	goto lose;
Packit 6c4009
      types[i].isgmt = c != 0;
Packit 6c4009
    }
Packit 6c4009
  while (i < num_types)
Packit 6c4009
    types[i++].isgmt = 0;
Packit 6c4009
Packit 6c4009
  /* Read the POSIX TZ-style information if possible.  */
Packit 6c4009
  if (tzspec != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Skip over the newline first.  */
Packit 6c4009
      if (__getc_unlocked (f) != '\n'
Packit 6c4009
	  || (__fread_unlocked (tzspec, 1, tzspec_len - 1, f)
Packit 6c4009
	      != tzspec_len - 1))
Packit 6c4009
	tzspec = NULL;
Packit 6c4009
      else
Packit 6c4009
	tzspec[tzspec_len - 1] = '\0';
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Don't use an empty TZ string.  */
Packit 6c4009
  if (tzspec != NULL && tzspec[0] == '\0')
Packit 6c4009
    tzspec = NULL;
Packit 6c4009
Packit 6c4009
  fclose (f);
Packit 6c4009
Packit 6c4009
  /* First "register" all timezone names.  */
Packit 6c4009
  for (i = 0; i < num_types; ++i)
Packit Service f1cc21
    if (__tzstring (&zone_names[types[i].idx]) == NULL)
Packit Service f1cc21
      goto ret_free_transitions;
Packit 6c4009
Packit 6c4009
  /* Find the standard and daylight time offsets used by the rule file.
Packit 6c4009
     We choose the offsets in the types of each flavor that are
Packit 6c4009
     transitioned to earliest in time.  */
Packit 6c4009
  __tzname[0] = NULL;
Packit 6c4009
  __tzname[1] = NULL;
Packit 6c4009
  for (i = num_transitions; i > 0; )
Packit 6c4009
    {
Packit 6c4009
      int type = type_idxs[--i];
Packit 6c4009
      int dst = types[type].isdst;
Packit 6c4009
Packit 6c4009
      if (__tzname[dst] == NULL)
Packit 6c4009
	{
Packit 6c4009
	  int idx = types[type].idx;
Packit 6c4009
Packit 6c4009
	  __tzname[dst] = __tzstring (&zone_names[idx]);
Packit 6c4009
Packit 6c4009
	  if (__tzname[1 - dst] != NULL)
Packit 6c4009
	    break;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  if (__tzname[0] == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* This should only happen if there are no transition rules.
Packit 6c4009
	 In this case there should be only one single type.  */
Packit 6c4009
      assert (num_types == 1);
Packit 6c4009
      __tzname[0] = __tzstring (zone_names);
Packit 6c4009
    }
Packit 6c4009
  if (__tzname[1] == NULL)
Packit 6c4009
    __tzname[1] = __tzname[0];
Packit 6c4009
Packit 6c4009
  if (num_transitions == 0)
Packit 6c4009
    /* Use the first rule (which should also be the only one).  */
Packit 6c4009
    rule_stdoff = rule_dstoff = types[0].offset;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      int stdoff_set = 0, dstoff_set = 0;
Packit 6c4009
      rule_stdoff = rule_dstoff = 0;
Packit 6c4009
      i = num_transitions - 1;
Packit 6c4009
      do
Packit 6c4009
	{
Packit 6c4009
	  if (!stdoff_set && !types[type_idxs[i]].isdst)
Packit 6c4009
	    {
Packit 6c4009
	      stdoff_set = 1;
Packit 6c4009
	      rule_stdoff = types[type_idxs[i]].offset;
Packit 6c4009
	    }
Packit 6c4009
	  else if (!dstoff_set && types[type_idxs[i]].isdst)
Packit 6c4009
	    {
Packit 6c4009
	      dstoff_set = 1;
Packit 6c4009
	      rule_dstoff = types[type_idxs[i]].offset;
Packit 6c4009
	    }
Packit 6c4009
	  if (stdoff_set && dstoff_set)
Packit 6c4009
	    break;
Packit 6c4009
	}
Packit 6c4009
      while (i-- > 0);
Packit 6c4009
Packit 6c4009
      if (!dstoff_set)
Packit 6c4009
	rule_dstoff = rule_stdoff;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __daylight = rule_stdoff != rule_dstoff;
Packit 6c4009
  __timezone = -rule_stdoff;
Packit 6c4009
Packit 6c4009
 done:
Packit 6c4009
  __use_tzfile = 1;
Packit 6c4009
  free (new);
Packit 6c4009
  return;
Packit 6c4009
Packit 6c4009
 lose:
Packit 6c4009
  fclose (f);
Packit 6c4009
 ret_free_transitions:
Packit 6c4009
  free (new);
Packit 6c4009
  free ((void *) transitions);
Packit 6c4009
  transitions = NULL;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
/* The user specified a hand-made timezone, but not its DST rules.
Packit 6c4009
   We will use the names and offsets from the user, and the rules
Packit 6c4009
   from the TZDEFRULES file.  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__tzfile_default (const char *std, const char *dst,
Packit 6c4009
		  long int stdoff, long int dstoff)
Packit 6c4009
{
Packit 6c4009
  size_t stdlen = strlen (std) + 1;
Packit 6c4009
  size_t dstlen = strlen (dst) + 1;
Packit 6c4009
  size_t i;
Packit 6c4009
  int isdst;
Packit 6c4009
  char *cp;
Packit 6c4009
Packit 6c4009
  __tzfile_read (TZDEFRULES, stdlen + dstlen, &cp;;
Packit 6c4009
  if (!__use_tzfile)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  if (num_types < 2)
Packit 6c4009
    {
Packit 6c4009
      __use_tzfile = 0;
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Ignore the zone names read from the file and use the given ones
Packit 6c4009
     instead.  */
Packit 6c4009
  __mempcpy (__mempcpy (cp, std, stdlen), dst, dstlen);
Packit 6c4009
  zone_names = cp;
Packit 6c4009
Packit 6c4009
  /* Now there are only two zones, regardless of what the file contained.  */
Packit 6c4009
  num_types = 2;
Packit 6c4009
Packit 6c4009
  /* Now correct the transition times for the user-specified standard and
Packit 6c4009
     daylight offsets from GMT.  */
Packit 6c4009
  isdst = 0;
Packit 6c4009
  for (i = 0; i < num_transitions; ++i)
Packit 6c4009
    {
Packit 6c4009
      struct ttinfo *trans_type = &types[type_idxs[i]];
Packit 6c4009
Packit 6c4009
      /* We will use only types 0 (standard) and 1 (daylight).
Packit 6c4009
	 Fix up this transition to point to whichever matches
Packit 6c4009
	 the flavor of its original type.  */
Packit 6c4009
      type_idxs[i] = trans_type->isdst;
Packit 6c4009
Packit 6c4009
      if (trans_type->isgmt)
Packit 6c4009
	/* The transition time is in GMT.  No correction to apply.  */ ;
Packit 6c4009
      else if (isdst && !trans_type->isstd)
Packit 6c4009
	/* The type says this transition is in "local wall clock time", and
Packit 6c4009
	   wall clock time as of the previous transition was DST.  Correct
Packit 6c4009
	   for the difference between the rule's DST offset and the user's
Packit 6c4009
	   DST offset.  */
Packit 6c4009
	transitions[i] += dstoff - rule_dstoff;
Packit 6c4009
      else
Packit 6c4009
	/* This transition is in "local wall clock time", and wall clock
Packit 6c4009
	   time as of this iteration is non-DST.  Correct for the
Packit 6c4009
	   difference between the rule's standard offset and the user's
Packit 6c4009
	   standard offset.  */
Packit 6c4009
	transitions[i] += stdoff - rule_stdoff;
Packit 6c4009
Packit 6c4009
      /* The DST state of "local wall clock time" for the next iteration is
Packit 6c4009
	 as specified by this transition.  */
Packit 6c4009
      isdst = trans_type->isdst;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Now that we adjusted the transitions to the requested offsets,
Packit 6c4009
     reset the rule_stdoff and rule_dstoff values appropriately.  They
Packit 6c4009
     are used elsewhere.  */
Packit 6c4009
  rule_stdoff = stdoff;
Packit 6c4009
  rule_dstoff = dstoff;
Packit 6c4009
Packit 6c4009
  /* Reset types 0 and 1 to describe the user's settings.  */
Packit 6c4009
  types[0].idx = 0;
Packit 6c4009
  types[0].offset = stdoff;
Packit 6c4009
  types[0].isdst = 0;
Packit 6c4009
  types[1].idx = stdlen;
Packit 6c4009
  types[1].offset = dstoff;
Packit 6c4009
  types[1].isdst = 1;
Packit 6c4009
Packit 6c4009
  /* Reset the zone names to point to the user's names.  */
Packit 6c4009
  __tzname[0] = (char *) std;
Packit 6c4009
  __tzname[1] = (char *) dst;
Packit 6c4009
Packit 6c4009
  /* Set the timezone.  */
Packit 6c4009
  __timezone = -types[0].offset;
Packit 6c4009
Packit 6c4009
  /* Invalidate the tzfile attribute cache to force rereading
Packit 6c4009
     TZDEFRULES the next time it is used.  */
Packit 6c4009
  tzfile_dev = 0;
Packit 6c4009
  tzfile_ino = 0;
Packit 6c4009
  tzfile_mtime = 0;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
void
Packit 6c4009
__tzfile_compute (internal_time_t timer, int use_localtime,
Packit 6c4009
		  long int *leap_correct, int *leap_hit,
Packit 6c4009
		  struct tm *tp)
Packit 6c4009
{
Packit 6c4009
  size_t i;
Packit 6c4009
Packit 6c4009
  if (use_localtime)
Packit 6c4009
    {
Packit 6c4009
      __tzname[0] = NULL;
Packit 6c4009
      __tzname[1] = NULL;
Packit 6c4009
Packit 6c4009
      if (__glibc_unlikely (num_transitions == 0 || timer < transitions[0]))
Packit 6c4009
	{
Packit 6c4009
	  /* TIMER is before any transition (or there are no transitions).
Packit 6c4009
	     Choose the first non-DST type
Packit 6c4009
	     (or the first if they're all DST types).  */
Packit 6c4009
	  i = 0;
Packit 6c4009
	  while (i < num_types && types[i].isdst)
Packit 6c4009
	    {
Packit 6c4009
	      if (__tzname[1] == NULL)
Packit 6c4009
		__tzname[1] = __tzstring (&zone_names[types[i].idx]);
Packit 6c4009
Packit 6c4009
	      ++i;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (i == num_types)
Packit 6c4009
	    i = 0;
Packit 6c4009
	  __tzname[0] = __tzstring (&zone_names[types[i].idx]);
Packit 6c4009
	  if (__tzname[1] == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      size_t j = i;
Packit 6c4009
	      while (j < num_types)
Packit 6c4009
		if (types[j].isdst)
Packit 6c4009
		  {
Packit 6c4009
		    __tzname[1] = __tzstring (&zone_names[types[j].idx]);
Packit 6c4009
		    break;
Packit 6c4009
		  }
Packit 6c4009
		else
Packit 6c4009
		  ++j;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else if (__glibc_unlikely (timer >= transitions[num_transitions - 1]))
Packit 6c4009
	{
Packit 6c4009
	  if (__glibc_unlikely (tzspec == NULL))
Packit 6c4009
	    {
Packit 6c4009
	    use_last:
Packit 6c4009
	      i = num_transitions;
Packit 6c4009
	      goto found;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Parse the POSIX TZ-style string.  */
Packit 6c4009
	  __tzset_parse_tz (tzspec);
Packit 6c4009
Packit 6c4009
	  /* Convert to broken down structure.  If this fails do not
Packit 6c4009
	     use the string.  */
Packit 6c4009
	  {
Packit 6c4009
	    time_t truncated = timer;
Packit 6c4009
	    if (__glibc_unlikely (truncated != timer
Packit 6c4009
				  || ! __offtime (&truncated, 0, tp)))
Packit 6c4009
	      goto use_last;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	  /* Use the rules from the TZ string to compute the change.
Packit 6c4009
	     timer fits into time_t due to the truncation check
Packit 6c4009
	     above.  */
Packit 6c4009
	  __tz_compute (timer, tp, 1);
Packit 6c4009
Packit 6c4009
	  /* If tzspec comes from posixrules loaded by __tzfile_default,
Packit 6c4009
	     override the STD and DST zone names with the ones user
Packit 6c4009
	     requested in TZ envvar.  */
Packit 6c4009
	  if (__glibc_unlikely (zone_names == (char *) &leaps[num_leaps]))
Packit 6c4009
	    {
Packit 6c4009
	      assert (num_types == 2);
Packit 6c4009
	      __tzname[0] = __tzstring (zone_names);
Packit 6c4009
	      __tzname[1] = __tzstring (&zone_names[strlen (zone_names) + 1]);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  goto leap;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  /* Find the first transition after TIMER, and
Packit 6c4009
	     then pick the type of the transition before it.  */
Packit 6c4009
	  size_t lo = 0;
Packit 6c4009
	  size_t hi = num_transitions - 1;
Packit 6c4009
	  /* Assume that DST is changing twice a year and guess
Packit 6c4009
	     initial search spot from it.  Half of a gregorian year
Packit 6c4009
	     has on average 365.2425 * 86400 / 2 = 15778476 seconds.
Packit 6c4009
	     The value i can be truncated if size_t is smaller than
Packit 6c4009
	     internal_time_t, but this is harmless because it is just
Packit 6c4009
	     a guess.  */
Packit 6c4009
	  i = (transitions[num_transitions - 1] - timer) / 15778476;
Packit 6c4009
	  if (i < num_transitions)
Packit 6c4009
	    {
Packit 6c4009
	      i = num_transitions - 1 - i;
Packit 6c4009
	      if (timer < transitions[i])
Packit 6c4009
		{
Packit 6c4009
		  if (i < 10 || timer >= transitions[i - 10])
Packit 6c4009
		    {
Packit 6c4009
		      /* Linear search.  */
Packit 6c4009
		      while (timer < transitions[i - 1])
Packit 6c4009
			--i;
Packit 6c4009
		      goto found;
Packit 6c4009
		    }
Packit 6c4009
		  hi = i - 10;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  if (i + 10 >= num_transitions || timer < transitions[i + 10])
Packit 6c4009
		    {
Packit 6c4009
		      /* Linear search.  */
Packit 6c4009
		      while (timer >= transitions[i])
Packit 6c4009
			++i;
Packit 6c4009
		      goto found;
Packit 6c4009
		    }
Packit 6c4009
		  lo = i + 10;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Binary search.  */
Packit 6c4009
	  /* assert (timer >= transitions[lo] && timer < transitions[hi]); */
Packit 6c4009
	  while (lo + 1 < hi)
Packit 6c4009
	    {
Packit 6c4009
	      i = (lo + hi) / 2;
Packit 6c4009
	      if (timer < transitions[i])
Packit 6c4009
		hi = i;
Packit 6c4009
	      else
Packit 6c4009
		lo = i;
Packit 6c4009
	    }
Packit 6c4009
	  i = hi;
Packit 6c4009
Packit 6c4009
	found:
Packit 6c4009
	  /* assert (timer >= transitions[i - 1]
Packit 6c4009
	     && (i == num_transitions || timer < transitions[i])); */
Packit 6c4009
	  __tzname[types[type_idxs[i - 1]].isdst]
Packit 6c4009
	    = __tzstring (&zone_names[types[type_idxs[i - 1]].idx]);
Packit 6c4009
	  size_t j = i;
Packit 6c4009
	  while (j < num_transitions)
Packit 6c4009
	    {
Packit 6c4009
	      int type = type_idxs[j];
Packit 6c4009
	      int dst = types[type].isdst;
Packit 6c4009
	      int idx = types[type].idx;
Packit 6c4009
Packit 6c4009
	      if (__tzname[dst] == NULL)
Packit 6c4009
		{
Packit 6c4009
		  __tzname[dst] = __tzstring (&zone_names[idx]);
Packit 6c4009
Packit 6c4009
		  if (__tzname[1 - dst] != NULL)
Packit 6c4009
		    break;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      ++j;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (__glibc_unlikely (__tzname[0] == NULL))
Packit 6c4009
	    __tzname[0] = __tzname[1];
Packit 6c4009
Packit 6c4009
	  i = type_idxs[i - 1];
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      struct ttinfo *info = &types[i];
Packit 6c4009
      __daylight = rule_stdoff != rule_dstoff;
Packit 6c4009
      __timezone = -rule_stdoff;
Packit 6c4009
Packit 6c4009
      if (__tzname[0] == NULL)
Packit 6c4009
	{
Packit 6c4009
	  /* This should only happen if there are no transition rules.
Packit 6c4009
	     In this case there should be only one single type.  */
Packit 6c4009
	  assert (num_types == 1);
Packit 6c4009
	  __tzname[0] = __tzstring (zone_names);
Packit 6c4009
	}
Packit 6c4009
      if (__tzname[1] == NULL)
Packit 6c4009
	/* There is no daylight saving time.  */
Packit 6c4009
	__tzname[1] = __tzname[0];
Packit 6c4009
      tp->tm_isdst = info->isdst;
Packit 6c4009
      assert (strcmp (&zone_names[info->idx], __tzname[tp->tm_isdst]) == 0);
Packit 6c4009
      tp->tm_zone = __tzname[tp->tm_isdst];
Packit 6c4009
      tp->tm_gmtoff = info->offset;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 leap:
Packit 6c4009
  *leap_correct = 0L;
Packit 6c4009
  *leap_hit = 0;
Packit 6c4009
Packit 6c4009
  /* Find the last leap second correction transition time before TIMER.  */
Packit 6c4009
  i = num_leaps;
Packit 6c4009
  do
Packit 6c4009
    if (i-- == 0)
Packit 6c4009
      return;
Packit 6c4009
  while (timer < leaps[i].transition);
Packit 6c4009
Packit 6c4009
  /* Apply its correction.  */
Packit 6c4009
  *leap_correct = leaps[i].change;
Packit 6c4009
Packit 6c4009
  if (timer == leaps[i].transition && /* Exactly at the transition time.  */
Packit 6c4009
      ((i == 0 && leaps[i].change > 0) ||
Packit 6c4009
       leaps[i].change > leaps[i - 1].change))
Packit 6c4009
    {
Packit 6c4009
      *leap_hit = 1;
Packit 6c4009
      while (i > 0
Packit 6c4009
	     && leaps[i].transition == leaps[i - 1].transition + 1
Packit 6c4009
	     && leaps[i].change == leaps[i - 1].change + 1)
Packit 6c4009
	{
Packit 6c4009
	  ++*leap_hit;
Packit 6c4009
	  --i;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
}