pidgin/purple-plugin-pack
Clone
Summary
Browse
Changes
Graph
Chat userlist logging for inital joining of chats. Doesn't work because the
org.guifications.plugins
2008-07-04, rekkanoryo
be1e40781fd8
Chat userlist logging for inital joining of chats. Doesn't work because the
chat-joined signal fires before libpurple gets the userlist. Still thinking
about how to fix it.
/*************************************************************************
* Timezone Module
* copied from the Olson Timezone code, licence unchanged.
* by Martijn van Oosterhout <kleptog@svana.org> April 2006
* Original Licence below (public domain).
*
* This code has been copied from the Olson Timezone code, but heavily
* adapted to meet my needs. In particular, you can load multiple timezones
* and specify which timezone to convert with.
*************************************************************************/
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
#define TM_GMTOFF tm_gmtoff
#define TM_ZONE tm_zone
/*
** Leap second handling from Bradley White.
** POSIX-style TZ environment variable handling from Guy Harris.
*/
/*LINTLIBRARY*/
#include
"private.h"
#include
"tzfile.h"
#include
"fcntl.h"
#include
"float.h"
/* for FLT_MAX and DBL_MAX */
#ifndef TZ_ABBR_MAX_LEN
#define TZ_ABBR_MAX_LEN 16
#endif
/* !defined TZ_ABBR_MAX_LEN */
#ifndef TZ_ABBR_CHAR_SET
#define TZ_ABBR_CHAR_SET \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
#endif
/* !defined TZ_ABBR_CHAR_SET */
#ifndef TZ_ABBR_ERR_CHAR
#define TZ_ABBR_ERR_CHAR '_'
#endif
/* !defined TZ_ABBR_ERR_CHAR */
/*
** SunOS 4.1.1 headers lack O_BINARY.
*/
#ifdef O_BINARY
#define OPEN_MODE (O_RDONLY | O_BINARY)
#endif
/* defined O_BINARY */
#ifndef O_BINARY
#define OPEN_MODE O_RDONLY
#endif
/* !defined O_BINARY */
#ifndef WILDABBR
/*
** Someone might make incorrect use of a time zone abbreviation:
** 1. They might reference tzname[0] before calling tzset (explicitly
** or implicitly).
** 2. They might reference tzname[1] before calling tzset (explicitly
** or implicitly).
** 3. They might reference tzname[1] after setting to a time zone
** in which Daylight Saving Time is never observed.
** 4. They might reference tzname[0] after setting to a time zone
** in which Standard Time is never observed.
** 5. They might reference tm.TM_ZONE after calling offtime.
** What's best to do in the above cases is open to debate;
** for now, we just set things up so that in any of the five cases
** WILDABBR is used. Another possibility: initialize tzname[0] to the
** string "tzname[0] used before set", and similarly for the other cases.
** And another: initialize tzname[0] to "ERA", with an explanation in the
** manual page of what this "time zone abbreviation" means (doing this so
** that tzname[0] has the "normal" length of three characters).
*/
#define WILDABBR " "
#endif
/* !defined WILDABBR */
static
char
wildabbr
[]
=
WILDABBR
;
static
const
char
gmt
[]
=
"GMT"
;
static
char
*
tzdir
;
/*
** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
** We default to US rules as of 1999-08-17.
** POSIX 1003.1 section 8.1.1 says that the default DST rules are
** implementation dependent; for historical reasons, US rules are a
** common default.
*/
#ifndef TZDEFRULESTRING
#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
#endif
/* !defined TZDEFDST */
struct
ttinfo
{
/* time type information */
long
tt_gmtoff
;
/* UTC offset in seconds */
int
tt_isdst
;
/* used to set tm_isdst */
int
tt_abbrind
;
/* abbreviation list index */
int
tt_ttisstd
;
/* TRUE if transition is std time */
int
tt_ttisgmt
;
/* TRUE if transition is UTC */
};
struct
lsinfo
{
/* leap second information */
time_t
ls_trans
;
/* transition time */
long
ls_corr
;
/* correction to apply */
};
#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
#ifdef TZNAME_MAX
#define MY_TZNAME_MAX TZNAME_MAX
#endif
/* defined TZNAME_MAX */
#ifndef TZNAME_MAX
#define MY_TZNAME_MAX 255
#endif
/* !defined TZNAME_MAX */
struct
state
{
int
leapcnt
;
int
timecnt
;
int
typecnt
;
int
charcnt
;
time_t
ats
[
TZ_MAX_TIMES
];
unsigned
char
types
[
TZ_MAX_TIMES
];
struct
ttinfo
ttis
[
TZ_MAX_TYPES
];
char
chars
[
BIGGEST
(
BIGGEST
(
TZ_MAX_CHARS
+
1
,
sizeof
gmt
),
(
2
*
(
MY_TZNAME_MAX
+
1
)))];
struct
lsinfo
lsis
[
TZ_MAX_LEAPS
];
};
struct
rule
{
int
r_type
;
/* type of rule--see below */
int
r_day
;
/* day number of rule */
int
r_week
;
/* week number of rule */
int
r_mon
;
/* month number of rule */
long
r_time
;
/* transition time of rule */
};
#define JULIAN_DAY 0
/* Jn - Julian day */
#define DAY_OF_YEAR 1
/* n - day of year */
#define MONTH_NTH_DAY_OF_WEEK 2
/* Mm.n.d - month, week, day of week */
/*
** Prototypes for static functions.
*/
static
long
detzcode
P
((
const
char
*
codep
));
static
const
char
*
getzname
P
((
const
char
*
strp
));
static
const
char
*
getqzname
P
((
const
char
*
strp
,
const
char
delim
));
static
const
char
*
getnum
P
((
const
char
*
strp
,
int
*
nump
,
int
min
,
int
max
));
static
const
char
*
getsecs
P
((
const
char
*
strp
,
long
*
secsp
));
static
const
char
*
getoffset
P
((
const
char
*
strp
,
long
*
offsetp
));
static
const
char
*
getrule
P
((
const
char
*
strp
,
struct
rule
*
rulep
));
static
void
gmtload
P
((
struct
state
*
sp
));
struct
tm
*
gmtsub
P
((
const
time_t
*
timep
,
long
offset
,
struct
tm
*
tmp
));
struct
tm
*
localsub
P
((
const
time_t
*
timep
,
long
offset
,
struct
tm
*
tmp
,
struct
state
*
sp
));
static
int
increment_overflow
P
((
int
*
number
,
int
delta
));
static
int
leaps_thru_end_of
P
((
int
y
));
static
struct
tm
*
timesub
P
((
const
time_t
*
timep
,
long
offset
,
const
struct
state
*
sp
,
struct
tm
*
tmp
));
static
time_t
transtime
P
((
time_t
janfirst
,
int
year
,
const
struct
rule
*
rulep
,
long
offset
));
static
int
tzload
P
((
const
char
*
name
,
struct
state
*
sp
));
static
int
tzparse
P
((
const
char
*
name
,
struct
state
*
sp
,
int
lastditch
));
struct
state
*
timezone_load
P
((
const
char
*
name
));
#ifdef ALL_STATE
static
struct
state
*
gmtptr
;
#endif
/* defined ALL_STATE */
#ifndef ALL_STATE
static
struct
state
gmtmem
;
#define gmtptr (&gmtmem)
#endif
/* State Farm */
#ifndef TZ_STRLEN_MAX
#define TZ_STRLEN_MAX 255
#endif
/* !defined TZ_STRLEN_MAX */
//static char lcl_TZname[TZ_STRLEN_MAX + 1];
//static int lcl_is_set;
static
int
gmt_is_set
;
/*
** Section 4.12.3 of X3.159-1989 requires that
** Except for the strftime function, these functions [asctime,
** ctime, gmtime, localtime] return values in one of two static
** objects: a broken-down time structure and an array of char.
** Thanks to Paul Eggert for noting this.
*/
//static struct tm tm;
#ifdef USG_COMPAT
time_t
timezone
=
0
;
int
daylight
=
0
;
#endif
/* defined USG_COMPAT */
#ifdef ALTZONE
time_t
altzone
=
0
;
#endif
/* defined ALTZONE */
static
long
detzcode
(
codep
)
const
char
*
const
codep
;
{
register
long
result
;
register
int
i
;
result
=
(
codep
[
0
]
&
0x80
)
?
~
0L
:
0L
;
for
(
i
=
0
;
i
<
4
;
++
i
)
result
=
(
result
<<
8
)
|
(
codep
[
i
]
&
0xff
);
return
result
;
}
static
int
tzload
(
name
,
sp
)
register
const
char
*
name
;
register
struct
state
*
const
sp
;
{
register
const
char
*
p
;
register
int
i
;
register
int
fid
;
if
(
name
==
NULL
&&
(
name
=
TZDEFAULT
)
==
NULL
)
return
-1
;
{
register
int
doaccess
;
/*
** Section 4.9.1 of the C standard says that
** "FILENAME_MAX expands to an integral constant expression
** that is the size needed for an array of char large enough
** to hold the longest file name string that the implementation
** guarantees can be opened."
*/
char
fullname
[
FILENAME_MAX
+
1
];
if
(
name
[
0
]
==
':'
)
++
name
;
doaccess
=
name
[
0
]
==
'/'
;
if
(
!
doaccess
)
{
if
((
p
=
tzdir
)
==
NULL
)
return
-1
;
if
((
strlen
(
p
)
+
strlen
(
name
)
+
1
)
>=
sizeof
fullname
)
return
-1
;
(
void
)
strcpy
(
fullname
,
p
);
(
void
)
strcat
(
fullname
,
"/"
);
(
void
)
strcat
(
fullname
,
name
);
/*
** Set doaccess if '.' (as in "../") shows up in name.
*/
if
(
strchr
(
name
,
'.'
)
!=
NULL
)
doaccess
=
TRUE
;
name
=
fullname
;
}
if
(
doaccess
&&
access
(
name
,
R_OK
)
!=
0
)
return
-1
;
if
((
fid
=
open
(
name
,
OPEN_MODE
))
==
-1
)
return
-1
;
}
{
struct
tzhead
*
tzhp
;
union
{
struct
tzhead
tzhead
;
char
buf
[
sizeof
*
sp
+
sizeof
*
tzhp
];
}
u
;
int
ttisstdcnt
;
int
ttisgmtcnt
;
i
=
read
(
fid
,
u
.
buf
,
sizeof
u
.
buf
);
if
(
close
(
fid
)
!=
0
)
return
-1
;
ttisstdcnt
=
(
int
)
detzcode
(
u
.
tzhead
.
tzh_ttisstdcnt
);
ttisgmtcnt
=
(
int
)
detzcode
(
u
.
tzhead
.
tzh_ttisgmtcnt
);
sp
->
leapcnt
=
(
int
)
detzcode
(
u
.
tzhead
.
tzh_leapcnt
);
sp
->
timecnt
=
(
int
)
detzcode
(
u
.
tzhead
.
tzh_timecnt
);
sp
->
typecnt
=
(
int
)
detzcode
(
u
.
tzhead
.
tzh_typecnt
);
sp
->
charcnt
=
(
int
)
detzcode
(
u
.
tzhead
.
tzh_charcnt
);
p
=
u
.
tzhead
.
tzh_charcnt
+
sizeof
u
.
tzhead
.
tzh_charcnt
;
if
(
sp
->
leapcnt
<
0
||
sp
->
leapcnt
>
TZ_MAX_LEAPS
||
sp
->
typecnt
<=
0
||
sp
->
typecnt
>
TZ_MAX_TYPES
||
sp
->
timecnt
<
0
||
sp
->
timecnt
>
TZ_MAX_TIMES
||
sp
->
charcnt
<
0
||
sp
->
charcnt
>
TZ_MAX_CHARS
||
(
ttisstdcnt
!=
sp
->
typecnt
&&
ttisstdcnt
!=
0
)
||
(
ttisgmtcnt
!=
sp
->
typecnt
&&
ttisgmtcnt
!=
0
))
return
-1
;
if
(
i
-
(
p
-
u
.
buf
)
<
sp
->
timecnt
*
4
+
/* ats */
sp
->
timecnt
+
/* types */
sp
->
typecnt
*
(
4
+
2
)
+
/* ttinfos */
sp
->
charcnt
+
/* chars */
sp
->
leapcnt
*
(
4
+
4
)
+
/* lsinfos */
ttisstdcnt
+
/* ttisstds */
ttisgmtcnt
)
/* ttisgmts */
return
-1
;
for
(
i
=
0
;
i
<
sp
->
timecnt
;
++
i
)
{
sp
->
ats
[
i
]
=
detzcode
(
p
);
p
+=
4
;
}
for
(
i
=
0
;
i
<
sp
->
timecnt
;
++
i
)
{
sp
->
types
[
i
]
=
(
unsigned
char
)
*
p
++
;
if
(
sp
->
types
[
i
]
>=
sp
->
typecnt
)
return
-1
;
}
for
(
i
=
0
;
i
<
sp
->
typecnt
;
++
i
)
{
register
struct
ttinfo
*
ttisp
;
ttisp
=
&
sp
->
ttis
[
i
];
ttisp
->
tt_gmtoff
=
detzcode
(
p
);
p
+=
4
;
ttisp
->
tt_isdst
=
(
unsigned
char
)
*
p
++
;
if
(
ttisp
->
tt_isdst
!=
0
&&
ttisp
->
tt_isdst
!=
1
)
return
-1
;
ttisp
->
tt_abbrind
=
(
unsigned
char
)
*
p
++
;
if
(
ttisp
->
tt_abbrind
<
0
||
ttisp
->
tt_abbrind
>
sp
->
charcnt
)
return
-1
;
}
for
(
i
=
0
;
i
<
sp
->
charcnt
;
++
i
)
sp
->
chars
[
i
]
=
*
p
++
;
sp
->
chars
[
i
]
=
'\0'
;
/* ensure '\0' at end */
for
(
i
=
0
;
i
<
sp
->
leapcnt
;
++
i
)
{
register
struct
lsinfo
*
lsisp
;
lsisp
=
&
sp
->
lsis
[
i
];
lsisp
->
ls_trans
=
detzcode
(
p
);
p
+=
4
;
lsisp
->
ls_corr
=
detzcode
(
p
);
p
+=
4
;
}
for
(
i
=
0
;
i
<
sp
->
typecnt
;
++
i
)
{
register
struct
ttinfo
*
ttisp
;
ttisp
=
&
sp
->
ttis
[
i
];
if
(
ttisstdcnt
==
0
)
ttisp
->
tt_ttisstd
=
FALSE
;
else
{
ttisp
->
tt_ttisstd
=
*
p
++
;
if
(
ttisp
->
tt_ttisstd
!=
TRUE
&&
ttisp
->
tt_ttisstd
!=
FALSE
)
return
-1
;
}
}
for
(
i
=
0
;
i
<
sp
->
typecnt
;
++
i
)
{
register
struct
ttinfo
*
ttisp
;
ttisp
=
&
sp
->
ttis
[
i
];
if
(
ttisgmtcnt
==
0
)
ttisp
->
tt_ttisgmt
=
FALSE
;
else
{
ttisp
->
tt_ttisgmt
=
*
p
++
;
if
(
ttisp
->
tt_ttisgmt
!=
TRUE
&&
ttisp
->
tt_ttisgmt
!=
FALSE
)
return
-1
;
}
}
/*
** Out-of-sort ats should mean we're running on a
** signed time_t system but using a data file with
** unsigned values (or vice versa).
*/
for
(
i
=
0
;
i
<
sp
->
timecnt
-
2
;
++
i
)
if
(
sp
->
ats
[
i
]
>
sp
->
ats
[
i
+
1
])
{
++
i
;
if
(
TYPE_SIGNED
(
time_t
))
{
/*
** Ignore the end (easy).
*/
sp
->
timecnt
=
i
;
}
else
{
/*
** Ignore the beginning (harder).
*/
register
int
j
;
for
(
j
=
0
;
j
+
i
<
sp
->
timecnt
;
++
j
)
{
sp
->
ats
[
j
]
=
sp
->
ats
[
j
+
i
];
sp
->
types
[
j
]
=
sp
->
types
[
j
+
i
];
}
sp
->
timecnt
=
j
;
}
break
;
}
}
return
0
;
}
struct
state
*
timezone_load
(
name
)
const
char
*
name
;
{
struct
state
*
sp
=
malloc
(
sizeof
(
struct
state
)
);
int
res
;
if
(
!
sp
)
return
NULL
;
res
=
tzload
(
name
,
sp
);
if
(
res
<
0
)
{
free
(
sp
);
return
NULL
;
}
return
sp
;
}
static
const
int
mon_lengths
[
2
][
MONSPERYEAR
]
=
{
{
31
,
28
,
31
,
30
,
31
,
30
,
31
,
31
,
30
,
31
,
30
,
31
},
{
31
,
29
,
31
,
30
,
31
,
30
,
31
,
31
,
30
,
31
,
30
,
31
}
};
static
const
int
year_lengths
[
2
]
=
{
DAYSPERNYEAR
,
DAYSPERLYEAR
};
/*
** Given a pointer into a time zone string, scan until a character that is not
** a valid character in a zone name is found. Return a pointer to that
** character.
*/
static
const
char
*
getzname
(
strp
)
register
const
char
*
strp
;
{
register
char
c
;
while
((
c
=
*
strp
)
!=
'\0'
&&
!
is_digit
(
c
)
&&
c
!=
','
&&
c
!=
'-'
&&
c
!=
'+'
)
++
strp
;
return
strp
;
}
/*
** Given a pointer into an extended time zone string, scan until the ending
** delimiter of the zone name is located. Return a pointer to the delimiter.
**
** As with getzname above, the legal character set is actually quite
** restricted, with other characters producing undefined results.
** We choose not to care - allowing almost anything to be in the zone abbrev.
*/
static
const
char
*
#if __STDC__
getqzname
(
register
const
char
*
strp
,
const
char
delim
)
#else
/* !__STDC__ */
getqzname
(
strp
,
delim
)
register
const
char
*
strp
;
const
char
delim
;
#endif
/* !__STDC__ */
{
register
char
c
;
while
((
c
=
*
strp
)
!=
'\0'
&&
c
!=
delim
)
++
strp
;
return
strp
;
}
/*
** Given a pointer into a time zone string, extract a number from that string.
** Check that the number is within a specified range; if it is not, return
** NULL.
** Otherwise, return a pointer to the first character not part of the number.
*/
static
const
char
*
getnum
(
strp
,
nump
,
min
,
max
)
register
const
char
*
strp
;
int
*
const
nump
;
const
int
min
;
const
int
max
;
{
register
char
c
;
register
int
num
;
if
(
strp
==
NULL
||
!
is_digit
(
c
=
*
strp
))
return
NULL
;
num
=
0
;
do
{
num
=
num
*
10
+
(
c
-
'0'
);
if
(
num
>
max
)
return
NULL
;
/* illegal value */
c
=
*++
strp
;
}
while
(
is_digit
(
c
));
if
(
num
<
min
)
return
NULL
;
/* illegal value */
*
nump
=
num
;
return
strp
;
}
/*
** Given a pointer into a time zone string, extract a number of seconds,
** in hh[:mm[:ss]] form, from the string.
** If any error occurs, return NULL.
** Otherwise, return a pointer to the first character not part of the number
** of seconds.
*/
static
const
char
*
getsecs
(
strp
,
secsp
)
register
const
char
*
strp
;
long
*
const
secsp
;
{
int
num
;
/*
** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
** "M10.4.6/26", which does not conform to Posix,
** but which specifies the equivalent of
** ``02:00 on the first Sunday on or after 23 Oct''.
*/
strp
=
getnum
(
strp
,
&
num
,
0
,
HOURSPERDAY
*
DAYSPERWEEK
-
1
);
if
(
strp
==
NULL
)
return
NULL
;
*
secsp
=
num
*
(
long
)
SECSPERHOUR
;
if
(
*
strp
==
':'
)
{
++
strp
;
strp
=
getnum
(
strp
,
&
num
,
0
,
MINSPERHOUR
-
1
);
if
(
strp
==
NULL
)
return
NULL
;
*
secsp
+=
num
*
SECSPERMIN
;
if
(
*
strp
==
':'
)
{
++
strp
;
/* `SECSPERMIN' allows for leap seconds. */
strp
=
getnum
(
strp
,
&
num
,
0
,
SECSPERMIN
);
if
(
strp
==
NULL
)
return
NULL
;
*
secsp
+=
num
;
}
}
return
strp
;
}
/*
** Given a pointer into a time zone string, extract an offset, in
** [+-]hh[:mm[:ss]] form, from the string.
** If any error occurs, return NULL.
** Otherwise, return a pointer to the first character not part of the time.
*/
static
const
char
*
getoffset
(
strp
,
offsetp
)
register
const
char
*
strp
;
long
*
const
offsetp
;
{
register
int
neg
=
0
;
if
(
*
strp
==
'-'
)
{
neg
=
1
;
++
strp
;
}
else
if
(
*
strp
==
'+'
)
++
strp
;
strp
=
getsecs
(
strp
,
offsetp
);
if
(
strp
==
NULL
)
return
NULL
;
/* illegal time */
if
(
neg
)
*
offsetp
=
-*
offsetp
;
return
strp
;
}
/*
** Given a pointer into a time zone string, extract a rule in the form
** date[/time]. See POSIX section 8 for the format of "date" and "time".
** If a valid rule is not found, return NULL.
** Otherwise, return a pointer to the first character not part of the rule.
*/
static
const
char
*
getrule
(
strp
,
rulep
)
const
char
*
strp
;
register
struct
rule
*
const
rulep
;
{
if
(
*
strp
==
'J'
)
{
/*
** Julian day.
*/
rulep
->
r_type
=
JULIAN_DAY
;
++
strp
;
strp
=
getnum
(
strp
,
&
rulep
->
r_day
,
1
,
DAYSPERNYEAR
);
}
else
if
(
*
strp
==
'M'
)
{
/*
** Month, week, day.
*/
rulep
->
r_type
=
MONTH_NTH_DAY_OF_WEEK
;
++
strp
;
strp
=
getnum
(
strp
,
&
rulep
->
r_mon
,
1
,
MONSPERYEAR
);
if
(
strp
==
NULL
)
return
NULL
;
if
(
*
strp
++
!=
'.'
)
return
NULL
;
strp
=
getnum
(
strp
,
&
rulep
->
r_week
,
1
,
5
);
if
(
strp
==
NULL
)
return
NULL
;
if
(
*
strp
++
!=
'.'
)
return
NULL
;
strp
=
getnum
(
strp
,
&
rulep
->
r_day
,
0
,
DAYSPERWEEK
-
1
);
}
else
if
(
is_digit
(
*
strp
))
{
/*
** Day of year.
*/
rulep
->
r_type
=
DAY_OF_YEAR
;
strp
=
getnum
(
strp
,
&
rulep
->
r_day
,
0
,
DAYSPERLYEAR
-
1
);
}
else
return
NULL
;
/* invalid format */
if
(
strp
==
NULL
)
return
NULL
;
if
(
*
strp
==
'/'
)
{
/*
** Time specified.
*/
++
strp
;
strp
=
getsecs
(
strp
,
&
rulep
->
r_time
);
}
else
rulep
->
r_time
=
2
*
SECSPERHOUR
;
/* default = 2:00:00 */
return
strp
;
}
/*
** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
** year, a rule, and the offset from UTC at the time that rule takes effect,
** calculate the Epoch-relative time that rule takes effect.
*/
static
time_t
transtime
(
janfirst
,
year
,
rulep
,
offset
)
const
time_t
janfirst
;
const
int
year
;
register
const
struct
rule
*
const
rulep
;
const
long
offset
;
{
register
int
leapyear
;
register
time_t
value
;
register
int
i
;
int
d
,
m1
,
yy0
,
yy1
,
yy2
,
dow
;
INITIALIZE
(
value
);
leapyear
=
isleap
(
year
);
switch
(
rulep
->
r_type
)
{
case
JULIAN_DAY
:
/*
** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
** years.
** In non-leap years, or if the day number is 59 or less, just
** add SECSPERDAY times the day number-1 to the time of
** January 1, midnight, to get the day.
*/
value
=
janfirst
+
(
rulep
->
r_day
-
1
)
*
SECSPERDAY
;
if
(
leapyear
&&
rulep
->
r_day
>=
60
)
value
+=
SECSPERDAY
;
break
;
case
DAY_OF_YEAR
:
/*
** n - day of year.
** Just add SECSPERDAY times the day number to the time of
** January 1, midnight, to get the day.
*/
value
=
janfirst
+
rulep
->
r_day
*
SECSPERDAY
;
break
;
case
MONTH_NTH_DAY_OF_WEEK
:
/*
** Mm.n.d - nth "dth day" of month m.
*/
value
=
janfirst
;
for
(
i
=
0
;
i
<
rulep
->
r_mon
-
1
;
++
i
)
value
+=
mon_lengths
[
leapyear
][
i
]
*
SECSPERDAY
;
/*
** Use Zeller's Congruence to get day-of-week of first day of
** month.
*/
m1
=
(
rulep
->
r_mon
+
9
)
%
12
+
1
;
yy0
=
(
rulep
->
r_mon
<=
2
)
?
(
year
-
1
)
:
year
;
yy1
=
yy0
/
100
;
yy2
=
yy0
%
100
;
dow
=
((
26
*
m1
-
2
)
/
10
+
1
+
yy2
+
yy2
/
4
+
yy1
/
4
-
2
*
yy1
)
%
7
;
if
(
dow
<
0
)
dow
+=
DAYSPERWEEK
;
/*
** "dow" is the day-of-week of the first day of the month. Get
** the day-of-month (zero-origin) of the first "dow" day of the
** month.
*/
d
=
rulep
->
r_day
-
dow
;
if
(
d
<
0
)
d
+=
DAYSPERWEEK
;
for
(
i
=
1
;
i
<
rulep
->
r_week
;
++
i
)
{
if
(
d
+
DAYSPERWEEK
>=
mon_lengths
[
leapyear
][
rulep
->
r_mon
-
1
])
break
;
d
+=
DAYSPERWEEK
;
}
/*
** "d" is the day-of-month (zero-origin) of the day we want.
*/
value
+=
d
*
SECSPERDAY
;
break
;
}
/*
** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
** question. To get the Epoch-relative time of the specified local
** time on that day, add the transition time and the current offset
** from UTC.
*/
return
value
+
rulep
->
r_time
+
offset
;
}
/*
** Given a POSIX section 8-style TZ string, fill in the rule tables as
** appropriate.
*/
static
int
tzparse
(
name
,
sp
,
lastditch
)
const
char
*
name
;
register
struct
state
*
const
sp
;
const
int
lastditch
;
{
const
char
*
stdname
;
const
char
*
dstname
;
size_t
stdlen
;
size_t
dstlen
;
long
stdoffset
;
long
dstoffset
;
register
time_t
*
atp
;
register
unsigned
char
*
typep
;
register
char
*
cp
;
register
int
load_result
;
INITIALIZE
(
dstname
);
stdname
=
name
;
if
(
lastditch
)
{
stdlen
=
strlen
(
name
);
/* length of standard zone name */
name
+=
stdlen
;
if
(
stdlen
>=
sizeof
sp
->
chars
)
stdlen
=
(
sizeof
sp
->
chars
)
-
1
;
stdoffset
=
0
;
}
else
{
if
(
*
name
==
'<'
)
{
name
++
;
stdname
=
name
;
name
=
getqzname
(
name
,
'>'
);
if
(
*
name
!=
'>'
)
return
(
-1
);
stdlen
=
name
-
stdname
;
name
++
;
}
else
{
name
=
getzname
(
name
);
stdlen
=
name
-
stdname
;
}
if
(
*
name
==
'\0'
)
return
-1
;
name
=
getoffset
(
name
,
&
stdoffset
);
if
(
name
==
NULL
)
return
-1
;
}
load_result
=
tzload
(
TZDEFRULES
,
sp
);
if
(
load_result
!=
0
)
sp
->
leapcnt
=
0
;
/* so, we're off a little */
if
(
*
name
!=
'\0'
)
{
if
(
*
name
==
'<'
)
{
dstname
=
++
name
;
name
=
getqzname
(
name
,
'>'
);
if
(
*
name
!=
'>'
)
return
-1
;
dstlen
=
name
-
dstname
;
name
++
;
}
else
{
dstname
=
name
;
name
=
getzname
(
name
);
dstlen
=
name
-
dstname
;
/* length of DST zone name */
}
if
(
*
name
!=
'\0'
&&
*
name
!=
','
&&
*
name
!=
';'
)
{
name
=
getoffset
(
name
,
&
dstoffset
);
if
(
name
==
NULL
)
return
-1
;
}
else
dstoffset
=
stdoffset
-
SECSPERHOUR
;
if
(
*
name
==
'\0'
&&
load_result
!=
0
)
name
=
TZDEFRULESTRING
;
if
(
*
name
==
','
||
*
name
==
';'
)
{
struct
rule
start
;
struct
rule
end
;
register
int
year
;
register
time_t
janfirst
;
time_t
starttime
;
time_t
endtime
;
++
name
;
if
((
name
=
getrule
(
name
,
&
start
))
==
NULL
)
return
-1
;
if
(
*
name
++
!=
','
)
return
-1
;
if
((
name
=
getrule
(
name
,
&
end
))
==
NULL
)
return
-1
;
if
(
*
name
!=
'\0'
)
return
-1
;
sp
->
typecnt
=
2
;
/* standard time and DST */
/*
** Two transitions per year, from EPOCH_YEAR to 2037.
*/
sp
->
timecnt
=
2
*
(
2037
-
EPOCH_YEAR
+
1
);
if
(
sp
->
timecnt
>
TZ_MAX_TIMES
)
return
-1
;
sp
->
ttis
[
0
].
tt_gmtoff
=
-
dstoffset
;
sp
->
ttis
[
0
].
tt_isdst
=
1
;
sp
->
ttis
[
0
].
tt_abbrind
=
stdlen
+
1
;
sp
->
ttis
[
1
].
tt_gmtoff
=
-
stdoffset
;
sp
->
ttis
[
1
].
tt_isdst
=
0
;
sp
->
ttis
[
1
].
tt_abbrind
=
0
;
atp
=
sp
->
ats
;
typep
=
sp
->
types
;
janfirst
=
0
;
for
(
year
=
EPOCH_YEAR
;
year
<=
2037
;
++
year
)
{
starttime
=
transtime
(
janfirst
,
year
,
&
start
,
stdoffset
);
endtime
=
transtime
(
janfirst
,
year
,
&
end
,
dstoffset
);
if
(
starttime
>
endtime
)
{
*
atp
++
=
endtime
;
*
typep
++
=
1
;
/* DST ends */
*
atp
++
=
starttime
;
*
typep
++
=
0
;
/* DST begins */
}
else
{
*
atp
++
=
starttime
;
*
typep
++
=
0
;
/* DST begins */
*
atp
++
=
endtime
;
*
typep
++
=
1
;
/* DST ends */
}
janfirst
+=
year_lengths
[
isleap
(
year
)]
*
SECSPERDAY
;
}
}
else
{
register
long
theirstdoffset
;
register
long
theirdstoffset
;
register
long
theiroffset
;
register
int
isdst
;
register
int
i
;
register
int
j
;
if
(
*
name
!=
'\0'
)
return
-1
;
/*
** Initial values of theirstdoffset and theirdstoffset.
*/
theirstdoffset
=
0
;
for
(
i
=
0
;
i
<
sp
->
timecnt
;
++
i
)
{
j
=
sp
->
types
[
i
];
if
(
!
sp
->
ttis
[
j
].
tt_isdst
)
{
theirstdoffset
=
-
sp
->
ttis
[
j
].
tt_gmtoff
;
break
;
}
}
theirdstoffset
=
0
;
for
(
i
=
0
;
i
<
sp
->
timecnt
;
++
i
)
{
j
=
sp
->
types
[
i
];
if
(
sp
->
ttis
[
j
].
tt_isdst
)
{
theirdstoffset
=
-
sp
->
ttis
[
j
].
tt_gmtoff
;
break
;
}
}
/*
** Initially we're assumed to be in standard time.
*/
isdst
=
FALSE
;
theiroffset
=
theirstdoffset
;
/*
** Now juggle transition times and types
** tracking offsets as you do.
*/
for
(
i
=
0
;
i
<
sp
->
timecnt
;
++
i
)
{
j
=
sp
->
types
[
i
];
sp
->
types
[
i
]
=
sp
->
ttis
[
j
].
tt_isdst
;
if
(
sp
->
ttis
[
j
].
tt_ttisgmt
)
{
/* No adjustment to transition time */
}
else
{
/*
** If summer time is in effect, and the
** transition time was not specified as
** standard time, add the summer time
** offset to the transition time;
** otherwise, add the standard time
** offset to the transition time.
*/
/*
** Transitions from DST to DDST
** will effectively disappear since
** POSIX provides for only one DST
** offset.
*/
if
(
isdst
&&
!
sp
->
ttis
[
j
].
tt_ttisstd
)
{
sp
->
ats
[
i
]
+=
dstoffset
-
theirdstoffset
;
}
else
{
sp
->
ats
[
i
]
+=
stdoffset
-
theirstdoffset
;
}
}
theiroffset
=
-
sp
->
ttis
[
j
].
tt_gmtoff
;
if
(
sp
->
ttis
[
j
].
tt_isdst
)
theirdstoffset
=
theiroffset
;
else
theirstdoffset
=
theiroffset
;
}
/*
** Finally, fill in ttis.
** ttisstd and ttisgmt need not be handled.
*/
sp
->
ttis
[
0
].
tt_gmtoff
=
-
stdoffset
;
sp
->
ttis
[
0
].
tt_isdst
=
FALSE
;
sp
->
ttis
[
0
].
tt_abbrind
=
0
;
sp
->
ttis
[
1
].
tt_gmtoff
=
-
dstoffset
;
sp
->
ttis
[
1
].
tt_isdst
=
TRUE
;
sp
->
ttis
[
1
].
tt_abbrind
=
stdlen
+
1
;
sp
->
typecnt
=
2
;
}
}
else
{
dstlen
=
0
;
sp
->
typecnt
=
1
;
/* only standard time */
sp
->
timecnt
=
0
;
sp
->
ttis
[
0
].
tt_gmtoff
=
-
stdoffset
;
sp
->
ttis
[
0
].
tt_isdst
=
0
;
sp
->
ttis
[
0
].
tt_abbrind
=
0
;
}
sp
->
charcnt
=
stdlen
+
1
;
if
(
dstlen
!=
0
)
sp
->
charcnt
+=
dstlen
+
1
;
if
((
size_t
)
sp
->
charcnt
>
sizeof
sp
->
chars
)
return
-1
;
cp
=
sp
->
chars
;
(
void
)
strncpy
(
cp
,
stdname
,
stdlen
);
cp
+=
stdlen
;
*
cp
++
=
'\0'
;
if
(
dstlen
!=
0
)
{
(
void
)
strncpy
(
cp
,
dstname
,
dstlen
);
*
(
cp
+
dstlen
)
=
'\0'
;
}
return
0
;
}
static
void
gmtload
(
sp
)
struct
state
*
const
sp
;
{
if
(
tzload
(
gmt
,
sp
)
!=
0
)
(
void
)
tzparse
(
gmt
,
sp
,
TRUE
);
}
/*
** The easy way to behave "as if no library function calls" localtime
** is to not call it--so we drop its guts into "localsub", which can be
** freely called. (And no, the PANS doesn't require the above behavior--
** but it *is* desirable.)
**
** The unused offset argument is for the benefit of mktime variants.
*/
struct
tm
*
localsub
(
timep
,
offset
,
tmp
,
sp
)
const
time_t
*
const
timep
;
const
long
offset
;
struct
tm
*
const
tmp
;
struct
state
*
sp
;
{
register
const
struct
ttinfo
*
ttisp
;
register
int
i
;
register
struct
tm
*
result
;
const
time_t
t
=
*
timep
;
#ifdef ALL_STATE
if
(
sp
==
NULL
)
return
gmtsub
(
timep
,
offset
,
tmp
);
#endif
/* defined ALL_STATE */
if
(
sp
->
timecnt
==
0
||
t
<
sp
->
ats
[
0
])
{
i
=
0
;
while
(
sp
->
ttis
[
i
].
tt_isdst
)
if
(
++
i
>=
sp
->
typecnt
)
{
i
=
0
;
break
;
}
}
else
{
for
(
i
=
1
;
i
<
sp
->
timecnt
;
++
i
)
if
(
t
<
sp
->
ats
[
i
])
break
;
i
=
(
int
)
sp
->
types
[
i
-
1
];
}
ttisp
=
&
sp
->
ttis
[
i
];
/*
** To get (wrong) behavior that's compatible with System V Release 2.0
** you'd replace the statement below with
** t += ttisp->tt_gmtoff;
** timesub(&t, 0L, sp, tmp);
*/
result
=
timesub
(
&
t
,
ttisp
->
tt_gmtoff
,
sp
,
tmp
);
tmp
->
tm_isdst
=
ttisp
->
tt_isdst
;
tzname
[
tmp
->
tm_isdst
]
=
&
sp
->
chars
[
ttisp
->
tt_abbrind
];
#ifdef TM_ZONE
tmp
->
TM_ZONE
=
&
sp
->
chars
[
ttisp
->
tt_abbrind
];
#endif
/* defined TM_ZONE */
return
result
;
}
/*
** gmtsub is to gmtime as localsub is to localtime.
*/
struct
tm
*
gmtsub
(
timep
,
offset
,
tmp
)
const
time_t
*
const
timep
;
const
long
offset
;
struct
tm
*
const
tmp
;
{
register
struct
tm
*
result
;
if
(
!
gmt_is_set
)
{
gmt_is_set
=
TRUE
;
#ifdef ALL_STATE
gmtptr
=
(
struct
state
*
)
malloc
(
sizeof
*
gmtptr
);
if
(
gmtptr
!=
NULL
)
#endif
/* defined ALL_STATE */
gmtload
(
gmtptr
);
}
result
=
timesub
(
timep
,
offset
,
gmtptr
,
tmp
);
#ifdef TM_ZONE
/*
** Could get fancy here and deliver something such as
** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
** but this is no time for a treasure hunt.
*/
if
(
offset
!=
0
)
tmp
->
TM_ZONE
=
wildabbr
;
else
{
#ifdef ALL_STATE
if
(
gmtptr
==
NULL
)
tmp
->
TM_ZONE
=
gmt
;
else
tmp
->
TM_ZONE
=
gmtptr
->
chars
;
#endif
/* defined ALL_STATE */
#ifndef ALL_STATE
tmp
->
TM_ZONE
=
gmtptr
->
chars
;
#endif
/* State Farm */
}
#endif
/* defined TM_ZONE */
return
result
;
}
/*
** Return the number of leap years through the end of the given year
** where, to make the math easy, the answer for year zero is defined as zero.
*/
static
int
leaps_thru_end_of
(
y
)
register
const
int
y
;
{
return
(
y
>=
0
)
?
(
y
/
4
-
y
/
100
+
y
/
400
)
:
-
(
leaps_thru_end_of
(
-
(
y
+
1
))
+
1
);
}
static
struct
tm
*
timesub
(
timep
,
offset
,
sp
,
tmp
)
const
time_t
*
const
timep
;
const
long
offset
;
register
const
struct
state
*
const
sp
;
register
struct
tm
*
const
tmp
;
{
register
const
struct
lsinfo
*
lp
;
register
time_t
tdays
;
register
int
idays
;
/* unsigned would be so 2003 */
register
long
rem
;
int
y
;
register
const
int
*
ip
;
register
long
corr
;
register
int
hit
;
register
int
i
;
corr
=
0
;
hit
=
0
;
#ifdef ALL_STATE
i
=
(
sp
==
NULL
)
?
0
:
sp
->
leapcnt
;
#endif
/* defined ALL_STATE */
#ifndef ALL_STATE
i
=
sp
->
leapcnt
;
#endif
/* State Farm */
while
(
--
i
>=
0
)
{
lp
=
&
sp
->
lsis
[
i
];
if
(
*
timep
>=
lp
->
ls_trans
)
{
if
(
*
timep
==
lp
->
ls_trans
)
{
hit
=
((
i
==
0
&&
lp
->
ls_corr
>
0
)
||
lp
->
ls_corr
>
sp
->
lsis
[
i
-
1
].
ls_corr
);
if
(
hit
)
while
(
i
>
0
&&
sp
->
lsis
[
i
].
ls_trans
==
sp
->
lsis
[
i
-
1
].
ls_trans
+
1
&&
sp
->
lsis
[
i
].
ls_corr
==
sp
->
lsis
[
i
-
1
].
ls_corr
+
1
)
{
++
hit
;
--
i
;
}
}
corr
=
lp
->
ls_corr
;
break
;
}
}
y
=
EPOCH_YEAR
;
tdays
=
*
timep
/
SECSPERDAY
;
rem
=
*
timep
-
tdays
*
SECSPERDAY
;
while
(
tdays
<
0
||
tdays
>=
year_lengths
[
isleap
(
y
)])
{
int
newy
;
register
time_t
tdelta
;
register
int
idelta
;
register
int
leapdays
;
tdelta
=
tdays
/
DAYSPERLYEAR
;
idelta
=
tdelta
;
if
(
tdelta
-
idelta
>=
1
||
idelta
-
tdelta
>=
1
)
return
NULL
;
if
(
idelta
==
0
)
idelta
=
(
tdays
<
0
)
?
-1
:
1
;
newy
=
y
;
if
(
increment_overflow
(
&
newy
,
idelta
))
return
NULL
;
leapdays
=
leaps_thru_end_of
(
newy
-
1
)
-
leaps_thru_end_of
(
y
-
1
);
tdays
-=
((
time_t
)
newy
-
y
)
*
DAYSPERNYEAR
;
tdays
-=
leapdays
;
y
=
newy
;
}
{
register
long
seconds
;
seconds
=
tdays
*
SECSPERDAY
+
0.5
;
tdays
=
seconds
/
SECSPERDAY
;
rem
+=
seconds
-
tdays
*
SECSPERDAY
;
}
/*
** Given the range, we can now fearlessly cast...
*/
idays
=
tdays
;
rem
+=
offset
-
corr
;
while
(
rem
<
0
)
{
rem
+=
SECSPERDAY
;
--
idays
;
}
while
(
rem
>=
SECSPERDAY
)
{
rem
-=
SECSPERDAY
;
++
idays
;
}
while
(
idays
<
0
)
{
if
(
increment_overflow
(
&
y
,
-1
))
return
NULL
;
idays
+=
year_lengths
[
isleap
(
y
)];
}
while
(
idays
>=
year_lengths
[
isleap
(
y
)])
{
idays
-=
year_lengths
[
isleap
(
y
)];
if
(
increment_overflow
(
&
y
,
1
))
return
NULL
;
}
tmp
->
tm_year
=
y
;
if
(
increment_overflow
(
&
tmp
->
tm_year
,
-
TM_YEAR_BASE
))
return
NULL
;
tmp
->
tm_yday
=
idays
;
/*
** The "extra" mods below avoid overflow problems.
*/
tmp
->
tm_wday
=
EPOCH_WDAY
+
((
y
-
EPOCH_YEAR
)
%
DAYSPERWEEK
)
*
(
DAYSPERNYEAR
%
DAYSPERWEEK
)
+
leaps_thru_end_of
(
y
-
1
)
-
leaps_thru_end_of
(
EPOCH_YEAR
-
1
)
+
idays
;
tmp
->
tm_wday
%=
DAYSPERWEEK
;
if
(
tmp
->
tm_wday
<
0
)
tmp
->
tm_wday
+=
DAYSPERWEEK
;
tmp
->
tm_hour
=
(
int
)
(
rem
/
SECSPERHOUR
);
rem
%=
SECSPERHOUR
;
tmp
->
tm_min
=
(
int
)
(
rem
/
SECSPERMIN
);
/*
** A positive leap second requires a special
** representation. This uses "... ??:59:60" et seq.
*/
tmp
->
tm_sec
=
(
int
)
(
rem
%
SECSPERMIN
)
+
hit
;
ip
=
mon_lengths
[
isleap
(
y
)];
for
(
tmp
->
tm_mon
=
0
;
idays
>=
ip
[
tmp
->
tm_mon
];
++
(
tmp
->
tm_mon
))
idays
-=
ip
[
tmp
->
tm_mon
];
tmp
->
tm_mday
=
(
int
)
(
idays
+
1
);
tmp
->
tm_isdst
=
0
;
#ifdef TM_GMTOFF
tmp
->
TM_GMTOFF
=
offset
;
#endif
/* defined TM_GMTOFF */
return
tmp
;
}
/*
** Adapted from code provided by Robert Elz, who writes:
** The "best" way to do mktime I think is based on an idea of Bob
** Kridle's (so its said...) from a long time ago.
** It does a binary search of the time_t space. Since time_t's are
** just 32 bits, its a max of 32 iterations (even at 64 bits it
** would still be very reasonable).
*/
#ifndef WRONG
#define WRONG (-1)
#endif
/* !defined WRONG */
/*
** Simplified normalize logic courtesy Paul Eggert.
*/
static
int
increment_overflow
(
number
,
delta
)
int
*
number
;
int
delta
;
{
int
number0
;
number0
=
*
number
;
*
number
+=
delta
;
return
(
*
number
<
number0
)
!=
(
delta
<
0
);
}
int
tz_init
(
const
char
*
zoneinfo_dir
)
{
char
*
ptr
;
int
fd
;
if
(
zoneinfo_dir
==
NULL
)
zoneinfo_dir
=
TZDIR
;
ptr
=
malloc
(
strlen
(
zoneinfo_dir
)
+
10
);
sprintf
(
ptr
,
"%s/zone.tab"
,
zoneinfo_dir
);
fd
=
open
(
ptr
,
O_RDONLY
);
free
(
ptr
);
if
(
fd
<
0
)
return
-1
;
close
(
fd
);
if
(
tzdir
)
free
(
tzdir
);
tzdir
=
strdup
(
zoneinfo_dir
);
return
0
;
}