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