Coverage Report

Created: 2026-08-14 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/edit.c
Line
Count
Source
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
48
RCSID("$Id: 587257da8817ce748e8400e69e4f0351752f73f9 $")
49
50
#include <freeradius-devel/util/value.h>
51
#include "edit.h"
52
#include "calc.h"
53
54
typedef enum {
55
  FR_EDIT_INVALID = 0,
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
61
} fr_edit_op_t;
62
63
#if 0
64
/*
65
 *  For debugging.
66
 */
67
static 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
 */
78
typedef 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
 */
101
struct fr_edit_list_s {
102
  /*
103
   *  List of undo changes to be made, in order.
104
   */
105
  fr_dlist_head_t undo;
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
   */
113
  fr_pair_list_t  deleted_pairs;
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
119
typedef struct {
120
  fr_dlist_t  entry;
121
  fr_pair_list_t  *list;    //!< list to ignore (never dereferenced)
122
} fr_edit_ignore_t;
123
124
125
static bool fr_edit_list_empty(fr_edit_list_t *el)
126
0
{
127
0
  return fr_dlist_empty(&el->undo) && fr_dlist_empty(&el->ignore) && fr_pair_list_empty(&el->deleted_pairs);
128
0
}
129
130
/** Undo one particular edit.
131
 */
132
static int edit_undo(fr_edit_t *e)
133
0
{
134
0
  fr_pair_t *vp = e->vp;
135
0
#ifndef NDEBUG
136
0
  int rcode;
137
0
#endif
138
139
  /*
140
   *  FR_EDIT_CHILD has vp == NULL, handle it
141
   *  before the vp assertions.
142
   */
143
0
  if (e->op == FR_EDIT_CHILD) {
144
0
    fr_edit_list_abort(e->child_edit);
145
0
    return 0;
146
0
  }
147
148
0
  fr_assert(vp != NULL);
149
0
  PAIR_VERIFY(vp);
150
151
0
  switch (e->op) {
152
0
  case FR_EDIT_INVALID:
153
0
    return -1;
154
155
0
  case FR_EDIT_VALUE:
156
0
    fr_assert(fr_type_is_leaf(vp->vp_type));
157
0
    if (!fr_type_is_fixed_size(vp->vp_type)) fr_value_box_clear(&vp->data);
158
0
    if (unlikely(fr_value_box_copy(vp, &vp->data, &e->data) < 0)) return -1;
159
0
    break;
160
161
0
  case FR_EDIT_CLEAR:
162
0
    fr_assert(fr_type_is_structural(vp->vp_type));
163
164
0
    fr_pair_list_free(&vp->vp_group);
165
0
    fr_pair_list_append(&vp->vp_group, &e->children);
166
0
    break;
167
168
0
  case FR_EDIT_DELETE:
169
0
    fr_assert(e->list != NULL);
170
0
#ifndef NDEBUG
171
0
    rcode =
172
0
#endif
173
0
    fr_pair_insert_after(e->list, e->ref, vp);
174
0
    fr_assert(rcode == 0);
175
0
    break;
176
177
0
  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
0
    fr_pair_delete(e->list, vp);
185
0
    break;
186
187
0
  case FR_EDIT_CHILD:
188
0
    fr_assert(0);  /* handled above */
189
0
    return -1;
190
0
  }
191
192
0
  return 0;
193
0
}
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
 */
202
void fr_edit_list_abort(fr_edit_list_t *el)
203
0
{
204
0
  fr_edit_t *e;
205
206
0
  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
0
  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
0
  while ((e = fr_dlist_pop_tail(&el->undo)) != NULL) {
222
0
    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
0
  }
229
230
  /*
231
   *  There's a parent, we remove ourselves from the parent undo list.
232
   */
233
0
  if (el->parent) {
234
0
    fr_dlist_remove(&el->parent->undo, el->e);
235
0
    talloc_free(el->e);
236
0
  }
237
238
0
  talloc_free(el);
239
0
}
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
 */
255
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)
256
0
{
257
0
  fr_edit_t *e;
258
259
0
  fr_assert(el != NULL);
260
0
  fr_assert(vp != NULL);
261
262
0
  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
0
  fr_dlist_foreach(&el->ignore, fr_edit_ignore_t, i) {
273
0
    if (i->list != list) continue;
274
275
0
    switch (op) {
276
      /*
277
       *  No need to save the value.
278
       */
279
0
    case FR_EDIT_VALUE:
280
0
      if (fr_pair_immutable(vp)) {
281
0
        fr_strerror_printf("Cannot modify immutable value for %s", vp->da->name);
282
0
        return -1;
283
0
      }
284
0
      return 0;
285
286
      /*
287
       *  No need to save the value.
288
       */
289
0
    case FR_EDIT_DELETE:
290
      /*
291
       *  We silently refuse to delete immutable attributes.
292
       */
293
0
      if (fr_pair_immutable(vp)) return 0;
294
295
0
      if (vp->vp_edit) return 0;
296
297
0
      fr_pair_remove(list, vp);
298
0
      return 0;
299
300
      /*
301
       *  Delete all of the children.
302
       */
303
0
    case FR_EDIT_CLEAR:
304
0
      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
0
      fr_pair_list_free(&vp->vp_group);
314
0
      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
0
    case FR_EDIT_INSERT:
322
0
      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
0
      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
0
      goto insert_ignore;
335
336
0
    default:
337
0
      return -1;
338
0
    }
339
0
  }
340
341
  /*
342
   *  Catch NOOPs
343
   */
344
0
  if (op == FR_EDIT_CLEAR) {
345
0
    fr_assert(fr_type_is_structural(vp->vp_type));
346
347
0
    if (fr_pair_list_empty(&vp->vp_group)) return 0;
348
0
  }
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
0
  for (e = fr_dlist_head(&el->undo);
359
0
       e != NULL;
360
0
       e = fr_dlist_next(&el->undo, e)) {
361
0
    fr_assert(e->vp != NULL);
362
363
0
    if (e->vp != vp) continue;
364
365
0
    switch (op) {
366
0
    case FR_EDIT_INVALID:
367
0
      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
0
    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
                         */
395
0
      fr_assert(e->op != FR_EDIT_DELETE);
396
0
      fr_assert(fr_type_is_leaf(vp->vp_type));
397
0
      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
0
    case FR_EDIT_INSERT:
407
0
      fr_assert(0);
408
0
      return -1;
409
410
0
    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
       */
424
0
      fr_assert(e->op == FR_EDIT_INSERT);
425
0
      fr_assert(fr_type_is_structural(vp->vp_type));
426
427
0
      fr_pair_list_append(&el->deleted_pairs, &vp->vp_group);
428
0
      break;
429
430
      /*
431
       *  We're being asked to delete something
432
       *  we previously inserted, or previously
433
       *  edited.
434
       */
435
0
    case FR_EDIT_DELETE:
436
      /*
437
       *  We can't delete something which was
438
       *  already deleted.
439
       */
440
0
      fr_assert(e->op != FR_EDIT_DELETE);
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
0
      if (e->op == FR_EDIT_INSERT) {
455
0
        fr_assert(e->list == list);
456
457
0
        fr_pair_remove(list, vp);
458
0
        fr_pair_append(&el->deleted_pairs, vp);
459
460
0
        fr_dlist_remove(&el->undo, e);
461
0
        talloc_free(e);
462
0
        return 0;
463
0
      }
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
0
      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
0
      e->op = FR_EDIT_DELETE;
488
0
      fr_value_box_clear(&e->data);
489
0
      fr_dlist_remove(&el->undo, e);
490
0
      goto delete;
491
492
0
    case FR_EDIT_CHILD: /* e->vp==NULL, so this should never happen */
493
0
      fr_assert(0);
494
0
      return -1;
495
0
    }
496
0
  } /* loop over existing edits */
497
498
  /*
499
   *  No edit for this pair exists.  Create a new edit entry.
500
   */
501
0
  e = talloc_zero(el, fr_edit_t);
502
0
  if (!e) return -1;
503
504
0
  e->op = op;
505
0
  e->vp = vp;
506
0
  fr_value_box_init_null(&e->data);
507
508
0
  switch (op) {
509
0
  case FR_EDIT_INVALID:
510
0
  case FR_EDIT_CHILD:
511
0
  fail:
512
0
    talloc_free(e);
513
0
    return -1;
514
515
0
  case FR_EDIT_VALUE:
516
0
    fr_assert(list == NULL);
517
0
    fr_assert(ref == NULL);
518
519
0
    fr_assert(fr_type_is_leaf(vp->vp_type));
520
0
    if (unlikely(fr_value_box_copy(e, &e->data, &vp->data) < 0)) goto fail;
521
0
    break;
522
523
0
  case FR_EDIT_CLEAR:
524
0
    fr_assert(list == NULL);
525
0
    fr_assert(ref == NULL);
526
527
0
    fr_assert(fr_type_is_structural(vp->vp_type));
528
0
    fr_pair_list_init(&e->children);
529
0
    fr_pair_list_append(&e->children, &vp->vp_group);
530
0
    break;
531
532
0
  case FR_EDIT_INSERT:
533
0
    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
0
    e->list = list;
540
0
    if (fr_pair_insert_after(list, ref, vp) < 0) goto fail;
541
0
    break;
542
543
0
  case FR_EDIT_DELETE:
544
0
  delete:
545
    /*
546
     *  We silently refuse to delete immutable attributes.
547
     */
548
0
    if (fr_pair_immutable(e->vp)) {
549
0
      talloc_free(e);
550
0
      return 0;
551
0
    }
552
553
0
    if (e->vp->vp_edit) {
554
0
      talloc_free(e);
555
0
      return 0;
556
0
    }
557
558
0
    fr_assert(list != NULL);
559
0
    fr_assert(ref == NULL);
560
561
0
    e->list = list;
562
0
    e->ref = fr_pair_list_prev(list, vp);
563
564
0
    fr_pair_remove(list, vp);
565
0
    break;
566
0
  }
567
568
0
  fr_dlist_insert_tail(&el->undo, e);
569
570
  /*
571
   *  Insert an "ignore" entry.
572
   */
573
0
  if ((op == FR_EDIT_INSERT) && fr_type_is_structural(vp->vp_type)) {
574
0
    fr_edit_ignore_t *i;
575
576
0
  insert_ignore:
577
0
    i = talloc_zero(el, fr_edit_ignore_t);
578
0
    if (!i) return -1;
579
580
0
    i->list = &vp->vp_group;
581
0
    fr_dlist_insert_tail(&el->ignore, i);
582
0
  }
583
584
0
  return 0;
585
0
}
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
 */
