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.
8 #include "soup-path-map.h"
11 /* This could be replaced with something more clever, like a Patricia
12 * trie, but it's probably not worth it since the total number of
13 * mappings is likely to always be small. So we keep an array of
14 * paths, sorted by decreasing length. (The first prefix match will
15 * therefore be the longest.)
26 GDestroyNotify free_func;
31 * @data_free_func: function to use to free data added with
32 * soup_path_map_add().
34 * Creates a new %SoupPathMap.
36 * Return value: the new %SoupPathMap
39 soup_path_map_new (GDestroyNotify data_free_func)
43 map = g_slice_new0 (SoupPathMap);
44 map->mappings = g_array_new (FALSE, FALSE, sizeof (SoupPathMapping));
45 map->free_func = data_free_func;
52 * @map: a %SoupPathMap
54 * Frees @map and all data stored in it.
57 soup_path_map_free (SoupPathMap *map)
59 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
62 for (i = 0; i < map->mappings->len; i++) {
63 g_free (mappings[i].path);
65 map->free_func (mappings[i].data);
67 g_array_free (map->mappings, TRUE);
69 g_slice_free (SoupPathMap, map);
72 /* Scan @map looking for @path or one of its ancestors.
73 * Sets *@match to the index of a match, or -1 if no match is found.
74 * Sets *@insert to the index to insert @path at if a new mapping is
75 * desired. Returns %TRUE if *@match is an exact match.
78 mapping_lookup (SoupPathMap *map, const char *path, int *match, int *insert)
80 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
82 gboolean exact = FALSE;
86 path_len = strcspn (path, "?");
87 for (i = 0; i < map->mappings->len; i++) {
88 if (mappings[i].len > path_len)
91 if (insert && mappings[i].len < path_len) {
93 /* Clear insert so we don't try to set it again */
97 if (!strncmp (mappings[i].path, path, mappings[i].len)) {
99 if (path_len == mappings[i].len)
113 * @map: a %SoupPathMap
117 * Adds @data to @map at @path. If there was already data at @path it
121 soup_path_map_add (SoupPathMap *map, const char *path, gpointer data)
123 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
126 if (mapping_lookup (map, path, &match, &insert)) {
128 map->free_func (mappings[match].data);
129 mappings[match].data = data;
131 SoupPathMapping mapping;
133 mapping.path = g_strdup (path);
134 mapping.len = strlen (path);
136 g_array_insert_val (map->mappings, insert, mapping);
141 * soup_path_map_remove:
142 * @map: a %SoupPathMap
145 * Removes @data from @map at @path. (This must be called with the same
146 * path the data was originally added with, not a subdirectory.)
149 soup_path_map_remove (SoupPathMap *map, const char *path)
151 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
154 if (!mapping_lookup (map, path, &match, NULL))
158 map->free_func (mappings[match].data);
159 g_free (mappings[match].path);
160 g_array_remove_index (map->mappings, match);
164 * soup_path_map_lookup:
165 * @map: a %SoupPathMap
168 * Finds the data associated with @path in @map. If there is no data
169 * specifically associated with @path, it will return the data for the
170 * closest parent directory of @path that has data associated with it.
172 * Return value: the data set with soup_path_map_add(), or %NULL if no
173 * data could be found for @path or any of its ancestors.
176 soup_path_map_lookup (SoupPathMap *map, const char *path)
178 SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
181 mapping_lookup (map, path, &match, NULL);
185 return mappings[match].data;