diff options
author | Benjamin Berg <bberg@redhat.com> | 2020-04-25 23:23:23 +0200 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2021-01-11 20:31:52 +0100 |
commit | f4f8bd4b808722a3274783f60ffbab9437114c29 (patch) | |
tree | f106ccfa85362971fa967b62856a0c08f967135b | |
parent | 5bdaac8f0b32d0a031503d5f56acaf87dc43c840 (diff) | |
download | libgweather-f4f8bd4b808722a3274783f60ffbab9437114c29.tar.gz |
gweather: Port to new GVariant based in-memory DB
This makes GWeather use a memory mapped GVariant as an in-memory
database. Doing this allows much faster access and decreases memory
consumption considerably as items can be fetched from the database
on-demand.
-rw-r--r-- | libgweather/gweather-location.c | 1094 | ||||
-rw-r--r-- | libgweather/gweather-location.h | 3 | ||||
-rw-r--r-- | libgweather/gweather-parser.c | 223 | ||||
-rw-r--r-- | libgweather/gweather-parser.h | 46 | ||||
-rw-r--r-- | libgweather/gweather-private.h | 43 | ||||
-rw-r--r-- | libgweather/gweather-timezone.c | 196 | ||||
-rw-r--r-- | libgweather/meson.build | 7 | ||||
-rw-r--r-- | libgweather/test_libgweather.c | 23 | ||||
-rw-r--r-- | libgweather/test_locations.c | 2 | ||||
-rw-r--r-- | libgweather/test_locations_utc.c | 2 | ||||
-rw-r--r-- | meson.build | 2 |
11 files changed, 713 insertions, 928 deletions
diff --git a/libgweather/gweather-location.c b/libgweather/gweather-location.c index 85cd9a9..4c69b49 100644 --- a/libgweather/gweather-location.c +++ b/libgweather/gweather-location.c @@ -30,9 +30,7 @@ #include <libxml/xmlreader.h> #include <geocode-glib/geocode-glib.h> -#include "gweather-location.h" #include "gweather-timezone.h" -#include "gweather-parser.h" #include "gweather-private.h" /* This is the precision of coordinates in the database */ @@ -42,6 +40,17 @@ * airport to a city, see also test_distance() */ #define AIRPORT_MAX_DISTANCE 100.0 +static inline GWeatherLocation* +_iter_up(GWeatherLocation *loc) +{ + GWeatherLocation *tmp; + + tmp = gweather_location_get_parent (loc); + gweather_location_unref (loc); + return tmp; +} +#define ITER_UP(start, _p) for ((_p) = gweather_location_ref (start); (_p); (_p) = _iter_up(_p)) + /** * SECTION:gweatherlocation * @Title: GWeatherLocation @@ -85,48 +94,6 @@ * create detached instances, but deserializing might. **/ -static int -sort_locations_by_name (gconstpointer a, gconstpointer b) -{ - GWeatherLocation *loc_a = *(GWeatherLocation **)a; - GWeatherLocation *loc_b = *(GWeatherLocation **)b; - - return g_utf8_collate (loc_a->local_sort_name, loc_b->local_sort_name); -} - -static int -sort_locations_by_distance (gconstpointer a, gconstpointer b, gpointer user_data) -{ - GWeatherLocation *loc_a = *(GWeatherLocation **)a; - GWeatherLocation *loc_b = *(GWeatherLocation **)b; - GWeatherLocation *city = (GWeatherLocation *)user_data; - double dist_a, dist_b; - - dist_a = gweather_location_get_distance (loc_a, city); - dist_b = gweather_location_get_distance (loc_b, city); - if (dist_a < dist_b) - return -1; - else if (dist_a > dist_b) - return 1; - else - return 0; -} - -static gboolean -parse_coordinates (const char *coordinates, - double *latitude, double *longitude) -{ - char *p; - - *latitude = g_ascii_strtod (coordinates, &p) * M_PI / 180.0; - if (p == (char *)coordinates) - return FALSE; - if (*p++ != ' ') - return FALSE; - *longitude = g_ascii_strtod (p, &p) * M_PI / 180.0; - return !*p; -} - static GWeatherLocation * location_new (GWeatherLocationLevel level) { @@ -136,300 +103,87 @@ location_new (GWeatherLocationLevel level) loc->latitude = loc->longitude = DBL_MAX; loc->level = level; loc->ref_count = 1; + loc->db_idx = INVALID_IDX; + loc->tz_hint_idx = INVALID_IDX; return loc; } static void add_timezones (GWeatherLocation *loc, GPtrArray *zones); -static void -add_nearest_weather_station (GWeatherLocation *location) -{ - GWeatherLocation **siblings, *station; - GWeatherLocation *closest = NULL; - double min_distance = G_MAXDOUBLE; - GPtrArray *zones; - guint i; - - g_assert (location->parent); - g_assert (gweather_location_get_level (location) == GWEATHER_LOCATION_CITY); - - if (location->children != NULL) - return; - - siblings = location->parent->children; - for (i = 0; siblings[i] != NULL; i++) { - double distance; - - if (siblings[i] == location) - continue; - - if (siblings[i]->level != GWEATHER_LOCATION_WEATHER_STATION) - continue; - - /* Skip siblings without valid coordinates */ - if (!siblings[i]->latlon_valid) - continue; - - distance = gweather_location_get_distance (location, siblings[i]); - if (distance < min_distance) { - closest = siblings[i]; - min_distance = distance; - } - } - - /* This should not happen */ - if (!closest) { - g_critical ("Location '%s' has no valid airports attached", location->english_name); - return; - } - - /* This could however */ - if (min_distance > AIRPORT_MAX_DISTANCE) { - g_debug ("Not adding airport '%s' as it's too far from '%s' (%.1lf km)\n", - gweather_location_get_name (closest), - gweather_location_get_name (location), - min_distance); - return; - } - - location->children = g_new0 (GWeatherLocation *, 2); - location->children[0] = g_slice_new0 (GWeatherLocation); - station = location->children[0]; - station->english_name = g_strdup (closest->english_name); - station->local_name = g_strdup (closest->local_name); - station->msgctxt = g_strdup (closest->msgctxt); - station->local_sort_name = g_strdup (closest->local_sort_name); - station->english_sort_name = g_strdup (closest->english_sort_name); - station->parent = location; - station->level = GWEATHER_LOCATION_WEATHER_STATION; - station->country_code = g_strdup (closest->country_code); - station->tz_hint = g_strdup (closest->tz_hint); - station->station_code = g_strdup (closest->station_code); - station->forecast_zone = g_strdup (closest->forecast_zone); - station->radar = g_strdup (closest->radar); - station->latitude = closest->latitude; - station->longitude = closest->longitude; - station->latlon_valid = closest->latlon_valid; - - zones = g_ptr_array_new (); - add_timezones (station, zones); - g_ptr_array_add (zones, NULL); - station->zones = (GWeatherTimezone **)g_ptr_array_free (zones, FALSE); - - station->ref_count = 1; -} - -static void -add_nearest_weather_stations (GWeatherLocation *location) -{ - GWeatherLocation **children; - guint i; - - /* For each city without a <location>, add the nearest airport in the - * same country or state to it */ - children = gweather_location_get_children (location); - for (i = 0; children[i] != NULL; i++) { - if (gweather_location_get_level (children[i]) == GWEATHER_LOCATION_CITY) - add_nearest_weather_station (children[i]); - else - add_nearest_weather_stations (children[i]); - } -} - static GWeatherLocation * -location_new_from_xml (GWeatherParser *parser, GWeatherLocationLevel level, - GWeatherLocation *parent) +location_ref_for_idx (GWeatherDb *db, + guint16 idx, + GWeatherLocation *nearest_of) { - GWeatherLocation *loc, *child; - GPtrArray *children = NULL; - const char *tagname; - char *value, *normalized; - int tagtype; - unsigned int i; - - loc = location_new (level); - loc->parent = parent; - if (level == GWEATHER_LOCATION_WORLD) { - loc->metar_code_cache = g_hash_table_ref (parser->metar_code_cache); - loc->country_code_cache = g_hash_table_ref (parser->country_code_cache); - loc->timezone_cache = g_hash_table_ref (parser->timezone_cache); - } - children = g_ptr_array_new (); - - if (xmlTextReaderRead (parser->xml) != 1) - goto error_out; - while ((tagtype = xmlTextReaderNodeType (parser->xml)) != - XML_READER_TYPE_END_ELEMENT) { - if (tagtype != XML_READER_TYPE_ELEMENT) { - if (xmlTextReaderRead (parser->xml) != 1) - goto error_out; - continue; - } - - tagname = (const char *) xmlTextReaderConstName (parser->xml); - if ((!strcmp (tagname, "name") || !strcmp (tagname, "_name")) && !loc->english_name) { - loc->msgctxt = _gweather_parser_get_msgctxt_value (parser); - value = _gweather_parser_get_value (parser); - if (!value) - goto error_out; - - loc->english_name = g_strdup (value); - - if (loc->msgctxt) { - loc->local_name = g_strdup (g_dpgettext2 ("libgweather-locations", - (char*) loc->msgctxt, value)); - } else { - loc->local_name = g_strdup (g_dgettext ("libgweather-locations", value)); - } + GWeatherLocation *loc; + DbLocationRef ref; - normalized = g_utf8_normalize (loc->local_name, -1, G_NORMALIZE_ALL); - loc->local_sort_name = g_utf8_casefold (normalized, -1); - g_free (normalized); - - normalized = g_utf8_normalize (loc->english_name, -1, G_NORMALIZE_ALL); - loc->english_sort_name = g_utf8_casefold (normalized, -1); - g_free (normalized); - xmlFree (value); - } else if (!strcmp (tagname, "iso-code") && !loc->country_code) { - value = _gweather_parser_get_value (parser); - if (!value) - goto error_out; - loc->country_code = g_strdup (value); - xmlFree (value); - } else if (!strcmp (tagname, "tz-hint") && !loc->tz_hint) { - value = _gweather_parser_get_value (parser); - if (!value) - goto error_out; - loc->tz_hint = g_strdup (value); - xmlFree (value); - } else if (!strcmp (tagname, "code") && !loc->station_code) { - value = _gweather_parser_get_value (parser); - if (!value) - goto error_out; - loc->station_code = g_strdup (value); - xmlFree (value); - } else if (!strcmp (tagname, "coordinates") && !loc->latlon_valid) { - value = _gweather_parser_get_value (parser); - if (!value) - goto error_out; - if (parse_coordinates (value, &loc->latitude, &loc->longitude)) - loc->latlon_valid = TRUE; - else - g_warning ("Coordinates could not be parsed: '%s'", value); - xmlFree (value); - } else if (!strcmp (tagname, "zone") && !loc->forecast_zone) { - value = _gweather_parser_get_value (parser); - if (!value) - goto error_out; - loc->forecast_zone = g_strdup (value); - xmlFree (value); - } else if (!strcmp (tagname, "radar") && !loc->radar) { - value = _gweather_parser_get_value (parser); - if (!value) - goto error_out; - loc->radar = g_strdup (value); - xmlFree (value); - - } else if (!strcmp (tagname, "region")) { - child = location_new_from_xml (parser, GWEATHER_LOCATION_REGION, loc); - if (!child) - goto error_out; - g_ptr_array_add (children, child); - } else if (!strcmp (tagname, "country")) { - child = location_new_from_xml (parser, GWEATHER_LOCATION_COUNTRY, loc); - if (!child) - goto error_out; - g_ptr_array_add (children, child); - } else if (!strcmp (tagname, "state")) { - child = location_new_from_xml (parser, GWEATHER_LOCATION_ADM1, loc); - if (!child) - goto error_out; - g_ptr_array_add (children, child); - } else if (!strcmp (tagname, "city")) { - child = location_new_from_xml (parser, GWEATHER_LOCATION_CITY, loc); - if (!child) - goto error_out; - g_ptr_array_add (children, child); - } else if (!strcmp (tagname, "location")) { - child = location_new_from_xml (parser, GWEATHER_LOCATION_WEATHER_STATION, loc); - if (!child) - goto error_out; - g_ptr_array_add (children, child); - - } else if (!strcmp (tagname, "named-timezone")) { - child = location_new_from_xml (parser, GWEATHER_LOCATION_NAMED_TIMEZONE, loc); - if (!child) - goto error_out; - g_ptr_array_add (children, child); - - } else if (!strcmp (tagname, "timezones")) { - loc->zones = _gweather_timezones_parse_xml (parser); - if (!loc->zones) - goto error_out; + g_assert (db); + g_assert (idx < db->locations->len); - } else { - if (xmlTextReaderNext (parser->xml) != 1) - goto error_out; + /* nearest copies are cached by the parent */ + if (!nearest_of) { + loc = g_ptr_array_index (db->locations, idx); + if (loc) { + return gweather_location_ref (loc); } } - if (xmlTextReaderRead (parser->xml) != 1 && parent) - goto error_out; - - if (level == GWEATHER_LOCATION_WEATHER_STATION || - level == GWEATHER_LOCATION_NAMED_TIMEZONE) { - /* Cache weather stations by METAR code */ - GList *a, *b; - - a = g_hash_table_lookup (parser->metar_code_cache, loc->station_code); - b = g_list_append (a, gweather_location_ref (loc)); - if (b != a) - g_hash_table_replace (parser->metar_code_cache, loc->station_code, b); - } - if (level == GWEATHER_LOCATION_COUNTRY) { - if (loc->country_code) { - GWeatherLocation *existing; + ref = db_arrayof_location_get_at (db->locations_ref, idx); + loc = location_new (db_location_get_level (ref)); + loc->db = db; + loc->db_idx = idx; + loc->ref = ref; - existing = g_hash_table_lookup (parser->country_code_cache, loc->country_code); - if (existing) - g_warning ("A country with country code '%s' is already in the database", - loc->country_code); - g_hash_table_replace (parser->country_code_cache, loc->country_code, - gweather_location_ref (loc)); - } - } + /* Override parent information for "nearest" copies. */ + if (nearest_of) + loc->parent_idx = nearest_of->db_idx; + else + loc->parent_idx = db_location_get_parent (ref); - if (children->len) { - if (level == GWEATHER_LOCATION_CITY) - g_ptr_array_sort_with_data (children, sort_locations_by_distance, loc); - else - g_ptr_array_sort (children, sort_locations_by_name); + loc->tz_hint_idx = db_location_get_tz_hint (ref); - g_ptr_array_add (children, NULL); - loc->children = (GWeatherLocation **)g_ptr_array_free (children, FALSE); - } else - g_ptr_array_free (children, TRUE); + loc->latitude = db_coordinate_get_lat (db_location_get_coordinates (ref)); + loc->longitude = db_coordinate_get_lon (db_location_get_coordinates (ref)); + loc->latlon_valid = isfinite(loc->latitude) && isfinite(loc->longitude); - return loc; + /* Note, we used to sort locations by distance (for cities) and name; + * Distance sorting is done in the variant already, + * name sorting however needs translations and is not done anymore. */ -error_out: - gweather_location_unref (loc); - for (i = 0; i < children->len; i++) - gweather_location_unref (children->pdata[i]); - g_ptr_array_free (children, TRUE); + /* Store a weak reference in the cache. + * Implicit "nearest" copies do not have a weak reference, they simply + * belong to the parent. */ + if (!nearest_of) + g_ptr_array_index (db->locations, idx) = loc; - return NULL; + return loc; } -static GWeatherLocation *global_world = NULL; - -static void _gweather_location_unref_no_check (GWeatherLocation *loc); +static GWeatherDb *world_db; GWEATHER_EXTERN void _gweather_location_reset_world (void) { - g_clear_pointer (&global_world, _gweather_location_unref_no_check); + gsize i; + g_return_if_fail (world_db); + + /* At this point, we had a leak if the caches are not completely empty. */ + for (i = 0; i < world_db->locations->len; i++) { + if (G_UNLIKELY (g_ptr_array_index (world_db->locations, i) != NULL)) { + g_warning ("Location with index %li and name %s is still referenced!", + i, gweather_location_get_name (g_ptr_array_index (world_db->locations, i))); + g_assert_not_reached (); + } + } + for (i = 0; i < world_db->timezones->len; i++) { + if (G_UNLIKELY (g_ptr_array_index (world_db->timezones, i) != NULL)) { + g_warning ("Timezone with index %li and tzid %s is still referenced!", + i, gweather_timezone_get_tzid (g_ptr_array_index (world_db->timezones, i))); + g_assert_not_reached (); + } + } } /** @@ -445,34 +199,66 @@ _gweather_location_reset_world (void) * location, or %NULL if Locations.xml could not be found or could not be parsed. **/ GWeatherLocation * -gweather_location_get_world (void) +gweather_location_get_world () { - GWeatherParser *parser; + g_autoptr(GError) error = NULL; + GMappedFile *map; - if (!global_world) { + if (!world_db) { const char *locations_path; + g_autofree char *filename = NULL; + time_t now; + struct tm tm; locations_path = g_getenv ("LIBGWEATHER_LOCATIONS_PATH"); if (locations_path) { - parser = _gweather_parser_new_for_path (locations_path); - if (!parser) { - g_warning ("Failed to open '%s' as LIBGWEATHER_LOCATIONS_PATH", - locations_path); - parser = _gweather_parser_new (); - } - } else { - parser = _gweather_parser_new (); + filename = g_strdup (locations_path); + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { + g_warning ("User specified database %s does not exist", filename); + g_clear_pointer (&filename, g_free); + } } - if (!parser) + + if (!filename) + filename = g_build_filename (GWEATHER_BIN_LOCATION_DIR, "Locations.bin", NULL); + + map = g_mapped_file_new (filename, FALSE, &error); + if (!map) { + g_warning ("Faile to open database %s: %s", filename, error->message); + return NULL; + } + + world_db = g_new0 (GWeatherDb, 1); + world_db->map = map; + world_db->world = db_world_from_data (g_mapped_file_get_contents (map), g_mapped_file_get_length (map)); + /* This is GWthDB01 */ + if (db_world_get_magic (world_db->world) != 0x5747687442443130) { + g_mapped_file_unref (world_db->map); + g_free (world_db); return NULL; + } + + world_db->locations_ref = db_world_get_locations (world_db->world); + world_db->timezones_ref = db_world_get_timezones (world_db->world); - global_world = location_new_from_xml (parser, GWEATHER_LOCATION_WORLD, NULL); - if (!g_getenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST")) - add_nearest_weather_stations (global_world); - _gweather_parser_free (parser); + world_db->locations = g_ptr_array_new (); + world_db->timezones = g_ptr_array_new (); + + g_ptr_array_set_size (world_db->locations, db_arrayof_location_get_length (world_db->locations_ref)); + g_ptr_array_set_size (world_db->timezones, db_world_timezones_get_length (world_db->timezones_ref)); + + /* Get timestamps for the start and end of this year */ + now = time (NULL); + tm = *gmtime (&now); + tm.tm_mon = 0; + tm.tm_mday = 1; + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + world_db->year_start = mktime (&tm); + tm.tm_year++; + world_db->year_end = mktime (&tm); } - return gweather_location_ref (global_world); + return location_ref_for_idx (world_db, 0, NULL); } /** @@ -492,49 +278,6 @@ gweather_location_ref (GWeatherLocation *loc) return loc; } -static void -_gweather_location_unref_no_check (GWeatherLocation *loc) -{ - int i; - - if (--loc->ref_count) - return; - - g_free (loc->english_name); - g_free (loc->local_name); - g_free (loc->msgctxt); - g_free (loc->local_sort_name); - g_free (loc->english_sort_name); - g_free (loc->country_code); - g_free (loc->tz_hint); - g_free (loc->station_code); - g_free (loc->forecast_zone); - g_free (loc->radar); - - if (loc->children) { - for (i = 0; loc->children[i]; i++) { - loc->children[i]->parent = NULL; - gweather_location_unref (loc->children[i]); - } - g_free (loc->children); - } - - if (loc->zones) { - for (i = 0; loc->zones[i]; i++) - gweather_timezone_unref (loc->zones[i]); - g_free (loc->zones); - } - - if (loc->metar_code_cache) - g_hash_table_unref (loc->metar_code_cache); - if (loc->timezone_cache) - g_hash_table_unref (loc->timezone_cache); - if (loc->country_code_cache) - g_hash_table_unref (loc->country_code_cache); - - g_slice_free (GWeatherLocation, loc); -} - /** * gweather_location_unref: * @loc: a #GWeatherLocation @@ -546,9 +289,35 @@ void gweather_location_unref (GWeatherLocation *loc) { g_return_if_fail (loc != NULL); - g_return_if_fail (loc != global_world || loc->ref_count > 1); - _gweather_location_unref_no_check (loc); + int i; + + if (--loc->ref_count) + return; + + /* Remove weak reference from DB object; but only if it points to us. + * It may point elsewhere if we are an implicit nearest child. */ + if (loc->db && g_ptr_array_index (loc->db->locations, loc->db_idx) == loc) + g_ptr_array_index (loc->db->locations, loc->db_idx) = NULL; + + g_free (loc->_english_name); + g_free (loc->_local_name); + g_free (loc->_local_sort_name); + g_free (loc->_english_sort_name); + g_free (loc->_country_code); + g_free (loc->_station_code); + + if (loc->_children) { + for (i = 0; loc->_children[i]; i++) { + gweather_location_unref (loc->_children[i]); + } + g_free (loc->_children); + } + + g_clear_pointer (&loc->_parent, gweather_location_unref); + g_clear_pointer (&loc->_timezone, gweather_timezone_unref); + + g_slice_free (GWeatherLocation, loc); } GType @@ -579,7 +348,25 @@ gweather_location_get_name (GWeatherLocation *loc) { g_return_val_if_fail (loc != NULL, NULL); - return loc->local_name; + if (loc->_local_name) + return loc->_local_name; + + if (loc->db && IDX_VALID (loc->db_idx)) { + const char *english_name; + const char *msgctxt; + english_name = EMPTY_TO_NULL (db_i18n_get_str (db_location_get_name (loc->ref))); + msgctxt = EMPTY_TO_NULL (db_i18n_get_msgctxt (db_location_get_name (loc->ref))); + + if (msgctxt) { + loc->_local_name = g_strdup (g_dpgettext2 ("libgweather-locations", + msgctxt, english_name)); + } else { + loc->_local_name = g_strdup (g_dgettext ("libgweather-locations", english_name)); + } + return loc->_local_name; + } else { + return NULL; + } } /** @@ -596,8 +383,21 @@ gweather_location_get_name (GWeatherLocation *loc) const char * gweather_location_get_sort_name (GWeatherLocation *loc) { + const char *local_name; + g_autofree char *normalized = NULL; g_return_val_if_fail (loc != NULL, NULL); - return loc->local_sort_name; + + if (loc->_local_sort_name) + return loc->_local_sort_name; + + local_name = gweather_location_get_name (loc); + if (!local_name) + return NULL; + + normalized = g_utf8_normalize (local_name, -1, G_NORMALIZE_ALL); + loc->_local_sort_name = g_utf8_casefold (normalized, -1); + + return loc->_local_sort_name; } /** @@ -614,7 +414,13 @@ gweather_location_get_english_name (GWeatherLocation *loc) { g_return_val_if_fail (loc != NULL, NULL); - return loc->english_name; + if (loc->_english_name) + return loc->_english_name; + + if (loc->db && IDX_VALID (loc->db_idx)) + return EMPTY_TO_NULL (db_i18n_get_str (db_location_get_name (loc->ref))); + + return NULL; } /** @@ -632,9 +438,21 @@ gweather_location_get_english_name (GWeatherLocation *loc) const char * gweather_location_get_english_sort_name (GWeatherLocation *loc) { + const char *english_name; + g_autofree char *normalized = NULL; g_return_val_if_fail (loc != NULL, NULL); - return loc->english_sort_name; + if (loc->_english_sort_name) + return loc->_english_sort_name; + + english_name = gweather_location_get_english_name (loc); + if (!english_name) + return NULL; + + normalized = g_utf8_normalize (english_name, -1, G_NORMALIZE_ALL); + loc->_english_sort_name = g_utf8_casefold (normalized, -1); + + return loc->_english_sort_name; } /** @@ -705,7 +523,122 @@ gweather_location_get_parent (GWeatherLocation *loc) { g_return_val_if_fail (loc != NULL, NULL); - return loc->parent ? gweather_location_ref (loc->parent) : NULL; + if (loc->_parent) + return gweather_location_ref (loc->_parent); + + if (loc->level == GWEATHER_LOCATION_WORLD) + return NULL; + + /* No database or root object */ + if (!loc->db || !IDX_VALID(loc->db_idx)) + return NULL; + + /* Note: We cannot use db_location_get_parent here in case this is an + * implicit nearest copy! */ + g_assert (IDX_VALID(loc->parent_idx) && loc->parent_idx != loc->db_idx); + return location_ref_for_idx (loc->db, loc->parent_idx, NULL); +} + +/** + * gweather_location_next_child: + * @loc: a #GWeatherLocation + * @child: (transfer full): The child + * + * Allows iterating all children. Pass %NULL to get the first child, + * and any child to get the next one. Note that the reference to @child + * is taken, meaning iterating all children is as simple as: + * + * |[<!-- language="C" --> + * g_autoptr(GWeatherLocation) child = NULL; + * while ((child = gweather_location_next_child (location, child))) + * { + * // Do something with child + * } + * ]| + * + * Returns: (transfer full): The next child, or %NULL + * + * Since: 40 + **/ +GWeatherLocation* +gweather_location_next_child (GWeatherLocation *loc, GWeatherLocation *_child) +{ + g_autoptr(GWeatherLocation) child = _child; + DbArrayofuint16Ref children_ref; + gsize length; + gsize next_idx; + gsize i; + + g_return_val_if_fail (loc != NULL, NULL); + + /* Easy case, just look up the child and grab the next one. */ + if (loc->_children) { + if (child == NULL) { + if (loc->_children[0]) + return gweather_location_ref (loc->_children[0]); + else + return NULL; + } + + for (i = 0; loc->_children[i]; i++) { + if (loc->_children[i] == child) { + if (loc->_children[i + 1]) + return gweather_location_ref (loc->_children[i + 1]); + else + return NULL; + } + } + + goto invalid_child; + } + + /* If we have a magic nearest child, iterate over that. */ + if (!g_getenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST") && + IDX_VALID (db_location_get_nearest (loc->ref))) { + if (child && (!child->db || !IDX_VALID (child->db_idx) || child->parent_idx != loc->db_idx)) + goto invalid_child; + + if (child) + return NULL; + else + return location_ref_for_idx (loc->db, db_location_get_nearest (loc->ref), loc); + } + + if (!loc->db || !IDX_VALID (loc->db_idx)) { + if (child) + goto invalid_child; + + return NULL; + } + + children_ref = db_location_get_children (loc->ref); + length = db_arrayofuint16_get_length (children_ref); + + if (!child) { + next_idx = 0; + } else { + /* Find child index in DB. */ + for (i = 0; i < length; i++) { + if (child->db_idx == db_arrayofuint16_get_at (children_ref, i)) + break; + } + + if (i == length) + goto invalid_child; + next_idx = i + 1; + } + + if (next_idx == length) + return NULL; + else + return location_ref_for_idx (loc->db, + db_arrayofuint16_get_at (children_ref, next_idx), + NULL); + + +invalid_child: + g_critical ("%s: Passed child %p is not a child of the given location %p", G_STRFUNC, loc, child); + return NULL; } /** @@ -717,18 +650,48 @@ gweather_location_get_parent (GWeatherLocation *loc) * * Return value: (transfer none) (array zero-terminated=1): @loc's * children. (May be empty, but will not be %NULL.) + * + * Deprecated: 40. Use gweather_location_next_child() instead to avoid high + * memory consumption **/ GWeatherLocation ** gweather_location_get_children (GWeatherLocation *loc) { static GWeatherLocation *no_children = NULL; + DbArrayofuint16Ref children_ref; + gsize length; + gsize i; - g_return_val_if_fail (loc != NULL, NULL); + g_return_val_if_fail (loc != NULL, &no_children); - if (loc->children) - return loc->children; - else + if (loc->_children) + return loc->_children; + + if (!loc->db) return &no_children; + + /* Fill in the magic nearest child if we need to and should. */ + if (!g_getenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST") && + IDX_VALID (db_location_get_nearest (loc->ref))) { + loc->_children = g_new0 (GWeatherLocation*, 2); + loc->_children[0] = location_ref_for_idx (loc->db, db_location_get_nearest (loc->ref), loc); + + return loc->_children; + } + + /* Get the actual children. */ + children_ref = db_location_get_children (loc->ref); + length = db_arrayofuint16_get_length (children_ref); + if (length == 0) + return &no_children; + + loc->_children = g_new0 (GWeatherLocation*, length + 1); + for (i = 0; i < length; i++) + loc->_children[i] = location_ref_for_idx (loc->db, + db_arrayofuint16_get_at (children_ref, i), + NULL); + + return loc->_children; } static void @@ -752,10 +715,11 @@ foreach_city (GWeatherLocation *loc, if (loc->level == GWEATHER_LOCATION_CITY) { callback (loc, user_data); - } else if (loc->children) { - int i; - for (i = 0; loc->children[i]; i++) - foreach_city (loc->children[i], callback, user_data, country_code, func, user_data_func); + } else { + g_autoptr(GWeatherLocation) child = NULL; + + while ((child = gweather_location_next_child (loc, child))) + foreach_city (child, callback, user_data, country_code, func, user_data_func); } } @@ -798,7 +762,8 @@ find_nearest_city (GWeatherLocation *location, data->latitude, data->longitude); if (data->location == NULL || data->distance > distance) { - data->location = location; + g_clear_pointer (&data->location, gweather_location_unref); + data->location = gweather_location_ref (location); data->distance = distance; } } @@ -849,7 +814,7 @@ gweather_location_find_nearest_city (GWeatherLocation *loc, foreach_city (loc, (GFunc) find_nearest_city, &data, NULL, NULL, NULL); - return gweather_location_ref (data.location); + return data.location; } /** @@ -1101,11 +1066,21 @@ gweather_location_get_distance (GWeatherLocation *loc, GWeatherLocation *loc2) const char * gweather_location_get_country (GWeatherLocation *loc) { + g_autoptr(GWeatherLocation) s = NULL; g_return_val_if_fail (loc != NULL, NULL); - while (loc->parent && !loc->country_code) - loc = loc->parent; - return loc->country_code; + ITER_UP(loc, s) { + if (s->_country_code) + return s->_country_code; + + if (s->db && IDX_VALID(s->db_idx)) { + const char *country_code; + country_code = EMPTY_TO_NULL (db_location_get_country_code (s->ref)); + if (country_code) + return country_code; + } + } + return NULL; } /** @@ -1123,29 +1098,20 @@ gweather_location_get_country (GWeatherLocation *loc) GWeatherTimezone * gweather_location_get_timezone (GWeatherLocation *loc) { - const char *tz_hint; - int i; + g_autoptr(GWeatherLocation) s = NULL; g_return_val_if_fail (loc != NULL, NULL); - while (loc && !loc->tz_hint) - loc = loc->parent; - if (!loc) - return NULL; - tz_hint = loc->tz_hint; + if (loc->_timezone) + return loc->_timezone; - while (loc) { - while (loc && !loc->zones) - loc = loc->parent; - if (!loc) - return NULL; - for (i = 0; loc->zones[i]; i++) { - if (!strcmp (tz_hint, gweather_timezone_get_tzid (loc->zones[i]))) - return loc->zones[i]; - } - loc = loc->parent; - } + ITER_UP(loc, s) { + if (!IDX_VALID (s->tz_hint_idx)) + continue; + loc->_timezone = _gweather_timezone_ref_for_idx (s->db, s->tz_hint_idx); + return loc->_timezone; + } return NULL; } @@ -1164,31 +1130,44 @@ gweather_location_get_timezone (GWeatherLocation *loc) const char * gweather_location_get_timezone_str (GWeatherLocation *loc) { - const char *tz_hint; + g_autoptr(GWeatherLocation) s = NULL; g_return_val_if_fail (loc != NULL, NULL); - while (loc && !loc->tz_hint) - loc = loc->parent; - if (!loc) - return NULL; - tz_hint = loc->tz_hint; + ITER_UP(loc, s) { + if (s->_timezone) + return gweather_timezone_get_tzid (s->_timezone); + + if (s->db && IDX_VALID(s->tz_hint_idx)) { + return db_world_timezones_entry_get_key (db_world_timezones_get_at (s->db->timezones_ref, s->tz_hint_idx)); + } + } - return tz_hint; + return NULL; } static void add_timezones (GWeatherLocation *loc, GPtrArray *zones) { - int i; - - if (loc->zones) { - for (i = 0; loc->zones[i]; i++) - g_ptr_array_add (zones, gweather_timezone_ref (loc->zones[i])); + gsize i; + + /* NOTE: Only DB backed locations can have timezones */ + if (loc->db && IDX_VALID (loc->db_idx)) { + DbArrayofuint16Ref ref; + gsize len; + + ref = db_location_get_timezones (loc->ref); + len = db_arrayofuint16_get_length (ref); + for (i = 0; i < len; i++) + g_ptr_array_add (zones, + _gweather_timezone_ref_for_idx (loc->db, + db_arrayofuint16_get_at (ref, i))); } - if (loc->level < GWEATHER_LOCATION_COUNTRY && loc->children) { - for (i = 0; loc->children[i]; i++) - add_timezones (loc->children[i], zones); + if (loc->level < GWEATHER_LOCATION_COUNTRY) { + g_autoptr(GWeatherLocation) child = NULL; + + while ((child = gweather_location_next_child (loc, child))) + add_timezones (child, zones); } } @@ -1251,7 +1230,14 @@ const char * gweather_location_get_code (GWeatherLocation *loc) { g_return_val_if_fail (loc != NULL, NULL); - return loc->station_code; + if (loc->_station_code) + return loc->_station_code; + + if (loc->db && IDX_VALID(loc->db_idx)) { + return EMPTY_TO_NULL (db_location_get_metar_code (loc->ref)); + } + + return NULL; } /** @@ -1273,13 +1259,19 @@ gweather_location_get_city_name (GWeatherLocation *loc) if (loc->level == GWEATHER_LOCATION_CITY || loc->level == GWEATHER_LOCATION_DETACHED) { - return g_strdup (loc->local_name); - } else if (loc->level == GWEATHER_LOCATION_WEATHER_STATION && - loc->parent && - loc->parent->level == GWEATHER_LOCATION_CITY) { - return g_strdup (loc->parent->local_name); - } else - return NULL; + return g_strdup (gweather_location_get_name (loc)); + } else { + g_autoptr(GWeatherLocation) parent = NULL; + parent = gweather_location_get_parent (loc); + + if (loc->level == GWEATHER_LOCATION_WEATHER_STATION && + parent && + parent->level == GWEATHER_LOCATION_CITY) { + return g_strdup (gweather_location_get_name (parent)); + } + } + + return NULL; } /** @@ -1299,57 +1291,64 @@ gweather_location_get_city_name (GWeatherLocation *loc) char * gweather_location_get_country_name (GWeatherLocation *loc) { - GWeatherLocation *country; + g_autoptr(GWeatherLocation) country = NULL; g_return_val_if_fail (loc != NULL, NULL); - country = loc; - while (country != NULL && country->level != GWEATHER_LOCATION_COUNTRY) { - country = country->parent; + ITER_UP(loc, country) { + if (country->level == GWEATHER_LOCATION_COUNTRY) + return g_strdup (gweather_location_get_name (country)); } - return country != NULL ? g_strdup (country->local_name) : NULL; + return NULL; } void _gweather_location_update_weather_location (GWeatherLocation *gloc, WeatherLocation *loc) { - const char *code = NULL, *zone = NULL, *radar = NULL, *tz_hint = NULL, *country = NULL; + char *code = NULL, *zone = NULL, *radar = NULL, *tz_hint = NULL; gboolean latlon_valid = FALSE; gdouble lat = DBL_MAX, lon = DBL_MAX; - GWeatherLocation *l; + g_autoptr(GWeatherLocation) start = NULL; + g_autoptr(GWeatherLocation) l = NULL; - if (gloc->level == GWEATHER_LOCATION_CITY && gloc->children) - l = gloc->children[0]; - else - l = gloc; - - while (l && (!code || !zone || !radar || !tz_hint || !latlon_valid || !country)) { - if (!code && l->station_code) - code = l->station_code; - if (!zone && l->forecast_zone) - zone = l->forecast_zone; - if (!radar && l->radar) - radar = l->radar; - if (!tz_hint && l->tz_hint) - tz_hint = l->tz_hint; - if (!country && l->country_code) - country = l->country_code; - if (!latlon_valid && l->latlon_valid) { + if (gloc->level == GWEATHER_LOCATION_CITY) { + GWeatherLocation *first_child; + first_child = gweather_location_next_child (gloc, NULL); + + if (first_child) + start = first_child; + } + if (!start) + start = gweather_location_ref (gloc); + + ITER_UP(start, l) { + if (!code) + code = g_strdup (gweather_location_get_code (l)); + if (!zone && l->db && IDX_VALID(l->db_idx)) + zone = g_strdup (EMPTY_TO_NULL (db_location_get_forecast_zone (l->ref))); + if (!radar && l->db && IDX_VALID(l->db_idx)) + radar = g_strdup (EMPTY_TO_NULL (db_location_get_radar (l->ref))); + if (!tz_hint && l->db && IDX_VALID(l->tz_hint_idx)) + tz_hint = g_strdup (db_world_timezones_entry_get_key (db_world_timezones_get_at (l->db->timezones_ref, l->tz_hint_idx))); + if (!latlon_valid) { lat = l->latitude; lon = l->longitude; - latlon_valid = TRUE; + latlon_valid = l->latlon_valid; } - l = l->parent; + + if (code && zone && radar && tz_hint && latlon_valid) + break; } - loc->name = g_strdup (gloc->local_name), - loc->code = g_strdup (code); - loc->zone = g_strdup (zone); - loc->radar = g_strdup (radar); - loc->country_code = g_strdup (country); - loc->tz_hint = g_strdup (tz_hint); + loc->name = g_strdup (gweather_location_get_name (gloc)), + loc->code = code; + loc->zone = zone; + loc->radar = radar; + /* This walks the hierarchy again ... */ + loc->country_code = g_strdup (gweather_location_get_country (start)); + loc->tz_hint = tz_hint; loc->latlon_valid = latlon_valid; loc->latitude = lat; @@ -1375,12 +1374,19 @@ _gweather_location_update_weather_location (GWeatherLocation *gloc, */ GWeatherLocation * gweather_location_find_by_station_code (GWeatherLocation *world, - const gchar *station_code) + const gchar *station_code) { - GList *l; + DbWorldLocByMetarRef loc_by_metar; + guint16 idx; + + if (!world->db) + return NULL; - l = g_hash_table_lookup (world->metar_code_cache, station_code); - return l ? gweather_location_ref (l->data) : NULL; + loc_by_metar = db_world_get_loc_by_metar (world->db->world); + if (!db_world_loc_by_metar_lookup (loc_by_metar, station_code, NULL, &idx)) + return NULL; + + return location_ref_for_idx (world->db, idx, NULL); } /** @@ -1399,11 +1405,17 @@ GWeatherLocation * gweather_location_find_by_country_code (GWeatherLocation *world, const gchar *country_code) { - GWeatherLocation *res; + DbWorldLocByCountryRef loc_by_country; + guint16 idx; - res = g_hash_table_lookup (world->country_code_cache, country_code); + if (!world->db) + return NULL; - return res ? gweather_location_ref (res) : NULL; + loc_by_country = db_world_get_loc_by_country (world->db->world); + if (!db_world_loc_by_country_lookup (loc_by_country, country_code, NULL, &idx)) + return NULL; + + return location_ref_for_idx (world->db, idx, NULL); } /** @@ -1428,11 +1440,15 @@ gboolean gweather_location_equal (GWeatherLocation *one, GWeatherLocation *two) { + g_autoptr(GWeatherLocation) p1 = NULL, p2 = NULL; int level; if (one == two) return TRUE; + p1 = gweather_location_get_parent (one); + p2 = gweather_location_get_parent (two); + if (one->level != two->level && one->level != GWEATHER_LOCATION_DETACHED && two->level != GWEATHER_LOCATION_DETACHED) @@ -1443,22 +1459,24 @@ gweather_location_equal (GWeatherLocation *one, level = two->level; if (level == GWEATHER_LOCATION_COUNTRY) - return g_strcmp0 (one->country_code, two->country_code); + return g_strcmp0 (gweather_location_get_country (one), + gweather_location_get_country (two)); if (level == GWEATHER_LOCATION_ADM1) { - if (g_strcmp0 (one->english_sort_name, two->english_sort_name) != 0) + if (g_strcmp0 (gweather_location_get_english_sort_name (one), gweather_location_get_english_sort_name (two)) != 0) return FALSE; - return one->parent && two->parent && - gweather_location_equal (one->parent, two->parent); + return p1 && p2 && + gweather_location_equal (p1, p2); } - if (g_strcmp0 (one->station_code, two->station_code) != 0) + if (g_strcmp0 (gweather_location_get_code (one), + gweather_location_get_code (two)) != 0) return FALSE; if (one->level != GWEATHER_LOCATION_DETACHED && two->level != GWEATHER_LOCATION_DETACHED && - !gweather_location_equal (one->parent, two->parent)) + !gweather_location_equal (p1, p2)) return FALSE; return ABS(one->latitude - two->latitude) < EPSILON && @@ -1472,32 +1490,41 @@ gweather_location_equal (GWeatherLocation *one, static GVariant * gweather_location_format_two_serialize (GWeatherLocation *location) { + g_autoptr(GWeatherLocation) real_loc = NULL; + g_autoptr(GWeatherLocation) parent = NULL; const char *name; + const char *station_code; gboolean is_city; GVariantBuilder latlon_builder; GVariantBuilder parent_latlon_builder; - name = location->english_name; + name = gweather_location_get_english_name (location); /* Normalize location to be a weather station or detached */ if (location->level == GWEATHER_LOCATION_CITY) { - if (location->children != NULL) - location = location->children[0]; + real_loc = gweather_location_next_child (location, NULL); is_city = TRUE; } else { is_city = FALSE; } + if (!real_loc) + real_loc = gweather_location_ref (location); + + parent = gweather_location_get_parent (real_loc); g_variant_builder_init (&latlon_builder, G_VARIANT_TYPE ("a(dd)")); - if (location->latlon_valid) - g_variant_builder_add (&latlon_builder, "(dd)", location->latitude, location->longitude); + if (real_loc->latlon_valid) + g_variant_builder_add (&latlon_builder, "(dd)", real_loc->latitude, real_loc->longitude); g_variant_builder_init (&parent_latlon_builder, G_VARIANT_TYPE ("a(dd)")); - if (location->parent && location->parent->latlon_valid) - g_variant_builder_add (&parent_latlon_builder, "(dd)", location->parent->latitude, location->parent->longitude); + if (parent && parent->latlon_valid) + g_variant_builder_add (&parent_latlon_builder, "(dd)", parent->latitude, parent->longitude); + station_code = gweather_location_get_code (real_loc); return g_variant_new ("(ssba(dd)a(dd))", - name, location->station_code ? location->station_code : "", is_city, + name, + station_code ? station_code : "", + is_city, &latlon_builder, &parent_latlon_builder); } @@ -1511,29 +1538,27 @@ _gweather_location_new_detached (GWeatherLocation *nearest_station, GWeatherLocation *self; char *normalized; - self = g_slice_new0 (GWeatherLocation); - self->ref_count = 1; - self->level = GWEATHER_LOCATION_DETACHED; + self = location_new (GWEATHER_LOCATION_DETACHED); if (name != NULL) { - self->english_name = g_strdup (name); - self->local_name = g_strdup (name); + self->_english_name = g_strdup (name); + self->_local_name = g_strdup (name); normalized = g_utf8_normalize (name, -1, G_NORMALIZE_ALL); - self->english_sort_name = g_utf8_casefold (normalized, -1); - self->local_sort_name = g_strdup (self->english_sort_name); + self->_english_sort_name = g_utf8_casefold (normalized, -1); + self->_local_sort_name = g_strdup (self->_english_sort_name); g_free (normalized); } else if (nearest_station) { - self->english_name = g_strdup (nearest_station->english_name); - self->local_name = g_strdup (nearest_station->local_name); - self->english_sort_name = g_strdup (nearest_station->english_sort_name); - self->local_sort_name = g_strdup (nearest_station->local_sort_name); + self->_english_name = g_strdup (gweather_location_get_english_name (nearest_station)); + self->_local_name = g_strdup (gweather_location_get_name (nearest_station)); + self->_english_sort_name = g_strdup (gweather_location_get_english_sort_name (nearest_station)); + self->_local_sort_name = g_strdup (gweather_location_get_sort_name (nearest_station)); } - self->parent = nearest_station; - self->children = NULL; + self->_parent = nearest_station; /* a reference is passed */ + self->_children = NULL; if (nearest_station) - self->station_code = g_strdup (nearest_station->station_code); + self->_station_code = g_strdup (gweather_location_get_code (nearest_station)); g_assert (nearest_station || latlon_valid); @@ -1562,8 +1587,10 @@ gweather_location_common_deserialize (GWeatherLocation *world, gdouble parent_latitude, gdouble parent_longitude) { - GList *candidates, *l; + g_autoptr(GWeatherLocation) *by_station_code = NULL; + DbWorldLocByMetarRef loc_by_metar; GWeatherLocation *found; + gsize i; /* Since weather stations are no longer attached to cities, first try to find what claims to be a city by name and coordinates */ @@ -1571,8 +1598,9 @@ gweather_location_common_deserialize (GWeatherLocation *world, found = gweather_location_find_nearest_city (world, latitude / M_PI * 180.0, longitude / M_PI * 180.0); - if (found && (g_strcmp0 (name, found->english_name) == 0 || - g_strcmp0 (name, found->local_name) == 0)) + + if (found && (g_strcmp0 (name, gweather_location_get_english_name (found)) == 0 || + g_strcmp0 (name, gweather_location_get_name (found)) == 0)) return g_steal_pointer (&found); g_clear_pointer (&found, gweather_location_unref); } @@ -1580,28 +1608,35 @@ gweather_location_common_deserialize (GWeatherLocation *world, if (station_code[0] == '\0') return _gweather_location_new_detached (NULL, name, latlon_valid, latitude, longitude); - /* First find the list of candidate locations */ - candidates = g_hash_table_lookup (world->metar_code_cache, station_code); + /* Lookup by station code, this may return NULL. */ + by_station_code = gweather_location_find_by_station_code (world, station_code); /* A station code beginning with @ indicates a named timezone entry, just * return it directly */ if (station_code[0] == '@') - return gweather_location_ref (candidates->data); + return g_steal_pointer (&by_station_code); /* If we don't have coordinates, fallback immediately to making up * a location */ if (!latlon_valid) - return candidates ? _gweather_location_new_detached (candidates->data, name, FALSE, 0, 0) : NULL; + return by_station_code + ? _gweather_location_new_detached (by_station_code, + name, FALSE, 0, 0) + : NULL; found = NULL; + loc_by_metar = db_world_get_loc_by_metar (world->db->world); - /* First try weather stations directly. */ - for (l = candidates; l; l = l->next) { - GWeatherLocation *ws, *city; + for (i = 0; i < db_world_loc_by_metar_get_length (loc_by_metar); i++) { + g_autoptr(GWeatherLocation) ws = NULL, city = NULL; + /* Skip if the metar code does not match */ + if (!g_str_equal (station_code, db_world_loc_by_metar_entry_get_key (db_world_loc_by_metar_get_at (loc_by_metar, i)))) + continue; - ws = l->data; + /* Be lazy and allocate the location */ + ws = location_ref_for_idx (world->db, db_world_loc_by_metar_entry_get_value (db_world_loc_by_metar_get_at (loc_by_metar, i)), NULL); if (!ws->latlon_valid || ABS(ws->latitude - latitude) >= EPSILON || @@ -1609,45 +1644,44 @@ gweather_location_common_deserialize (GWeatherLocation *world, /* Not what we're looking for... */ continue; + city = gweather_location_get_parent (ws); + /* If we can't check for the latitude and longitude of the parent, we just assume we found what we needed */ - if ((!parent_latlon_valid || !ws->parent || !ws->parent->latlon_valid) || - (ABS(parent_latitude - ws->parent->latitude) < EPSILON && - ABS(parent_longitude - ws->parent->longitude) < EPSILON)) { + if ((!parent_latlon_valid || !city || !city->latlon_valid) || + (ABS(parent_latitude - city->latitude) < EPSILON && + ABS(parent_longitude - city->longitude) < EPSILON)) { /* Found! Now check which one we want (ws or city) and the name... */ if (is_city) - city = ws->parent; + found = city; else - city = ws; + found = ws; - if (city == NULL) { + if (found == NULL) { /* Oops! There is no city for this weather station! */ continue; } if (name == NULL || - g_strcmp0 (name, city->english_name) == 0 || - g_strcmp0 (name, city->local_name) == 0) - found = gweather_location_ref (city); + g_str_equal (name, gweather_location_get_english_name (found)) || + g_str_equal (name, gweather_location_get_name (found))) + found = gweather_location_ref (found); else - found = _gweather_location_new_detached (ws, name, TRUE, latitude, longitude); + found = _gweather_location_new_detached (gweather_location_ref (ws), name, TRUE, latitude, longitude); - break; + return found; } } - if (found) - return found; - /* No weather station matches the serialized data, let's pick one at random from the station code list */ - if (candidates) - return _gweather_location_new_detached (candidates->data, - name, TRUE, latitude, longitude); + if (by_station_code) + return _gweather_location_new_detached (by_station_code, + name, TRUE, latitude, longitude); else - return NULL; + return NULL; } static GWeatherLocation * diff --git a/libgweather/gweather-location.h b/libgweather/gweather-location.h index badf222..2f99006 100644 --- a/libgweather/gweather-location.h +++ b/libgweather/gweather-location.h @@ -74,6 +74,9 @@ GWEATHER_EXTERN GWeatherLocation *gweather_location_get_parent (GWeatherLocation *loc); GWEATHER_EXTERN +GWeatherLocation *gweather_location_next_child (GWeatherLocation *loc, GWeatherLocation *child); +GWEATHER_EXTERN +G_DEPRECATED_FOR(gweather_location_next_child) GWeatherLocation **gweather_location_get_children (GWeatherLocation *loc); GWEATHER_EXTERN diff --git a/libgweather/gweather-parser.c b/libgweather/gweather-parser.c deleted file mode 100644 index 912e9f7..0000000 --- a/libgweather/gweather-parser.c +++ /dev/null @@ -1,223 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* parser.c - Locations.xml parser - * - * Copyright 2008, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see - * <https://www.gnu.org/licenses/>. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> -#include <glib.h> -#include <libxml/xmlreader.h> -#include <glib/gi18n-lib.h> - -#include "gweather-private.h" -#include "gweather-parser.h" - -/* - * _gweather_parser_get_value: - * @parser: a #GWeatherParser - * - * Gets the text of the element whose start tag @parser is pointing to. - * Leaves @parser pointing at the next node after the element's end tag. - * - * Return value: the text of the current node, as a libxml-allocated - * string, or %NULL if the node is empty. - **/ -char * -_gweather_parser_get_value (GWeatherParser *parser) -{ - char *value; - - /* check for null node */ - if (xmlTextReaderIsEmptyElement (parser->xml)) - return NULL; - - /* the next "node" is the text node containing the value we want to get */ - if (xmlTextReaderRead (parser->xml) != 1) - return NULL; - - value = (char *) xmlTextReaderValue (parser->xml); - - /* move on to the end of this node */ - while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_END_ELEMENT) { - if (xmlTextReaderRead (parser->xml) != 1) { - xmlFree (value); - return NULL; - } - } - - /* consume the end element too */ - if (xmlTextReaderRead (parser->xml) != 1) { - xmlFree (value); - return NULL; - } - - return value; -} - -/* - * _gweather_parser_get_localized_value: - * @parser: a #GWeatherParser - * - * Looks at the name of the element @parser is currently pointing to, and - * returns the content of either that node, or the translation for - * it from the gettext domain for gweather locations. - * - * Return value: the localized (or unlocalized) text, as a - * glib-allocated string, or %NULL if the node is empty. - **/ -char * -_gweather_parser_get_localized_value (GWeatherParser *parser) -{ - char *untranslated_value = _gweather_parser_get_value (parser); - char *ret; - - ret = (char*) g_dgettext ("libgweather-locations", (char*) untranslated_value); - - ret = g_strdup (ret); - xmlFree (untranslated_value); - return ret; -} - -char * -_gweather_parser_get_msgctxt_value (GWeatherParser *parser) -{ - const char *value; - const char *name; - - while(xmlTextReaderMoveToNextAttribute(parser->xml)) { - name = (const char *)xmlTextReaderConstName(parser->xml); - if (!strcmp (name, "msgctxt")) { - value = (const char *)xmlTextReaderConstValue(parser->xml); - return g_strdup (value); - } - } - - return NULL; -} - -static void -gweather_location_list_free (gpointer list) -{ - g_list_free_full (list, (GDestroyNotify) gweather_location_unref); -} - -GWeatherParser * -_gweather_parser_new (void) -{ - int zlib_support; - char *filename; - GWeatherParser *parser; - - zlib_support = xmlHasFeature (XML_WITH_ZLIB); - - filename = g_build_filename (GWEATHER_XML_LOCATION_DIR, "Locations.xml", NULL); - - if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR) && zlib_support) { - g_free (filename); - filename = g_build_filename (GWEATHER_XML_LOCATION_DIR, "Locations.xml.gz", NULL); - } - - parser = _gweather_parser_new_for_path (filename); - - g_free (filename); - - return parser; -} - -GWeatherParser * -_gweather_parser_new_for_path (const char *path) -{ - GWeatherParser *parser; - int keep_going; - char *tagname, *format; - time_t now; - struct tm tm; - - _gweather_gettext_init (); - - parser = g_slice_new0 (GWeatherParser); - - /* Open the xml file containing the different locations */ - parser->xml = xmlNewTextReaderFilename (path); - - if (parser->xml == NULL) - goto error_out; - - /* fast forward to the first element */ - do { - /* if we encounter a problem here, exit right away */ - if (xmlTextReaderRead (parser->xml) != 1) - goto error_out; - } while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_ELEMENT); - - /* check the name and format */ - tagname = (char *) xmlTextReaderName (parser->xml); - keep_going = tagname && !strcmp (tagname, "gweather"); - xmlFree (tagname); - - if (!keep_going) - goto error_out; - - format = (char *) xmlTextReaderGetAttribute (parser->xml, (xmlChar *) "format"); - keep_going = format && !strcmp (format, "1.0"); - xmlFree (format); - - if (!keep_going) - goto error_out; - - /* Get timestamps for the start and end of this year */ - now = time (NULL); - tm = *gmtime (&now); - tm.tm_mon = 0; - tm.tm_mday = 1; - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - parser->year_start = mktime (&tm); - tm.tm_year++; - parser->year_end = mktime (&tm); - - parser->metar_code_cache = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, gweather_location_list_free); - parser->timezone_cache = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify) gweather_timezone_unref); - parser->country_code_cache = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify) gweather_location_unref); - - return parser; - -error_out: - _gweather_parser_free (parser); - return NULL; -} - -void -_gweather_parser_free (GWeatherParser *parser) -{ - if (parser->xml) - xmlFreeTextReader (parser->xml); - if (parser->metar_code_cache) - g_hash_table_unref (parser->metar_code_cache); - if (parser->country_code_cache) - g_hash_table_unref (parser->country_code_cache); - if (parser->timezone_cache) - g_hash_table_unref (parser->timezone_cache); - - g_slice_free (GWeatherParser, parser); -} diff --git a/libgweather/gweather-parser.h b/libgweather/gweather-parser.h deleted file mode 100644 index 391f91c..0000000 --- a/libgweather/gweather-parser.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* parser.h - Locations.xml parser - * - * Copyright 2008, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see - * <https://www.gnu.org/licenses/>. - */ - -#ifndef GWEATHER_PARSER_H -#define GWEATHER_PARSER_H 1 - -#include <libxml/xmlreader.h> -#include "gweather-timezone.h" - -typedef struct { - xmlTextReaderPtr xml; - time_t year_start, year_end; - GHashTable *metar_code_cache; - GHashTable *timezone_cache; - GHashTable *country_code_cache; -} GWeatherParser; - -GWeatherParser *_gweather_parser_new (void); -GWeatherParser *_gweather_parser_new_for_path (const char *path); -void _gweather_parser_free (GWeatherParser *parser); - -char *_gweather_parser_get_value (GWeatherParser *parser); -char *_gweather_parser_get_msgctxt_value (GWeatherParser *parser); -char *_gweather_parser_get_localized_value (GWeatherParser *parser); - -/* from gweather-timezone.c */ -GWeatherTimezone **_gweather_timezones_parse_xml (GWeatherParser *parser); - -#endif diff --git a/libgweather/gweather-private.h b/libgweather/gweather-private.h index 925abd8..0c740e9 100644 --- a/libgweather/gweather-private.h +++ b/libgweather/gweather-private.h @@ -30,21 +30,44 @@ #include "gweather-weather.h" #include "gweather-location.h" +#include "gweather-db.h" + +#define INVALID_IDX G_MAXUINT16 +#define IDX_VALID(idx) ((idx) >= 0 && (idx) < 0xffff) +#define EMPTY_TO_NULL(s) ((s)[0] == '\0' ? NULL : (s)) void _gweather_gettext_init (void); + +typedef struct { + GMappedFile *map; + DbWorldRef world; + DbArrayofLocationRef locations_ref; + DbWorldTimezonesRef timezones_ref; + + GPtrArray *locations; + GPtrArray *timezones; + + time_t year_start; + time_t year_end; +} GWeatherDb; + struct _GWeatherLocation { - char *english_name, *local_name, *msgctxt, *local_sort_name, *english_sort_name; - GWeatherLocation *parent, **children; + GWeatherDb *db; + guint db_idx; + DbLocationRef ref; + + /* Attributes with _ may be fetched/filled from the database on the fly */ + char *_english_name, *_local_name, *_local_sort_name, *_english_sort_name; + guint16 parent_idx; /* From the DB, except for nearest clones */ + GWeatherLocation *_parent, **_children; + GWeatherTimezone *_timezone; GWeatherLocationLevel level; - char *country_code, *tz_hint; - char *station_code, *forecast_zone, *radar; + char *_country_code; + guint16 tz_hint_idx; + char *_station_code; double latitude, longitude; gboolean latlon_valid; - GWeatherTimezone **zones; - GHashTable *metar_code_cache; - GHashTable *timezone_cache; - GHashTable *country_code_cache; int ref_count; }; @@ -72,6 +95,10 @@ GWeatherLocation *_gweather_location_new_detached (GWeatherLocation *nearest_sta void _gweather_location_update_weather_location (GWeatherLocation *gloc, WeatherLocation *loc); +GWeatherTimezone * _gweather_timezone_ref_for_idx (GWeatherDb *db, + guint idx); + + /* * Weather information. */ diff --git a/libgweather/gweather-timezone.c b/libgweather/gweather-timezone.c index daa5ed8..0f358cf 100644 --- a/libgweather/gweather-timezone.c +++ b/libgweather/gweather-timezone.c @@ -25,7 +25,6 @@ #include <string.h> #include "gweather-timezone.h" -#include "gweather-parser.h" #include "gweather-private.h" /** @@ -41,7 +40,11 @@ */ struct _GWeatherTimezone { - char *id, *name; + GWeatherDb *db; + guint db_idx; + + /* Attributes with _ may be fetched/filled from the database on the fly. */ + char *_id, *_name; int offset, dst_offset; gboolean has_dst; @@ -149,133 +152,73 @@ parse_tzdata (const char *tz_name, time_t start, time_t end, return TRUE; } -/** - * gweather_timezone_get_by_tzid: - * @tzid: A timezone identifier, like "America/New_York" or "Europe/London" - * - * Get the #GWeatherTimezone for @tzid. - * - * Prior to version 40 no reference was returned. - * - * Returns: (transfer full): A #GWeatherTimezone. - * - * Since: 3.12 - */ GWeatherTimezone * -gweather_timezone_get_by_tzid (const char *tzid) +_gweather_timezone_ref_for_idx (GWeatherDb *db, + guint idx) { - g_autoptr(GWeatherLocation) world = NULL; - GWeatherTimezone *res; - - g_return_val_if_fail (tzid != NULL, NULL); - - world = gweather_location_get_world (); - res = g_hash_table_lookup (world->timezone_cache, tzid); - - return res ? gweather_timezone_ref (res) : NULL; -} - -static GWeatherTimezone * -parse_timezone (GWeatherParser *parser) -{ - GWeatherTimezone *zone = NULL; - char *id = NULL; - char *name = NULL; + GWeatherTimezone *zone; + DbWorldTimezonesEntryRef ref; + const char *id; int offset = 0, dst_offset = 0; gboolean has_dst = FALSE; - id = (char *) xmlTextReaderGetAttribute (parser->xml, (xmlChar *) "id"); - if (!id) { - xmlTextReaderNext (parser->xml); - return NULL; - } - - if (!xmlTextReaderIsEmptyElement (parser->xml)) { - if (xmlTextReaderRead (parser->xml) != 1) { - xmlFree (id); - return NULL; - } + g_assert (db); + g_assert (idx < db->timezones->len); + zone = g_ptr_array_index (db->timezones, idx); + if (zone) + return gweather_timezone_ref (zone); - while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_END_ELEMENT) { - if (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_ELEMENT) { - if (xmlTextReaderRead (parser->xml) != 1) - break; - continue; - } - - if (!strcmp ((const char *) xmlTextReaderConstName (parser->xml), "_name")) - name = _gweather_parser_get_localized_value (parser); - else { - if (xmlTextReaderNext (parser->xml) != 1) - break; - } - } - } + ref = db_world_timezones_get_at (db->timezones_ref, idx); + id = db_world_timezones_entry_get_key (ref); - if (parse_tzdata (id, parser->year_start, parser->year_end, + if (parse_tzdata (id, db->year_start, db->year_end, &offset, &has_dst, &dst_offset)) { zone = g_slice_new0 (GWeatherTimezone); zone->ref_count = 1; - zone->id = g_strdup (id); - zone->name = name; + zone->db = db; + zone->db_idx = idx; + zone->offset = offset; zone->has_dst = has_dst; zone->dst_offset = dst_offset; - g_hash_table_insert (parser->timezone_cache, zone->id, gweather_timezone_ref (zone)); - - name = NULL; + /* Insert weak reference */ + g_ptr_array_index (db->timezones, idx) = zone; } - g_free (name); - xmlFree (id); - return zone; } -GWeatherTimezone ** -_gweather_timezones_parse_xml (GWeatherParser *parser) +/** + * gweather_timezone_get_by_tzid: + * @tzid: A timezone identifier, like "America/New_York" or "Europe/London" + * + * Get the #GWeatherTimezone for @tzid. + * + * Prior to version 40 no reference was returned. + * + * Returns: (transfer full): A #GWeatherTimezone. + * + * Since: 3.12 + */ +GWeatherTimezone * +gweather_timezone_get_by_tzid (const char *tzid) { - GPtrArray *zones; - GWeatherTimezone *zone; - const char *tagname; - int tagtype; - unsigned int i; - - zones = g_ptr_array_new (); - - if (xmlTextReaderRead (parser->xml) != 1) - goto error_out; - while ((tagtype = xmlTextReaderNodeType (parser->xml)) != - XML_READER_TYPE_END_ELEMENT) { - if (tagtype != XML_READER_TYPE_ELEMENT) { - if (xmlTextReaderRead (parser->xml) != 1) - goto error_out; - continue; - } - - tagname = (const char *) xmlTextReaderConstName (parser->xml); + GWeatherLocation *world; + GWeatherDb *db; + gsize idx; - if (!strcmp (tagname, "timezone")) { - zone = parse_timezone (parser); - if (zone) - g_ptr_array_add (zones, zone); - } + g_return_val_if_fail (tzid != NULL, NULL); - if (xmlTextReaderNext (parser->xml) != 1) - goto error_out; - } - if (xmlTextReaderRead (parser->xml) != 1) - goto error_out; + /* TODO: Get the DB directly */ + world = gweather_location_get_world (); + db = world->db; + gweather_location_unref (world); - g_ptr_array_add (zones, NULL); - return (GWeatherTimezone **)g_ptr_array_free (zones, FALSE); + if (!db_world_timezones_lookup (db->timezones_ref, tzid, &idx, NULL)) + return NULL; -error_out: - for (i = 0; i < zones->len; i++) - gweather_timezone_unref (zones->pdata[i]); - g_ptr_array_free (zones, TRUE); - return NULL; + return _gweather_timezone_ref_for_idx (db, idx); } /** @@ -307,8 +250,11 @@ gweather_timezone_unref (GWeatherTimezone *zone) g_return_if_fail (zone != NULL); if (!--zone->ref_count) { - g_free (zone->id); - g_free (zone->name); + if (zone->db) + g_ptr_array_index (zone->db->timezones, zone->db_idx) = 0; + + g_free (zone->_id); + g_free (zone->_name); g_slice_free (GWeatherTimezone, zone); } } @@ -342,8 +288,9 @@ gweather_timezone_get_utc (void) zone = g_slice_new0 (GWeatherTimezone); zone->ref_count = 1; - zone->id = g_strdup ("GMT"); - zone->name = g_strdup (_("Greenwich Mean Time")); + zone->db_idx = INVALID_IDX; + zone->_id = g_strdup ("GMT"); + zone->_name = g_strdup (_("Greenwich Mean Time")); zone->offset = 0; zone->has_dst = FALSE; zone->dst_offset = 0; @@ -367,8 +314,29 @@ gweather_timezone_get_utc (void) const char * gweather_timezone_get_name (GWeatherTimezone *zone) { + DbTimezoneRef ref; + const char *name; + const char *msgctxt; + g_return_val_if_fail (zone != NULL, NULL); - return zone->name; + if (zone->_name) + return zone->_name; + + if (!zone->db || !IDX_VALID (zone->db_idx)) + return NULL; + + ref = db_world_timezones_entry_get_value (db_world_timezones_get_at (zone->db->timezones_ref, zone->db_idx)); + name = EMPTY_TO_NULL (db_i18n_get_str (db_timezone_get_name (ref))); + msgctxt = EMPTY_TO_NULL (db_i18n_get_msgctxt (db_timezone_get_name (ref))); + + if (!name) + return NULL; + + if (msgctxt) + zone->_name = g_strdup (g_dpgettext2 ("libgweather-locations", msgctxt, name)); + else + zone->_name = g_strdup (g_dgettext ("libgweather-locations", name)); + return zone->_name; } /** @@ -383,7 +351,13 @@ const char * gweather_timezone_get_tzid (GWeatherTimezone *zone) { g_return_val_if_fail (zone != NULL, NULL); - return zone->id; + if (zone->_id) + return zone->_id; + + if (!zone->db || !IDX_VALID (zone->db_idx)) + return NULL; + + return db_world_timezones_entry_get_key (db_world_timezones_get_at (zone->db->timezones_ref, zone->db_idx)); } /** diff --git a/libgweather/meson.build b/libgweather/meson.build index d7f0d25..e84ae24 100644 --- a/libgweather/meson.build +++ b/libgweather/meson.build @@ -52,8 +52,7 @@ gweather_c_sources = [ 'gweather-location.c', 'gweather-timezone.c', 'gweather-location-entry.c', - 'gweather-timezone-menu.c', - 'gweather-parser.c'] + 'gweather-timezone-menu.c'] introspection_sources = gweather_c_sources + gweather_new_headers lib_libgweather = shared_library('gweather-3', @@ -98,7 +97,7 @@ if enable_vala ) endif -test_cargs = ['-DTEST_SRCDIR="@0@/"'.format(meson.current_source_dir()), +test_cargs = ['-DTEST_LOCATIONS="@0@"'.format(locations_bin.full_path()), '-DSCHEMASDIR="@0@/schemas"'.format(meson.source_root()), '-DSCHEMAS_BUILDDIR="@0@/schemas"'.format(meson.build_root())] @@ -118,7 +117,7 @@ exe = executable('test_libgweather', c_args: test_cargs, dependencies: libgweather_dep, install: false) -test('test_named_timezones', exe) +test('test_named_timezones', exe, depends: [locations_bin]) executable('test_metar', ['test_metar.c', gweather_c_sources], diff --git a/libgweather/test_libgweather.c b/libgweather/test_libgweather.c index 10c7075..ea5d076 100644 --- a/libgweather/test_libgweather.c +++ b/libgweather/test_libgweather.c @@ -27,6 +27,10 @@ #include "gweather-location.h" #include "gweather-weather.h" +/* We use/test gweather_location_get_children */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + extern void _gweather_location_reset_world (void); /* For test_metar_weather_stations */ @@ -61,6 +65,7 @@ test_named_timezones (void) g_assert_true (code[0] == '@'); } + g_clear_pointer (&world, gweather_location_unref); _gweather_location_reset_world (); } @@ -156,9 +161,9 @@ test_named_timezones_deserialized (void) } g_list_free (list); + g_clear_pointer (&world, gweather_location_unref); + /* test_timezones will clear the DB */ test_timezones (); - - _gweather_location_reset_world (); } static void @@ -190,6 +195,9 @@ test_no_code_serialize (void) g_assert_cmpstr (gweather_location_get_name (loc), ==, gweather_location_get_name (new_loc)); g_assert_true (gweather_location_equal (loc, new_loc)); + g_clear_pointer (&world, gweather_location_unref); + g_clear_pointer (&loc, gweather_location_unref); + g_clear_pointer (&new_loc, gweather_location_unref); _gweather_location_reset_world (); } @@ -251,6 +259,7 @@ test_timezones (void) test_timezones_children (world); + g_clear_pointer (&world, gweather_location_unref); _gweather_location_reset_world (); } @@ -303,6 +312,7 @@ test_airport_distance_sanity (void) if (g_test_failed ()) g_warning ("Maximum city to airport distance is %.1f km", max_distance); + g_clear_pointer (&world, gweather_location_unref); _gweather_location_reset_world (); } @@ -438,6 +448,7 @@ test_metar_weather_stations (void) g_hash_table_unref (stations_ht); + g_clear_pointer (&world, gweather_location_unref); _gweather_location_reset_world (); } @@ -509,6 +520,8 @@ test_utc_sunset (void) g_object_unref (info); + g_clear_pointer (&world, gweather_location_unref); + g_clear_pointer (&utc, gweather_location_unref); _gweather_location_reset_world (); } @@ -592,6 +605,7 @@ test_bad_duplicate_weather_stations (void) g_hash_table_unref (stations_ht); g_unsetenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST"); + g_clear_pointer (&world, gweather_location_unref); _gweather_location_reset_world (); } @@ -648,6 +662,7 @@ test_duplicate_weather_stations (void) test_duplicate_weather_stations_children (world); g_unsetenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST"); + g_clear_pointer (&world, gweather_location_unref); _gweather_location_reset_world (); } @@ -684,6 +699,8 @@ test_location_names (void) gweather_location_unref (brussels); setlocale (LC_ALL, ""); + g_clear_pointer (&world, gweather_location_unref); + g_clear_pointer (&brussels, gweather_location_unref); _gweather_location_reset_world (); } @@ -797,7 +814,7 @@ main (int argc, char *argv[]) g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG, log_handler, NULL); g_setenv ("LIBGWEATHER_LOCATIONS_PATH", - TEST_SRCDIR "../data/Locations.xml", + TEST_LOCATIONS, FALSE); set_gsettings (); diff --git a/libgweather/test_locations.c b/libgweather/test_locations.c index b03375e..37b333e 100644 --- a/libgweather/test_locations.c +++ b/libgweather/test_locations.c @@ -38,7 +38,7 @@ main (int argc, char **argv) gtk_init (&argc, &argv); g_setenv ("LIBGWEATHER_LOCATIONS_PATH", - TEST_SRCDIR "../data/Locations.xml", + TEST_LOCATIONS, FALSE); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); diff --git a/libgweather/test_locations_utc.c b/libgweather/test_locations_utc.c index 3c599af..2804448 100644 --- a/libgweather/test_locations_utc.c +++ b/libgweather/test_locations_utc.c @@ -39,7 +39,7 @@ main (int argc, char **argv) gtk_init (&argc, &argv); g_setenv ("LIBGWEATHER_LOCATIONS_PATH", - TEST_SRCDIR "../data/Locations.xml", + TEST_LOCATIONS, FALSE); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); diff --git a/meson.build b/meson.build index bd3b331..66dc23b 100644 --- a/meson.build +++ b/meson.build @@ -60,7 +60,7 @@ config_h.set_quoted('GETTEXT_PACKAGE', GETTEXT_PACKAGE) config_h.set_quoted('LOCALEDIR', join_paths(datadir, 'locale')) config_h.set_quoted('GNOMELOCALEDIR', join_paths(datadir, 'locale')) config_h.set_quoted('G_LOG_DOMAIN', 'GWeather') -config_h.set_quoted('GWEATHER_XML_LOCATION_DIR', pkgdatadir) +config_h.set_quoted('GWEATHER_BIN_LOCATION_DIR', pkglibdir) config_h.set_quoted('LIBGWEATHER_VERSION', meson.project_version()) if c_compiler.has_member('struct tm', 'tm_gmtoff', prefix: '#include <time.h>') |