Blame lib/udf/udf_time.c

Packit dd8086
/* 
Packit dd8086
  Copyright (C) 2005, 2008, 2011 Rocky Bernstein <rocky@gnu.org>
Packit dd8086
  Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
Packit dd8086
Packit dd8086
  Modified From part of the GNU C Library.
Packit dd8086
  Contributed by Paul Eggert.
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
Packit dd8086
/* Some history from the GNU/Linux kernel from which this is also taken...
Packit dd8086
   dgb 10/02/98: ripped this from glibc source to help convert
Packit dd8086
                 timestamps to unix time
Packit dd8086
Packit dd8086
       10/04/98: added new table-based lookup after seeing how ugly the
Packit dd8086
                 gnu code is
Packit dd8086
  
Packit dd8086
   blf 09/27/99: ripped out all the old code and inserted new table from
Packit dd8086
                 John Brockmeyer (without leap second corrections)
Packit dd8086
                 rewrote udf_stamp_to_time and fixed timezone
Packit dd8086
                 accounting in udf_timespec_to_stamp.
Packit dd8086
*/
Packit dd8086
Packit dd8086
/*
Packit dd8086
 * We don't take into account leap seconds. This may be correct or incorrect.
Packit dd8086
 * For more NIST information (especially dealing with leap seconds), see:
Packit dd8086
 *  http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm
Packit dd8086
 */
Packit dd8086
Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
#include "config.h"
Packit dd8086
# define __CDIO_CONFIG_H__ 1
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef NEED_TIMEZONEVAR
Packit dd8086
#define timezonevar 1
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include "udf_private.h"
Packit dd8086
#include <cdio/udf.h>
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Imagine the below enum values as #define'd or constant values
Packit dd8086
   rather than distinct values of an enum.
Packit dd8086
*/
Packit dd8086
enum {
Packit dd8086
  HOURS_PER_DAY    =   24,
Packit dd8086
  SECS_PER_MINUTE  =   60,
Packit dd8086
  MAX_YEAR_SECONDS =   69,
Packit dd8086
  DAYS_PER_YEAR    =  365,  /* That is, in most of the years. */
Packit dd8086
  EPOCH_YEAR       = 1970,
Packit dd8086
  SECS_PER_HOUR	   = (60 * SECS_PER_MINUTE),
Packit dd8086
  SECS_PER_DAY	   = SECS_PER_HOUR * HOURS_PER_DAY
Packit dd8086
} debug_udf_time_enum;
Packit dd8086
  
Packit dd8086
#ifndef __isleap
Packit dd8086
/* Nonzero if YEAR is a leap year (every 4 years,
Packit dd8086
   except every 100th isn't, and every 400th is).  */
Packit dd8086
#define	__isleap(year)	\
Packit dd8086
  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/* How many days come before each month (0-12).  */
Packit dd8086
static const unsigned short int __mon_yday[2][13] =
Packit dd8086
  {
Packit dd8086
    /* Normal years.  */
Packit dd8086
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, DAYS_PER_YEAR },
Packit dd8086
    /* Leap years.  */
Packit dd8086
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, DAYS_PER_YEAR+1 }
Packit dd8086
  };
Packit dd8086
Packit dd8086
#define SPY(y,l,s) (SECS_PER_DAY * (DAYS_PER_YEAR*y+l)+s) /* Seconds per year */
Packit dd8086
Packit dd8086
static time_t year_seconds[MAX_YEAR_SECONDS]= {
Packit dd8086
  /*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), 
Packit dd8086
  /*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), 
Packit dd8086
  /*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), 
Packit dd8086
  /*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), 
Packit dd8086
  /*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), 
Packit dd8086
  /*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), 
Packit dd8086
  /*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), 
Packit dd8086
  /*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), 
Packit dd8086
  /*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), 
Packit dd8086
  /*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), 
Packit dd8086
  /*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), 
Packit dd8086
  /*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), 
Packit dd8086
  /*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), 
Packit dd8086
  /*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), 
Packit dd8086
  /*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), 
Packit dd8086
  /*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), 
Packit dd8086
  /*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), 
Packit dd8086
  /*2038*/ SPY(68,17,0)
Packit dd8086
};
Packit dd8086
Packit dd8086
#if defined(HAVE_TIMEZONE_VAR) && !defined(_WIN32)
Packit dd8086
extern long timezone;
Packit dd8086
#endif
Packit dd8086
Packit dd8086
time_t *
Packit dd8086
udf_stamp_to_time(time_t *dest, long int *dest_usec, 
Packit dd8086
		  const udf_timestamp_t src)
