Distinguish between single VP inserts, and list merges
[freeradius.git] / src / lib / cursor.c
1 /*
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.
5  *
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.
10  *
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
14  */
15
16 /**
17  * $Id$
18  * @file cursor.c
19  * @brief Functions to iterate over collections of VALUE_PAIRs
20  *
21  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22  * @copyright 2013 The FreeRADIUS Server Project.
23  */
24
25 #include <freeradius-devel/libradius.h>
26
27 /** Setup a cursor to iterate over attribute pairs
28  *
29  * @param cursor Where to initialise the cursor (uses existing structure).
30  * @param node to start from.
31  */
32 VALUE_PAIR *_fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR const * const *node)
33 {
34         memset(cursor, 0, sizeof(*cursor));
35
36         if (!node || !cursor) {
37                 return NULL;
38         }
39
40         /*
41          *  Useful check to see if uninitialised memory is pointed
42          *  to by node
43          */
44 #ifndef NDEBUG
45         if (*node) VERIFY_VP(*node);
46 #endif
47         memcpy(&cursor->first, &node, sizeof(cursor->first));
48         cursor->current = *cursor->first;
49
50         if (cursor->current) {
51                 VERIFY_VP(cursor->current);
52                 cursor->next = cursor->current->next;
53         }
54
55         return cursor->current;
56 }
57
58 void fr_cursor_copy(vp_cursor_t *out, vp_cursor_t *in)
59 {
60         memcpy(out, in, sizeof(*out));
61 }
62
63 VALUE_PAIR *fr_cursor_first(vp_cursor_t *cursor)
64 {
65         cursor->current = *cursor->first;
66
67         if (cursor->current) {
68                 VERIFY_VP(cursor->current);
69                 cursor->next = cursor->current->next;
70                 if (cursor->next) VERIFY_VP(cursor->next);
71                 cursor->found = NULL;
72         }
73
74         return cursor->current;
75 }
76
77 /** Return the last pair in the list
78  *
79  */
80 VALUE_PAIR *fr_cursor_last(vp_cursor_t *cursor)
81 {
82         if (!*cursor->first) return NULL;
83
84         /* Need to start at the start */
85         if (!cursor->current) {
86                 fr_cursor_first(cursor);
87         }
88
89         /* Wind to the end */
90         while (cursor->next) {
91                 fr_cursor_next(cursor);
92         }
93
94         return fr_cursor_current(cursor);
95 }
96
97 /** Iterate over attributes of a given type in the pairlist
98  *
99  *
100  */
101 VALUE_PAIR *fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int attr, unsigned int vendor, int8_t tag)
102 {
103         VALUE_PAIR *i;
104
105         i = pairfind(!cursor->found ? cursor->current : cursor->found->next, attr, vendor, tag);
106         if (!i) {
107                 cursor->next = NULL;
108                 cursor->current = NULL;
109
110                 return NULL;
111         }
112
113         cursor->next = i->next;
114         cursor->current = i;
115         cursor->found = i;
116
117         return i;
118 }
119
120 /** Iterate over attributes of a given DA in the pairlist
121  *
122  *
123  */
124 VALUE_PAIR *fr_cursor_next_by_da(vp_cursor_t *cursor, DICT_ATTR const *da, int8_t tag)
125 {
126         VALUE_PAIR *i;
127
128         i = pairfind_da(!cursor->found ? cursor->current : cursor->found->next, da, tag);
129         if (!i) {
130                 cursor->next = NULL;
131                 cursor->current = NULL;
132
133                 return NULL;
134         }
135
136         cursor->next = i->next;
137         cursor->current = i;
138         cursor->found = i;
139
140         return i;
141 }
142
143 /** Retrieve the next VALUE_PAIR
144  *
145  *
146  */
147 VALUE_PAIR *fr_cursor_next(vp_cursor_t *cursor)
148 {
149         cursor->current = cursor->next;
150         if (cursor->current) {
151                 VERIFY_VP(cursor->current);
152
153                 /*
154                  *      Set this now in case 'current' gets freed before
155                  *      fr_cursor_next is called again.
156                  */
157                 cursor->next = cursor->current->next;
158
159                 /*
160                  *      Next call to fr_cursor_next_by_num will start from the current
161                  *      position in the list, not the last found instance.
162                  */
163                 cursor->found = NULL;
164         }
165
166         return cursor->current;
167 }
168
169 /** Return what's coming next without advancing the cursor
170  *
171  */
172 VALUE_PAIR *fr_cursor_next_peek(vp_cursor_t *cursor)
173 {
174         return cursor->next;
175 }
176
177 VALUE_PAIR *fr_cursor_current(vp_cursor_t *cursor)
178 {
179         if (cursor->current) {
180                 VERIFY_VP(cursor->current);
181         }
182
183         return cursor->current;
184 }
185
186 /** Insert a single VP
187  *
188  * @todo don't use with pairdelete
189  */
190 void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
191 {
192         VALUE_PAIR *i;
193
194         if (!add) {
195                 return;
196         }
197
198         VERIFY_VP(add);
199
200         /*
201          *      Only allow one VP to by inserted at a time
202          */
203         add->next = NULL;
204
205         /*
206          *      Cursor was initialised with a pointer to a NULL value_pair
207          */
208         if (!*cursor->first) {
209                 *cursor->first = add;
210                 cursor->current = add;
211
212                 return;
213         }
214
215         /*
216          *      We don't yet know where the last VALUE_PAIR is
217          *
218          *      Assume current is closer to the end of the list and use that if available.
219          */
220         if (!cursor->last) {
221                 cursor->last = cursor->current ? cursor->current : *cursor->first;
222         }
223
224         VERIFY_VP(cursor->last);
225
226         /*
227          *      Something outside of the cursor added another VALUE_PAIR
228          */
229         if (cursor->last->next) {
230                 for (i = cursor->last; i; i = i->next) {
231                         VERIFY_VP(i);
232                         cursor->last = i;
233                 }
234         }
235
236         /*
237          *      Either current was never set, or something iterated to the end of the
238          *      attribute list.
239          */
240         if (!cursor->current) {
241                 cursor->current = add;
242         }
243
244         /*
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.
247          */
248         if (!cursor->next) {
249                 cursor->next = add->next;
250         }
251
252         cursor->last->next = add;
253 }
254
255 /** Merges two sets of VPs
256  *
257  * The list represented by cursor will hold the union of cursor and
258  * add lists.
259  *
260  * @param cursor to insert VALUE_PAIRs with
261  * @param add one or more VALUE_PAIRs.
262  */
263 void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *add)
264 {
265         vp_cursor_t from;
266         VALUE_PAIR *vp;
267
268         for (vp = fr_cursor_init(&from, &add);
269              vp;
270              vp = fr_cursor_next(&from)) {
271                 fr_cursor_insert(cursor, vp);
272         }
273 }
274
275 /** Remove the current pair
276  *
277  * @todo this is really inefficient and should be fixed...
278  *
279  * @param cursor to remove the current pair from.
280  * @return NULL on error, else the VALUE_PAIR we just removed.
281  */
282 VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor)
283 {
284         VALUE_PAIR *vp, **last;
285
286         vp = fr_cursor_current(cursor);
287         if (!vp) {
288                 return NULL;
289         }
290
291         last = cursor->first;
292         while (*last != vp) {
293                 last = &(*last)->next;
294         }
295
296         fr_cursor_next(cursor);   /* Advance the cursor past the one were about to delete */
297
298         *last = vp->next;
299         vp->next = NULL;
300
301         /* Fixup cursor->found if we removed the VP it was referring to */
302         if (vp == cursor->found) cursor->found = *last;
303
304         return vp;
305 }
306
307 /** Replace the current pair
308  *
309  * @todo this is really inefficient and should be fixed...
310  *
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.
314  */
315 VALUE_PAIR *fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new)
316 {
317         VALUE_PAIR *vp, **last;
318
319         vp = fr_cursor_current(cursor);
320         if (!vp) {
321                 *cursor->first = new;
322                 return NULL;
323         }
324
325         last = cursor->first;
326         while (*last != vp) {
327             last = &(*last)->next;
328         }
329
330         fr_cursor_next(cursor);   /* Advance the cursor past the one were about to replace */
331
332         *last = new;
333         new->next = vp->next;
334         vp->next = NULL;
335
336         return vp;
337 }