214 lines
3.0 KiB
C
214 lines
3.0 KiB
C
#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 MIT license used in the rest of the distribution.
|
|
*/
|
|
#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;
|
|
}
|
|
}
|