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: 472aa05d4f12d704f2b2aaf36f88fedec615e798 $
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: 472aa05d4f12d704f2b2aaf36f88fedec615e798 $")
28
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/unlang/xlat_priv.h>
31#include <freeradius-devel/util/calc.h>
32
33static void xlat_value_list_to_xlat(xlat_exp_head_t *head, fr_value_box_list_t *list)
34{
35 fr_value_box_t *box;
36 xlat_exp_t *node;
37
38 while ((box = fr_value_box_list_pop_head(list)) != NULL) {
39 MEM(node = xlat_exp_alloc(head, XLAT_BOX, NULL, 0));
40 fr_value_box_copy(node, &node->data, box);
41
42 if (node->data.type == FR_TYPE_STRING) {
44 xlat_exp_set_name_buffer(node, node->data.vb_strvalue); /* later changes can free strvalue */
45 } else {
46 char *name;
47
48 node->quote = T_BARE_WORD;
49 MEM(fr_value_box_aprint(node, &name, box, NULL) >= 0);
51 }
52 talloc_free(box);
53
55 }
56}
57
59
64
66{
67 int rcode;
68 bool success;
69 fr_value_box_list_t list;
70 xlat_flags_t our_flags;
71 xlat_exp_t *node, *next;
72
73 if (!head->flags.can_purify) return 0;
74
75 /*
76 * We can't purify things which need resolving,
77 */
78 if (head->flags.needs_resolving) return -1;
79
80 our_flags = head->flags;
81 our_flags.constant = our_flags.pure = true; /* we flip these if the children are not pure */
82
83 for (node = fr_dlist_head(&head->dlist);
84 next = fr_dlist_next(&head->dlist, node), node != NULL;
85 node = next) {
86 if (!node->flags.can_purify) continue;
87
88 switch (node->type) {
89 case XLAT_TMPL:
90 if (tmpl_is_attr(node->vpt)) break;
91
92 /*
93 * Optimize it by replacing the xlat -> tmpl -> xlat with just an xlat.
94 *
95 * That way we avoid a bounce through the tmpl code at run-time.
96 */
97 if (tmpl_contains_xlat(node->vpt)) {
98 xlat_exp_head_t *xlat = tmpl_xlat(node->vpt);
99
100 rcode = xlat_purify_list_internal(xlat, request, node->vpt->quote);
101 if (rcode < 0) return rcode;
102
103 node->flags = xlat->flags;
104
105 /*
106 * We can't do any more optimizations, stop processing it.
107 */
108 if (!node->flags.constant) break;
109
110 /*
111 * @todo - fix this!
112 */
113 if (tmpl_rules_cast(node->vpt) != FR_TYPE_NULL) break;
114
115 /*
116 * We have a quoted string which is constant. Convert it to a value-box.
117 *
118 * Don't change node->fmt though, for some vague reason of "knowing where
119 * it came from".
120 */
121 if ((node->vpt->quote != T_BARE_WORD) || (quote != T_BARE_WORD)) {
122 fr_sbuff_t *sbuff;
123 ssize_t slen;
124
125 FR_SBUFF_TALLOC_THREAD_LOCAL(&sbuff, 256, SIZE_MAX);
126
127 slen = xlat_print(sbuff, xlat, NULL);
128 if (slen < 0) return -1;
129
130 xlat_exp_set_type(node, XLAT_BOX); /* frees node->group, and therefore xlat */
131 fr_value_box_init(&node->data, FR_TYPE_STRING, NULL, false);
132
133 if (fr_value_box_bstrndup(node, &node->data, NULL,
134 fr_sbuff_start(sbuff), fr_sbuff_used(sbuff), false) < 0) return -1;
135 break;
136 }
137
138 /*
139 * The tmpl is constant, but not quoted. Keep the group wrapper, which
140 * ensures that the entire sub-expression results in one output value.
141 */
142 (void) talloc_steal(node, node->vpt->name);
143 (void) talloc_steal(node, xlat);
144 xlat_exp_set_type(node, XLAT_GROUP); /* frees node->vpt, and xlat if we didn't steal it */
145 talloc_free(node->group);
146 node->group = xlat;
147 break;
148 }
149 break;
150
151 case XLAT_BOX:
152 case XLAT_ONE_LETTER:
153 case XLAT_REGEX:
154 break;
155
156 case XLAT_INVALID:
158 fr_assert(0);
159 return -1;
160
161 case XLAT_GROUP: {
162 bool xlat = node->flags.xlat;
163
164 rcode = xlat_purify_list_internal(node->group, request, quote);
165 if (rcode < 0) return rcode;
166
167 node->flags = node->group->flags;
168 node->flags.xlat = xlat;
169
170 /*
171 * If the group is constant, hoist it.
172 *
173 * The group wrapper isn't actually used for anything, and is added only to wrap
174 * %{...}. But we should likely double-check that there are no unexpected side
175 * effects with things like %{foo.[*]}. Are there any differences between
176 * returning _one_ value-box which contains a list, or returning a _list_ of
177 * value-boxes?
178 *
179 * i.e. are these two situations identical?
180 *
181 * foo = bar.[*]
182 * foo = %{bar.[*]}
183 *
184 * If "foo" is a leaf type, then perhaps the first one is "create multiple copies of
185 * 'foo', one for each value. And the second is likely illegal.
186 *
187 * if "foo" is a structural type, then the first one could assign multiple
188 * structures to 'foo', just like the leaf example above. But only if the things
189 * returned from 'bar.[*]' are structures of the same type as 'foo'. The second
190 * example is then assigning _one_ structure to 'foo'.
191 *
192 * The caveat here is that the data returned from 'bar.[*]' must be of the
193 * correct types for the structure members. So it's likely to work only for
194 * groups. If we want to copy one structure to another, we just assign them:
195 *
196 * foo = bar
197 *
198 * If we hoist the contents of %{bar.[*]}, then for a leaf type, the two
199 * situations become identical. For a structural type, we change the meaning so
200 * that the two situations become identical.
201 *
202 * And then none of this matters is we're in a quoted string, because the results
203 * will be concatenated anyways.
204 */
205 if (node->flags.constant && node->flags.xlat &&
206 ((quote != T_BARE_WORD) || (fr_dlist_num_elements(&node->group->dlist) == 1))) {
207 xlat_exp_t *child, *to_free;
208
209 fr_dlist_remove(&head->dlist, node);
210 to_free = node;
211
212 while ((child = fr_dlist_pop_head(&to_free->group->dlist)) != NULL) {
213 (void) talloc_steal(head, child);
214
215 fr_dlist_insert_before(&head->dlist, next, child);
216 child->flags.can_purify = false;
217 xlat_flags_merge(&our_flags, &child->flags);
218
219 node = child;
220 }
221 talloc_free(to_free);
222 }
223 }
224 break;
225
226 case XLAT_FUNC:
227 /*
228 * If the node is not pure, then maybe there's a callback to purify it, OR maybe
229 * we can purify the function arguments.
230 */
231 if (!node->flags.pure) {
232 if (node->call.func->purify) {
233 if (node->call.func->purify(node, node->call.inst->data, request) < 0) return -1;
234 } else {
235 if (xlat_purify_list_internal(node->call.args, request, T_BARE_WORD) < 0) return -1;
236 }
237
238 /*
239 * It may have been purified into an XLAT_BOX. But if not, ensure that
240 * the flags are all correct.
241 */
242 if (node->type == XLAT_FUNC) {
243 node->flags = node->call.func->flags;
244 xlat_exp_foreach(node->call.args, arg) {
245 xlat_flags_merge(&node->flags, &arg->flags);
246 }
247 }
248 break;
249 }
250
251 /*
252 * The node is entirely pure, we don't worry about any callbacks, we just
253 * evaluate the entire thing to purify it.
254 */
255 fr_assert(node->flags.pure);
256 fr_value_box_list_init(&list);
257 success = false;
258 if (unlang_xlat_push_node(head, &success, &list, request, node) < 0) {
259 return -1;
260 }
261
262 /*
263 * Hope to god it doesn't yield. :)
264 */
265
266 (void) unlang_interpret_synchronous(NULL, request);
267 if (!success) return -1;
268
269 /*
270 * The function call becomes a GROUP of boxes
271 */
273 xlat_exp_set_type(node, XLAT_GROUP); /* Frees the argument list */
274
275 xlat_value_list_to_xlat(node->group, &list);
276 node->flags = node->group->flags;
277 break;
278 }
279
280 node->flags.can_purify = false;
281 xlat_flags_merge(&our_flags, &node->flags);
282 }
283
284 /*
285 * Let's not call xlat_purify() repeatedly, so we clear the flag.
286 *
287 * @todo - if all of the children of "head" are "pure", then at the end of the purification
288 * process, there should only be one child, of type XLAT_BOX.
289 */
290 our_flags.can_purify = false;
291 head->flags = our_flags;
292
293 return 0;
294}
295
296/** Purify an xlat
297 *
298 * @param head the xlat to be purified
299 * @param intp the interpreter to use.
300 *
301 */
303{
304 int rcode;
305 request_t *request;
306
307 if (!head->flags.can_purify) return 0;
308
309 request = request_local_alloc_internal(NULL, NULL);
310 if (!request) return -1;
311
312 if (intp) unlang_interpret_set(request, intp);
313
314 rcode = xlat_purify_list(head, request);
315 talloc_free(request);
316 if (rcode < 0) return rcode;
317
318 fr_assert(!head->flags.can_purify);
319
320 return 0;
321}
322
324{
325#ifdef STATIC_ANALYZER
326 if (!node) return NULL;
327#endif
328
329 if (node->type == XLAT_BOX) {
330 return &node->data;
331
332 } else if ((node->type == XLAT_TMPL) && tmpl_is_data(node->vpt)) {
333 return tmpl_value(node->vpt);
334 }
335
336 return NULL;
337}
338
339
340static bool is_truthy(xlat_exp_t *node, bool *out)
341{
342 fr_value_box_t const *box;
343
344 box = xlat_value_box(node);
345 if (!box) {
346 *out = false;
347 return false;
348 }
349
351 return true;
352}
353
354/*
355 * Do some optimizations.
356 *
357 */
359{
360 bool value;
361
362 /*
363 * LHS isn't truthy, we can't do anything. If the LHS
364 * passes, we return the value of the LHS.
365 *
366 * FOO || ... --> FOO || ...
367 */
368 if (!is_truthy(lhs, &value)) {
369 /*
370 * FOO || 0 --> FOO much of the time
371 * FOO || 1 --> FOO much of the time
372 */
373 if (!is_truthy(rhs, &value)) return NULL;
374
375 /*
376 * BOOL || 1 --> 1
377 *
378 * Because if the LHS is 1, then we return the LHS (1)
379 * On the other hand, it the LHS is 0, then we return
380 * the RHS, which is also 1.
381 *
382 * But we can't do
383 *
384 * <type> || 1 --> 1
385 */
386 if (value && (lhs->type == XLAT_FUNC) && (lhs->call.func->return_type == FR_TYPE_BOOL)) {
387 talloc_free(lhs);
388 return rhs;
389 }
390
391 return NULL;
392 }
393
394 /*
395 * 1 || FOO --> 1
396 * 0 || FOO --> FOO
397 */
398 if (value) {
399 talloc_free(rhs);
400 return lhs;
401 }
402
403 talloc_free(lhs);
404 return rhs;
405}
406
407
408/*
409 * Do some optimizations.
410 *
411 */
413{
414 bool value;
415
416 /*
417 * LHS isn't truthy
418 *
419 * FOO && ... --> FOO && ...
420 */
421 if (!is_truthy(lhs, &value)) {
422 /*
423 * FOO && 0 --> 0
424 * FOO && 1 --> FOO
425 */
426 if (!is_truthy(rhs, &value)) return NULL;
427
428 if (!value) {
429 talloc_free(lhs);
430 return rhs;
431 }
432
433 talloc_free(rhs);
434 return lhs;
435 }
436
437 /*
438 * 0 && FOO --> 0
439 * 1 && FOO --> FOO
440 */
441 if (!value) {
442 talloc_free(rhs);
443 return lhs;
444 }
445
446 talloc_free(lhs);
447 return rhs;
448}
449
450/*
451 * Do peephole optimizations.
452 */
453static int binary_peephole_optimize(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
454{
455 fr_value_box_t *lhs_box, *rhs_box;
456 fr_value_box_t box;
457 xlat_exp_t *node;
458 char *name;
459
460#if 0
461 /*
462 * @todo - more peephole optimizations here. We can't enable this code as yet, because of
463 * upcasting rules (e.g. calc.c) where comparisons between IP prefixes and IP addresses (or
464 * v4/v6) are upcast, and then the values compared.
465 *
466 * We should probably expose some of the upcast functionality in calc.c so that this function can
467 * use it.
468 */
469
470 /*
471 * Attribute op value.
472 */
473 if ((lhs->type == XLAT_TMPL) && tmpl_is_attr(lhs->vpt) &&
474 (rhs->type == XLAT_TMPL) && (tmpl_is_data_unresolved(rhs->vpt) || tmpl_is_data(rhs->vpt))) {
475 fr_type_t dst_type;
476 fr_dict_attr_t const *da;
477
478 resolve:
479 dst_type = tmpl_rules_cast(rhs->vpt);
480 da = tmpl_attr_tail_da(lhs->vpt);
481
482 /*
483 * Cast to the final type. If there are two different casts, we ignore the one for the
484 * data.
485 */
486 if (fr_type_is_null(dst_type)) {
487 dst_type = tmpl_rules_cast(lhs->vpt);
488 if (fr_type_is_null(dst_type)) dst_type = da->type;
489 }
490
491 if (tmpl_cast_in_place(rhs->vpt, dst_type, da) < 0) return -1;
492
493 rhs->flags.needs_resolving = false;
494 return 0;
495 }
496
497 /*
498 * value op attribute
499 *
500 * We just swap LHS and RHS without caring about the operator, because we don't use the
501 * operator, and the caller has no idea that we swapped the pointers..
502 */
503 if ((rhs->type == XLAT_TMPL) && tmpl_is_attr(rhs->vpt) &&
504 (lhs->type == XLAT_TMPL) && (tmpl_is_data_unresolved(lhs->vpt) || tmpl_is_data(lhs->vpt))) {
505 xlat_exp_t *tmp = lhs;
506 lhs = rhs;
507 rhs = tmp;
508 goto resolve;
509 }
510#endif
511
512 /*
513 * The tmpl_tokenize code takes care of resolving the data if there's a cast.
514 */
515 lhs_box = xlat_value_box(lhs);
516 if (!lhs_box) return 0;
517
518 rhs_box = xlat_value_box(rhs);
519 if (!rhs_box) return 0;
520
521 if (fr_value_calc_binary_op(lhs, &box, FR_TYPE_NULL, lhs_box, op, rhs_box) < 0) return -1;
522
523 MEM(node = xlat_exp_alloc(ctx, XLAT_BOX, NULL, 0));
524
525 if (box.type == FR_TYPE_BOOL) box.enumv = attr_expr_bool_enum;
526
527 MEM(fr_value_box_aprint(node, &name, &box, NULL) >= 0);
529 fr_value_box_copy(node, &node->data, &box);
530
531 *out = node;
532
533 return 1;
534}
535
536int xlat_purify_op(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
537{
538 if (op == T_LOR) {
539 xlat_exp_t *node;
540
541 node = peephole_optimize_lor(lhs, rhs);
542 if (!node) return 0;
543
544 *out = node;
545 return 1;
546 }
547
548 if (op == T_LAND) {
549 xlat_exp_t *node;
550
551 node = peephole_optimize_land(lhs, rhs);
552 if (!node) return 0;
553
554 *out = node;
555 return 1;
556 }
557
558 return binary_peephole_optimize(ctx, out, lhs, op, rhs);
559}
#define RCSID(id)
Definition build.h:485
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:1803
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
#define request_local_alloc_internal(_ctx, _args)
Allocate a new internal request outside of the request pool.
Definition request.h:339
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
goto success
Definition tmpl_eval.c:1443
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, bool *p_success, 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:302
uint8_t xlat
it's an xlat wrapper
Definition xlat.h:114
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:416
uint8_t needs_resolving
Needs pass2 resolution.
Definition xlat.h:108
uint8_t can_purify
if the xlat has a pure function with pure arguments.
Definition xlat.h:111
uint8_t pure
has no external side effects, true for BOX, LITERAL, and some functions
Definition xlat.h:109
uint8_t constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition xlat.h:113
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:107
#define fr_type_is_null(_x)
Definition types.h:326
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:3963
bool fr_value_box_is_truthy(fr_value_box_t const *in)
Check truthiness of values.
Definition value.c:6574
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:4382
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:598
static size_t char ** out
Definition value.h:1012
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:41
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:228
#define xlat_exp_set_type(_node, _type)
Definition xlat_priv.h:275
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:281
#define xlat_exp_foreach(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition xlat_priv.h:221
static int xlat_exp_insert_tail(xlat_exp_head_t *head, xlat_exp_t *node)
Definition xlat_priv.h:237
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 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:60
static int xlat_purify_list_internal(xlat_exp_head_t *head, request_t *request, fr_token_t quote)
Definition xlat_purify.c:65
static xlat_exp_t * peephole_optimize_land(xlat_exp_t *lhs, xlat_exp_t *rhs)
static void xlat_value_list_to_xlat(xlat_exp_head_t *head, fr_value_box_list_t *list)
Definition xlat_purify.c:33