Blame timezone/zdump.c

Packit 6c4009
/*
Packit 6c4009
** This file is in the public domain, so clarified as of
Packit 6c4009
** 2009-05-17 by Arthur David Olson.
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
#include "version.h"
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
** This code has been made independent of the rest of the time
Packit 6c4009
** conversion package to increase confidence in the verification it provides.
Packit 6c4009
** You can use this code to help in verifying other implementations.
Packit 6c4009
** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
#ifndef NETBSD_INSPIRED
Packit 6c4009
# define NETBSD_INSPIRED 1
Packit 6c4009
#endif
Packit 6c4009
#ifndef USE_LTZ
Packit 6c4009
# define USE_LTZ 1
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include "private.h"
Packit 6c4009
Packit 6c4009
#ifndef HAVE_LOCALTIME_R
Packit 6c4009
# define HAVE_LOCALTIME_R 1
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef HAVE_LOCALTIME_RZ
Packit 6c4009
# ifdef TM_ZONE
Packit 6c4009
#  define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
Packit 6c4009
# else
Packit 6c4009
#  define HAVE_LOCALTIME_RZ 0
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef HAVE_TZSET
Packit 6c4009
# define HAVE_TZSET 1
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef ZDUMP_LO_YEAR
Packit 6c4009
#define ZDUMP_LO_YEAR	(-500)
Packit 6c4009
#endif /* !defined ZDUMP_LO_YEAR */
Packit 6c4009
Packit 6c4009
#ifndef ZDUMP_HI_YEAR
Packit 6c4009
#define ZDUMP_HI_YEAR	2500
Packit 6c4009
#endif /* !defined ZDUMP_HI_YEAR */
Packit 6c4009
Packit 6c4009
#ifndef MAX_STRING_LENGTH
Packit 6c4009
#define MAX_STRING_LENGTH	1024
Packit 6c4009
#endif /* !defined MAX_STRING_LENGTH */
Packit 6c4009
Packit 6c4009
#define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
Packit 6c4009
#define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
Packit 6c4009
#define SECSPER400YEARS	(SECSPERNYEAR * (intmax_t) (300 + 3)	\
Packit 6c4009
			 + SECSPERLYEAR * (intmax_t) (100 - 3))
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
** True if SECSPER400YEARS is known to be representable as an
Packit 6c4009
** intmax_t.  It's OK that SECSPER400YEARS_FITS can in theory be false
Packit 6c4009
** even if SECSPER400YEARS is representable, because when that happens
Packit 6c4009
** the code merely runs a bit more slowly, and this slowness doesn't
Packit 6c4009
** occur on any practical platform.
Packit 6c4009
*/
Packit 6c4009
enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
Packit 6c4009
Packit 6c4009
#if HAVE_GETTEXT
Packit 6c4009
#include <locale.h>	/* for setlocale */
Packit 6c4009
#endif /* HAVE_GETTEXT */
Packit 6c4009
Packit 6c4009
#if ! HAVE_LOCALTIME_RZ
Packit 6c4009
# undef  timezone_t
Packit 6c4009
# define timezone_t char **
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
extern char **	environ;
Packit 6c4009
Packit 6c4009
#if !HAVE_POSIX_DECLS
Packit 6c4009
extern int	getopt(int argc, char * const argv[],
Packit 6c4009
			const char * options);