595
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)
596
0
{
597
0
  if (!el) return fr_pair_insert_after(list, pos, vp);
598
599
0
  return edit_record(el, FR_EDIT_INSERT, vp, list, pos);
600
0
}
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
 */
608
int fr_edit_list_pair_delete(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_t *vp)
609
0
{
610
0
  if (!el) {
611
0
    fr_pair_delete(list, vp);
612
0
    return 0;
613
0
  }
614
615
0
  return edit_record(el, FR_EDIT_DELETE, vp, list, NULL);
616
0
}
617
618
/** Delete VPs with a matching da
619
 *
620
 *  This function mirrors fr_pair_delete_by_da()
621
 */
622
int fr_edit_list_pair_delete_by_da(fr_edit_list_t *el, fr_pair_list_t *list, fr_dict_attr_t const *da)
623
0
{
624
0
  if (!el) {
625
0
    fr_pair_delete_by_da(list, da);
626
0
    return 0;
627
0
  }
628
629
  /*
630
   *  Delete all VPs with a matching da.
631
   */
632
0
  fr_pair_list_foreach(list, vp) {
633
0
    if (vp->da != da) continue;
634
635
0
    if (edit_record(el, FR_EDIT_DELETE, vp, list, NULL) < 0) return -1;
636
0
  }
637
638
0
  return 0;
639
0
}
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
 */
