1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-path-map.c: URI path prefix-matcher
5 * Copyright (C) 2007 Novell, Inc.
10 #include "soup-path-map.h"
12 /* This could be replaced with something more clever, like a Patricia
13 * trie, but it's probably not worth it since the total number of
14 * mappings is likely to always be small. So we keep an array of
15 * paths, sorted by decreasing length. (The first prefix match will
16 * therefore be the longest.)
27 GDestroyNotify free_func;
32 * @data_free_func: function to use to free data added with
33 * soup_path_map_add().
35 * Creates a new %SoupPathMap.
37 * Return value: the new %SoupPathMap
40 soup_path_map_new (GDestroyNotify data_free_func)
44 map = g_slice_new0 (SoupPathMap);
45 map->mappings = g_array_new (FALSE, FALSE, sizeof (SoupPathMapping));
46 map->free_func = data_free_func;
53 * @map: a %SoupPathMap
55 * Frees @map and all data stored in it.
58 soup_path_map_free (SoupPathMap *map)
60 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
63 for (i = 0; i < map->mappings->len; i++) {
64 g_free (mappings[i].path);
66 map->free_func (mappings[i].data);
68 g_array_free (map->mappings, TRUE);
70 g_slice_free (SoupPathMap, map);
73 /* Scan @map looking for @path or one of its ancestors.
74 * Sets *@match to the index of a match, or -1 if no match is found.
75 * Sets *@insert to the index to insert @path at if a new mapping is
76 * desired. Returns %TRUE if *@match is an exact match.
79 mapping_lookup (SoupPathMap *map, const char *path, int *match, int *insert)
81 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
83 gboolean exact = FALSE;
87 path_len = strcspn (path, "?");
88 for (i = 0; i < map->mappings->len; i++) {
89 if (mappings[i].len > path_len)
92 if (insert && mappings[i].len < path_len) {
94 /* Clear insert so we don't try to set it again */
98 if (!strncmp (mappings[i].path, path, mappings[i].len)) {
100 if (path_len == mappings[i].len)
114 * @map: a %SoupPathMap
118 * Adds @data to @map at @path. If there was already data at @path it
122 soup_path_map_add (SoupPathMap *map, const char *path, gpointer data)
124 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
127 if (mapping_lookup (map, path, &match, &insert)) {
129 map->free_func (mappings[match].data);
130 mappings[match].data = data;
132 SoupPathMapping mapping;
134 mapping.path = g_strdup (path);
135 mapping.len = strlen (path);
137 g_array_insert_val (map->mappings, insert, mapping);
142 * soup_path_map_remove:
143 * @map: a %SoupPathMap
146 * Removes @data from @map at @path. (This must be called with the same
147 * path the data was originally added with, not a subdirectory.)
150 soup_path_map_remove (SoupPathMap *map, const char *path)
152 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
155 if (!mapping_lookup (map, path, &match, NULL))
159 map->free_func (mappings[match].data);
160 g_free (mappings[match].path);
161 g_array_remove_index (map->mappings, match);
165 * soup_path_map_lookup:
166 * @map: a %SoupPathMap
169 * Finds the data associated with @path in @map. If there is no data
170 * specifically associated with @path, it will return the data for the
171 * closest parent directory of @path that has data associated with it.
173 * Return value: the data set with soup_path_map_add(), or %NULL if no
174 * data could be found for @path or any of its ancestors.
177 soup_path_map_lookup (SoupPathMap *map, const char *path)
179 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
182 mapping_lookup (map, path, &match, NULL);
186 return mappings[match].data;