The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
edit.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 21e5c007d1212e4cb7d853911dcca1dbb47d57df $
19 *
20 * @file src/lib/util/edit.c
21 * @brief Functions to edit pair lists, and track undo operations
22 *
23 * This file implements an "edit list" for changing values of
24 * #fr_pair_t. After some investigation, it turns out that it's much
25 * easier to have an "undo list" than to track partially applied
26 * transactions. Tracking partial transactions means that none of
27 * the fr_pair_foo() functions will work, as some pairs are in the
28 * "old" list and some in the "new" list. Also, a transaction may
29 * still fail when we finalize it by moving the pairs around.
30 *
31 * In contrast, an "undo" list means that all of the fr_pair_foo()
32 * functions will work, as any list contains only "active" pairs.
33 * And we never need to "finalize" a transaction, as the lists are
34 * already in their final form. The only thing needed for
35 * finalization is to free the undo list. Which can never fail.
36 *
37 * Note that the functions here require the input VPs to already have
38 * the correct talloc parent! The only thing the edit list does is
39 * to record "undo" actions.
40 *
41 * The only exception to this is fr_edit_list_apply_list_assignment().
42 * Which does call talloc_steal, and then also frees any pairs which
43 * weren't applied to the LHS.
44 *
45 * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
46 */
47
48RCSID("$Id: 21e5c007d1212e4cb7d853911dcca1dbb47d57df $")
49
50#include <freeradius-devel/util/value.h>
51#include <freeradius-devel/util/talloc.h>
52#include "edit.h"
53#include "calc.h"
54
55typedef enum {
57 FR_EDIT_DELETE, //!< delete a VP
58 FR_EDIT_VALUE, //!< edit a VP in place
59 FR_EDIT_CLEAR, //!< clear the children of a structural entry.
60 FR_EDIT_INSERT, //!< insert a VP into a list, after another one.
61 FR_EDIT_CHILD, //!< child edit list
63
64#if 0
65/*
66 * For debugging.
67 */
68static const char *edit_names[4] = {
69 "invalid",
70 "delete",
71 "value",
72 "clear",
73 "insert",
74};
75#endif
76
77/** Track one particular edit.
78 */
79typedef struct {
80 fr_edit_op_t op; //!< edit operation to perform
81 fr_dlist_t entry; //!< linked list of edits
82
83 fr_pair_t *vp; //!< pair edited, deleted, or inserted
84
85 union {
86 union {
87 fr_value_box_t data; //!< original data
88 fr_pair_list_t children; //!< original child list, for "clear"
89 fr_edit_list_t *child_edit;
90 };
91
92 struct {
93 fr_pair_list_t *list; //!< parent list
94 fr_pair_t *ref; //!< reference pair for delete, insert before/after
95 };
96 };
97} fr_edit_t;
98
99/** Track a series of edits.
100 *
101 */
103 /*
104 * List of undo changes to be made, in order.
105 */
107
108 fr_dlist_head_t ignore; //!< lists to ignore
109
110 /*
111 * VPs which were inserted, and then over-written by a
112 * later edit.
113 */
115
116 fr_edit_list_t *parent; //!< for nested transactions
117 fr_edit_t *e; //!< so we don't have to loop over parent edits on abort
118};
119
120typedef struct {
122 fr_pair_list_t *list; //!< list to ignore (never dereferenced)
124
125
127{
128 return fr_dlist_empty(&el->undo) && fr_dlist_empty(&el->ignore) && fr_pair_list_empty(&el->deleted_pairs);
129}
130
131/** Undo one particular edit.
132 */
133static int edit_undo(fr_edit_t *e)
134{
135 fr_pair_t *vp = e->vp;
136#ifndef NDEBUG
137 int rcode;
138#endif
139
140 fr_assert(vp != NULL);
142
143 switch (e->op) {
144 case FR_EDIT_INVALID:
145 return -1;
146
147 case FR_EDIT_VALUE:
148 fr_assert(fr_type_is_leaf(vp->vp_type));
149 if (!fr_type_is_fixed_size(vp->vp_type)) fr_value_box_clear(&vp->data);
150 fr_value_box_copy(vp, &vp->data, &e->data);
151 break;
152
153 case FR_EDIT_CLEAR:
155
156 fr_pair_list_free(&vp->vp_group);
157 fr_pair_list_append(&vp->vp_group, &e->children);
158 break;
159
160 case FR_EDIT_DELETE:
161 fr_assert(e->list != NULL);
162#ifndef NDEBUG
163 rcode =
164#endif
165 fr_pair_insert_after(e->list, e->ref, vp);
166 fr_assert(rcode == 0);
167 break;
168
169 case FR_EDIT_INSERT:
170 /*
171 * We can free the VP here, as any edits to its'
172 * children MUST come after the creation of the
173 * VP. And any deletion of VPs after this one
174 * must come after this VP was created.
175 */
176 fr_pair_delete(e->list, vp);
177 break;
178
179 case FR_EDIT_CHILD:
180 fr_edit_list_abort(e->child_edit);
181 break;
182 }
183
184 return 0;
185}
186
187/** Abort the entries in an edit list.
188 *
189 * After this call, the input list(s) are unchanged from before any
190 * edits were made.
191 *
192 * the caller does not have to call talloc_free(el);
193 */
195{
196 fr_edit_t *e;
197
198 if (!el) return;
199
200 /*
201 * All of these pairs are already in the edit list. They
202 * have the correct parent, and will be placed back into
203 * their correct location by edit_undo()
204 */
205 fr_pair_list_init(&el->deleted_pairs);
206
207 /*
208 * Undo edits in reverse order, as later edits depend on
209 * earlier ones. We don't have multiple edits of the
210 * same VP, but we can create a VP, and then later edit
211 * its children.
212 */
213 while ((e = fr_dlist_pop_tail(&el->undo)) != NULL) {
214 edit_undo(e);
215 /*
216 * Don't free "e", it will be cleaned up when we
217 * talloc_free(el). That should be somewhat
218 * faster than doing it incrementally.
219 */
220 }
221
222 /*
223 * There's a parent, we remove ourselves from the parent undo list.
224 */
225 if (el->parent) {
226 fr_dlist_remove(&el->parent->undo, el->e);
227 talloc_free(el->e);
228 }
229
231}
232
233/** Record one particular edit
234 *
235 * For INSERT / DELETE, this function will also insert / delete the
236 * VP.
237 *
238 * For VALUE changes, this function must be called BEFORE the value
239 * is changed. Once this function has been called, it is then safe
240 * to edit the value in place.
241 *
242 * Note that VALUE changes for structural types are allowed ONLY when
243 * using T_OP_SET, which over-writes previous values. For every
244 * other modification to structural types, we MUST instead call
245 * insert / delete on the vp_group.
246 */
248{
249 fr_edit_t *e;
250
251 fr_assert(el != NULL);
252 fr_assert(vp != NULL);
253
254 fr_assert(op != FR_EDIT_CHILD); /* only used by fr_edit_alloc() */
255
256 /*
257 * When we insert a structural type, we also want to
258 * not track edits to it's children. The "ignore list"
259 * allows us to see which lists don't have edits recorded.
260 *
261 * Perform the operation. We're not recording
262 * it, but we still need to do the work.
263 */
264 fr_dlist_foreach(&el->ignore, fr_edit_ignore_t, i) {
265 if (i->list != list) continue;
266
267 switch (op) {
268 /*
269 * No need to save the value.
270 */
271 case FR_EDIT_VALUE:
272 if (fr_pair_immutable(vp)) {
273 fr_strerror_printf("Cannot modify immutable value for %s", vp->da->name);
274 return -1;
275 }
276 return 0;
277
278 /*
279 * No need to save the value.
280 */
281 case FR_EDIT_DELETE:
282 /*
283 * We silently refuse to delete immutable attributes.
284 */
285 if (fr_pair_immutable(vp)) return 0;
286
287 if (vp->vp_edit) return 0;
288
289 fr_pair_remove(list, vp);
290 return 0;
291
292 /*
293 * Delete all of the children.
294 */
295 case FR_EDIT_CLEAR:
296 if (!fr_type_is_structural(vp->vp_type)) return 0;
297
298 /*
299 * The VP is a child of an attribute which was previously inserted as part of
300 * this edit. We therefore allow the "clear" to clear it, even if it contains
301 * immutable children. Because this operation is equivalent to just never
302 * creating the children.
303 */
304
305 fr_pair_list_free(&vp->vp_group);
306 return 0;
307
308 /*
309 * Insert it, and perhaps save the list
310 * for a structural VP saying "don't
311 * record edits to this, either".
312 */
313 case FR_EDIT_INSERT:
314 if (fr_pair_insert_after(list, ref, vp) < 0) return -1;
315
316 /*
317 * Non-structural types don't have any other work to do.
318 */
319 if (!fr_type_is_structural(vp->vp_type)) return 0;
320
321 /*
322 * Otherwise we're inserting a VP which has a
323 * child list. Remember that we need to ignore
324 * edits to the children of this VP, too.
325 */
326 goto insert_ignore;
327
328 default:
329 return -1;
330 }
331 }
332
333 /*
334 * Catch NOOPs
335 */
336 if (op == FR_EDIT_CLEAR) {
338
339 if (fr_pair_list_empty(&vp->vp_group)) return 0;
340 }
341
342 /*
343 * Search for previous edits.
344 *
345 * @todo - if we're modifying values of a child VP, and
346 * it's parent is marked as INSERT, then we don't need to
347 * record FR_EDIT_VALUE changes to the children. It's
348 * not yet clear how best to track this.
349 */
350 for (e = fr_dlist_head(&el->undo);
351 e != NULL;
352 e = fr_dlist_next(&el->undo, e)) {
353 fr_assert(e->vp != NULL);
354
355 if (e->vp != vp) continue;
356
357 switch (op) {
358 case FR_EDIT_INVALID:
359 return -1;
360
361 /*
362 * We're editing a previous edit.
363 * There's no need to record anything
364 * new, as we've already recorded the
365 * original value.
366 *
367 * Note that we can insert a pair and
368 * then edit it. The undo list only
369 * saves the insert, as the later edit is
370 * irrelevant. If we're undoing, we
371 * simply delete the new attribute which
372 * was inserted.
373 */
374 case FR_EDIT_VALUE:
375 /*
376 * If we delete a pair, we can't later
377 * edit it. That indicates serious
378 * issues with the code.
379 *
380 * However, if we previously inserted
381 * this VP, then we don't need to record
382 * changes to its value. Similarly, if
383 * we had previously changed its value,
384 * we don't need to record that
385 * information again.
386 */
388 fr_assert(fr_type_is_leaf(vp->vp_type));
389 return 0;
390
391 /*
392 * We're inserting a new pair.
393 *
394 * We can't have previously edited this
395 * pair (inserted, deleted, or updated
396 * the value), as the pair is new!
397 */
398 case FR_EDIT_INSERT:
399 fr_assert(0);
400 return -1;
401
402 case FR_EDIT_CLEAR:
403 /*
404 * If we're clearing it, we MUST have
405 * previously inserted it. So just nuke
406 * it's children, as merging the
407 * operations of "insert with stuff" and
408 * then "clear" is just "insert empty
409 * pair".
410 *
411 * However, we don't yet delete the
412 * children, as there may be other edit
413 * operations which are referring to
414 * them.
415 */
418
419 fr_pair_list_append(&el->deleted_pairs, &vp->vp_group);
420 break;
421
422 /*
423 * We're being asked to delete something
424 * we previously inserted, or previously
425 * edited.
426 */
427 case FR_EDIT_DELETE:
428 /*
429 * We can't delete something which was
430 * already deleted.
431 */
433
434 /*
435 * We had previously inserted it. So
436 * just delete the insert operation, and
437 * delete the VP from the list.
438 *
439 * Other edits may refer to children of
440 * this pair. So we don't free the VP
441 * immediately, but instead reparent it
442 * to the edit list. So that when the
443 * edit list is freed, the VP will be
444 * freed.
445 */
446 if (e->op == FR_EDIT_INSERT) {
447 fr_assert(e->list == list);
448
449 fr_pair_remove(list, vp);
450 fr_pair_append(&el->deleted_pairs, vp);
451
452 fr_dlist_remove(&el->undo, e);
453 talloc_free(e);
454 return 0;
455 }
456
457 /*
458 * We had previously changed the value,
459 * but now we're going to delete it.
460 *
461 * Since it had previously existed, we
462 * have to reset its value to the
463 * original one, and then track the
464 * deletion.
465 */
466 edit_undo(e);
467
468 /*
469 * Rewrite the edit to be delete.
470 *
471 * And move the deletion to the tail of
472 * the edit list, because edits between
473 * "here" and the tail of the list may
474 * refer to "vp". If we leave the
475 * deletion in place, then subsequent
476 * edit list entries will refer to a VP
477 * which has been deleted!
478 */
479 e->op = FR_EDIT_DELETE;
480 fr_dlist_remove(&el->undo, e);
481 goto delete;
482
483 case FR_EDIT_CHILD: /* e->vp==NULL, so this should never happen */
484 fr_assert(0);
485 return -1;
486 }
487 } /* loop over existing edits */
488
489 /*
490 * No edit for this pair exists. Create a new edit entry.
491 */
492 e = talloc_zero(el, fr_edit_t);
493 if (!e) return -1;
494
495 e->op = op;
496 e->vp = vp;
497 fr_value_box_init_null(&e->data);
498
499 switch (op) {
500 case FR_EDIT_INVALID:
501 case FR_EDIT_CHILD:
502 fail:
503 talloc_free(e);
504 return -1;
505
506 case FR_EDIT_VALUE:
507 fr_assert(list == NULL);
508 fr_assert(ref == NULL);
509
510 fr_assert(fr_type_is_leaf(vp->vp_type));
511 fr_value_box_copy(e, &e->data, &vp->data);
512 break;
513
514 case FR_EDIT_CLEAR:
515 fr_assert(list == NULL);
516 fr_assert(ref == NULL);
517
519 fr_pair_list_init(&e->children);
520 fr_pair_list_append(&e->children, &vp->vp_group);
521 break;
522
523 case FR_EDIT_INSERT:
524 fr_assert(list != NULL);
525
526 /*
527 * There's no need to record "prev". On undo, we
528 * just delete this pair from the list.
529 */
530 e->list = list;
531 if (fr_pair_insert_after(list, ref, vp) < 0) goto fail;
532 break;
533
534 case FR_EDIT_DELETE:
535 delete:
536 /*
537 * We silently refuse to delete immutable attributes.
538 */
539 if (fr_pair_immutable(e->vp)) {
540 talloc_free(e);
541 return 0;
542 }
543
544 if (e->vp->vp_edit) {
545 talloc_free(e);
546 return 0;
547 }
548
549 fr_assert(list != NULL);
550 fr_assert(ref == NULL);
551
552 e->list = list;
553 e->ref = fr_pair_list_prev(list, vp);
554
555 fr_pair_remove(list, vp);
556 break;
557 }
558
559 fr_dlist_insert_tail(&el->undo, e);
560
561 /*
562 * Insert an "ignore" entry.
563 */
564 if ((op == FR_EDIT_INSERT) && fr_type_is_structural(vp->vp_type)) {
566
567 insert_ignore:
568 i = talloc_zero(el, fr_edit_ignore_t);
569 if (!i) return 0;
570
571 i->list = &vp->vp_group;
572 fr_dlist_insert_tail(&el->ignore, i);
573 }
574
575 return 0;
576}
577
578
579/** Insert a new VP after an existing one.
580 *
581 * This function mirrors fr_pair_insert_after().
582 *
583 * After this function returns, the new VP has been inserted into the
584 * list.
585 */
587{
588 if (!el) return fr_pair_insert_after(list, pos, vp);
589
590 return edit_record(el, FR_EDIT_INSERT, vp, list, pos);
591}
592
593/** Delete a VP
594 *
595 * This function mirrors fr_pair_delete()
596 *
597 * After this function returns, the VP has been removed from the list.
598 */
600{
601 if (!el) {
602 fr_pair_delete(list, vp);
603 return 0;
604 }
605
606 return edit_record(el, FR_EDIT_DELETE, vp, list, NULL);
607}
608
609/** Delete VPs with a matching da
610 *
611 * This function mirrors fr_pair_delete_by_da()
612 */
614{
615 if (!el) {
616 fr_pair_delete_by_da(list, da);
617 return 0;
618 }
619
620 /*
621 * Delete all VPs with a matching da.
622 */
623 fr_pair_list_foreach(list, vp) {
624 if (vp->da != da) continue;
625
626 if (edit_record(el, FR_EDIT_DELETE, vp, list, NULL) < 0) return -1;
627 }
628
629 return 0;
630}
631
632
633/** Record the value of a leaf #fr_value_box_t
634 *
635 * After this function returns, it's safe to edit the pair.
636 */
638{
639 if (!el) return 0;
640
641 if (!fr_type_is_leaf(vp->vp_type)) return -1;
642
643 return edit_record(el, FR_EDIT_VALUE, vp, NULL, NULL);
644}
645
646/** Write a new value to the #fr_value_box_t
647 *
648 * After this function returns, the value has been updated.
649 */
651{
652 if (!fr_type_is_leaf(vp->vp_type)) return -1;
653
654 if (el && (edit_record(el, FR_EDIT_VALUE, vp, NULL, NULL) < 0)) return -1;
655
656 if (!fr_type_is_fixed_size(vp->vp_type)) fr_value_box_clear(&vp->data);
657 fr_value_box_copy_shallow(NULL, &vp->data, box);
658 return 0;
659}
660
661/** Replace a pair with another one.
662 *
663 * This function mirrors fr_pair_replace().
664 *
665 * After this function returns, the new VP has replaced the old one,
666 * and the new one can be edited.
667 */
669{
670 if (to_replace->da != vp->da) return -1;
671
672 if (!el) {
673 if (fr_pair_insert_after(list, to_replace, vp) < 0) return -1;
674 fr_pair_delete(list, to_replace);
675 return -1;
676 }
677
678 /*
679 * We call edit_record() twice, which involves two
680 * complete passes over the edit list. That's fine,
681 * either the edit list is small, OR we will eventually
682 * put the VPs to be edited into an RB tree.
683 */
684 if (edit_record(el, FR_EDIT_INSERT, vp, list, to_replace) < 0) return -1;
685
686 /*
687 * If deleting the old entry fails, then the new entry
688 * above MUST be the last member of the edit list. If
689 * it's not the last member, then it means that it
690 * already existed in the list (either VP list of edit
691 * list). The edit_record() function checks for that,
692 * and errors if so.
693 */
694 if (edit_record(el, FR_EDIT_DELETE, to_replace, list, NULL) < 0) {
695 fr_edit_t *e = fr_dlist_pop_tail(&el->undo);
696
697 fr_assert(e != NULL);
698 fr_assert(e->vp == vp);
699 talloc_free(e);
700 return -1;
701 }
702
703 return 0;
704}
705
706
707/** Free children of a structural pair.
708 *
709 * This function mirrors fr_pair_replace().
710 *
711 * After this function returns, the new VP has replaced the old one,
712 * and the new one can be edited.
713 */
715{
716 if (!fr_type_is_structural(vp->vp_type)) return -1;
717
718 if (!el) {
719 fr_pair_list_free(&vp->children);
720 return 0;
721 }
722
723 /*
724 * No children == do nothing.
725 */
726 if (fr_pair_list_empty(&vp->vp_group)) return 0;
727
728 /*
729 * Record the list, even if it's empty. That way if we
730 * later add children to it, the "undo" operation can
731 * reset the children list to be empty.
732 */
733 return edit_record(el, FR_EDIT_CLEAR, vp, NULL, NULL);
734}
735
736/** Finalize the edits when we destroy the edit list.
737 *
738 * Which in large part means freeing the VPs which have been deleted,
739 * or saved, and then deleting the edit list.
740 */
742{
743 fr_edit_t *e;
744
745 fr_assert(el != NULL);
746
747 for (e = fr_dlist_head(&el->undo);
748 e != NULL;
749 e = fr_dlist_next(&el->undo, e)) {
750 switch (e->op) {
751 case FR_EDIT_INVALID:
752 fr_assert(0);
753 break;
754
755 case FR_EDIT_INSERT:
756 break;
757
758 case FR_EDIT_DELETE:
759 fr_assert(e->vp != NULL);
760 talloc_free(e->vp);
761 break;
762
763 case FR_EDIT_CLEAR:
764 fr_pair_list_free(&e->children);
765 break;
766
767 case FR_EDIT_VALUE:
768 fr_assert(fr_type_is_leaf(e->vp->vp_type));
769 fr_value_box_clear(&e->data);
770 break;
771
772 case FR_EDIT_CHILD:
773 talloc_free(e->child_edit);
774 break;
775 }
776 }
777
778 fr_pair_list_free(&el->deleted_pairs);
779
781
782 return 0;
783}
784
785/** Allocate an edit list.
786 *
787 * Edit lists can be nested. If the child list commits, then it does nothing
788 * until the parent list commits. On the other hand, if the child list aborts,
789 * then the parent list might continue.
790 *
791 * @param ctx talloc context. Ignored if parent!=NULL
792 * @param hint how many edits we are likely to allocate
793 * @param parent a parent edit list
794 */
796{
798 fr_edit_t *e;
799
800 /*
801 * If we have nested transactions, then allocate this
802 * list in the context of the parent. Otherwise it will
803 * be freed too soon.
804 */
805 if (parent) ctx = parent;
806
807 el = talloc_zero_pooled_object(ctx, fr_edit_list_t, hint, hint * sizeof(fr_edit_t));
808 if (!el) return NULL;
809
810 fr_dlist_init(&el->undo, fr_edit_t, entry);
811 fr_dlist_init(&el->ignore, fr_edit_ignore_t, entry);
812
813 fr_pair_list_init(&el->deleted_pairs);
814
815 talloc_set_destructor(el, _edit_list_destructor);
816
817 el->parent = parent;
818
819 if (!parent) return el;
820
821 e = talloc_zero(parent, fr_edit_t);
822 if (!e) {
824 return NULL;
825 }
826
827 /*
828 * Insert the child into the tail of the current edit list.
829 */
830 e->op = FR_EDIT_CHILD;
831 e->vp = NULL;
832 e->child_edit = el;
833
834 fr_dlist_insert_tail(&parent->undo, e);
835 el->e = e;
836
837 return el;
838}
839
840/** Commit an edit list.
841 *
842 * If there are nested transactions, then this transaction is
843 * committed only when the parent transaction has been committed.
844 *
845 */
847{
848 if (el->parent) return;
849
851}
852
853/** Notes
854 *
855 * Unlike "update" sections, edits are _not_ hierarchical. If we're
856 * editing values a list, then the list has to exist. If we're
857 * inserting pairs in a list, then we find the lowest existing pair,
858 * and add pairs there.
859 *
860 * The functions tmpl_extents_find() and tmpl_extents_build_to_leaf_parent()
861 * should help us figure out where the VPs exist or not.
862 *
863 * The overall "update" algorithm is now:
864 *
865 * alloc(edit list)
866 *
867 * foreach entry in the things to do
868 * expand LHS if needed to local TMPL
869 * expand RHS if needed to local box / cursor / TMPL
870 *
871 * use LHS/RHS cursors to find VPs
872 * edit VPs, recording edits
873 *
874 * free temporary map
875 * commit(edit list)
876 */
877
878/**********************************************************************
879 *
880 * Now we have helper functions which use the edit list to get things
881 * done.
882 *
883 **********************************************************************/
884
885/** Insert a list after a particular point in another list.
886 *
887 * This function mirrors fr_pair_list_append(), but with a bit more
888 * control over where the to_insert list ends up.
889 *
890 * There's nothing magical about this function, it's just easier to
891 * have it here than in multiple places in the code.
892 */
894{
895 fr_pair_t *prev, *vp;
896
897 prev = pos;
898
899 if (!el) {
900 /*
901 * @todo - this should really be an O(1) dlist
902 * operation.
903 */
904 while ((vp = fr_pair_list_head(to_insert)) != NULL) {
905 (void) fr_pair_remove(to_insert, vp);
906 (void) fr_pair_insert_after(list, prev, vp);
907 prev = vp;
908 }
909
910 return 0;
911 }
912
913 /*
914 * We have to record each individual insert as a separate
915 * item. Some later edit may insert pairs in the middle
916 * of the ones we've added.
917 */
918 while ((vp = fr_pair_list_head(to_insert)) != NULL) {
919 (void) fr_pair_remove(to_insert, vp);
920
921 if (edit_record(el, FR_EDIT_INSERT, vp, list, prev) < 0) {
922 fr_pair_prepend(to_insert, vp); /* don't lose it! */
923 return -1;
924 }
925
926 prev = vp;
927 }
928
929 return 0;
930}
931
932/** Removes elements matching a list
933 *
934 * O(N^2) unfortunately.
935 */
937{
938 /*
939 * We have a list of VPs with operators and values. Those contain the list of things we want to
940 * be removed from the main "list".
941 */
942 fr_pair_list_foreach(to_remove, vp) {
943 fr_pair_t *found, *next;
944
945 /*
946 * @todo - do this recursively.
947 */
948 if (fr_type_is_structural(vp->vp_type)) continue;
949
950 /*
951 * Search the list to edit for VPs which match the ones we're trying to delete.
952 */
953 for (found = fr_pair_find_by_da(list, NULL, vp->da);
954 found != NULL;
955 found = next) {
956 int rcode;
957
958 next = fr_pair_find_by_da(list, found, vp->da);
959
960 /*
961 * It doesn't match, keep it. If it matches, delete it.
962 */
963 rcode = fr_value_box_cmp_op(vp->op, &found->data, &vp->data);
964 if (rcode < 0) return -1;
965
966 if (!rcode) continue;
967
968 if (fr_edit_list_pair_delete(el, list, found) < 0) return -1;
969 }
970 }
971
972 return 0;
973}
974
975/** Apply operators to pairs.
976 *
977 * := is "if found vp, call fr_edit_list_pair_replace(). Otherwise call fr_edit_list_insert_pair_tail()
978 * = is "if found vp, do nothing. Otherwise call fr_edit_list_insert_pair_tail()
979 *
980 */
982{
983 fr_value_box_t box;
984
985 switch (op) {
986 case T_OP_LE:
987 case T_OP_LT:
988 case T_OP_GT:
989 case T_OP_GE:
990 if (fr_value_calc_binary_op(vp, &box, FR_TYPE_BOOL, &vp->data, op, in) < 0) return -1;
991
992 if (box.vb_bool) return 0;
993
994 if (el && (fr_edit_list_save_pair_value(el, vp) < 0)) return -1;
995
997
998 /*
999 * The input type may be different, so we can't just copy it.
1000 */
1001 return fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->data.enumv, in);
1002
1003 default:
1004 break;
1005
1006 }
1007
1008 if (el && (fr_edit_list_save_pair_value(el, vp) < 0)) return -1;
1009
1010 return fr_value_calc_assignment_op(vp, &vp->data, op, in);
1011}
1012
1013#undef COPY
1014#define COPY(_x) do { if (copy) { \
1015 c = fr_pair_copy(dst, _x); \
1016 if (!c) return -1; \
1017 } else { \
1018 c = talloc_steal(dst, _x); \
1019 fr_pair_remove(src, c); \
1020 } \
1021 } while (0)
1022
1023#define NEXT_A do { a = an; an = fr_pair_list_next(&dst->children, a); } while (0)
1024#define NEXT_B do { b = bn; bn = fr_pair_list_next(src, b); } while (0)
1025
1026
1027/** A UNION B
1028 *
1029 */
1030static int list_union(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
1031{
1032 fr_pair_t *a, *an;
1033 fr_pair_t *b, *bn;
1034 fr_pair_t *c;
1035
1036 /*
1037 * Prevent people from doing stupid things.
1038 * While it's technically possible to take a
1039 * UNION of structs, that would work ONLY when
1040 * the two structs had disjoint members.
1041 * e.g. {1, 3, 4} and {2, 5, 6}. That's too
1042 * complex to check right now, so we punt on the
1043 * problem.
1044 */
1045 if (dst->vp_type == FR_TYPE_STRUCT) {
1046 fr_strerror_printf("Cannot take union of STRUCT data types, it would break the structure");
1047 return -1;
1048 }
1049
1052
1053 PAIR_LIST_VERIFY(&dst->children);
1054 PAIR_LIST_VERIFY(src);
1055
1056 a = fr_pair_list_head(&dst->children);
1057 an = fr_pair_list_next(&dst->children, a);
1058 b = fr_pair_list_head(src);
1059 bn = fr_pair_list_next(src, b);
1060
1061 while (true) {
1062 int rcode;
1063
1064 /*
1065 * B is done, so we stop processing.
1066 */
1067 if (!b) break;
1068
1069 /*
1070 * A is done, so we can add in B at the end of A.
1071 */
1072 if (!a) {
1073 COPY(b);
1074
1075 if (fr_edit_list_insert_pair_tail(el, &dst->children, c) < 0) {
1076 return -1;
1077 }
1078
1079 NEXT_B;
1080 continue;
1081 }
1082
1083 /*
1084 * Compare the da's
1085 */
1086 rcode = fr_pair_cmp_by_parent_num(a, b);
1087
1088 /*
1089 * We've seen things in A which aren't in B, so
1090 * we just increment A.
1091 */
1092 if (rcode < 0) {
1093 NEXT_A;
1094 continue;
1095 }
1096
1097 /*
1098 * a > b
1099 *
1100 * This means that in the ordered set, the
1101 * equivalent to B does not exist. So we copy B
1102 * to after A.
1103 */
1104 if (rcode > 0) {
1105 COPY(b);
1106
1107 if (fr_edit_list_insert_pair_after(el, &dst->children, a, c) < 0) {
1108 return -1;
1109 }
1110
1111 NEXT_B;
1112 continue;
1113 }
1114
1115 fr_assert(rcode == 0);
1116
1117 /*
1118 * They're the same.
1119 */
1120 fr_assert(a->da == b->da);
1121
1122 /*
1123 * Union lists recursively.
1124 *
1125 * Note that this doesn't mean copying both VPs! We just merge their contents.
1126 */
1127 if (fr_type_is_structural(a->vp_type)) {
1128 rcode = list_union(el, a, &b->children, copy);
1129 if (rcode < 0) return rcode;
1130
1131 NEXT_A;
1132 NEXT_B;
1133 continue;
1134 }
1135
1136 /*
1137 * Process all identical attributes, but by
1138 * value. If the value is the same, we keep only
1139 * one. If the values are different, we keep
1140 * both.
1141 */
1142 while (a && b && (a->da == b->da)) {
1143 /*
1144 * Check if the values are the same. This
1145 * returns 0 for "equal", and non-zero for
1146 * anything else.
1147 */
1148 rcode = fr_value_box_cmp(&a->data, &b->data);
1149 if (rcode != 0) {
1150 COPY(b);
1151
1152 if (fr_edit_list_insert_pair_after(el, &dst->children, a, c) < 0) {
1153 return -1;
1154 }
1155 }
1156
1157 NEXT_A;
1158 NEXT_B;
1159 }
1160 }
1161
1162 return 0;
1163}
1164
1165/** A MERGE B
1166 *
1167 * with priority to A
1168 */
1169static int list_merge_lhs(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
1170{
1171 fr_pair_t *a, *an;
1172 fr_pair_t *b, *bn;
1173 fr_pair_t *c;
1174
1177
1178 PAIR_LIST_VERIFY(&dst->children);
1179 PAIR_LIST_VERIFY(src);
1180
1181 a = fr_pair_list_head(&dst->children);
1182 an = fr_pair_list_next(&dst->children, a);
1183 b = fr_pair_list_head(src);
1184 bn = fr_pair_list_next(src, b);
1185
1186 while (true) {
1187 int rcode;
1188
1189 /*
1190 * B is done, so we stop processing.
1191 */
1192 if (!b) break;
1193
1194 /*
1195 * A is done, so we can add in B at the end of A.
1196 */
1197 if (!a) {
1198 COPY(b);
1199
1200 if (fr_edit_list_insert_pair_tail(el, &dst->children, c) < 0) {
1201 return -1;
1202 }
1203
1204 NEXT_B;
1205 continue;
1206 }
1207
1208 /*
1209 * Compare the da's
1210 */
1211 rcode = fr_pair_cmp_by_parent_num(a, b);
1212
1213 /*
1214 * We've seen things in A which aren't in B, so
1215 * we just increment A.
1216 */
1217 if (rcode < 0) {
1218 NEXT_A;
1219 continue;
1220 }
1221
1222 /*
1223 * a > b
1224 *
1225 * This means that in the ordered set, the
1226 * equivalent to B does not exist. So we copy B
1227 * to before A.
1228 */
1229 if (rcode > 0) {
1230 COPY(b);
1231
1232 if (fr_edit_list_insert_pair_before(el, &dst->children, a, c) < 0) {
1233 return -1;
1234 }
1235
1236 NEXT_B;
1237 continue;
1238 }
1239
1240 fr_assert(rcode == 0);
1241
1242 /*
1243 * They're the same.
1244 */
1245 fr_assert(a->da == b->da);
1246
1247 /*
1248 * Merge lists recursively.
1249 */
1250 if (fr_type_is_structural(a->vp_type)) {
1251 rcode = list_merge_lhs(el, a, &b->children, copy);
1252 if (rcode < 0) return rcode;
1253
1254 goto next_both;
1255 }
1256
1257 /*
1258 * We have both A and B, so we prefer A, which means just skipping B.
1259 */
1260
1261 next_both:
1262 NEXT_A;
1263 NEXT_B;
1264 }
1265
1266 return 0;
1267}
1268
1269/** A MERGE B
1270 *
1271 * with priority to B.
1272 */
1273static int list_merge_rhs(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
1274{
1275 fr_pair_t *a, *an;
1276 fr_pair_t *b, *bn;
1277 fr_pair_t *c;
1278
1281
1282 PAIR_LIST_VERIFY(&dst->children);
1283 PAIR_LIST_VERIFY(src);
1284
1285 a = fr_pair_list_head(&dst->children);
1286 an = fr_pair_list_next(&dst->children, a);
1287 b = fr_pair_list_head(src);
1288 bn = fr_pair_list_next(src, b);
1289
1290 while (true) {
1291 int rcode;
1292
1293 /*
1294 * B is done, so we stop processing.
1295 */
1296 if (!b) break;
1297
1298 /*
1299 * A is done, so we can in B at the end of A.
1300 */
1301 if (!a) {
1302 COPY(b);
1303
1304 if (fr_edit_list_insert_pair_tail(el, &dst->children, c) < 0) {
1305 return -1;
1306 }
1307
1308 NEXT_B;
1309 continue;
1310 }
1311
1312 /*
1313 * Compare the da's
1314 */
1315 rcode = fr_pair_cmp_by_parent_num(a, b);
1316
1317 /*
1318 * We've seen things in A which aren't in B, so
1319 * we just increment A.
1320 */
1321 if (rcode < 0) {
1322 NEXT_A;
1323 continue;
1324 }
1325
1326 /*
1327 * a > b
1328 *
1329 * This means that in the ordered set, the
1330 * equivalent to B does not exist. So we copy B
1331 * to before A.
1332 */
1333 if (rcode > 0) {
1334 COPY(b);
1335
1336 if (fr_edit_list_insert_pair_before(el, &dst->children, a, c) < 0) {
1337 return -1;
1338 }
1339
1340 NEXT_B;
1341 continue;
1342 }
1343
1344 fr_assert(rcode == 0);
1345
1346 /*
1347 * They're the same.
1348 */
1349 fr_assert(a->da == b->da);
1350
1351 /*
1352 * Merge lists recursively.
1353 */
1354 if (fr_type_is_structural(a->vp_type)) {
1355 rcode = list_merge_rhs(el, a, &b->children, copy);
1356 if (rcode < 0) return rcode;
1357
1358 goto next_both;
1359 }
1360
1361 /*
1362 * We have both A and B, so we prefer B.
1363 */
1364 COPY(b);
1365 if (fr_edit_list_replace_pair(el, &dst->children, a, c) < 0) {
1366 return -1;
1367 }
1368
1369 next_both:
1370 NEXT_A;
1371 NEXT_B;
1372 }
1373
1374 return 0;
1375}
1376
1377/** A INTERSECTION B
1378 *
1379 */
1381{
1382 fr_pair_t *a, *an;
1383 fr_pair_t *b, *bn;
1384
1385 /*
1386 * Prevent people from doing stupid things.
1387 */
1388 if (dst->vp_type == FR_TYPE_STRUCT) {
1389 fr_strerror_printf("Cannot take intersection of STRUCT data types, it would break the structure");
1390 return -1;
1391 }
1392
1395
1396 a = fr_pair_list_head(&dst->children);
1397 an = fr_pair_list_next(&dst->children, a);
1398 b = fr_pair_list_head(src);
1399 bn = fr_pair_list_next(src, b);
1400
1401 while (true) {
1402 int rcode;
1403
1404 /*
1405 * A is done, so we can return. We don't need to
1406 * delete everything from B, as that will be
1407 * cleaned up by the caller when we exit.
1408 */
1409 if (!a) break;
1410
1411 /*
1412 * B is done, so we delete everything else in A.
1413 */
1414 if (!b) {
1415 delete_a:
1416 if (fr_edit_list_pair_delete(el, &dst->children, a) < 0) return -1;
1417 NEXT_A;
1418 continue;
1419 }
1420
1421 /*
1422 * Compare the da's
1423 */
1424 rcode = fr_pair_cmp_by_parent_num(a, b);
1425
1426 /*
1427 * a < b
1428 *
1429 * A gets removed.
1430 */
1431 if (rcode < 0) goto delete_a;
1432
1433 /*
1434 * a > b
1435 *
1436 * Skip forward in B until we have it better matching A.
1437 */
1438 if (rcode > 0) {
1439 NEXT_B;
1440 continue;
1441 }
1442
1443 fr_assert(rcode == 0);
1444
1445 /*
1446 * INTERSECT the children, and then leave A
1447 * alone, unless it's empty, in which case A
1448 * INTERSECT B is empty, so we also delete A.
1449 */
1450 if (fr_type_is_structural(a->vp_type)) {
1451 rcode = list_intersection(el, a, &b->children);
1452 if (rcode < 0) return rcode;
1453
1454 NEXT_B;
1455
1456 if (fr_pair_list_empty(&a->children)) goto delete_a;
1457
1458 NEXT_A;
1459 continue;
1460 }
1461
1462 /*
1463 * Process all identical attributes, but by
1464 * value.
1465 */
1466 while (a && b && (a->da == b->da)) {
1467 /*
1468 * Check if the values are the same. This
1469 * returns 0 for "equal", and non-zero for
1470 * anything else.
1471 */
1472 rcode = fr_value_box_cmp(&a->data, &b->data);
1473 if (rcode != 0) {
1474 if (fr_edit_list_pair_delete(el, &dst->children, a) < 0) return -1;
1475 }
1476
1477 NEXT_A;
1478 NEXT_B;
1479 }
1480 }
1481
1482 return 0;
1483}
1484
1485
1486/** Apply operators to lists.
1487 *
1488 * = is "if found vp, do nothing. Otherwise call fr_edit_list_insert_pair_tail()
1489 *
1490 * The src list is sorted, but is otherwise not modified.
1491 */
1493{
1494 fr_pair_list_t list;
1495
1496 if (!fr_type_is_structural(dst->vp_type)) {
1497 fr_strerror_printf("Cannot perform list assignment to non-structural type '%s'",
1498 fr_type_to_str(dst->vp_type));
1499 return -1;
1500 }
1501
1502#undef COPY
1503#define COPY do { if (copy) { \
1504 fr_pair_list_init(&list); \
1505 if (fr_pair_list_copy(dst, &list, src) < 0) return -1;\
1506 src = &list; \
1507 } else { \
1508 fr_pair_list_steal(dst, src); \
1509 } \
1510 } while (0)
1511
1512
1513 switch (op) {
1514 /*
1515 * Over-ride existing value (i.e. children) with
1516 * new list.
1517 */
1518 case T_OP_SET:
1519 if (&dst->children == src) return 0; /* A := A == A */
1520
1521 if (fr_edit_list_free_pair_children(el, dst) < 0) return -1;
1523
1524 case T_OP_ADD_EQ:
1525 if (&dst->children == src) {
1526 fr_strerror_printf("Cannot append list to itself");
1527 return -1;
1528 }
1529
1530 COPY;
1531 return fr_edit_list_insert_list_tail(el, &dst->children, src);
1532
1533 case T_OP_SUB_EQ:
1534 /*
1535 * foo -= foo --> {}
1536 */
1537 if (&dst->children == src) {
1538 fr_pair_t *vp;
1539
1540 while ((vp = fr_pair_list_head(&dst->children)) != NULL) {
1541 if (fr_edit_list_pair_delete(el, &dst->children, vp) < 0) return -1;
1542 }
1543
1544 return 0;
1545 }
1546
1547 return fr_edit_list_delete_list(el, &dst->children, src);
1548
1549 case T_OP_PREPEND:
1550 if (&dst->children == src) {
1551 fr_strerror_printf("Cannot prepend list to itself");
1552 return -1;
1553 }
1554
1555 COPY;
1556 return fr_edit_list_insert_list_head(el, &dst->children, src);
1557
1558 case T_OP_AND_EQ:
1559 if (&dst->children == src) return 0; /* A INTERSECTION A == A */
1560
1561 if (!fr_edit_list_empty(el)) {
1562 not_empty:
1563 fr_strerror_printf("Failed to perform %s - undo list is not empty", fr_tokens[op]);
1564 return -1;
1565 }
1566
1567 return list_intersection(el, dst, src);
1568
1569 case T_OP_OR_EQ:
1570 if (&dst->children == src) return 0; /* A UNION A == A */
1571
1572 if (!fr_edit_list_empty(el)) goto not_empty;
1573
1574 return list_union(el, dst, src, copy);
1575
1576 case T_OP_GE:
1577 if (&dst->children == src) return 0; /* A MERGE A == A */
1578
1579 if (!fr_edit_list_empty(el)) goto not_empty;
1580
1581 return list_merge_lhs(el, dst, src, copy);
1582
1583 case T_OP_LE:
1584 if (&dst->children == src) return 0; /* A MERGE A == A */
1585
1586 if (!fr_edit_list_empty(el)) goto not_empty;
1587
1588 return list_merge_rhs(el, dst, src, copy);
1589
1590 default:
1591 break;
1592 }
1593
1594 fr_strerror_printf("Invalid assignment operator %s for destination type %s",
1595 fr_tokens[op],
1596 fr_type_to_str(dst->vp_type));
1597 return -1;
1598}
#define RCSID(id)
Definition build.h:485
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
int fr_value_calc_assignment_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_token_t op, fr_value_box_t const *src)
Calculate DST OP SRC.
Definition calc.c:2420
int fr_value_calc_binary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
Calculate DST = A OP B.
Definition calc.c:1924
static fr_slen_t in
Definition dict.h:831
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:260
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:486
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:94
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:501
static void * fr_dlist_pop_tail(fr_dlist_head_t *list_head)
Remove the tail item in a list.
Definition dlist.h:688
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:378
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(reap)
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_BOOL
A truth value.
int8_t fr_pair_cmp_by_parent_num(void const *a, void const *b)
Order attributes by their parent(s), attribute number, and tag.
Definition pair.c:1923
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:695
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1347
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition pair.c:1691
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
bool fr_pair_immutable(fr_pair_t const *vp)
Definition pair.c:2282
int fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list and free.
Definition pair.c:1828
int fr_pair_insert_after(fr_pair_list_t *list, fr_pair_t *pos, fr_pair_t *to_add)
Add a VP after another VP.
Definition pair.c:1375
int fr_pair_prepend(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the start of the list.
Definition pair.c:1316
#define fr_assert(_expr)
Definition rad_assert.h:38
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define talloc_zero_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition talloc.h:177
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:78
enum fr_token fr_token_t
@ T_OP_SUB_EQ
Definition token.h:70
@ T_OP_AND_EQ
Definition token.h:74
@ T_OP_SET
Definition token.h:84
@ T_OP_ADD_EQ
Definition token.h:69
@ T_OP_LE
Definition token.h:100
@ T_OP_GE
Definition token.h:98
@ T_OP_GT
Definition token.h:99
@ T_OP_OR_EQ
Definition token.h:73
@ T_OP_LT
Definition token.h:101
@ T_OP_PREPEND
Definition token.h:85
static fr_event_list_t * el
int fr_edit_list_apply_list_assignment(fr_edit_list_t *el, fr_pair_t *dst, fr_token_t op, fr_pair_list_t *src, bool copy)
Apply operators to lists.
Definition edit.c:1492
int fr_edit_list_pair_delete(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_t *vp)
Delete a VP.
Definition edit.c:599
int fr_edit_list_replace_pair_value(fr_edit_list_t *el, fr_pair_t *vp, fr_value_box_t *box)
Write a new value to the fr_value_box_t.
Definition edit.c:650
static int edit_undo(fr_edit_t *e)
Undo one particular edit.
Definition edit.c:133
void fr_edit_list_commit(fr_edit_list_t *el)
Commit an edit list.
Definition edit.c:846
fr_edit_op_t op
edit operation to perform
Definition edit.c:80
fr_edit_list_t * parent
for nested transactions
Definition edit.c:116
int fr_edit_list_insert_pair_after(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_t *pos, fr_pair_t *vp)
Insert a new VP after an existing one.
Definition edit.c:586
#define NEXT_B
Definition edit.c:1024
fr_dlist_t entry
linked list of edits
Definition edit.c:81
int fr_edit_list_save_pair_value(fr_edit_list_t *el, fr_pair_t *vp)
Record the value of a leaf fr_value_box_t.
Definition edit.c:637
#define COPY(_x)
Definition edit.c:1014
static int _edit_list_destructor(fr_edit_list_t *el)
Finalize the edits when we destroy the edit list.
Definition edit.c:741
static int list_merge_lhs(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
A MERGE B.
Definition edit.c:1169
static int edit_record(fr_edit_list_t *el, fr_edit_op_t op, fr_pair_t *vp, fr_pair_list_t *list, fr_pair_t *ref)
Record one particular edit.
Definition edit.c:247
static int fr_edit_list_delete_list(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_list_t *to_remove)
Removes elements matching a list.
Definition edit.c:936
fr_dlist_head_t undo
Definition edit.c:106
int fr_edit_list_insert_list_after(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_t *pos, fr_pair_list_t *to_insert)
Notes.
Definition edit.c:893
fr_dlist_t entry
Definition edit.c:121
void fr_edit_list_abort(fr_edit_list_t *el)
Abort the entries in an edit list.
Definition edit.c:194
int fr_edit_list_replace_pair(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_t *to_replace, fr_pair_t *vp)
Replace a pair with another one.
Definition edit.c:668
static int list_intersection(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src)
A INTERSECTION B.
Definition edit.c:1380
static int list_merge_rhs(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
A MERGE B.
Definition edit.c:1273
fr_pair_t * vp
pair edited, deleted, or inserted
Definition edit.c:83
fr_dlist_head_t ignore
lists to ignore
Definition edit.c:108
fr_pair_list_t deleted_pairs
Definition edit.c:114
static bool fr_edit_list_empty(fr_edit_list_t *el)
Definition edit.c:126
fr_pair_list_t * list
list to ignore (never dereferenced)
Definition edit.c:122
fr_edit_op_t
Definition edit.c:55
@ FR_EDIT_CHILD
child edit list
Definition edit.c:61
@ FR_EDIT_INVALID
Definition edit.c:56
@ FR_EDIT_CLEAR
clear the children of a structural entry.
Definition edit.c:59
@ FR_EDIT_DELETE
delete a VP
Definition edit.c:57
@ FR_EDIT_VALUE
edit a VP in place
Definition edit.c:58
@ FR_EDIT_INSERT
insert a VP into a list, after another one.
Definition edit.c:60
#define NEXT_A
Definition edit.c:1023
int fr_edit_list_free_pair_children(fr_edit_list_t *el, fr_pair_t *vp)
Free children of a structural pair.
Definition edit.c:714
int fr_edit_list_apply_pair_assignment(fr_edit_list_t *el, fr_pair_t *vp, fr_token_t op, fr_value_box_t const *in)
Apply operators to pairs.
Definition edit.c:981
static int list_union(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
A UNION B.
Definition edit.c:1030
int fr_edit_list_pair_delete_by_da(fr_edit_list_t *el, fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete VPs with a matching da.
Definition edit.c:613
fr_edit_t * e
so we don't have to loop over parent edits on abort
Definition edit.c:117
fr_edit_list_t * fr_edit_list_alloc(TALLOC_CTX *ctx, int hint, fr_edit_list_t *parent)
Allocate an edit list.
Definition edit.c:795
Track a series of edits.
Definition edit.c:102
Track one particular edit.
Definition edit.c:79
Structures and prototypes for editing lists.
#define fr_edit_list_insert_pair_tail(_el, _list, _vp)
Definition edit.h:51
#define fr_edit_list_insert_list_tail(_el, _list, _to_insert)
Definition edit.h:74
#define fr_edit_list_insert_pair_before(_el, _list, _pos, _vp)
Definition edit.h:45
#define fr_edit_list_insert_list_head(_el, _list, _to_insert)
Definition edit.h:72
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define PAIR_VERIFY(_x)
Definition pair.h:191
void fr_pair_list_sort(fr_pair_list_t *list, fr_cmp_t cmp)
Sort a doubly linked list of fr_pair_ts using merge sort.
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:70
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:94
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:261
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
#define PAIR_LIST_VERIFY(_x)
Definition pair.h:194
fr_pair_t * fr_pair_list_prev(fr_pair_list_t const *list, fr_pair_t const *item))
Get the previous item in a valuepair list before a specific entry.
Definition pair_inline.c:83
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:43
static fr_slen_t parent
Definition pair.h:845
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_type_is_structural(_x)
Definition types.h:371
#define fr_type_is_fixed_size(_x)
Definition types.h:366
#define fr_type_is_leaf(_x)
Definition types.h:372
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3370
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:676
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:3759
int fr_value_box_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two attributes using an operator.
Definition value.c:929
void fr_value_box_copy_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *src)
Perform a shallow copy of a value_box.
Definition value.c:3864
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:3700
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:3742
static fr_slen_t data
Definition value.h:1274
#define fr_value_box_init_null(_vb)
Initialise an empty/null box that will be filled later.
Definition value.h:604