646
int fr_edit_list_save_pair_value(fr_edit_list_t *el, fr_pair_t *vp)
647
0
{
648
0
  if (!el) return 0;
649
650
0
  if (!fr_type_is_leaf(vp->vp_type)) return -1;
651
652
0
  return edit_record(el, FR_EDIT_VALUE, vp, NULL, NULL);
653
0
}
654
655
/** Write a new value to the #fr_value_box_t
656
 *
657
 *  After this function returns, the value has been updated.
658
 */
659
int fr_edit_list_replace_pair_value(fr_edit_list_t *el, fr_pair_t *vp, fr_value_box_t *box)
660
0
{
661
0
  if (!fr_type_is_leaf(vp->vp_type)) return -1;
662
663
0
  if (el && (edit_record(el, FR_EDIT_VALUE, vp, NULL, NULL) < 0)) return -1;
664
665
0
  if (!fr_type_is_fixed_size(vp->vp_type)) fr_value_box_clear(&vp->data);
666
0
  fr_value_box_copy_shallow(NULL, &vp->data, box);
667
0
  return 0;
668
0
}
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
 */
677
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)
678
0
{
679
0
  if (to_replace->da != vp->da) return -1;
680
681
0
  if (!el) {
682
0
    if (fr_pair_insert_after(list, to_replace, vp) < 0) return -1;
683
0
    fr_pair_delete(list, to_replace);
684
0
    return 0;
685
0
  }
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
0
  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
0
  if (edit_record(el, FR_EDIT_DELETE, to_replace, list, NULL) < 0) {
704
0
    fr_edit_t *e = fr_dlist_pop_tail(&el->undo);
705
706
0
    fr_assert(e != NULL);
707
0
    fr_assert(e->vp == vp);
708
0
    talloc_free(e);
709
0
    return -1;
710
0
  }
711
712
0
  return 0;
713
0
}
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
 */
