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 VALUE_PAIR *fr_cursor_current(vp_cursor_t *cursor)
171 if (cursor->current) {
172 VERIFY_VP(cursor->current);
175 return cursor->current;
180 * @todo don't use with pairdelete
182 void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
193 * Cursor was initialised with a pointer to a NULL value_pair
195 if (!*cursor->first) {
196 *cursor->first = add;
197 cursor->current = add;
203 * We don't yet know where the last VALUE_PAIR is
205 * Assume current is closer to the end of the list and use that if available.
208 cursor->last = cursor->current ? cursor->current : *cursor->first;
211 VERIFY_VP(cursor->last);
214 * Something outside of the cursor added another VALUE_PAIR
216 if (cursor->last->next) {
217 for (i = cursor->last; i; i = i->next) {
224 * Either current was never set, or something iterated to the end of the
227 if (!cursor->current) {
228 cursor->current = add;
232 * If there's no next cursor, and the pair we just inserted has additional
233 * linked pairs, we need to set next to be the next VP in the list.
236 cursor->next = add->next;
239 cursor->last->next = add;
242 /** Remove the current pair
244 * @todo this is really inefficient and should be fixed...
246 * @param cursor to remove the current pair from.
247 * @return NULL on error, else the VALUE_PAIR we just removed.
249 VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor)
251 VALUE_PAIR *vp, **last;
253 vp = fr_cursor_current(cursor);
258 last = cursor->first;
259 while (*last != vp) {
260 last = &(*last)->next;
263 fr_cursor_next(cursor); /* Advance the cursor past the one were about to delete */
268 /* Fixup cursor->found if we removed the VP it was referring to */
269 if (vp == cursor->found) cursor->found = *last;
274 /** Replace the current pair
276 * @todo this is really inefficient and should be fixed...
278 * @param cursor to replace the current pair in.
279 * @param new VALUE_PAIR to insert.
280 * @return NULL on error, else the VALUE_PAIR we just replaced.
282 VALUE_PAIR *fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new)
284 VALUE_PAIR *vp, **last;
286 vp = fr_cursor_current(cursor);
288 *cursor->first = new;
292 last = cursor->first;
293 while (*last != vp) {
294 last = &(*last)->next;
297 fr_cursor_next(cursor); /* Advance the cursor past the one were about to replace */
300 new->next = vp->next;