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