Remove build warning
[platform/upstream/libsoup.git] / libsoup / soup-path-map.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-path-map.c: URI path prefix-matcher
4  *
5  * Copyright (C) 2007 Novell, Inc.
6  */
7
8 #include <string.h>
9
10 #include "soup-path-map.h"
11
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.)
17  */
18
19 typedef struct {
20         char     *path;
21         int       len;
22         gpointer  data;
23 } SoupPathMapping;
24
25 struct SoupPathMap {
26         GArray *mappings;
27         GDestroyNotify free_func;
28 };
29
30 /**
31  * soup_path_map_new:
32  * @data_free_func: function to use to free data added with
33  * soup_path_map_add().
34  *
35  * Creates a new %SoupPathMap.
36  *
37  * Return value: the new %SoupPathMap
38  **/
39 SoupPathMap *
40 soup_path_map_new (GDestroyNotify data_free_func)
41 {
42         SoupPathMap *map;
43
44         map = g_slice_new0 (SoupPathMap);
45         map->mappings = g_array_new (FALSE, FALSE, sizeof (SoupPathMapping));
46         map->free_func = data_free_func;
47
48         return map;
49 }
50
51 /**
52  * soup_path_map_free:
53  * @map: a %SoupPathMap
54  *
55  * Frees @map and all data stored in it.
56  **/
57 void
58 soup_path_map_free (SoupPathMap *map)
59 {
60         SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
61         int i;
62
63         for (i = 0; i < map->mappings->len; i++) {
64                 g_free (mappings[i].path);
65                 if (map->free_func)
66                         map->free_func (mappings[i].data);
67         }
68         g_array_free (map->mappings, TRUE);
69         
70         g_slice_free (SoupPathMap, map);
71 }
72
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.
77  */
78 static gboolean
79 mapping_lookup (SoupPathMap *map, const char *path, int *match, int *insert)
80 {
81         SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
82         int i, path_len;
83         gboolean exact = FALSE;
84
85         *match = -1;
86
87         path_len = strcspn (path, "?");
88         for (i = 0; i < map->mappings->len; i++) {
89                 if (mappings[i].len > path_len)
90                         continue;
91
92                 if (insert && mappings[i].len < path_len) {
93                         *insert = i;
94                         /* Clear insert so we don't try to set it again */
95                         insert = NULL;
96                 }
97
98                 if (!strncmp (mappings[i].path, path, mappings[i].len)) {
99                         *match = i;
100                         if (path_len == mappings[i].len)
101                                 exact = TRUE;
102                         if (!insert)
103                                 return exact;
104                 }
105         }
106
107         if (insert)
108                 *insert = i;
109         return exact;
110 }       
111
112 /**
113  * soup_path_map_add:
114  * @map: a %SoupPathMap
115  * @path: the path
116  * @data: the data
117  *
118  * Adds @data to @map at @path. If there was already data at @path it
119  * will be freed.
120  **/
121 void
122 soup_path_map_add (SoupPathMap *map, const char *path, gpointer data)
123 {
124         SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
125         int match, insert;
126
127         if (mapping_lookup (map, path, &match, &insert)) {
128                 if (map->free_func)
129                         map->free_func (mappings[match].data);
130                 mappings[match].data = data;
131         } else {
132                 SoupPathMapping mapping;
133
134                 mapping.path = g_strdup (path);
135                 mapping.len = strlen (path);
136                 mapping.data = data;
137                 g_array_insert_val (map->mappings, insert, mapping);
138         }
139 }
140
141 /**
142  * soup_path_map_remove:
143  * @map: a %SoupPathMap
144  * @path: the path
145  *
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.)
148  **/
149 void
150 soup_path_map_remove (SoupPathMap *map, const char *path)
151 {
152         SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
153         int match;
154
155         if (!mapping_lookup (map, path, &match, NULL))
156                 return;
157
158         if (map->free_func)
159                 map->free_func (mappings[match].data);
160         g_free (mappings[match].path);
161         g_array_remove_index (map->mappings, match);
162 }
163
164 /**
165  * soup_path_map_lookup:
166  * @map: a %SoupPathMap
167  * @path: the path
168  *
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.
172  *
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.
175  **/
176 gpointer
177 soup_path_map_lookup (SoupPathMap *map, const char *path)
178 {
179         SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
180         int match;
181
182         mapping_lookup (map, path, &match, NULL);
183         if (match == -1)
184                 return NULL;
185         else
186                 return mappings[match].data;
187 }