/* See LICENSE file for copyright and license details. */ #include "common.h" static char * get_timezone(struct libgeome_context *ctx, int *env_determined_out) { const char *tzdir; const char *tz; char *tzbuf = NULL; char *ret; size_t tzdirlen; tz = getenv("TZ"); *env_determined_out = !!tz; if (!tz) { tzdir = getenv("TZDIR"); if (!tzdir) tzdir = TZDIR; tz = tzbuf = libgeome_util_areadlink(ctx, TZFILE); if (!tz) return NULL; tzdirlen = strlen(tzdir); if (!strncmp(tz, tzdir, tzdirlen) && tz[tzdirlen] == '/') { tz = &tz[tzdirlen]; while (*tz == '/') tz++; } } ret = strdup(tz); if (!ret) ctx->print_error(ctx, "strdup: %s\n", strerror(errno)); free(tzbuf); return ret; } static int get_timezone_location3(struct libgeome_context *ctx, const char *tz, const char *tabfile, struct location *location_out) { const char *tzdir, *coords; char *path, *line = NULL, *p; size_t size = 0, tzlen = strlen(tz); int lat_sign, lat_degrees, lat_minutes, lat_seconds = 0; int lon_sign, lon_degrees, lon_minutes, lon_seconds = 0; ssize_t len; FILE *f; tzdir = getenv("TZDIR"); if (!tzdir) tzdir = TZDIR; path = malloc(strlen(tzdir) + strlen(tabfile) + sizeof("/")); if (!path) { ctx->print_error(ctx, "malloc: %s\n", strerror(errno)); return -1; } stpcpy(stpcpy(stpcpy(path, tzdir), "/"), tabfile); f = fopen(path, "r"); if (!f) { if (errno == ENOENT) ctx->print_debug(ctx, "fopen %s r: %s\n", path, strerror(errno)); else ctx->print_error(ctx, "fopen %s r: %s\n", path, strerror(errno)); free(path); return -1; } again: while (len = getline(&line, &size, f), len >= 0) { p = line; for (;; p++) { if (!*p || *p == '\r' || *p == '\n' || *p == '#') goto next_line; if (*p == '\t') break; } coords = ++p; for (;; p++) { if (!*p || *p == '\r' || *p == '\n' || *p == '#') goto next_line; if (*p == '\t') break; } *p++ = '\0'; if (strncmp(p, tz, tzlen)) goto next_line; p = &p[tzlen]; if (*p && *p != '\r' && *p != '\n' && *p != '#' && *p != '\t') goto next_line; goto found_coords; next_line:; } if (!feof(f)) { if (errno == EINTR) { clearerr(f); goto again; } ctx->print_error(ctx, "getline %s: %s\n", path, strerror(errno)); } ctx->print_debug(ctx, "entry for timezone %s not found in %s\n", tz, path); free(line); free(path); fclose(f); return -1; bad_coords: ctx->print_debug(ctx, "malformatted coordinates for %s in %s\n", tz, path); free(line); free(path); fclose(f); return -1; found_coords: if (coords[0] != '+' && coords[0] != '-') goto bad_coords; if (!isdigit(coords[1]) || !isdigit(coords[2])) goto bad_coords; if (!isdigit(coords[3]) || !isdigit(coords[4])) goto bad_coords; lat_sign = coords[0] == '-' ? -1 : +1; lat_degrees = (coords[1] & 15) * 10 + (coords[2] & 15); lat_minutes = (coords[3] & 15) * 10 + (coords[4] & 15); coords = &coords[5]; if (isdigit(coords[0]) && isdigit(coords[1])) { lat_seconds = (coords[0] & 15) * 10 + (coords[1] & 15); coords = &coords[2]; } if (coords[0] != '+' && coords[0] != '-') goto bad_coords; if (!isdigit(coords[1]) || !isdigit(coords[2]) || !isdigit(coords[3])) goto bad_coords; if (!isdigit(coords[4]) || !isdigit(coords[5])) goto bad_coords; lon_sign = coords[0] == '-' ? -1 : +1; lon_degrees = (coords[1] & 15) * 100 + (coords[2] & 15) * 10 + (coords[3] & 15); lon_minutes = (coords[4] & 15) * 10 + (coords[5] & 15); coords = &coords[6]; if (isdigit(coords[0]) && isdigit(coords[1])) { lon_seconds = (coords[0] & 15) * 10 + (coords[1] & 15); coords = &coords[2]; } if (*coords) goto bad_coords; if (lat_degrees > 90 || lon_degrees > 180) goto bad_coords; if (lat_minutes >= 60 || lon_minutes >= 60) goto bad_coords; if (lat_seconds >= 60 || lon_seconds >= 60) goto bad_coords; if (lat_degrees == 90 && lat_minutes + lat_seconds) goto bad_coords; if (lon_degrees == 180 && lon_minutes + lon_seconds) goto bad_coords; location_out->latitude = (double)lat_degrees; location_out->latitude += (double)lat_minutes / 60; location_out->latitude += (double)lat_seconds / 3600; location_out->latitude *= (double)lat_sign; location_out->longitude = (double)lon_degrees; location_out->longitude += (double)lon_minutes / 60; location_out->longitude += (double)lon_seconds / 3600; location_out->longitude *= (double)lon_sign; free(line); free(path); fclose(f); return 0; } static int get_timezone_location(struct libgeome_context *ctx, const char *tz, struct location *location_out) { if (!get_timezone_location3(ctx, tz, "zone1970.tab", location_out)) return 0; if (!get_timezone_location3(ctx, tz, "zone.tab", location_out)) return 0; if (!get_timezone_location3(ctx, tz, "zonenow.tab", location_out)) return 0; return -1; } int libgeome_get_from_timezone(struct libgeome_context *ctx, struct libgeome_data *out) { struct location location; char *tz; tz = get_timezone(ctx, &(int){0}); if (!tz) return -1; if (get_timezone_location(ctx, tz, &location)) { free(tz); return -1; } free(tz); out->requested_data &= LIBGEOME_DATUM_LATITUDE | LIBGEOME_DATUM_LONGITUDE; if (out->requested_data) { if (out->requested_data & LIBGEOME_DATUM_LATITUDE) out->latitude = location.latitude; if (out->requested_data & LIBGEOME_DATUM_LONGITUDE) out->longitude = location.longitude; } return 0; }