blob: 91f1885a85236e1dc5b360d747feefff870dedef [file] [log] [blame]
#include <u.h>
#include <libc.h>
/*
* Access local time entries of zoneinfo files.
* Formats 0 and 2 are supported, and 4-byte timestamps
*
* Copyright © 2008 M. Teichgräber
* Contributed under the terms of the Lucent Public License 1.02.
*/
#include "zoneinfo.h"
static
struct Zoneinfo
{
int timecnt; /* # of transition times */
int typecnt; /* # of local time types */
int charcnt; /* # of characters of time zone abbreviation strings */
uchar *ptime;
uchar *ptype;
uchar *ptt;
uchar *pzone;
} z;
static uchar *tzdata;
static
uchar*
readtzfile(char *file)
{
uchar *p;
int fd;
Dir *d;
fd = open(file, OREAD);
if (fd<0)
return nil;
d = dirfstat(fd);
if (d==nil)
return nil;
p = malloc(d->length);
if (p!=nil)
readn(fd, p, d->length);
free(d);
close(fd);
return p;
}
static char *zonefile;
void
tzfile(char *f)
{
if (tzdata!=nil) {
free(tzdata);
tzdata = nil;
}
z.timecnt = 0;
zonefile = f;
}
static
long
get4(uchar *p)
{
return (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
}
enum {
TTinfosz = 4+1+1,
};
static
int
parsehead(void)
{
uchar *p;
int ver;
ver = tzdata[4];
if (ver!=0)
if (ver!='2')
return -1;
p = tzdata + 4 + 1 + 15;
z.timecnt = get4(p+3*4);
z.typecnt = get4(p+4*4);
if (z.typecnt==0)
return -1;
z.charcnt = get4(p+5*4);
z.ptime = p+6*4;
z.ptype = z.ptime + z.timecnt*4;
z.ptt = z.ptype + z.timecnt;
z.pzone = z.ptt + z.typecnt*TTinfosz;
return 0;
}
static
void
ttinfo(Tinfo *ti, int tti)
{
uchar *p;
int i;
i = z.ptype[tti];
assert(i<z.typecnt);
p = z.ptt + i*TTinfosz;
ti->tzoff = get4(p);
ti->dlflag = p[4];
assert(p[5]<z.charcnt);
ti->zone = (char*)z.pzone + p[5];
}
static
void
readtimezone(void)
{
char *tmp;
z.timecnt = 0;
if(zonefile==nil) {
if ((tmp=getenv("timezone"))!=nil) {
tzdata = readtzfile(tmp);
free(tmp);
goto havedata;
}
zonefile = "/etc/localtime";
}
tzdata = readtzfile(zonefile);
if (tzdata==nil)
return;
havedata:
if (strncmp("TZif", (char*)tzdata, 4)!=0)
goto errfree;
if (parsehead()==-1) {
errfree:
free(tzdata);
tzdata = nil;
z.timecnt = 0;
return;
}
}
static
tlong
gett4(uchar *p)
{
long l;
l = get4(p);
if (l<0)
return 0;
return l;
}
int
zonetinfo(Tinfo *ti, int i)
{
if (tzdata==nil)
readtimezone();
if (i<0 || i>=z.timecnt)
return -1;
ti->t = gett4(z.ptime + 4*i);
ttinfo(ti, i);
return i;
}
int
zonelookuptinfo(Tinfo *ti, tlong t)
{
uchar *p;
int i;
tlong oldtt, tt;
if (tzdata==nil)
readtimezone();
oldtt = 0;
p = z.ptime;
for (i=0; i<z.timecnt; i++) {
tt = gett4(p);
if (t<tt)
break;
oldtt = tt;
p += 4;
}
if (i>0) {
ttinfo(ti, i-1);
ti->t = oldtt;
// fprint(2, "t:%ld off:%d dflag:%d %s\n", (long)ti->t, ti->tzoff, ti->dlflag, ti->zone);
return i-1;
}
return -1;
}
void
zonedump(int fd)
{
int i;
uchar *p;
tlong t;
Tinfo ti;
if (tzdata==nil)
readtimezone();
p = z.ptime;
for (i=0; i<z.timecnt; i++) {
t = gett4(p);
ttinfo(&ti, i);
fprint(fd, "%ld\t%d\t%d\t%s\n", (long)t, ti.tzoff, ti.dlflag, ti.zone);
p += 4;
}
}