723
int fr_edit_list_free_pair_children(fr_edit_list_t *el, fr_pair_t *vp)
724
0
{
725
0
  if (!fr_type_is_structural(vp->vp_type)) return -1;
726
727
0
  if (!el) {
728
0
    fr_pair_list_free(&vp->children);
729
0
    return 0;
730
0
  }
731
732
  /*
733
   *  No children == do nothing.
734
   */
735
0
  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
0
  return edit_record(el, FR_EDIT_CLEAR, vp, NULL, NULL);
743
0
}
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
 */
750
static int _edit_list_destructor(fr_edit_list_t *el)
751
0
{
752
0
  fr_edit_t *e;
753
754
0
  fr_assert(el != NULL);
755
756
0
  for (e = fr_dlist_head(&el->undo);
757
0
       e != NULL;
758
0
       e = fr_dlist_next(&el->undo, e)) {
759
0
    switch (e->op) {
760
0
    case FR_EDIT_INVALID:
761
0
      fr_assert(0);
762
0
      break;
763
764
0
    case FR_EDIT_INSERT:
765
0
      break;
766
767
0
    case FR_EDIT_DELETE:
768
0
      fr_assert(e->vp != NULL);
769
0
      talloc_free(e->vp);
770
0
      break;
771
772
0
    case FR_EDIT_CLEAR:
773
0
      fr_pair_list_free(&e->children);
774
0
      break;
775
776
0
    case FR_EDIT_VALUE:
777
0
      fr_assert(fr_type_is_leaf(e->vp->vp_type));
778
0
      fr_value_box_clear(&e->data);
779
0
      break;
780
781
0
    case FR_EDIT_CHILD:
782
0
      talloc_free(e->child_edit);
783
0
      break;
784
0
    }
785
0
  }
786
787
0
  fr_pair_list_free(&el->deleted_pairs);
788
789
0
  return 0;
790
0
}
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
 */
802
fr_edit_list_t *fr_edit_list_alloc(TALLOC_CTX *ctx, int hint, fr_edit_list_t *parent)
803
0
{
804
0
  fr_edit_list_t *el;
805
0
  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
0
  if (parent) ctx = parent;
813
814
0
  el = talloc_zero_pooled_object(ctx, fr_edit_list_t, hint, hint * sizeof(fr_edit_t));
815
0
  if (!el) return NULL;
816
817
0
  fr_dlist_init(&el->undo, fr_edit_t, entry);
818
0
  fr_dlist_init(&el->ignore, fr_edit_ignore_t, entry);
819
820
0
  fr_pair_list_init(&el->deleted_pairs);
821
822
0
  talloc_set_destructor(el, _edit_list_destructor);
823
824
0
  el->parent = parent;
825
826
0
  if (!parent) return el;
827
828
0
  e = talloc_zero(parent, fr_edit_t);
829
0
  if (!e) {
830
0
    talloc_free(el);
831
0
    return NULL;
832
0
  }
833
834
  /*
835
   *  Insert the child into the tail of the current edit list.
836
   */
837
0
  e->op = FR_EDIT_CHILD;
838
0
  e->vp = NULL;
839
0
  e->child_edit = el;
840
841
0
  fr_dlist_insert_tail(&parent->undo, e);
842
0
  el->e = e;
843
844
0
  return el;
845
0
}
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
 */
853
void fr_edit_list_commit(fr_edit_list_t *el)
854
0
{
855
0
  if (el->parent) return;
856
857
0
  talloc_free(el);
858
0
}
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
 */
900
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)
901
0
{
902
0
  fr_pair_t *prev, *vp;
903
904
0
  prev = pos;
905
906
0
  if (!el) {
907
    /*
908
     *  @todo - this should really be an O(1) dlist
909
     *  operation.
910
     */
911
0
    while ((vp = fr_pair_list_head(to_insert)) != NULL) {
912
0
      (void) fr_pair_remove(to_insert, vp);
913
0
      (void) fr_pair_insert_after(list, prev, vp);
914
0
      prev = vp;
915
0
    }
916
917
0
    return 0;
918
0
  }
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
0
  while ((vp = fr_pair_list_head(to_insert)) != NULL) {
926
0
    (void) fr_pair_remove(to_insert, vp);
927
928
0
    if (edit_record(el, FR_EDIT_INSERT, vp, list, prev) < 0) {
929
0
      fr_pair_prepend(to_insert, vp); /* don't lose it! */
930
0
      return -1;
931
0
    }
932
933
0
    prev = vp;
934
0
  }
935
936
0
  return 0;
937
0
}
938
939
/** Removes elements matching a list
940
 *
941
 *  O(N^2) unfortunately.
942
 */