Packit dd8086
{
Packit dd8086
  int yday;
Packit dd8086
  uint8_t type = src.type_tz >> 12;
Packit dd8086
  int16_t offset;
Packit dd8086
  
Packit dd8086
  if (type == 1) {
Packit dd8086
    offset = src.type_tz << 4;
Packit dd8086
    /* sign extent offset */
Packit dd8086
    offset = (offset >> 4);
Packit dd8086
    if (offset == -2047) /* unspecified offset */
Packit dd8086
      offset = 0;
Packit dd8086
  }
Packit dd8086
  else
Packit dd8086
    offset = 0;
Packit dd8086
  
Packit dd8086
  if ((src.year < EPOCH_YEAR) ||
Packit dd8086
      (src.year >= EPOCH_YEAR+MAX_YEAR_SECONDS))
Packit dd8086
    {
Packit dd8086
      *dest = -1;
Packit dd8086
      *dest_usec = -1;
Packit dd8086
      return NULL;
Packit dd8086
    }
Packit dd8086
  *dest = year_seconds[src.year - EPOCH_YEAR];
Packit dd8086
  *dest -= offset * SECS_PER_MINUTE;
Packit dd8086
  
Packit dd8086
  yday = ((__mon_yday[__isleap (src.year)]
Packit dd8086
	   [src.month-1]) + (src.day-1));
Packit dd8086
  *dest += src.second + 
Packit dd8086
    ( SECS_PER_MINUTE *
Packit dd8086
      ( ( (yday* HOURS_PER_DAY) + src.hour ) * 60 + src.minute ) );
Packit dd8086
Packit dd8086
  *dest_usec = src.microseconds
Packit dd8086
    + (src.centiseconds * 10000)
Packit dd8086
    + (src.hundreds_of_microseconds * 100);
Packit dd8086
  return dest;
Packit dd8086
}
Packit dd8086
Packit dd8086
#ifdef HAVE_STRUCT_TIMESPEC
Packit dd8086
/*!
Packit dd8086
  Convert a UDF timestamp to a time_t. If microseconds are desired,
Packit dd8086
  use dest_usec. The return value is the same as dest. */
Packit dd8086
udf_timestamp_t *
Packit dd8086
udf_timespec_to_stamp(const struct timespec ts, udf_timestamp_t *dest)
Packit dd8086
{
Packit dd8086
  long int days, rem, y;
Packit dd8086
  const unsigned short int *ip;
Packit dd8086
  int16_t offset = 0;
Packit dd8086
  int16_t tv_sec;
Packit dd8086
Packit dd8086
#ifdef HAVE_TIMEZONE_VAR  
Packit dd8086
  offset = -timezone;
Packit dd8086
#endif
Packit dd8086
  
Packit dd8086
  if (!dest)
Packit dd8086
    return dest;
Packit dd8086
  
Packit dd8086
  dest->type_tz = 0x1000 | (offset & 0x0FFF);
Packit dd8086
  
Packit dd8086
  tv_sec       = ts.tv_sec + (offset * SECS_PER_MINUTE);
Packit dd8086
  days         = tv_sec / SECS_PER_DAY;
Packit dd8086
  rem          = tv_sec % SECS_PER_DAY;
Packit dd8086
  dest->hour   = rem / SECS_PER_HOUR;
Packit dd8086
  rem         %= SECS_PER_HOUR;
Packit dd8086
  dest->minute = rem / SECS_PER_MINUTE;
Packit dd8086
  dest->second = rem % SECS_PER_MINUTE;
Packit dd8086
  y            = EPOCH_YEAR;
Packit dd8086
  
Packit dd8086
#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0))
Packit dd8086
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
Packit dd8086
  
Packit dd8086
  while (days < 0 || days >= (__isleap(y) ? DAYS_PER_YEAR+1 : DAYS_PER_YEAR)) {
Packit dd8086
    long int yg = y + days / DAYS_PER_YEAR - (days % DAYS_PER_YEAR < 0);
Packit dd8086
    
Packit dd8086
    /* Adjust DAYS and Y to match the guessed year.  */
Packit dd8086
    days -= ((yg - y) * DAYS_PER_YEAR
Packit dd8086
	     + LEAPS_THRU_END_OF (yg - 1)
Packit dd8086
	     - LEAPS_THRU_END_OF (y - 1));
Packit dd8086
    y = yg;
Packit dd8086
  }
Packit dd8086
  dest->year = y;
Packit dd8086
  ip = __mon_yday[__isleap(y)];
Packit dd8086
  for (y = 11; days < (long int) ip[y]; --y)
Packit dd8086
    continue;
Packit dd8086
  days -= ip[y];
Packit dd8086
  dest->month = y + 1;
Packit dd8086
  dest->day   = days + 1;
Packit dd8086
  
Packit dd8086
  dest->centiseconds = ts.tv_nsec / 10000000;
Packit dd8086
  dest->hundreds_of_microseconds = ( (ts.tv_nsec / 1000)
Packit dd8086
				     - (dest->centiseconds * 10000) ) / 100;
Packit dd8086
  dest->microseconds = ( (ts.tv_nsec / 1000) 
Packit dd8086
			 - (dest->centiseconds * 10000)
Packit dd8086
			 - (dest->hundreds_of_microseconds * 100) );
Packit dd8086
  return dest;
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the modification time of the file.
Packit dd8086
 */
Packit dd8086
time_t
Packit dd8086
udf_get_modification_time(const udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  if (p_udf_dirent) {
Packit dd8086
    time_t ret_time;
Packit dd8086
    long int usec;
Packit dd8086
    udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.modification_time);
Packit dd8086
    return ret_time;
Packit dd8086
  }
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the access time of the file.
Packit dd8086
 */
Packit dd8086
time_t
Packit dd8086
udf_get_access_time(const udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  if (p_udf_dirent) {
Packit dd8086
    time_t ret_time;
Packit dd8086
    long int usec;
Packit dd8086
    udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.access_time);
Packit dd8086
    return ret_time;
Packit dd8086
  }
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the attribute (most recent create or access) time of the file
Packit dd8086
 */
Packit dd8086
time_t
Packit dd8086
udf_get_attribute_time(const udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  if (p_udf_dirent) {
Packit dd8086
    time_t ret_time;
Packit dd8086
    long int usec;
Packit dd8086
    udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.attribute_time);
Packit dd8086
    return ret_time;
Packit dd8086
  }
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086