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