943
static int fr_edit_list_delete_list(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_list_t *to_remove)
944
0
{
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
0
  fr_pair_list_foreach(to_remove, vp) {
950
0
    fr_pair_t *found, *next;
951
952
    /*
953
     *  @todo - do this recursively.
954
     */
955
0
    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
0
    for (found = fr_pair_find_by_da(list, NULL, vp->da);
961
0
         found != NULL;
962
0
         found = next) {
963
0
      int rcode;
964
965
0
      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
0
      rcode = fr_value_box_cmp_op(vp->op, &found->data, &vp->data);
971
0
      if (rcode < 0) return -1;
972
973
0
      if (!rcode) continue;
974
975
0
      if (fr_edit_list_pair_delete(el, list, found) < 0) return -1;
976
0
    }
977
0
  }
978
979
0
  return 0;
980
0
}
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
 */
988
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)
989
0
{
990
0
  fr_value_box_t box;
991
992
0
  switch (op) {
993
0
  case T_OP_LE:
994
0
  case T_OP_LT:
995
0
  case T_OP_GT:
996
0
  case T_OP_GE:
997
0
    if (fr_value_calc_binary_op(vp, &box, FR_TYPE_BOOL, &vp->data, op, in) < 0) return -1;
998
999
0
    if (box.vb_bool) return 0;
1000
1001
0
    if (el && (fr_edit_list_save_pair_value(el, vp) < 0)) return -1;
1002
1003
0
    fr_value_box_clear_value(&vp->data);
1004
1005
    /*
1006
     *  The input type may be different, so we can't just copy it.
1007
     */
1008
0
    return fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->data.enumv, in);
1009
1010
0
  default:
1011
0
    break;
1012
1013
0
  }
1014
1015
0
  if (el && (fr_edit_list_save_pair_value(el, vp) < 0)) return -1;
1016
1017
0
  return fr_value_calc_assignment_op(vp, &vp->data, op, in);
1018
0
}
1019
1020
#undef COPY
1021
0
#define COPY(_x) do { if (copy) { \
1022
0
            c = fr_pair_copy(dst, _x); \
1023
0
      if (!c) return -1; \
1024
0
                      } else { \
1025
0
      c = talloc_steal(dst, _x); \
1026
0
      fr_pair_remove(src, c); \
1027
0
          } \
1028
0
     } while (0)
1029
1030
0
#define NEXT_A do { a = an; an = fr_pair_list_next(&dst->children, a); } while (0)
1031
0
#define NEXT_B do { b = bn; bn = fr_pair_list_next(src, b); } while (0)
1032
1033
1034
/** A UNION B
1035
 *
1036
 */
1037
static int list_union(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
1038
0
{
1039
0
  fr_pair_t *a, *an;
1040
0
  fr_pair_t *b, *bn;
1041
0
  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
0
  if (dst->vp_type == FR_TYPE_STRUCT) {
1053
0
    fr_strerror_printf("Cannot take union of STRUCT data types, it would break the structure");
1054
0
    return -1;
1055
0
  }
1056
1057
0
  fr_pair_list_sort(&dst->children, fr_pair_cmp_by_parent_num);
1058
0
  fr_pair_list_sort(src, fr_pair_cmp_by_parent_num);
1059
1060
0
  PAIR_VERIFY(dst);
1061
0
  PAIR_LIST_VERIFY(src);
1062
1063
0
  a = fr_pair_list_head(&dst->children);
1064
0
  an = fr_pair_list_next(&dst->children, a);
1065
0
  b = fr_pair_list_head(src);
1066
0
  bn = fr_pair_list_next(src, b);
1067
1068
0
  while (true) {
1069
0
    int rcode;
1070
1071
    /*
1072
     *  B is done, so we stop processing.
1073
     */
1074
0
    if (!b) break;
1075
1076
    /*
1077
     *  A is done, so we can add in B at the end of A.
1078
     */
1079
0
    if (!a) {
1080
0
      COPY(b);
1081
1082
0
      if (fr_edit_list_insert_pair_tail(el, &dst->children, c) < 0) {
1083
0
        return -1;
1084
0
      }
1085
1086
0
      NEXT_B;
1087
0
      continue;
1088
0
    }
1089
1090
    /*
1091
     *  Compare the da's
1092
     */
1093
0
    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
0
    if (rcode < 0) {
1100
0
      NEXT_A;
1101
0
      continue;
1102
0
    }
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
0
    if (rcode > 0) {
1112
0
      COPY(b);
1113
1114
0
      if (fr_edit_list_insert_pair_before(el, &dst->children, a, c) < 0) {
1115
0
        return -1;
1116
0
      }
1117
1118
0
      NEXT_B;
1119
0
      continue;
1120
0
    }
1121
1122
0
    fr_assert(rcode == 0);
1123
1124
    /*
1125
     *  They're the same.
1126
     */
1127
0
    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
0
    if (fr_type_is_structural(a->vp_type)) {
1135
0
      rcode = list_union(el, a, &b->children, copy);
1136
0
      if (rcode < 0) return rcode;
1137
1138
0
      NEXT_A;
1139
0
      NEXT_B;
1140
0
      continue;
1141
0
    }
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
0
    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
0
      rcode = fr_value_box_cmp(&a->data, &b->data);
1156
0
      if (rcode != 0) {
1157
0
        COPY(b);
1158
1159
0
        if (fr_edit_list_insert_pair_after(el, &dst->children, a, c) < 0) {
1160
0
          return -1;
1161
0
        }
1162
0
      }
1163
1164
0
      NEXT_A;
1165
0
      NEXT_B;
1166
0
    }
1167
0
  }
1168
1169
0
  return 0;
1170
0
}
1171
1172
/** A MERGE B
1173
 *
1174
 * with priority to A
1175
 */
