zoneinfo.c (3065B)
1 #include <u.h> 2 #include <libc.h> 3 4 /* 5 * Access local time entries of zoneinfo files. 6 * Formats 0 and 2 are supported, and 4-byte timestamps 7 * 8 * Copyright © 2008 M. Teichgräber 9 * Contributed under the MIT license used in the rest of the distribution. 10 */ 11 #include "zoneinfo.h" 12 13 static 14 struct Zoneinfo 15 { 16 int timecnt; /* # of transition times */ 17 int typecnt; /* # of local time types */ 18 int charcnt; /* # of characters of time zone abbreviation strings */ 19 20 uchar *ptime; 21 uchar *ptype; 22 uchar *ptt; 23 uchar *pzone; 24 } z; 25 26 static uchar *tzdata; 27 28 static 29 uchar* 30 readtzfile(char *file) 31 { 32 uchar *p; 33 int fd; 34 Dir *d; 35 36 fd = open(file, OREAD); 37 if (fd<0) 38 return nil; 39 d = dirfstat(fd); 40 if (d==nil) 41 return nil; 42 p = malloc(d->length); 43 if (p!=nil) 44 readn(fd, p, d->length); 45 free(d); 46 close(fd); 47 return p; 48 } 49 static char *zonefile; 50 void 51 tzfile(char *f) 52 { 53 if (tzdata!=nil) { 54 free(tzdata); 55 tzdata = nil; 56 } 57 z.timecnt = 0; 58 zonefile = f; 59 } 60 61 static 62 long 63 get4(uchar *p) 64 { 65 return (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; 66 } 67 68 enum { 69 TTinfosz = 4+1+1, 70 }; 71 72 static 73 int 74 parsehead(void) 75 { 76 uchar *p; 77 int ver; 78 79 ver = tzdata[4]; 80 if (ver!=0) 81 if (ver!='2') 82 return -1; 83 84 p = tzdata + 4 + 1 + 15; 85 86 z.timecnt = get4(p+3*4); 87 z.typecnt = get4(p+4*4); 88 if (z.typecnt==0) 89 return -1; 90 z.charcnt = get4(p+5*4); 91 z.ptime = p+6*4; 92 z.ptype = z.ptime + z.timecnt*4; 93 z.ptt = z.ptype + z.timecnt; 94 z.pzone = z.ptt + z.typecnt*TTinfosz; 95 return 0; 96 } 97 98 static 99 void 100 ttinfo(Tinfo *ti, int tti) 101 { 102 uchar *p; 103 int i; 104 105 i = z.ptype[tti]; 106 assert(i<z.typecnt); 107 p = z.ptt + i*TTinfosz; 108 ti->tzoff = get4(p); 109 ti->dlflag = p[4]; 110 assert(p[5]<z.charcnt); 111 ti->zone = (char*)z.pzone + p[5]; 112 } 113 114 static 115 void 116 readtimezone(void) 117 { 118 char *tmp; 119 120 z.timecnt = 0; 121 if(zonefile==nil) { 122 if ((tmp=getenv("timezone"))!=nil) { 123 tzdata = readtzfile(tmp); 124 free(tmp); 125 goto havedata; 126 } 127 zonefile = "/etc/localtime"; 128 } 129 tzdata = readtzfile(zonefile); 130 if (tzdata==nil) 131 return; 132 133 havedata: 134 if (strncmp("TZif", (char*)tzdata, 4)!=0) 135 goto errfree; 136 137 if (parsehead()==-1) { 138 errfree: 139 free(tzdata); 140 tzdata = nil; 141 z.timecnt = 0; 142 return; 143 } 144 } 145 146 static 147 tlong 148 gett4(uchar *p) 149 { 150 long l; 151 152 l = get4(p); 153 if (l<0) 154 return 0; 155 return l; 156 } 157 int 158 zonetinfo(Tinfo *ti, int i) 159 { 160 if (tzdata==nil) 161 readtimezone(); 162 if (i<0 || i>=z.timecnt) 163 return -1; 164 ti->t = gett4(z.ptime + 4*i); 165 ttinfo(ti, i); 166 return i; 167 } 168 169 int 170 zonelookuptinfo(Tinfo *ti, tlong t) 171 { 172 uchar *p; 173 int i; 174 tlong oldtt, tt; 175 176 if (tzdata==nil) 177 readtimezone(); 178 oldtt = 0; 179 p = z.ptime; 180 for (i=0; i<z.timecnt; i++) { 181 tt = gett4(p); 182 if (t<tt) 183 break; 184 oldtt = tt; 185 p += 4; 186 } 187 if (i>0) { 188 ttinfo(ti, i-1); 189 ti->t = oldtt; 190 // fprint(2, "t:%ld off:%d dflag:%d %s\n", (long)ti->t, ti->tzoff, ti->dlflag, ti->zone); 191 return i-1; 192 } 193 return -1; 194 } 195 196 void 197 zonedump(int fd) 198 { 199 int i; 200 uchar *p; 201 tlong t; 202 Tinfo ti; 203 204 if (tzdata==nil) 205 readtimezone(); 206 p = z.ptime; 207 for (i=0; i<z.timecnt; i++) { 208 t = gett4(p); 209 ttinfo(&ti, i); 210 fprint(fd, "%ld\t%d\t%d\t%s\n", (long)t, ti.tzoff, ti.dlflag, ti.zone); 211 p += 4; 212 } 213 }