| 197 | |
| 198 | private: |
| 199 | bool initFromFile() |
| 200 | { |
| 201 | PathName idsPath; |
| 202 | PathUtils::concatPath(idsPath, TimeZoneUtil::getTzDataPath(), "ids.dat"); |
| 203 | const int fileHandle = os_utils::open(idsPath.c_str(), O_RDONLY | O_BINARY, 0); |
| 204 | |
| 205 | if (fileHandle == -1) |
| 206 | return false; |
| 207 | |
| 208 | struct STAT stat; |
| 209 | if (os_utils::fstat(fileHandle, &stat) != 0) |
| 210 | { |
| 211 | close(fileHandle); |
| 212 | return false; |
| 213 | } |
| 214 | |
| 215 | Array<UCHAR> buffer(stat.st_size); |
| 216 | buffer.resize(stat.st_size); |
| 217 | const long bytesRead = read(fileHandle, buffer.begin(), buffer.getCount()); |
| 218 | |
| 219 | close(fileHandle); |
| 220 | |
| 221 | if (bytesRead != buffer.getCount()) |
| 222 | return false; |
| 223 | |
| 224 | const UCHAR* p = buffer.begin(); |
| 225 | const UCHAR* end = buffer.end(); |
| 226 | |
| 227 | if (buffer.getCount() > 10) |
| 228 | { |
| 229 | // file signature |
| 230 | if (memcmp(p, "FBTZ", 5) == 0) |
| 231 | { |
| 232 | p += 5; |
| 233 | if (isc_portable_integer(p, 2) == 1) // file version: must be 1 |
| 234 | { |
| 235 | string databaseVersion; |
| 236 | |
| 237 | for (p += 2; p < end && *p; ++p) |
| 238 | databaseVersion += *p; |
| 239 | |
| 240 | ++p; |
| 241 | |
| 242 | if (end - p >= 2) |
| 243 | { |
| 244 | unsigned count = isc_portable_integer(p, 2); |
| 245 | |
| 246 | // Our main criteria to choose the file or the builtin data is the count |
| 247 | // of entries. TZ database version is the second, as new version could |
| 248 | // have the same entries as before. |
| 249 | |
| 250 | if (count < FB_NELEM(BUILTIN_TIME_ZONE_LIST)) |
| 251 | { |
| 252 | gds__log("tzdata ids.dat file is older than builtin time zone list."); |
| 253 | return false; |
| 254 | } |
| 255 | else if (count == FB_NELEM(BUILTIN_TIME_ZONE_LIST) && |
| 256 | databaseVersion <= BUILTIN_TIME_ZONE_VERSION) |