1176
static int list_merge_lhs(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
1177
0
{
1178
0
  fr_pair_t *a, *an;
1179
0
  fr_pair_t *b, *bn;
1180
0
  fr_pair_t *c;
1181
1182
0
  fr_pair_list_sort(&dst->children, fr_pair_cmp_by_parent_num);
1183
0
  fr_pair_list_sort(src, fr_pair_cmp_by_parent_num);
1184
1185
0
  PAIR_VERIFY(dst);
1186
0
  PAIR_LIST_VERIFY(src);
1187
1188
0
  a = fr_pair_list_head(&dst->children);
1189
0
  an = fr_pair_list_next(&dst->children, a);
1190
0
  b = fr_pair_list_head(src);
1191
0
  bn = fr_pair_list_next(src, b);
1192
1193
0
  while (true) {
1194
0
    int rcode;
1195
1196
    /*
1197
     *  B is done, so we stop processing.
1198
     */
1199
0
    if (!b) break;
1200
1201
    /*
1202
     *  A is done, so we can add in B at the end of A.
1203
     */
1204
0
    if (!a) {
1205
0
      COPY(b);
1206
1207
0
      if (fr_edit_list_insert_pair_tail(el, &dst->children, c) < 0) {
1208
0
        return -1;
1209
0
      }
1210
1211
0
      NEXT_B;
1212
0
      continue;
1213
0
    }
1214
1215
    /*
1216
     *  Compare the da's
1217
     */
1218
0
    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
0
    if (rcode < 0) {
1225
0
      NEXT_A;
1226
0
      continue;
1227
0
    }
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
0
    if (rcode > 0) {
1237
0
      COPY(b);
1238
1239
0
      if (fr_edit_list_insert_pair_before(el, &dst->children, a, c) < 0) {
1240
0
        return -1;
1241
0
      }
1242
1243
0
      NEXT_B;
1244
0
      continue;
1245
0
    }
1246
1247
0
    fr_assert(rcode == 0);
1248
1249
    /*
1250
     *  They're the same.
1251
     */
1252
0
    fr_assert(a->da == b->da);
1253
1254
    /*
1255
     *  Merge lists recursively.
1256
     */
1257
0
    if (fr_type_is_structural(a->vp_type)) {
1258
0
      rcode = list_merge_lhs(el, a, &b->children, copy);
1259
0
      if (rcode < 0) return rcode;
1260
1261
0
      goto next_both;
1262
0
    }
1263
1264
    /*
1265
     *  We have both A and B, so we prefer A, which means just skipping B.
1266
     */
1267
1268
0
  next_both:
1269
0
    NEXT_A;
1270
0
    NEXT_B;
1271
0
  }
1272
1273
0
  return 0;
1274
0
}
1275
1276
/** A MERGE B
1277
 *
1278
 * with priority to B.
1279
 */
1280
static int list_merge_rhs(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src, bool copy)
1281
0
{
1282
0
  fr_pair_t *a, *an;
1283
0
  fr_pair_t *b, *bn;
1284
0
  fr_pair_t *c;
1285
1286
0
  fr_pair_list_sort(&dst->children, fr_pair_cmp_by_parent_num);
1287
0
  fr_pair_list_sort(src, fr_pair_cmp_by_parent_num);
1288
1289
0
  PAIR_VERIFY(dst);
1290
0
  PAIR_LIST_VERIFY(src);
1291
1292
0
  a = fr_pair_list_head(&dst->children);
1293
0
  an = fr_pair_list_next(&dst->children, a);
1294
0
  b = fr_pair_list_head(src);
1295
0
  bn = fr_pair_list_next(src, b);
1296
1297
0
  while (true) {
1298
0
    int rcode;
1299
1300
    /*
1301
     *  B is done, so we stop processing.
1302
     */
1303
0
    if (!b) break;
1304
1305
    /*
1306
     *  A is done, so we can in B at the end of A.
1307
     */
1308
0
    if (!a) {
1309
0
      COPY(b);
1310
1311
0
      if (fr_edit_list_insert_pair_tail(el, &dst->children, c) < 0) {
1312
0
        return -1;
1313
0
      }
1314
1315
0
      NEXT_B;
1316
0
      continue;
1317
0
    }
1318
1319
    /*
1320
     *  Compare the da's
1321
     */
1322
0
    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
0
    if (rcode < 0) {
1329
0
      NEXT_A;
1330
0
      continue;
1331
0
    }
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
0
    if (rcode > 0) {
1341
0
      COPY(b);
1342
1343
0
      if (fr_edit_list_insert_pair_before(el, &dst->children, a, c) < 0) {
1344
0
        return -1;
1345
0
      }
1346
1347
0
      NEXT_B;
1348
0
      continue;
1349
0
    }
1350
1351
0
    fr_assert(rcode == 0);
1352
1353
    /*
1354
     *  They're the same.
1355
     */
1356
0
    fr_assert(a->da == b->da);
1357
1358
    /*
1359
     *  Merge lists recursively.
1360
     */
1361
0
    if (fr_type_is_structural(a->vp_type)) {
1362
0
      rcode = list_merge_rhs(el, a, &b->children, copy);
1363
0
      if (rcode < 0) return rcode;
1364
1365
0
      goto next_both;
1366
0
    }
1367
1368
    /*
1369
     *  We have both A and B, so we prefer B.
1370
     */
1371
0
    COPY(b);
1372
0
    if (fr_edit_list_replace_pair(el, &dst->children, a, c) < 0) {
1373
0
      return -1;
1374
0
    }
1375
1376
0
  next_both:
1377
0
    NEXT_A;
1378
0
    NEXT_B;
1379
0
  }
1380
1381
0
  return 0;
1382
0
}
1383
1384
/** A INTERSECTION B
1385
 *
1386
 */
1387
static int list_intersection(fr_edit_list_t *el, fr_pair_t *dst, fr_pair_list_t *src)
1388
0
{
1389
0
  fr_pair_t *a, *an;
1390
0
  fr_pair_t *b, *bn;
1391
1392
  /*
1393
   *  Prevent people from doing stupid things.
1394
   */
1395
0
  if (dst->vp_type == FR_TYPE_STRUCT) {
1396
0
    fr_strerror_printf("Cannot take intersection of STRUCT data types, it would break the structure");
1397
0
    return -1;
1398
0
  }
1399
1400
0
  fr_pair_list_sort(&dst->children, fr_pair_cmp_by_parent_num);
1401
0
  fr_pair_list_sort(src, fr_pair_cmp_by_parent_num);
1402
1403
0
  a = fr_pair_list_head(&dst->children);
1404
0
  an = fr_pair_list_next(&dst->children, a);
1405
0
  b = fr_pair_list_head(src);
1406
0
  bn = fr_pair_list_next(src, b);
1407
1408
0
  while (true) {
1409
0
    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
0
    if (!a) break;
1417
1418
    /*
1419
     *  B is done, so we delete everything else in A.
1420
     */
1421
0
    if (!b) {
1422
0
    delete_a:
1423
0
      if (fr_edit_list_pair_delete(el, &dst->children, a) < 0) return -1;
1424
0
      NEXT_A;
1425
0
      continue;
1426
0
    }
1427
1428
    /*
1429
     *  Compare the da's
1430
     */
1431
0
    rcode = fr_pair_cmp_by_parent_num(a, b);
1432
1433
    /*
1434
     *  a < b
1435
     *
1436
     *  A gets removed.
1437
     */
1438
0
    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
0
    if (rcode > 0) {
1446
0
      NEXT_B;
1447
0
      continue;
1448
0
    }
1449
1450
0
    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
0
    if (fr_type_is_structural(a->vp_type)) {
1458
0
      rcode = list_intersection(el, a, &b->children);
1459
0
      if (rcode < 0) return rcode;
1460
1461
0
      NEXT_B;
1462
1463
0
      if (fr_pair_list_empty(&a->children)) goto delete_a;
1464
1465
0
      NEXT_A;
1466
0
      continue;
1467
0
    }
1468
1469
    /*
1470
     *  Process all identical attributes, but by
1471
     *  value.
1472
     */
1473
0
    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
0
      rcode = fr_value_box_cmp(&a->data, &b->data);
1480
0
      if (rcode != 0) {
1481
0
        if (fr_edit_list_pair_delete(el, &dst->children, a) < 0) return -1;
1482
0
      }
1483
1484
0
      NEXT_A;
1485
0
      NEXT_B;
1486
0
    }
1487
0
  }
1488
1489
0
  return 0;
1490
0
}
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
 */
