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