The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
xlat_purify.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; 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: f2ce3d6bbb622d441352cc9b242271b5cba046d1 $
19 *
20 * @file xlat_purify.c
21 * @brief Purification functions for xlats
22 *
23 * @copyright 2022 The FreeRADIUS server project
24 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
25 */
26
27RCSID("$Id: f2ce3d6bbb622d441352cc9b242271b5cba046d1 $")
28
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/unlang/mod_action.h>
31#include <freeradius-devel/unlang/xlat_priv.h>
32#include <freeradius-devel/util/calc.h>
33
34static int xlat_value_list_to_xlat(xlat_exp_head_t *head, fr_value_box_list_t *list)
35{
36 fr_value_box_t *box;
37 xlat_exp_t *node;
38
39 while ((box = fr_value_box_list_pop_head(list)) != NULL) {
40 MEM(node = xlat_exp_alloc(head, XLAT_BOX, NULL, 0));
41 if (unlikely(fr_value_box_copy(node, &node->data, box) < 0)) {
42 talloc_free(node);
43 return -1;
44 }
45
46 if (node->data.type == FR_TYPE_STRING) {
48 xlat_exp_set_name_buffer(node, node->data.vb_strvalue); /* later changes can free strvalue */
49 } else {
50 char *name;
51
52 node->quote = T_BARE_WORD;
53 MEM(fr_value_box_aprint(node, &name, box, NULL) >= 0);
55 }
56 talloc_free(box);
57
59 }
60
61 return 0;
62}
63
65
70
72{
73 int rcode;
75 fr_value_box_list_t list;
76 xlat_flags_t our_flags;
77 xlat_exp_t *node, *next;
78
79 if (!head->flags.can_purify) return 0;
80
81 /*
82 * We can't purify things which need resolving,
83 */
84 if (head->flags.needs_resolving) return -1;
85
86 our_flags = head->flags;
87 our_flags.constant = our_flags.pure = true; /* we flip these if the children are not pure */
88
89 for (node = fr_dlist_head(&head->dlist);
90 next = fr_dlist_next(&head->dlist, node), node != NULL;
91 node = next) {
92 if (!node->flags.can_purify) continue;
93
94 switch (node->type) {
95 case XLAT_TMPL:
96 if (tmpl_is_attr(node->vpt)) break;
97
98 /*
99 * Optimize it by replacing the xlat -> tmpl -> xlat with just an xlat.
100 *
101 * That way we avoid a bounce through the tmpl code at run-time.
102 */
103 if (tmpl_contains_xlat(node->vpt)) {
104 xlat_exp_head_t *xlat = tmpl_xlat(node->vpt);
105
106 rcode = xlat_purify_list_internal(xlat, request, node->vpt->quote);
107 if (rcode < 0) return rcode;
108
109 node->flags = xlat->flags;
110
111 /*
112 * We can't do any more optimizations, stop processing it.
113 */
114 if (!node->flags.constant) break;
115
116 /*
117 * @todo - fix this!
118 */
119 if (tmpl_rules_cast(node->vpt) != FR_TYPE_NULL) break;
120
121 /*
122 * We have a quoted string which is constant. Convert it to a value-box.
123 *
124 * Don't change node->fmt though, for some vague reason of "knowing where
125 * it came from".
126 */
127 if ((node->vpt->quote != T_BARE_WORD) || (quote != T_BARE_WORD)) {
128 fr_sbuff_t *sbuff;
129 ssize_t slen;
130
131 FR_SBUFF_TALLOC_THREAD_LOCAL(&sbuff, 256, SIZE_MAX);
132
133 slen = xlat_print(sbuff, xlat, NULL);
134 if (slen < 0) return -1;
135
136 xlat_exp_set_type(node, XLAT_BOX); /* frees node->group, and therefore xlat */
137 fr_value_box_init(&node->data, FR_TYPE_STRING, NULL, false);
138
139 if (fr_value_box_bstrndup(node, &node->data, NULL,
140 fr_sbuff_start(sbuff), fr_sbuff_used(sbuff), false) < 0) return -1;
141 break;
142 }
143
144 /*
145 * The tmpl is constant, but not quoted. Keep the group wrapper, which
146 * ensures that the entire sub-expression results in one output value.
147 */
148 (void) talloc_steal(node, node->vpt->name);
149 (void) talloc_steal(node, xlat);
150 xlat_exp_set_type(node, XLAT_GROUP); /* frees node->vpt, and xlat if we didn't steal it */
151 talloc_free(node->group);
152 node->group = xlat;
153 break;
154 }
155 break;
156
157 case XLAT_BOX:
158 case XLAT_ONE_LETTER:
159 case XLAT_REGEX:
160 break;
161
162 case XLAT_INVALID:
164 fr_assert(0);
165 return -1;
166
167 case XLAT_GROUP: {
168 bool xlat = node->flags.xlat;
169
170 rcode = xlat_purify_list_internal(node->group, request, quote);
171 if (rcode < 0) return rcode;
172
173 node->flags = node->group->flags;
174 node->flags.xlat = xlat;
175
176 /*
177 * If the group is constant, hoist it.
178 *
179 * The group wrapper isn't actually used for anything, and is added only to wrap
180 * %{...}. But we should likely double-check that there are no unexpected side
181 * effects with things like %{foo.[*]}. Are there any differences between
182 * returning _one_ value-box which contains a list, or returning a _list_ of
183 * value-boxes?
184 *
185 * i.e. are these two situations identical?
186 *
187 * foo = bar.[*]
188 * foo = %{bar.[*]}
189 *
190 * If "foo" is a leaf type, then perhaps the first one is "create multiple copies of
191 * 'foo', one for each value. And the second is likely illegal.
192 *
193 * if "foo" is a structural type, then the first one could assign multiple
194 * structures to 'foo', just like the leaf example above. But only if the things
195 * returned from 'bar.[*]' are structures of the same type as 'foo'. The second
196 * example is then assigning _one_ structure to 'foo'.
197 *
198 * The caveat here is that the data returned from 'bar.[*]' must be of the
199 * correct types for the structure members. So it's likely to work only for
200 * groups. If we want to copy one structure to another, we just assign them:
201 *
202 * foo = bar
203 *
204 * If we hoist the contents of %{bar.[*]}, then for a leaf type, the two
205 * situations become identical. For a structural type, we change the meaning so
206 * that the two situations become identical.
207 *
208 * And then none of this matters is we're in a quoted string, because the results
209 * will be concatenated anyways.
210 */
211 if (node->flags.constant && node->flags.xlat &&
212 ((quote != T_BARE_WORD) || (fr_dlist_num_elements(&node->group->dlist) == 1))) {
213 xlat_exp_t *child, *to_free;
214
215 fr_dlist_remove(&head->dlist, node);
216 to_free = node;
217
218 while ((child = fr_dlist_pop_head(&to_free->group->dlist)) != NULL) {
219 (void) talloc_steal(head, child);
220
221 fr_dlist_insert_before(&head->dlist, next, child);
222 child->flags.can_purify = false;
223 xlat_flags_merge(&our_flags, &child->flags);
224
225 node = child;
226 }
227 talloc_free(to_free);
228 }
229 }
230 break;
231
232 case XLAT_FUNC:
233 /*
234 * If the node is not pure, then maybe there's a callback to purify it, OR maybe
235 * we can purify the function arguments.
236 */
237 if (!node->flags.pure) {
238 if (node->call.func->purify) {
239 if (node->call.func->purify(node, node->call.inst->data, request) < 0) return -1;
240 } else {
241 if (xlat_purify_list_internal(node->call.args, request, T_BARE_WORD) < 0) return -1;
242 }
243
244 /*
245 * It may have been purified into an XLAT_BOX. But if not, ensure that
246 * the flags are all correct.
247 */
248 if (node->type == XLAT_FUNC) {
249 node->flags = node->call.func->flags;
250 xlat_exp_foreach(node->call.args, arg) {
251 xlat_flags_merge(&node->flags, &arg->flags);
252 }
253 }
254 break;
255 }
256
257 /*
258 * The node is entirely pure, we don't worry about any callbacks, we just
259 * evaluate the entire thing to purify it.
260 */
261 fr_assert(node->flags.pure);
262 fr_value_box_list_init(&list);
263 result.rcode = RLM_MODULE_NOT_SET;
264 if (unlang_xlat_push_node(head, &result, &list, request, node) < 0) {
265 return -1;
266 }
267
268 /*
269 * Hope to god it doesn't yield. :)
270 */
271
272 (void) unlang_interpret_synchronous(NULL, request);
273 if (!XLAT_RESULT_SUCCESS(&result)) return -1;
274
275 /*
276 * The function call becomes a GROUP of boxes
277 */
279 xlat_exp_set_type(node, XLAT_GROUP); /* Frees the argument list */
280
281 if (xlat_value_list_to_xlat(node->group, &list) < 0) return -1;
282 node->flags = node->group->flags;
283 break;
284 }
285
286 node->flags.can_purify = false;
287 xlat_flags_merge(&our_flags, &node->flags);
288 }
289
290 /*
291 * Let's not call xlat_purify() repeatedly, so we clear the flag.
292 *
293 * @todo - if all of the children of "head" are "pure", then at the end of the purification
294 * process, there should only be one child, of type XLAT_BOX.
295 */
296 our_flags.can_purify = false;
297 head->flags = our_flags;
298
299 return 0;
300}
301
302/** Purify an xlat
303 *
304 * @param head the xlat to be purified
305 * @param intp the interpreter to use.
306 *
307 */
309{
310 int rcode;
311 request_t *request;
312
313 if (!head->flags.can_purify) return 0;
314
315 request = request_local_alloc_internal(NULL, NULL);
316 if (!request) return -1;
317
318 if (intp) unlang_interpret_set(request, intp);
319
320 rcode = xlat_purify_list(head, request);
321 talloc_free(request);
322 if (rcode < 0) return rcode;
323
324 fr_assert(!head->flags.can_purify);
325
326 return 0;
327}
328
330{
331#ifdef STATIC_ANALYZER
332 if (!node) return NULL;
333#endif
334
335 if (node->type == XLAT_BOX) {
336 return &node->data;
337
338 } else if ((node->type == XLAT_TMPL) && tmpl_is_data(node->vpt)) {
339 return tmpl_value(node->vpt);
340 }
341
342 return NULL;
343}
344
345
346static bool is_truthy(xlat_exp_t *node, bool *out)
347{
348 fr_value_box_t const *box;
349
350 box = xlat_value_box(node);
351 if (!box) {
352 *out = false;
353 return false;
354 }
355
357 return true;
358}
359
360/*
361 * Do some optimizations.
362 *
363 */
365{
366 bool value;
367
368 /*
369 * LHS isn't truthy, we can't do anything. If the LHS
370 * passes, we return the value of the LHS.
371 *
372 * FOO || ... --> FOO || ...
373 */
374 if (!is_truthy(lhs, &value)) {
375 /*
376 * FOO || 0 --> FOO much of the time
377 * FOO || 1 --> FOO much of the time
378 */
379 if (!is_truthy(rhs, &value)) return NULL;
380
381 /*
382 * BOOL || 1 --> 1
383 *
384 * Because if the LHS is 1, then we return the LHS (1)
385 * On the other hand, it the LHS is 0, then we return
386 * the RHS, which is also 1.
387 *
388 * But we can't do
389 *
390 * <type> || 1 --> 1
391 */
392 if (value && (lhs->type == XLAT_FUNC) && (lhs->call.func->return_type == FR_TYPE_BOOL)) {
393 talloc_free(lhs);
394 return rhs;
395 }
396
397 return NULL;
398 }
399
400 /*
401 * 1 || FOO --> 1
402 * 0 || FOO --> FOO
403 */
404 if (value) {
405 talloc_free(rhs);
406 return lhs;
407 }
408
409 talloc_free(lhs);
410 return rhs;
411}
412
413
414/*
415 * Do some optimizations.
416 *
417 */
419{
420 bool value;
421
422 /*
423 * LHS isn't truthy
424 *
425 * FOO && ... --> FOO && ...
426 */
427 if (!is_truthy(lhs, &value)) {
428 /*
429 * FOO && 0 --> 0
430 * FOO && 1 --> FOO
431 */
432 if (!is_truthy(rhs, &value)) return NULL;
433
434 if (!value) {
435 talloc_free(lhs);
436 return rhs;
437 }
438
439 talloc_free(rhs);
440 return lhs;
441 }
442
443 /*
444 * 0 && FOO --> 0
445 * 1 && FOO --> FOO
446 */
447 if (!value) {
448 talloc_free(rhs);
449 return lhs;
450 }
451
452 talloc_free(lhs);
453 return rhs;
454}
455
456/*
457 * Do peephole optimizations.
458 */
459static int binary_peephole_optimize(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
460{
461 fr_value_box_t *lhs_box, *rhs_box;
462 fr_value_box_t box;
463 xlat_exp_t *node;
464 char *name;
465
466#if 0
467 /*
468 * @todo - more peephole optimizations here. We can't enable this code as yet, because of
469 * upcasting rules (e.g. calc.c) where comparisons between IP prefixes and IP addresses (or
470 * v4/v6) are upcast, and then the values compared.
471 *
472 * We should probably expose some of the upcast functionality in calc.c so that this function can
473 * use it.
474 */
475
476 /*
477 * Attribute op value.
478 */
479 if ((lhs->type == XLAT_TMPL) && tmpl_is_attr(lhs->vpt) &&
480 (rhs->type == XLAT_TMPL) && (tmpl_is_data_unresolved(rhs->vpt) || tmpl_is_data(rhs->vpt))) {
481 fr_type_t dst_type;
482 fr_dict_attr_t const *da;
483
484 resolve:
485 dst_type = tmpl_rules_cast(rhs->vpt);
486 da = tmpl_attr_tail_da(lhs->vpt);
487
488 /*
489 * Cast to the final type. If there are two different casts, we ignore the one for the
490 * data.
491 */
492 if (fr_type_is_null(dst_type)) {
493 dst_type = tmpl_rules_cast(lhs->vpt);
494 if (fr_type_is_null(dst_type)) dst_type = da->type;
495 }
496
497 if (tmpl_cast_in_place(rhs->vpt, dst_type, da) < 0) return -1;
498
499 rhs->flags.needs_resolving = false;
500 return 0;
501 }
502
503 /*
504 * value op attribute
505 *
506 * We just swap LHS and RHS without caring about the operator, because we don't use the
507 * operator, and the caller has no idea that we swapped the pointers..
508 */
509 if ((rhs->type == XLAT_TMPL) && tmpl_is_attr(rhs->vpt) &&
510 (lhs->type == XLAT_TMPL) && (tmpl_is_data_unresolved(lhs->vpt) || tmpl_is_data(lhs->vpt))) {
511 xlat_exp_t *tmp = lhs;
512 lhs = rhs;
513 rhs = tmp;
514 goto resolve;
515 }
516#endif
517
518 /*
519 * The tmpl_tokenize code takes care of resolving the data if there's a cast.
520 */
521 lhs_box = xlat_value_box(lhs);
522 if (!lhs_box) return 0;
523
524 rhs_box = xlat_value_box(rhs);
525 if (!rhs_box) return 0;
526
527 if (fr_value_calc_binary_op(lhs, &box, FR_TYPE_NULL, lhs_box, op, rhs_box) < 0) return -1;
528
529 MEM(node = xlat_exp_alloc(ctx, XLAT_BOX, NULL, 0));
530
531 if (box.type == FR_TYPE_BOOL) box.enumv = attr_expr_bool_enum;
532
533 MEM(fr_value_box_aprint(node, &name, &box, NULL) >= 0);
535 if (unlikely(fr_value_box_copy(node, &node->data, &box) < 0)) {
536 talloc_free(node);
537 return -1;
538 }
539
540 *out = node;
541
542 return 1;
543}
544
545int xlat_purify_op(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
546{
547 if (op == T_LOR) {
548 xlat_exp_t *node;
549
550 node = peephole_optimize_lor(lhs, rhs);
551 if (!node) return 0;
552
553 *out = node;
554 return 1;
555 }
556
557 if (op == T_LAND) {
558 xlat_exp_t *node;
559
560 node = peephole_optimize_land(lhs, rhs);
561 if (!node) return 0;
562
563 *out = node;
564 return 1;
565 }
566
567 return binary_peephole_optimize(ctx, out, lhs, op, rhs);
568}
#define RCSID(id)
Definition build.h:485
#define unlikely(_x)
Definition build.h:383
int fr_value_calc_binary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
Calculate DST = A OP B.
Definition calc.c:1924
#define MEM(x)
Definition debug.h:36
Test enumeration values.
Definition dict_test.h:92
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 void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
static int fr_dlist_insert_before(fr_dlist_head_t *list_head, void *pos, void *ptr)
Insert an item before an item already in the list.
Definition dlist.h:450
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:939
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:672
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
void unlang_interpret_set(request_t *request, unlang_interpret_t *intp)
Set a specific interpreter for a request.
Definition interpret.c:1998
#define UNLANG_RESULT_NOT_SET
Definition interpret.h:139
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
rlm_rcode_t unlang_interpret_synchronous(fr_event_list_t *el, request_t *request)
Execute an unlang section synchronously.
talloc_free(reap)
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_BOOL
A truth value.
long int ssize_t
#define fr_assert(_expr)
Definition rad_assert.h:38
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:41
#define request_local_alloc_internal(_ctx, _args)
Allocate a new internal request outside of the request pool.
Definition request.h:343
static char const * name
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_used(_sbuff_or_marker)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:227
#define tmpl_value(_tmpl)
Definition tmpl.h:937
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#define tmpl_xlat(_tmpl)
Definition tmpl.h:930
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:942
int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv))
Convert tmpl_t of type TMPL_TYPE_DATA_UNRESOLVED or TMPL_TYPE_DATA to TMPL_TYPE_DATA of type specifie...
#define tmpl_is_data(vpt)
Definition tmpl.h:206
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:217
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
enum fr_token fr_token_t
@ T_BARE_WORD
Definition token.h:120
@ T_LAND
Definition token.h:91
@ T_LOR
Definition token.h:92
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
int unlang_xlat_push_node(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_t *node)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:290
uint8_t xlat
it's an xlat wrapper
Definition xlat.h:115
fr_slen_t xlat_print(fr_sbuff_t *in, xlat_exp_head_t const *node, fr_sbuff_escape_rules_t const *e_rules)
Reconstitute an xlat expression from its constituent nodes.
static fr_slen_t head
Definition xlat.h:420
#define XLAT_RESULT_SUCCESS(_p_result)
Definition xlat.h:503
uint8_t needs_resolving
Needs pass2 resolution.
Definition xlat.h:109
uint8_t can_purify
if the xlat has a pure function with pure arguments.
Definition xlat.h:112
uint8_t pure
has no external side effects, true for BOX, LITERAL, and some functions
Definition xlat.h:110
uint8_t constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition xlat.h:114
int xlat_instance_unregister_func(xlat_exp_t *node)
Remove a node from the list of xlat instance data.
Definition xlat_inst.c:548
Flags that control resolution and evaluation.
Definition xlat.h:108
#define fr_type_is_null(_x)
Definition types.h:345
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:4156
bool fr_value_box_is_truthy(fr_value_box_t const *in)
Check truthiness of values.
Definition value.c:6931
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4596
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:609
static size_t char ** out
Definition value.h:1023
void xlat_exp_set_name_buffer(xlat_exp_t *node, char const *fmt)
Set the format string for an xlat node, copying from a talloc'd buffer.
Definition xlat_alloc.c:321
void xlat_exp_set_name_shallow(xlat_exp_t *node, char const *fmt)
Set the format string for an xlat node from a pre-existing buffer.
Definition xlat_alloc.c:338
fr_dict_attr_t const * attr_expr_bool_enum
Definition xlat_eval.c:44
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:154
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:190
fr_token_t quote
Type of quoting around XLAT_GROUP types.
Definition xlat_priv.h:152
@ XLAT_ONE_LETTER
Special "one-letter" expansion.
Definition xlat_priv.h:109
@ XLAT_BOX
fr_value_box_t
Definition xlat_priv.h:108
@ XLAT_TMPL
xlat attribute
Definition xlat_priv.h:112
@ XLAT_FUNC
xlat module
Definition xlat_priv.h:110
@ XLAT_GROUP
encapsulated string of xlats
Definition xlat_priv.h:116
@ XLAT_FUNC_UNRESOLVED
func needs resolution during pass2.
Definition xlat_priv.h:111
@ XLAT_INVALID
Bad expansion.
Definition xlat_priv.h:107
static void xlat_flags_merge(xlat_flags_t *parent, xlat_flags_t const *child)
Merge flags from child to parent.
Definition xlat_priv.h:229
#define xlat_exp_set_type(_node, _type)
Definition xlat_priv.h:276
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:155
#define xlat_exp_alloc(_ctx, _type, _in, _inlen)
Definition xlat_priv.h:282
#define xlat_exp_foreach(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition xlat_priv.h:222
static int xlat_exp_insert_tail(xlat_exp_head_t *head, xlat_exp_t *node)
Definition xlat_priv.h:238
An xlat expansion node.
Definition xlat_priv.h:148
static xlat_exp_t * peephole_optimize_lor(xlat_exp_t *lhs, xlat_exp_t *rhs)
static int xlat_value_list_to_xlat(xlat_exp_head_t *head, fr_value_box_list_t *list)
Definition xlat_purify.c:34
static int binary_peephole_optimize(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
static fr_value_box_t * xlat_value_box(xlat_exp_t *node)
static bool is_truthy(xlat_exp_t *node, bool *out)
int xlat_purify_op(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
int xlat_purify(xlat_exp_head_t *head, unlang_interpret_t *intp)
Purify an xlat.
int xlat_purify_list(xlat_exp_head_t *head, request_t *request)
Definition xlat_purify.c:66
static int xlat_purify_list_internal(xlat_exp_head_t *head, request_t *request, fr_token_t quote)
Definition xlat_purify.c:71
static xlat_exp_t * peephole_optimize_land(xlat_exp_t *lhs, xlat_exp_t *rhs)