1499
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)
1500
0
{
1501
0
  fr_pair_list_t list;
1502
1503
0
  if (!fr_type_is_structural(dst->vp_type)) {
1504
0
    fr_strerror_printf("Cannot perform list assignment to non-structural type '%s'",
1505
0
           fr_type_to_str(dst->vp_type));
1506
0
    return -1;
1507
0
  }
1508
1509
0
#undef COPY
1510
0
#define COPY do { if (copy) { \
1511
0
        fr_pair_list_init(&list); \
1512
0
        if (fr_pair_list_copy(dst, &list, src) < 0) return -1;\
1513
0
                    src = &list; \
1514
0
                  } else { \
1515
0
        fr_pair_list_steal(dst, src); \
1516
0
            } \
1517
0
  } while (0)
1518
1519
1520
0
  switch (op) {
1521
    /*
1522
     *  Over-ride existing value (i.e. children) with
1523
     *  new list.
1524
     */
1525
0
  case T_OP_SET:
1526
0
    if (&dst->children == src) return 0; /* A := A == A */
1527
1528
0
    if (fr_edit_list_free_pair_children(el, dst) < 0) return -1;
1529
0
    FALL_THROUGH;
1530
1531
0
  case T_OP_ADD_EQ:
1532
0
    if (&dst->children == src) {
1533
0
      fr_strerror_printf("Cannot append list to itself");
1534
0
      return -1;
1535
0
    }
1536
1537
0
    COPY;
1538
0
    return fr_edit_list_insert_list_tail(el, &dst->children, src);
1539
1540
0
  case T_OP_SUB_EQ:
1541
    /*
1542
     *  foo -= foo --> {}
1543
     */
1544
0
    if (&dst->children == src) {
1545
0
      fr_pair_t *vp;
1546
1547
0
      while ((vp = fr_pair_list_head(&dst->children)) != NULL) {
1548
0
        if (fr_edit_list_pair_delete(el, &dst->children, vp) < 0) return -1;
1549
0
      }
1550
1551
0
      return 0;
1552
0
    }
1553
1554
0
    return fr_edit_list_delete_list(el, &dst->children, src);
1555
1556
0
  case T_OP_PREPEND:
1557
0
    if (&dst->children == src) {
1558
0
      fr_strerror_printf("Cannot prepend list to itself");
1559
0
      return -1;
1560
0
    }
1561
1562
0
    COPY;
1563
0
    return fr_edit_list_insert_list_head(el, &dst->children, src);
1564
1565
0
  case T_OP_AND_EQ:
1566
0
    if (&dst->children == src) return 0; /* A INTERSECTION A == A */
1567
1568
0
    if (!fr_edit_list_empty(el)) {
1569
0
    not_empty:
1570
0
      fr_strerror_printf("Failed to perform %s - undo list is not empty", fr_tokens[op]);
1571
0
      return -1;
1572
0
    }
1573
1574
0
    return list_intersection(el, dst, src);
1575
1576
0
  case T_OP_OR_EQ:
1577
0
    if (&dst->children == src) return 0; /* A UNION A == A */
1578
1579
0
    if (!fr_edit_list_empty(el)) goto not_empty;
1580
1581
0
    return list_union(el, dst, src, copy);
1582
1583
0
  case T_OP_GE:
1584
0
    if (&dst->children == src) return 0; /* A MERGE A == A */
1585
1586
0
    if (!fr_edit_list_empty(el)) goto not_empty;
1587
1588
0
    return list_merge_lhs(el, dst, src, copy);
1589
1590
0
  case T_OP_LE:
1591
0
    if (&dst->children == src) return 0; /* A MERGE A == A */
1592
1593
0
    if (!fr_edit_list_empty(el)) goto not_empty;
1594
1595
0
    return list_merge_rhs(el, dst, src, copy);
1596
1597
0
  default:
1598
0
    break;
1599
0
  }
1600
1601
0
  fr_strerror_printf("Invalid assignment operator %s for destination type %s",
1602
0
         fr_tokens[op],
1603
0
         fr_type_to_str(dst->vp_type));
1604
0
  return -1;
1605
0
}