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