| #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; | 
 | 	} | 
 | } |