Packit 6c4009
extern char *	optarg;
Packit 6c4009
extern int	optind;
Packit 6c4009
extern char *	tzname[];
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* The minimum and maximum finite time values.  */
Packit 6c4009
enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 };
Packit 6c4009
static time_t const absolute_min_time =
Packit 6c4009
  ((time_t) -1 < 0
Packit 6c4009
   ? (- ((time_t) ~ (time_t) 0 < 0)
Packit 6c4009
      - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
Packit 6c4009
   : 0);
Packit 6c4009
static time_t const absolute_max_time =
Packit 6c4009
  ((time_t) -1 < 0
Packit 6c4009
   ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
Packit 6c4009
   : -1);
Packit 6c4009
static int	longest;
Packit 6c4009
static char *	progname;
Packit 6c4009
static bool	warned;
Packit 6c4009
static bool	errout;
Packit 6c4009
Packit 6c4009
static char const *abbr(struct tm const *);
Packit 6c4009
static intmax_t	delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
Packit 6c4009
static void dumptime(struct tm const *);
Packit 6c4009
static time_t hunt(timezone_t, char *, time_t, time_t);
Packit 6c4009
static void show(timezone_t, char *, time_t, bool);
Packit 6c4009
static void showtrans(char const *, struct tm const *, time_t, char const *,
Packit 6c4009
		      char const *);
Packit 6c4009
static const char *tformat(void);
Packit 6c4009
static time_t yeartot(intmax_t) ATTRIBUTE_PURE;
Packit 6c4009
Packit 6c4009
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
Packit 6c4009
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
Packit 6c4009
Packit 6c4009
/* Is A an alphabetic character in the C locale?  */
Packit 6c4009
static bool
Packit 6c4009
is_alpha(char a)
Packit 6c4009
{
Packit 6c4009
	switch (a) {
Packit 6c4009
	  default:
Packit 6c4009
		return false;
Packit 6c4009
	  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
Packit 6c4009
	  case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
Packit 6c4009
	  case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
Packit 6c4009
	  case 'V': case 'W': case 'X': case 'Y': case 'Z':
Packit 6c4009
	  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
Packit 6c4009
	  case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
Packit 6c4009
	  case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
Packit 6c4009
	  case 'v': case 'w': case 'x': case 'y': case 'z':
Packit 6c4009
		return true;
Packit 6c4009
	}
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return A + B, exiting if the result would overflow.  */
Packit 6c4009
static size_t
Packit 6c4009
sumsize(size_t a, size_t b)
Packit 6c4009
{
Packit 6c4009
  size_t sum = a + b;
Packit 6c4009
  if (sum < a) {
Packit 6c4009
    fprintf(stderr, "%s: size overflow\n", progname);
Packit 6c4009
    exit(EXIT_FAILURE);
Packit 6c4009
  }
Packit 6c4009
  return sum;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return a pointer to a newly allocated buffer of size SIZE, exiting
Packit 6c4009
   on failure.  SIZE should be nonzero.  */
Packit 6c4009
static void *
Packit 6c4009
xmalloc(size_t size)
Packit 6c4009
{
Packit 6c4009
  void *p = malloc(size);
Packit 6c4009
  if (!p) {
Packit 6c4009
    perror(progname);
Packit 6c4009
    exit(EXIT_FAILURE);
Packit 6c4009
  }
Packit 6c4009
  return p;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#if ! HAVE_TZSET
Packit 6c4009
# undef tzset
Packit 6c4009
# define tzset zdump_tzset
Packit 6c4009
static void tzset(void) { }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Assume gmtime_r works if localtime_r does.
Packit 6c4009
   A replacement localtime_r is defined below if needed.  */
Packit 6c4009
#if ! HAVE_LOCALTIME_R
Packit 6c4009
Packit 6c4009
# undef gmtime_r
Packit 6c4009
# define gmtime_r zdump_gmtime_r
Packit 6c4009
Packit 6c4009
static struct tm *
Packit 6c4009
gmtime_r(time_t *tp, struct tm *tmp)
Packit 6c4009
{
Packit 6c4009
  struct tm *r = gmtime(tp);
Packit 6c4009
  if (r) {
Packit 6c4009
    *tmp = *r;
Packit 6c4009
    r = tmp;
Packit 6c4009
  }
Packit 6c4009
  return r;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Platforms with TM_ZONE don't need tzname, so they can use the
Packit 6c4009
   faster localtime_rz or localtime_r if available.  */
Packit 6c4009
Packit 6c4009
#if defined TM_ZONE && HAVE_LOCALTIME_RZ
Packit 6c4009
# define USE_LOCALTIME_RZ true
Packit 6c4009
#else
Packit 6c4009
# define USE_LOCALTIME_RZ false
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if ! USE_LOCALTIME_RZ
Packit 6c4009
Packit 6c4009
# if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
Packit 6c4009
#  undef localtime_r
Packit 6c4009
#  define localtime_r zdump_localtime_r
Packit 6c4009
static struct tm *
Packit 6c4009
localtime_r(time_t *tp, struct tm *tmp)
Packit 6c4009
{
Packit 6c4009
  struct tm *r = localtime(tp);
Packit 6c4009
  if (r) {
Packit 6c4009
    *tmp = *r;
Packit 6c4009
    r = tmp;
Packit 6c4009
  }
Packit 6c4009
  return r;
Packit 6c4009
}
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
# undef localtime_rz
Packit 6c4009
# define localtime_rz zdump_localtime_rz
Packit 6c4009
static struct tm *
Packit 6c4009
localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp)
Packit 6c4009
{
Packit 6c4009
  return localtime_r(tp, tmp);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
# ifdef TYPECHECK
Packit 6c4009
#  undef mktime_z
Packit 6c4009
#  define mktime_z zdump_mktime_z
Packit 6c4009
static time_t
Packit 6c4009
mktime_z(timezone_t tz, struct tm *tmp)
Packit 6c4009
{
Packit 6c4009
  return mktime(tmp);
Packit 6c4009
}
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
# undef tzalloc
Packit 6c4009
# undef tzfree
Packit 6c4009
# define tzalloc zdump_tzalloc
Packit 6c4009
# define tzfree zdump_tzfree
Packit 6c4009
Packit 6c4009
static timezone_t
Packit 6c4009
tzalloc(char const *val)
Packit 6c4009
{
Packit 6c4009
  static char **fakeenv;
Packit 6c4009
  char **env = fakeenv;
Packit 6c4009
  char *env0;
Packit 6c4009
  if (! env) {
Packit 6c4009
    char **e = environ;
Packit 6c4009
    int to;
Packit 6c4009
Packit 6c4009
    while (*e++)
Packit 6c4009
      continue;
Packit 6c4009
    env = xmalloc(sumsize(sizeof *environ,
Packit 6c4009
			  (e - environ) * sizeof *environ));
Packit 6c4009
    to = 1;
Packit 6c4009
    for (e = environ; (env[to] = *e); e++)
Packit 6c4009
      to += strncmp(*e, "TZ=", 3) != 0;
Packit 6c4009
  }
Packit 6c4009
  env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
Packit 6c4009
  env[0] = strcat(strcpy(env0, "TZ="), val);
Packit 6c4009
  environ = fakeenv = env;
Packit 6c4009
  tzset();
Packit 6c4009
  return env;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
tzfree(timezone_t env)
Packit 6c4009
{
Packit 6c4009
  environ = env + 1;
Packit 6c4009
  free(env[0]);
Packit 6c4009
}
Packit 6c4009
#endif /* ! USE_LOCALTIME_RZ */
Packit 6c4009
Packit 6c4009
/* A UTC time zone, and its initializer.  */
Packit 6c4009
static timezone_t gmtz;
Packit 6c4009
static void
Packit 6c4009
gmtzinit(void)
Packit 6c4009
{
Packit 6c4009
  if (USE_LOCALTIME_RZ) {
Packit 6c4009
    static char const utc[] = "UTC0";
Packit 6c4009
    gmtz = tzalloc(utc);
Packit 6c4009
    if (!gmtz) {
Packit 6c4009
      perror(utc);
Packit 6c4009
      exit(EXIT_FAILURE);
Packit 6c4009
    }
Packit 6c4009
  }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Convert *TP to UTC, storing the broken-down time into *TMP.
Packit 6c4009
   Return TMP if successful, NULL otherwise.  This is like gmtime_r(TP, TMP),
Packit 6c4009
   except typically faster if USE_LOCALTIME_RZ.  */
Packit 6c4009
static struct tm *
Packit 6c4009
my_gmtime_r(time_t *tp, struct tm *tmp)
Packit 6c4009
{
Packit 6c4009
  return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#ifndef TYPECHECK
Packit 6c4009
# define my_localtime_rz localtime_rz
Packit 6c4009
#else /* !defined TYPECHECK */
Packit 6c4009
Packit 6c4009
static struct tm *
Packit 6c4009
my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
Packit 6c4009
{
Packit 6c4009
	tmp = localtime_rz(tz, tp, tmp);
Packit 6c4009
	if (tmp) {
Packit 6c4009
		struct tm	tm;
Packit 6c4009
		register time_t	t;
Packit 6c4009
Packit 6c4009
		tm = *tmp;
Packit 6c4009
		t = mktime_z(tz, &tm;;
Packit 6c4009
		if (t != *tp) {
Packit 6c4009
			fflush(stdout);
Packit 6c4009
			fprintf(stderr, "\n%s: ", progname);
Packit 6c4009
			fprintf(stderr, tformat(), *tp);
Packit 6c4009
			fprintf(stderr, " ->");
Packit 6c4009
			fprintf(stderr, " year=%d", tmp->tm_year);
Packit 6c4009
			fprintf(stderr, " mon=%d", tmp->tm_mon);
Packit 6c4009
			fprintf(stderr, " mday=%d", tmp->tm_mday);
Packit 6c4009
			fprintf(stderr, " hour=%d", tmp->tm_hour);
Packit 6c4009
			fprintf(stderr, " min=%d", tmp->tm_min);
Packit 6c4009
			fprintf(stderr, " sec=%d", tmp->tm_sec);
Packit 6c4009
			fprintf(stderr, " isdst=%d", tmp->tm_isdst);
Packit 6c4009
			fprintf(stderr, " -> ");
Packit 6c4009
			fprintf(stderr, tformat(), t);
Packit 6c4009
			fprintf(stderr, "\n");
Packit 6c4009
			errout = true;
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
	return tmp;
Packit 6c4009
}
Packit 6c4009
#endif /* !defined TYPECHECK */
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
abbrok(const char *const abbrp, const char *const zone)
Packit 6c4009
{
Packit 6c4009
	register const char *	cp;
Packit 6c4009
	register const char *	wp;
Packit 6c4009
Packit 6c4009
	if (warned)
Packit 6c4009
		return;
Packit 6c4009
	cp = abbrp;
Packit 6c4009
	while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
Packit 6c4009
		++cp;
Packit 6c4009
	if (cp - abbrp < 3)
Packit 6c4009
	  wp = _("has fewer than 3 characters");
Packit 6c4009
	else if (cp - abbrp > 6)
Packit 6c4009
	  wp = _("has more than 6 characters");
Packit 6c4009
	else if (*cp)
Packit 6c4009
	  wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
Packit 6c4009
	else
Packit 6c4009
	  return;
Packit 6c4009
	fflush(stdout);
Packit 6c4009
	fprintf(stderr,
Packit 6c4009
		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
Packit 6c4009
		progname, zone, abbrp, wp);
Packit 6c4009
	warned = errout = true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return a time zone abbreviation.  If the abbreviation needs to be
Packit 6c4009
   saved, use *BUF (of size *BUFALLOC) to save it, and return the
Packit 6c4009
   abbreviation in the possibly-reallocated *BUF.  Otherwise, just
Packit 6c4009
   return the abbreviation.  Get the abbreviation from TMP.
Packit 6c4009
   Exit on memory allocation failure.  */
Packit 6c4009
static char const *
Packit 6c4009
saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp)
Packit 6c4009
{
Packit 6c4009
  char const *ab = abbr(tmp);
Packit 6c4009
  if (HAVE_LOCALTIME_RZ)
Packit 6c4009
    return ab;
Packit 6c4009
  else {
Packit 6c4009
    size_t ablen = strlen(ab);
Packit 6c4009
    if (*bufalloc <= ablen) {
Packit 6c4009
      free(*buf);
Packit 6c4009
Packit 6c4009
      /* Make the new buffer at least twice as long as the old,
Packit 6c4009
	 to avoid O(N**2) behavior on repeated calls.  */
Packit 6c4009
      *bufalloc = sumsize(*bufalloc, ablen + 1);
Packit 6c4009
Packit 6c4009
      *buf = xmalloc(*bufalloc);
Packit 6c4009
    }
Packit 6c4009
    return strcpy(*buf, ab);
Packit 6c4009
  }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
close_file(FILE *stream)
Packit 6c4009
{
Packit 6c4009
  char const *e = (ferror(stream) ? _("I/O error")
Packit 6c4009
		   : fclose(stream) != 0 ? strerror(errno) : NULL);
Packit 6c4009
  if (e) {
Packit 6c4009
    fprintf(stderr, "%s: %s\n", progname, e);
Packit 6c4009
    exit(EXIT_FAILURE);
Packit 6c4009
  }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
usage(FILE * const stream, const int status)
Packit 6c4009
{
Packit 6c4009
	fprintf(stream,
Packit 6c4009
_("%s: usage: %s OPTIONS ZONENAME ...\n"
Packit 6c4009
  "Options include:\n"
Packit 6c4009
  "  -c [L,]U   Start at year L (default -500), end before year U (default 2500)\n"
Packit 6c4009
  "  -t [L,]U   Start at time L, end before time U (in seconds since 1970)\n"
Packit 6c4009
  "  -i         List transitions briefly (format is experimental)\n" \
Packit 6c4009
  "  -v         List transitions verbosely\n"
Packit 6c4009
  "  -V         List transitions a bit less verbosely\n"
Packit 6c4009
  "  --help     Output this help\n"
Packit 6c4009
  "  --version  Output version info\n"
Packit 6c4009
  "\n"
Packit 6c4009
  "Report bugs to %s.\n"),
Packit 6c4009
		progname, progname, REPORT_BUGS_TO);
Packit 6c4009
	if (status == EXIT_SUCCESS)
Packit 6c4009
	  close_file(stream);
Packit 6c4009
	exit(status);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main(int argc, char *argv[])
Packit 6c4009
{
Packit 6c4009
	/* These are static so that they're initially zero.  */
Packit 6c4009
	static char *		abbrev;
Packit 6c4009
	static size_t		abbrevsize;
Packit 6c4009
Packit 6c4009
	register int		i;
Packit 6c4009
	register bool		vflag;
Packit 6c4009
	register bool		Vflag;
Packit 6c4009
	register char *		cutarg;
Packit 6c4009
	register char *		cuttimes;
Packit 6c4009
	register time_t		cutlotime;
Packit 6c4009
	register time_t		cuthitime;
Packit 6c4009
	time_t			now;
Packit 6c4009
	bool iflag = false;
Packit 6c4009
Packit 6c4009
	cutlotime = absolute_min_time;
Packit 6c4009
	cuthitime = absolute_max_time;
Packit 6c4009
#if HAVE_GETTEXT
Packit 6c4009
	setlocale(LC_ALL, "");
Packit 6c4009
#ifdef TZ_DOMAINDIR
Packit 6c4009
	bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
Packit 6c4009
#endif /* defined TEXTDOMAINDIR */
Packit 6c4009
	textdomain(TZ_DOMAIN);
Packit 6c4009
#endif /* HAVE_GETTEXT */
Packit 6c4009
	progname = argv[0];
Packit 6c4009
	for (i = 1; i < argc; ++i)
Packit 6c4009
		if (strcmp(argv[i], "--version") == 0) {
Packit 6c4009
			printf("zdump %s%s\n", PKGVERSION, TZVERSION);
Packit 6c4009
			return EXIT_SUCCESS;
Packit 6c4009
		} else if (strcmp(argv[i], "--help") == 0) {
Packit 6c4009
			usage(stdout, EXIT_SUCCESS);
Packit 6c4009
		}
Packit 6c4009
	vflag = Vflag = false;
Packit 6c4009
	cutarg = cuttimes = NULL;
Packit 6c4009
	for (;;)
Packit 6c4009
	  switch (getopt(argc, argv, "c:it:vV")) {
Packit 6c4009
	  case 'c': cutarg = optarg; break;
Packit 6c4009
	  case 't': cuttimes = optarg; break;
Packit 6c4009
	  case 'i': iflag = true; break;
Packit 6c4009
	  case 'v': vflag = true; break;
Packit 6c4009
	  case 'V': Vflag = true; break;
Packit 6c4009
	  case -1:
Packit 6c4009
	    if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
Packit 6c4009
	      goto arg_processing_done;
Packit 6c4009
	    /* Fall through.  */
Packit 6c4009
	  default:
Packit 6c4009
	    usage(stderr, EXIT_FAILURE);
Packit 6c4009
	  }
Packit 6c4009
 arg_processing_done:;
Packit 6c4009
Packit 6c4009
	if (iflag | vflag | Vflag) {
Packit 6c4009
		intmax_t	lo;
Packit 6c4009
		intmax_t	hi;
Packit 6c4009
		char *loend, *hiend;
Packit 6c4009
		register intmax_t cutloyear = ZDUMP_LO_YEAR;
Packit 6c4009
		register intmax_t cuthiyear = ZDUMP_HI_YEAR;
Packit 6c4009
		if (cutarg != NULL) {
Packit 6c4009
			lo = strtoimax(cutarg, &loend, 10);
Packit 6c4009
			if (cutarg != loend && !*loend) {
Packit 6c4009
				hi = lo;
Packit 6c4009
				cuthiyear = hi;
Packit 6c4009
			} else if (cutarg != loend && *loend == ','
Packit 6c4009
				   && (hi = strtoimax(loend + 1, &hiend, 10),
Packit 6c4009
				       loend + 1 != hiend && !*hiend)) {
Packit 6c4009
				cutloyear = lo;
Packit 6c4009
				cuthiyear = hi;
Packit 6c4009
			} else {
Packit 6c4009
				fprintf(stderr, _("%s: wild -c argument %s\n"),
Packit 6c4009
					progname, cutarg);
Packit 6c4009
				return EXIT_FAILURE;
Packit 6c4009
			}
Packit 6c4009
		}
Packit 6c4009
		if (cutarg != NULL || cuttimes == NULL) {
Packit 6c4009
			cutlotime = yeartot(cutloyear);
Packit 6c4009
			cuthitime = yeartot(cuthiyear);
Packit 6c4009
		}
Packit 6c4009
		if (cuttimes != NULL) {
Packit 6c4009
			lo = strtoimax(cuttimes, &loend, 10);
Packit 6c4009
			if (cuttimes != loend && !*loend) {
Packit 6c4009
				hi = lo;
Packit 6c4009
				if (hi < cuthitime) {
Packit 6c4009
					if (hi < absolute_min_time)
Packit 6c4009
						hi = absolute_min_time;
Packit 6c4009
					cuthitime = hi;
Packit 6c4009
				}
Packit 6c4009
			} else if (cuttimes != loend && *loend == ','
Packit 6c4009
				   && (hi = strtoimax(loend + 1, &hiend, 10),
Packit 6c4009
				       loend + 1 != hiend && !*hiend)) {
Packit 6c4009
				if (cutlotime < lo) {
Packit 6c4009
					if (absolute_max_time < lo)
Packit 6c4009
						lo = absolute_max_time;
Packit 6c4009
					cutlotime = lo;
Packit 6c4009
				}
Packit 6c4009
				if (hi < cuthitime) {
Packit 6c4009
					if (hi < absolute_min_time)
Packit 6c4009
						hi = absolute_min_time;
Packit 6c4009
					cuthitime = hi;
Packit 6c4009
				}
Packit 6c4009
			} else {
Packit 6c4009
				fprintf(stderr,
Packit 6c4009
					_("%s: wild -t argument %s\n"),
Packit 6c4009
					progname, cuttimes);
Packit 6c4009
				return EXIT_FAILURE;
Packit 6c4009
			}
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
	gmtzinit();
Packit 6c4009
	INITIALIZE (now);
Packit 6c4009
	if (! (iflag | vflag | Vflag))
Packit 6c4009
	  now = time(NULL);
Packit 6c4009
	longest = 0;
Packit 6c4009
	for (i = optind; i < argc; i++) {
Packit 6c4009
	  size_t arglen = strlen(argv[i]);
Packit 6c4009
	  if (longest < arglen)
Packit 6c4009
	    longest = arglen < INT_MAX ? arglen : INT_MAX;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	for (i = optind; i < argc; ++i) {
Packit 6c4009
		timezone_t tz = tzalloc(argv[i]);
Packit 6c4009
		char const *ab;
Packit 6c4009
		time_t t;
Packit 6c4009
		struct tm tm, newtm;
Packit 6c4009
		bool tm_ok;
Packit 6c4009
		if (!tz) {
Packit 6c4009
		  perror(argv[i]);
Packit 6c4009
		  return EXIT_FAILURE;
Packit 6c4009
		}
Packit 6c4009
		if (! (iflag | vflag | Vflag)) {
Packit 6c4009
			show(tz, argv[i], now, false);
Packit 6c4009
			tzfree(tz);
Packit 6c4009
			continue;
Packit 6c4009
		}
Packit 6c4009
		warned = false;
Packit 6c4009
		t = absolute_min_time;
Packit 6c4009
		if (! (iflag | Vflag)) {
Packit 6c4009
			show(tz, argv[i], t, true);
Packit 6c4009
			t += SECSPERDAY;
Packit 6c4009
			show(tz, argv[i], t, true);
Packit 6c4009
		}
Packit 6c4009
		if (t < cutlotime)
Packit 6c4009
			t = cutlotime;
Packit 6c4009
		tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
Packit 6c4009
		if (tm_ok) {
Packit 6c4009
		  ab = saveabbr(&abbrev, &abbrevsize, &tm;;
Packit 6c4009
		  if (iflag) {
Packit 6c4009
		    showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
Packit 6c4009
		    showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
Packit 6c4009
		  }
Packit 6c4009
		}
Packit 6c4009
		while (t < cuthitime) {
Packit 6c4009
		  time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
Packit 6c4009
				  && t + SECSPERDAY / 2 < cuthitime)
Packit 6c4009
				 ? t + SECSPERDAY / 2
Packit 6c4009
				 : cuthitime);
Packit 6c4009
		  struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
Packit 6c4009
		  bool newtm_ok = newtmp != NULL;
Packit 6c4009
		  if (! (tm_ok & newtm_ok
Packit 6c4009
			 ? (delta(&newtm, &tm) == newt - t
Packit 6c4009
			    && newtm.tm_isdst == tm.tm_isdst
Packit 6c4009
			    && strcmp(abbr(&newtm), ab) == 0)
Packit 6c4009
			 : tm_ok == newtm_ok)) {
Packit 6c4009
		    newt = hunt(tz, argv[i], t, newt);
Packit 6c4009
		    newtmp = localtime_rz(tz, &newt, &newtm);
Packit 6c4009
		    newtm_ok = newtmp != NULL;
Packit 6c4009
		    if (iflag)
Packit 6c4009
		      showtrans("%Y-%m-%d\t%L\t%Q", newtmp, newt,
Packit 6c4009
				newtm_ok ? abbr(&newtm) : NULL, argv[i]);
Packit 6c4009
		    else {
Packit 6c4009
		      show(tz, argv[i], newt - 1, true);
Packit 6c4009
		      show(tz, argv[i], newt, true);
Packit 6c4009
		    }
Packit 6c4009
		  }
Packit 6c4009
		  t = newt;
Packit 6c4009
		  tm_ok = newtm_ok;
Packit 6c4009
		  if (newtm_ok) {
Packit 6c4009
		    ab = saveabbr(&abbrev, &abbrevsize, &newtm);
Packit 6c4009
		    tm = newtm;
Packit 6c4009
		  }
Packit 6c4009
		}
Packit 6c4009
		if (! (iflag | Vflag)) {
Packit 6c4009
			t = absolute_max_time;
Packit 6c4009
			t -= SECSPERDAY;
Packit 6c4009
			show(tz, argv[i], t, true);
Packit 6c4009
			t += SECSPERDAY;
Packit 6c4009
			show(tz, argv[i], t, true);
Packit 6c4009
		}
Packit 6c4009
		tzfree(tz);
Packit 6c4009
	}
Packit 6c4009
	close_file(stdout);
Packit 6c4009
	if (errout && (ferror(stderr) || fclose(stderr) != 0))
Packit 6c4009
	  return EXIT_FAILURE;
Packit 6c4009
	return EXIT_SUCCESS;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static time_t
Packit 6c4009
yeartot(intmax_t y)
Packit 6c4009
{
Packit 6c4009
	register intmax_t	myy, seconds, years;
Packit 6c4009
	register time_t		t;
Packit 6c4009
Packit 6c4009
	myy = EPOCH_YEAR;
Packit 6c4009
	t = 0;
Packit 6c4009
	while (myy < y) {
Packit 6c4009
		if (SECSPER400YEARS_FITS && 400 <= y - myy) {
Packit 6c4009
			intmax_t diff400 = (y - myy) / 400;
Packit 6c4009
			if (INTMAX_MAX / SECSPER400YEARS < diff400)
Packit 6c4009
				return absolute_max_time;
Packit 6c4009
			seconds = diff400 * SECSPER400YEARS;
Packit 6c4009
			years = diff400 * 400;
Packit 6c4009
                } else {
Packit 6c4009
			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
Packit 6c4009
			years = 1;
Packit 6c4009
		}
Packit 6c4009
		myy += years;
Packit 6c4009
		if (t > absolute_max_time - seconds)
Packit 6c4009
			return absolute_max_time;
Packit 6c4009
		t += seconds;
Packit 6c4009
	}
Packit 6c4009
	while (y < myy) {
Packit 6c4009
		if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
Packit 6c4009
			intmax_t diff400 = (myy - y) / 400;
Packit 6c4009
			if (INTMAX_MAX / SECSPER400YEARS < diff400)
Packit 6c4009
				return absolute_min_time;
Packit 6c4009
			seconds = diff400 * SECSPER400YEARS;
Packit 6c4009
			years = diff400 * 400;
Packit 6c4009
		} else {
Packit 6c4009
			seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
Packit 6c4009
			years = 1;
Packit 6c4009
		}
Packit 6c4009
		myy -= years;
Packit 6c4009
		if (t < absolute_min_time + seconds)
Packit 6c4009
			return absolute_min_time;
Packit 6c4009
		t -= seconds;
Packit 6c4009
	}
Packit 6c4009
	return t;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static time_t
Packit 6c4009
hunt(timezone_t tz, char *name, time_t lot, time_t hit)
Packit 6c4009
{
Packit 6c4009
	static char *		loab;
Packit 6c4009
	static size_t		loabsize;
Packit 6c4009
	char const *		ab;
Packit 6c4009
	time_t			t;
Packit 6c4009
	struct tm		lotm;
Packit 6c4009
	struct tm		tm;
Packit 6c4009
	bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
Packit 6c4009
	bool tm_ok;
Packit 6c4009
Packit 6c4009
	if (lotm_ok)
Packit 6c4009
	  ab = saveabbr(&loab, &loabsize, &lotm;;
Packit 6c4009
	for ( ; ; ) {
Packit 6c4009
		time_t diff = hit - lot;
Packit 6c4009
		if (diff < 2)
Packit 6c4009
			break;
Packit 6c4009
		t = lot;
Packit 6c4009
		t += diff / 2;
Packit 6c4009
		if (t <= lot)
Packit 6c4009
			++t;
Packit 6c4009
		else if (t >= hit)
Packit 6c4009
			--t;
Packit 6c4009
		tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
Packit 6c4009
		if (lotm_ok & tm_ok
Packit 6c4009
		    ? (delta(&tm, &lotm) == t - lot
Packit 6c4009
		       && tm.tm_isdst == lotm.tm_isdst
Packit 6c4009
		       && strcmp(abbr(&tm), ab) == 0)
Packit 6c4009
		    : lotm_ok == tm_ok) {
Packit 6c4009
		  lot = t;
Packit 6c4009
		  if (tm_ok)
Packit 6c4009
		    lotm = tm;
Packit 6c4009
		} else	hit = t;
Packit 6c4009
	}
Packit 6c4009
	return hit;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
** Thanks to Paul Eggert for logic used in delta.
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
static intmax_t
Packit 6c4009
delta(struct tm * newp, struct tm *oldp)
Packit 6c4009
{
Packit 6c4009
	register intmax_t	result;
Packit 6c4009
	register int		tmy;
Packit 6c4009
Packit 6c4009
	if (newp->tm_year < oldp->tm_year)
Packit 6c4009
		return -delta(oldp, newp);
Packit 6c4009
	result = 0;
Packit 6c4009
	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
Packit 6c4009
		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
Packit 6c4009
	result += newp->tm_yday - oldp->tm_yday;
Packit 6c4009
	result *= HOURSPERDAY;
Packit 6c4009
	result += newp->tm_hour - oldp->tm_hour;
Packit 6c4009
	result *= MINSPERHOUR;
Packit 6c4009
	result += newp->tm_min - oldp->tm_min;
Packit 6c4009
	result *= SECSPERMIN;
Packit 6c4009
	result += newp->tm_sec - oldp->tm_sec;
Packit 6c4009
	return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#ifndef TM_GMTOFF
Packit 6c4009
/* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
Packit 6c4009
   Assume A and B differ by at most one year.  */
Packit 6c4009
static int
Packit 6c4009
adjusted_yday(struct tm const *a, struct tm const *b)
Packit 6c4009
{
Packit 6c4009
  int yday = a->tm_yday;
Packit 6c4009
  if (b->tm_year < a->tm_year)
Packit 6c4009
    yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
Packit 6c4009
  return yday;
Packit 6c4009
}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* If A is the broken-down local time and B the broken-down UTC for
Packit 6c4009
   the same instant, return A's UTC offset in seconds, where positive
Packit 6c4009
   offsets are east of Greenwich.  On failure, return LONG_MIN.
Packit 6c4009
Packit 6c4009
   If T is nonnull, *T is the timestamp that corresponds to A; call
Packit 6c4009
   my_gmtime_r and use its result instead of B.  Otherwise, B is the
Packit 6c4009
   possibly nonnull result of an earlier call to my_gmtime_r.  */
Packit 6c4009
static long
Packit 6c4009
gmtoff(struct tm const *a, time_t *t, struct tm const *b)
Packit 6c4009
{
Packit 6c4009
#ifdef TM_GMTOFF
Packit 6c4009
  return a->TM_GMTOFF;
Packit 6c4009
#else
Packit 6c4009
  struct tm tm;
Packit 6c4009
  if (t)
Packit 6c4009
    b = my_gmtime_r(t, &tm;;
Packit 6c4009
  if (! b)
Packit 6c4009
    return LONG_MIN;
Packit 6c4009
  else {
Packit 6c4009
    int ayday = adjusted_yday(a, b);
Packit 6c4009
    int byday = adjusted_yday(b, a);
Packit 6c4009
    int days = ayday - byday;
Packit 6c4009
    long hours = a->tm_hour - b->tm_hour + 24 * days;
Packit 6c4009
    long minutes = a->tm_min - b->tm_min + 60 * hours;
Packit 6c4009
    long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
Packit 6c4009
    return seconds;
Packit 6c4009
  }
Packit 6c4009
#endif
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
show(timezone_t tz, char *zone, time_t t, bool v)
Packit 6c4009
{
Packit 6c4009
	register struct tm *	tmp;
Packit 6c4009
	register struct tm *	gmtmp;
Packit 6c4009
	struct tm tm, gmtm;
Packit 6c4009
Packit 6c4009
	printf("%-*s  ", longest, zone);
Packit 6c4009
	if (v) {
Packit 6c4009
		gmtmp = my_gmtime_r(&t, &gmtm);
Packit 6c4009
		if (gmtmp == NULL) {
Packit 6c4009
			printf(tformat(), t);
Packit 6c4009
		} else {
Packit 6c4009
			dumptime(gmtmp);
Packit 6c4009
			printf(" UT");
Packit 6c4009
		}
Packit 6c4009
		printf(" = ");
Packit 6c4009
	}
Packit 6c4009
	tmp = my_localtime_rz(tz, &t, &tm;;
Packit 6c4009
	dumptime(tmp);
Packit 6c4009
	if (tmp != NULL) {
Packit 6c4009
		if (*abbr(tmp) != '\0')
Packit 6c4009
			printf(" %s", abbr(tmp));
Packit 6c4009
		if (v) {
Packit 6c4009
			long off = gmtoff(tmp, NULL, gmtmp);
Packit 6c4009
			printf(" isdst=%d", tmp->tm_isdst);
Packit 6c4009
			if (off != LONG_MIN)
Packit 6c4009
			  printf(" gmtoff=%ld", off);
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
	printf("\n");
Packit 6c4009
	if (tmp != NULL && *abbr(tmp) != '\0')
Packit 6c4009
		abbrok(abbr(tmp), zone);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Store into BUF, of size SIZE, a formatted local time taken from *TM.
Packit 6c4009
   Use ISO 8601 format +HH:MM:SS.  Omit :SS if SS is zero, and omit
Packit 6c4009
   :MM too if MM is also zero.
Packit 6c4009
Packit 6c4009
   Return the length of the resulting string.  If the string does not
Packit 6c4009
   fit, return the length that the string would have been if it had
Packit 6c4009
   fit; do not overrun the output buffer.  */
Packit 6c4009
static int
Packit 6c4009
format_local_time(char *buf, size_t size, struct tm const *tm)
Packit 6c4009
{
Packit 6c4009
  int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
Packit 6c4009
  return (ss
Packit 6c4009
	  ? snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
Packit 6c4009
	  : mm
Packit 6c4009
	  ? snprintf(buf, size, "%02d:%02d", hh, mm)
Packit 6c4009
	  : snprintf(buf, size, "%02d", hh));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Store into BUF, of size SIZE, a formatted UTC offset for the
Packit 6c4009
   localtime *TM corresponding to time T.  Use ISO 8601 format
Packit 6c4009
   +HHMMSS, or -HHMMSS for timestamps west of Greenwich; use the
Packit 6c4009
   format -00 for unknown UTC offsets.  If the hour needs more than
Packit 6c4009
   two digits to represent, extend the length of HH as needed.
Packit 6c4009
   Otherwise, omit SS if SS is zero, and omit MM too if MM is also
Packit 6c4009
   zero.
Packit 6c4009
Packit 6c4009
   Return the length of the resulting string, or -1 if the result is
Packit 6c4009
   not representable as a string.  If the string does not fit, return
Packit 6c4009
   the length that the string would have been if it had fit; do not
Packit 6c4009
   overrun the output buffer.  */
Packit 6c4009
static int
Packit 6c4009
format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t)
Packit 6c4009
{
Packit 6c4009
  long off = gmtoff(tm, &t, NULL);
Packit 6c4009
  char sign = ((off < 0
Packit 6c4009
		|| (off == 0
Packit 6c4009
		    && (*abbr(tm) == '-' || strcmp(abbr(tm), "zzz") == 0)))
Packit 6c4009
	       ? '-' : '+');
Packit 6c4009
  long hh;
Packit 6c4009
  int mm, ss;
Packit 6c4009
  if (off < 0)
Packit 6c4009
    {
Packit 6c4009
      if (off == LONG_MIN)
Packit 6c4009
	return -1;
Packit 6c4009
      off = -off;
Packit 6c4009
    }
Packit 6c4009
  ss = off % 60;
Packit 6c4009
  mm = off / 60 % 60;
Packit 6c4009
  hh = off / 60 / 60;
Packit 6c4009
  return (ss || 100 <= hh
Packit 6c4009
	  ? snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
Packit 6c4009
	  : mm
Packit 6c4009
	  ? snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
Packit 6c4009
	  : snprintf(buf, size, "%c%02ld", sign, hh));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Store into BUF (of size SIZE) a quoted string representation of P.
Packit 6c4009
   If the representation's length is less than SIZE, return the
Packit 6c4009
   length; the representation is not null terminated.  Otherwise
Packit 6c4009
   return SIZE, to indicate that BUF is too small.  */
Packit 6c4009
static size_t
Packit 6c4009
format_quoted_string(char *buf, size_t size, char const *p)
Packit 6c4009
{
Packit 6c4009
  char *b = buf;
Packit 6c4009
  size_t s = size;
Packit 6c4009
  if (!s)
Packit 6c4009
    return size;
Packit 6c4009
  *b++ = '"', s--;
Packit 6c4009
  for (;;) {
Packit 6c4009
    char c = *p++;
Packit 6c4009
    if (s <= 1)
Packit 6c4009
      return size;
Packit 6c4009
    switch (c) {
Packit 6c4009
    default: *b++ = c, s--; continue;
Packit 6c4009
    case '\0': *b++ = '"', s--; return size - s;
Packit 6c4009
    case '"': case '\\': break;
Packit 6c4009
    case ' ': c = 's'; break;
Packit 6c4009
    case '\f': c = 'f'; break;
Packit 6c4009
    case '\n': c = 'n'; break;
Packit 6c4009
    case '\r': c = 'r'; break;
Packit 6c4009
    case '\t': c = 't'; break;
Packit 6c4009
    case '\v': c = 'v'; break;
Packit 6c4009
    }
Packit 6c4009
    *b++ = '\\', *b++ = c, s -= 2;
Packit 6c4009
  }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Store into BUF (of size SIZE) a timestamp formatted by TIME_FMT.
Packit 6c4009
   TM is the broken-down time, T the seconds count, AB the time zone
Packit 6c4009
   abbreviation, and ZONE_NAME the zone name.  Return true if
Packit 6c4009
   successful, false if the output would require more than SIZE bytes.
Packit 6c4009
   TIME_FMT uses the same format that strftime uses, with these
Packit 6c4009
   additions:
Packit 6c4009
Packit 6c4009
   %f zone name
Packit 6c4009
   %L local time as per format_local_time
Packit 6c4009
   %Q like "U\t%Z\tD" where U is the UTC offset as for format_utc_offset
Packit 6c4009
      and D is the isdst flag; except omit D if it is zero, omit %Z if
Packit 6c4009
      it equals U, quote and escape %Z if it contains nonalphabetics,
Packit 6c4009
      and omit any trailing tabs.  */
Packit 6c4009
Packit 6c4009
static bool
Packit 6c4009
istrftime(char *buf, size_t size, char const *time_fmt,
Packit 6c4009
	  struct tm const *tm, time_t t, char const *ab, char const *zone_name)
Packit 6c4009
{
Packit 6c4009
  char *b = buf;
Packit 6c4009
  size_t s = size;
Packit 6c4009
  char const *f = time_fmt, *p;
Packit 6c4009
Packit 6c4009
  for (p = f; ; p++)
Packit 6c4009
    if (*p == '%' && p[1] == '%')
Packit 6c4009
      p++;
Packit 6c4009
    else if (!*p
Packit 6c4009
	     || (*p == '%'
Packit 6c4009
		 && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
Packit 6c4009
      size_t formatted_len;
Packit 6c4009
      size_t f_prefix_len = p - f;
Packit 6c4009
      size_t f_prefix_copy_size = p - f + 2;
Packit 6c4009
      char fbuf[100];
Packit 6c4009
      bool oversized = sizeof fbuf <= f_prefix_copy_size;
Packit 6c4009
      char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf;
Packit 6c4009
      memcpy(f_prefix_copy, f, f_prefix_len);
Packit 6c4009
      strcpy(f_prefix_copy + f_prefix_len, "X");
Packit 6c4009
      formatted_len = strftime(b, s, f_prefix_copy, tm);
Packit 6c4009
      if (oversized)
Packit 6c4009
	free(f_prefix_copy);
Packit 6c4009
      if (formatted_len == 0)
Packit 6c4009
	return false;
Packit 6c4009
      formatted_len--;
Packit 6c4009
      b += formatted_len, s -= formatted_len;
Packit 6c4009
      if (!*p++)
Packit 6c4009
	break;
Packit 6c4009
      switch (*p) {
Packit 6c4009
      case 'f':
Packit 6c4009
	formatted_len = format_quoted_string(b, s, zone_name);
Packit 6c4009
	break;
Packit 6c4009
      case 'L':
Packit 6c4009
	formatted_len = format_local_time(b, s, tm);
Packit 6c4009
	break;
Packit 6c4009
      case 'Q':
Packit 6c4009
	{
Packit 6c4009
	  bool show_abbr;
Packit 6c4009
	  int offlen = format_utc_offset(b, s, tm, t);
Packit 6c4009
	  if (! (0 <= offlen && offlen < s))
Packit 6c4009
	    return false;
Packit 6c4009
	  show_abbr = strcmp(b, ab) != 0;
Packit 6c4009
	  b += offlen, s -= offlen;
Packit 6c4009
	  if (show_abbr) {
Packit 6c4009
	    char const *abp;
Packit 6c4009
	    size_t len;
Packit 6c4009
	    if (s <= 1)
Packit 6c4009
	      return false;
Packit 6c4009
	    *b++ = '\t', s--;
Packit 6c4009
	    for (abp = ab; is_alpha(*abp); abp++)
Packit 6c4009
	      continue;
Packit 6c4009
	    len = (!*abp && *ab
Packit 6c4009
		   ? snprintf(b, s, "%s", ab)
Packit 6c4009
		   : format_quoted_string(b, s, ab));
Packit 6c4009
	    if (s <= len)
Packit 6c4009
	      return false;
Packit 6c4009
	    b += len, s -= len;
Packit 6c4009
	  }
Packit 6c4009
	  formatted_len = (tm->tm_isdst
Packit 6c4009
			   ? snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
Packit 6c4009
			   : 0);
Packit 6c4009
	}
Packit 6c4009
	break;
Packit 6c4009
      }
Packit 6c4009
      if (s <= formatted_len)
Packit 6c4009
	return false;
Packit 6c4009
      b += formatted_len, s -= formatted_len;
Packit 6c4009
      f = p + 1;
Packit 6c4009
    }
Packit 6c4009
  *b = '\0';
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Show a time transition.  */
Packit 6c4009
static void
Packit 6c4009
showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
Packit 6c4009
	  char const *zone_name)
Packit 6c4009
{
Packit 6c4009
  if (!tm) {
Packit 6c4009
    printf(tformat(), t);
Packit 6c4009
    putchar('\n');
Packit 6c4009
  } else {
Packit 6c4009
    char stackbuf[1000];
Packit 6c4009
    size_t size = sizeof stackbuf;
Packit 6c4009
    char *buf = stackbuf;
Packit 6c4009
    char *bufalloc = NULL;
Packit 6c4009
    while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
Packit 6c4009
      size = sumsize(size, size);
Packit 6c4009
      free(bufalloc);
Packit 6c4009
      buf = bufalloc = xmalloc(size);
Packit 6c4009
    }
Packit 6c4009
    puts(buf);
Packit 6c4009
    free(bufalloc);
Packit 6c4009
  }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static char const *
Packit 6c4009
abbr(struct tm const *tmp)
Packit 6c4009
{
Packit 6c4009
#ifdef TM_ZONE
Packit 6c4009
	return tmp->TM_ZONE;
Packit 6c4009
#else
Packit 6c4009
	return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst]
Packit 6c4009
		? tzname[0 < tmp->tm_isdst]
Packit 6c4009
		: "");
Packit 6c4009
#endif
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
** The code below can fail on certain theoretical systems;
Packit 6c4009
** it works on all known real-world systems as of 2004-12-30.
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
static const char *
Packit 6c4009
tformat(void)
Packit 6c4009
{
Packit 6c4009
	if (0 > (time_t) -1) {		/* signed */
Packit 6c4009
		if (sizeof (time_t) == sizeof (intmax_t))
Packit 6c4009
			return "%"PRIdMAX;
Packit 6c4009
		if (sizeof (time_t) > sizeof (long))
Packit 6c4009
			return "%lld";
Packit 6c4009
		if (sizeof (time_t) > sizeof (int))
Packit 6c4009
			return "%ld";
Packit 6c4009
		return "%d";
Packit 6c4009
	}
Packit 6c4009
#ifdef PRIuMAX
Packit 6c4009
	if (sizeof (time_t) == sizeof (uintmax_t))
Packit 6c4009
		return "%"PRIuMAX;
Packit 6c4009
#endif
Packit 6c4009
	if (sizeof (time_t) > sizeof (unsigned long))
Packit 6c4009
		return "%llu";
Packit 6c4009
	if (sizeof (time_t) > sizeof (unsigned int))
Packit 6c4009
		return "%lu";
Packit 6c4009
	return "%u";
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
dumptime(register const struct tm *timeptr)
Packit 6c4009
{
Packit 6c4009
	static const char	wday_name[][3] = {
Packit 6c4009
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
Packit 6c4009
	};
Packit 6c4009
	static const char	mon_name[][3] = {
Packit 6c4009
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
Packit 6c4009
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
Packit 6c4009
	};
Packit 6c4009
	register const char *	wn;
Packit 6c4009
	register const char *	mn;
Packit 6c4009
	register int		lead;
Packit 6c4009
	register int		trail;
Packit 6c4009
Packit 6c4009
	if (timeptr == NULL) {
Packit 6c4009
		printf("NULL");
Packit 6c4009
		return;
Packit 6c4009
	}
Packit 6c4009
	/*
Packit 6c4009
	** The packaged localtime_rz and gmtime_r never put out-of-range
Packit 6c4009
	** values in tm_wday or tm_mon, but since this code might be compiled
Packit 6c4009
	** with other (perhaps experimental) versions, paranoia is in order.
Packit 6c4009
	*/
Packit 6c4009
	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
Packit 6c4009
		(int) (sizeof wday_name / sizeof wday_name[0]))
Packit 6c4009
			wn = "???";
Packit 6c4009
	else		wn = wday_name[timeptr->tm_wday];
Packit 6c4009
	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
Packit 6c4009
		(int) (sizeof mon_name / sizeof mon_name[0]))
Packit 6c4009
			mn = "???";
Packit 6c4009
	else		mn = mon_name[timeptr->tm_mon];
Packit 6c4009
	printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
Packit 6c4009
		wn, mn,
Packit 6c4009
		timeptr->tm_mday, timeptr->tm_hour,
Packit 6c4009
		timeptr->tm_min, timeptr->tm_sec);
Packit 6c4009
#define DIVISOR	10
Packit 6c4009
	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
Packit 6c4009
	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
Packit 6c4009
		trail / DIVISOR;
Packit 6c4009
	trail %= DIVISOR;
Packit 6c4009
	if (trail < 0 && lead > 0) {
Packit 6c4009
		trail += DIVISOR;
Packit 6c4009
		--lead;
Packit 6c4009
	} else if (lead < 0 && trail > 0) {
Packit 6c4009
		trail -= DIVISOR;
Packit 6c4009
		++lead;
Packit 6c4009
	}
Packit 6c4009
	if (lead == 0)
Packit 6c4009
		printf("%d", trail);
Packit 6c4009
	else	printf("%d%d", lead, ((trail < 0) ? -trail : trail));
Packit 6c4009
}