1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* soup-path-map.c: URI path prefix-matcher
*
* Copyright (C) 2007 Novell, Inc.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include "soup-path-map.h"
/* This could be replaced with something more clever, like a Patricia
* trie, but it's probably not worth it since the total number of
* mappings is likely to always be small. So we keep an array of
* paths, sorted by decreasing length. (The first prefix match will
* therefore be the longest.)
*/
typedef struct {
char *path;
int len;
gpointer data;
} SoupPathMapping;
struct SoupPathMap {
GArray *mappings;
GDestroyNotify free_func;
};
/**
* soup_path_map_new:
* @data_free_func: function to use to free data added with
* soup_path_map_add().
*
* Creates a new %SoupPathMap.
*
* Return value: the new %SoupPathMap
**/
SoupPathMap *
soup_path_map_new (GDestroyNotify data_free_func)
{
SoupPathMap *map;
map = g_slice_new0 (SoupPathMap);
map->mappings = g_array_new (FALSE, FALSE, sizeof (SoupPathMapping));
map->free_func = data_free_func;
return map;
}
/**
* soup_path_map_free:
* @map: a %SoupPathMap
*
* Frees @map and all data stored in it.
**/
void
soup_path_map_free (SoupPathMap *map)
{
SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
guint i;
for (i = 0; i < map->mappings->len; i++) {
g_free (mappings[i].path);
if (map->free_func)
map->free_func (mappings[i].data);
}
g_array_free (map->mappings, TRUE);
g_slice_free (SoupPathMap, map);
}
/* Scan @map looking for @path or one of its ancestors.
* Sets *@match to the index of a match, or -1 if no match is found.
* Sets *@insert to the index to insert @path at if a new mapping is
* desired. Returns %TRUE if *@match is an exact match.
*/
static gboolean
mapping_lookup (SoupPathMap *map, const char *path, int *match, int *insert)
{
SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
guint i;
int path_len;
gboolean exact = FALSE;
*match = -1;
path_len = strcspn (path, "?");
for (i = 0; i < map->mappings->len; i++) {
if (mappings[i].len > path_len)
continue;
if (insert && mappings[i].len < path_len) {
*insert = i;
/* Clear insert so we don't try to set it again */
insert = NULL;
}
if (!strncmp (mappings[i].path, path, mappings[i].len)) {
*match = i;
if (path_len == mappings[i].len)
exact = TRUE;
if (!insert)
return exact;
}
}
if (insert)
*insert = i;
return exact;
}
/**
* soup_path_map_add:
* @map: a %SoupPathMap
* @path: the path
* @data: the data
*
* Adds @data to @map at @path. If there was already data at @path it
* will be freed.
**/
void
soup_path_map_add (SoupPathMap *map, const char *path, gpointer data)
{
SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
int match, insert;
if (mapping_lookup (map, path, &match, &insert)) {
if (map->free_func)
map->free_func (mappings[match].data);
mappings[match].data = data;
} else {
SoupPathMapping mapping;
mapping.path = g_strdup (path);
mapping.len = strlen (path);
mapping.data = data;
g_array_insert_val (map->mappings, insert, mapping);
}
}
/**
* soup_path_map_remove:
* @map: a %SoupPathMap
* @path: the path
*
* Removes @data from @map at @path. (This must be called with the same
* path the data was originally added with, not a subdirectory.)
**/
void
soup_path_map_remove (SoupPathMap *map, const char *path)
{
SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
int match;
if (!mapping_lookup (map, path, &match, NULL))
return;
if (map->free_func)
map->free_func (mappings[match].data);
g_free (mappings[match].path);
g_array_remove_index (map->mappings, match);
}
/**
* soup_path_map_lookup:
* @map: a %SoupPathMap
* @path: the path
*
* Finds the data associated with @path in @map. If there is no data
* specifically associated with @path, it will return the data for the
* closest parent directory of @path that has data associated with it.
*
* Return value: (nullable): the data set with soup_path_map_add(), or
* %NULL if no data could be found for @path or any of its ancestors.
**/
gpointer
soup_path_map_lookup (SoupPathMap *map, const char *path)
{
SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
int match;
mapping_lookup (map, path, &match, NULL);
if (match == -1)
return NULL;
else
return mappings[match].data;
}
|