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