39b9f9eb2700fe3a4d6b6c74328a555a7770bdf7
[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 VALUE_PAIR *fr_cursor_current(vp_cursor_t *cursor)
170 {
171         if (cursor->current) {
172                 VERIFY_VP(cursor->current);
173         }
174
175         return cursor->current;
176 }
177
178 /** Insert a VP
179  *
180  * @todo don't use with pairdelete
181  */
182 void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
183 {
184         VALUE_PAIR *i;
185
186         if (!add) {
187                 return;
188         }
189
190         VERIFY_VP(add);
191
192         /*
193          *      Cursor was initialised with a pointer to a NULL value_pair
194          */
195         if (!*cursor->first) {
196                 *cursor->first = add;
197                 cursor->current = add;
198
199                 return;
200         }
201
202         /*
203          *      We don't yet know where the last VALUE_PAIR is
204          *
205          *      Assume current is closer to the end of the list and use that if available.
206          */
207         if (!cursor->last) {
208                 cursor->last = cursor->current ? cursor->current : *cursor->first;
209         }
210
211         VERIFY_VP(cursor->last);
212
213         /*
214          *      Something outside of the cursor added another VALUE_PAIR
215          */
216         if (cursor->last->next) {
217                 for (i = cursor->last; i; i = i->next) {
218                         VERIFY_VP(i);
219                         cursor->last = i;
220                 }
221         }
222
223         /*
224          *      Either current was never set, or something iterated to the end of the
225          *      attribute list.
226          */
227         if (!cursor->current) {
228                 cursor->current = add;
229         }
230
231         /*
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.
234          */
235         if (!cursor->next) {
236                 cursor->next = add->next;
237         }
238
239         cursor->last->next = add;
240 }
241
242 /** Remove the current pair
243  *
244  * @todo this is really inefficient and should be fixed...
245  *
246  * @param cursor to remove the current pair from.
247  * @return NULL on error, else the VALUE_PAIR we just removed.
248  */
249 VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor)
250 {
251         VALUE_PAIR *vp, **last;
252
253         vp = fr_cursor_current(cursor);
254         if (!vp) {
255                 return NULL;
256         }
257
258         last = cursor->first;
259         while (*last != vp) {
260                 last = &(*last)->next;
261         }
262
263         fr_cursor_next(cursor);   /* Advance the cursor past the one were about to delete */
264
265         *last = vp->next;
266         vp->next = NULL;
267
268         /* Fixup cursor->found if we removed the VP it was referring to */
269         if (vp == cursor->found) cursor->found = *last;
270
271         return vp;
272 }
273
274 /** Replace the current pair
275  *
276  * @todo this is really inefficient and should be fixed...
277  *
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.
281  */
282 VALUE_PAIR *fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new)
283 {
284         VALUE_PAIR *vp, **last;
285
286         vp = fr_cursor_current(cursor);
287         if (!vp) {
288                 *cursor->first = new;
289                 return NULL;
290         }
291
292         last = cursor->first;
293         while (*last != vp) {
294             last = &(*last)->next;
295         }
296
297         fr_cursor_next(cursor);   /* Advance the cursor past the one were about to replace */
298
299         *last = new;
300         new->next = vp->next;
301         vp->next = NULL;
302
303         return vp;
304 }