2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief Functions to iterate over collections of VALUE_PAIRs
21 * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 * @copyright 2013 The FreeRADIUS Server Project.
25 #include <freeradius-devel/libradius.h>
27 /** Setup a cursor to iterate over attribute pairs
29 * @param cursor Where to initialise the cursor (uses existing structure).
30 * @param node to start from.
32 VALUE_PAIR *_fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR const * const *node)
34 memset(cursor, 0, sizeof(*cursor));
36 if (!node || !cursor) {
41 * Useful check to see if uninitialised memory is pointed
45 if (*node) VERIFY_VP(*node);
47 memcpy(&cursor->first, &node, sizeof(cursor->first));
48 cursor->current = *cursor->first;
50 if (cursor->current) {
51 VERIFY_VP(cursor->current);
52 cursor->next = cursor->current->next;
55 return cursor->current;
58 void fr_cursor_copy(vp_cursor_t *out, vp_cursor_t *in)
60 memcpy(out, in, sizeof(*out));
63 VALUE_PAIR *fr_cursor_first(vp_cursor_t *cursor)
65 cursor->current = *cursor->first;
67 if (cursor->current) {
68 VERIFY_VP(cursor->current);
69 cursor->next = cursor->current->next;
70 if (cursor->next) VERIFY_VP(cursor->next);
74 return cursor->current;
77 /** Return the last pair in the list
80 VALUE_PAIR *fr_cursor_last(vp_cursor_t *cursor)
82 if (!*cursor->first) return NULL;
84 /* Need to start at the start */
85 if (!cursor->current) {
86 fr_cursor_first(cursor);
90 while (cursor->next) {
91 fr_cursor_next(cursor);
94 return fr_cursor_current(cursor);
97 /** Iterate over attributes of a given type in the pairlist
101 VALUE_PAIR *fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int attr, unsigned int vendor, int8_t tag)
105 i = pairfind(!cursor->found ? cursor->current : cursor->found->next, attr, vendor, tag);
108 cursor->current = NULL;
113 cursor->next = i->next;
120 /** Iterate over attributes of a given DA in the pairlist
124 VALUE_PAIR *fr_cursor_next_by_da(vp_cursor_t *cursor, DICT_ATTR const *da, int8_t tag)
128 i = pairfind_da(!cursor->found ? cursor->current : cursor->found->next, da, tag);
131 cursor->current = NULL;
136 cursor->next = i->next;
143 /** Retrieve the next VALUE_PAIR
147 VALUE_PAIR *fr_cursor_next(vp_cursor_t *cursor)
149 cursor->current = cursor->next;
150 if (cursor->current) {
151 VERIFY_VP(cursor->current);
154 * Set this now in case 'current' gets freed before
155 * fr_cursor_next is called again.
157 cursor->next = cursor->current->next;
160 * Next call to fr_cursor_next_by_num will start from the current
161 * position in the list, not the last found instance.
163 cursor->found = NULL;
166 return cursor->current;
169 /** Return what's coming next without advancing the cursor
172 VALUE_PAIR *fr_cursor_next_peek(vp_cursor_t *cursor)
177 VALUE_PAIR *fr_cursor_current(vp_cursor_t *cursor)
179 if (cursor->current) {
180 VERIFY_VP(cursor->current);
183 return cursor->current;
186 /** Insert a single VP
188 * @todo don't use with pairdelete
190 void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
201 * Only allow one VP to by inserted at a time
206 * Cursor was initialised with a pointer to a NULL value_pair
208 if (!*cursor->first) {
209 *cursor->first = add;
210 cursor->current = add;
216 * We don't yet know where the last VALUE_PAIR is
218 * Assume current is closer to the end of the list and use that if available.
221 cursor->last = cursor->current ? cursor->current : *cursor->first;
224 VERIFY_VP(cursor->last);
227 * Something outside of the cursor added another VALUE_PAIR
229 if (cursor->last->next) {
230 for (i = cursor->last; i; i = i->next) {
237 * Either current was never set, or something iterated to the end of the
240 if (!cursor->current) {
241 cursor->current = add;
245 * If there's no next cursor, and the pair we just inserted has additional
246 * linked pairs, we need to set next to be the next VP in the list.
249 cursor->next = add->next;
252 cursor->last->next = add;
255 /** Merges two sets of VPs
257 * The list represented by cursor will hold the union of cursor and
260 * @param cursor to insert VALUE_PAIRs with
261 * @param add one or more VALUE_PAIRs.
263 void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *add)
268 for (vp = fr_cursor_init(&from, &add);
270 vp = fr_cursor_next(&from)) {
271 fr_cursor_insert(cursor, vp);
275 /** Remove the current pair
277 * @todo this is really inefficient and should be fixed...
279 * @param cursor to remove the current pair from.
280 * @return NULL on error, else the VALUE_PAIR we just removed.
282 VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor)
284 VALUE_PAIR *vp, **last;
286 vp = fr_cursor_current(cursor);
291 last = cursor->first;
292 while (*last != vp) {
293 last = &(*last)->next;
296 fr_cursor_next(cursor); /* Advance the cursor past the one were about to delete */
301 /* Fixup cursor->found if we removed the VP it was referring to */
302 if (vp == cursor->found) cursor->found = *last;
307 /** Replace the current pair
309 * @todo this is really inefficient and should be fixed...
311 * @param cursor to replace the current pair in.
312 * @param new VALUE_PAIR to insert.
313 * @return NULL on error, else the VALUE_PAIR we just replaced.
315 VALUE_PAIR *fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new)
317 VALUE_PAIR *vp, **last;
319 vp = fr_cursor_current(cursor);
321 *cursor->first = new;
325 last = cursor->first;
326 while (*last != vp) {
327 last = &(*last)->next;
330 fr_cursor_next(cursor); /* Advance the cursor past the one were about to replace */
333 new->next = vp->next;