blob: 276ca6b7d6a1790632b0dc5065c4f5d2b49b11b2 [file] [log] [blame]
Radek Krejcib1646a92018-11-02 16:08:26 +01001/**
2 * @file xpath.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief YANG XPath evaluation functions
5 *
Michal Vasko49fec8e2022-05-24 10:28:33 +02006 * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
Radek Krejcib1646a92018-11-02 16:08:26 +01007 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
Christian Hopps32874e12021-05-01 09:43:54 -040014#define _GNU_SOURCE /* asprintf, strdup */
Radek Krejcib1646a92018-11-02 16:08:26 +010015
Radek Krejci535ea9f2020-05-29 16:01:05 +020016#include "xpath.h"
Radek Krejcib1646a92018-11-02 16:08:26 +010017
Radek Krejci535ea9f2020-05-29 16:01:05 +020018#include <assert.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010019#include <ctype.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020020#include <errno.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020021#include <math.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020022#include <stdint.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010023#include <stdio.h>
24#include <stdlib.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010025#include <string.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010026
Radek Krejci535ea9f2020-05-29 16:01:05 +020027#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020028#include "compat.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "context.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020030#include "dict.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020031#include "hash_table.h"
Radek Krejci47fab892020-11-05 17:02:41 +010032#include "out.h"
Radek Krejci7931b192020-06-25 17:05:03 +020033#include "parser_data.h"
Michal Vasko004d3152020-06-11 19:59:22 +020034#include "path.h"
Michal Vasko03ff5a72019-09-11 13:49:33 +020035#include "plugins_types.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020036#include "printer_data.h"
Michal Vasko1a7a7bd2020-10-16 14:39:15 +020037#include "schema_compile_node.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020038#include "tree.h"
Radek Krejci77114102021-03-10 15:21:57 +010039#include "tree_data.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020040#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010041#include "tree_edit.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020042#include "tree_schema_internal.h"
43#include "xml.h"
Michal Vasko03ff5a72019-09-11 13:49:33 +020044
Michal Vasko7333cb32022-07-29 16:30:29 +020045static LY_ERR set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
46 enum lyxp_axis axis, uint32_t *index_p);
Michal Vaskodd528af2022-08-08 14:35:07 +020047static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth);
48static LY_ERR eval_expr_select(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_expr_type etype,
Michal Vasko40308e72020-10-20 16:38:40 +020049 struct lyxp_set *set, uint32_t options);
Michal Vaskofe1af042022-07-29 14:58:59 +020050static LY_ERR moveto_resolve_model(const char **qname, uint32_t *qname_len, const struct lyxp_set *set,
Michal Vasko93923692021-05-07 15:28:02 +020051 const struct lysc_node *ctx_scnode, const struct lys_module **moveto_mod);
Michal Vasko47da6562022-07-14 15:43:15 +020052static LY_ERR moveto_axis_node_next(const struct lyd_node **iter, enum lyxp_node_type *iter_type,
53 const struct lyd_node *node, enum lyxp_node_type node_type, enum lyxp_axis axis, struct lyxp_set *set);
Michal Vasko49fec8e2022-05-24 10:28:33 +020054static LY_ERR moveto_node(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname,
55 enum lyxp_axis axis, uint32_t options);
56static LY_ERR moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname,
57 enum lyxp_axis axis, uint32_t options);
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +020058static LY_ERR moveto_op_comp(struct lyxp_set *set1, struct lyxp_set *set2, const char *op, ly_bool *result);
Michal Vasko03ff5a72019-09-11 13:49:33 +020059
aPiecek96dc1e32021-10-08 15:45:59 +020060/* Functions are divided into the following basic classes:
61 *
62 * (re)parse functions:
63 * Parse functions parse the expression into
64 * tokens (syntactic analysis).
65 * Reparse functions perform semantic analysis
66 * (do not save the result, just a check) of
67 * the expression and fill repeat indices.
68 *
69 * warn functions:
70 * Warn functions check specific reasonable conditions for schema XPath
71 * and print a warning if they are not satisfied.
72 *
73 * moveto functions:
74 * They and only they actually change the context (set).
75 *
76 * eval functions:
77 * They execute a parsed XPath expression on some data subtree.
78 */
79
Michal Vasko03ff5a72019-09-11 13:49:33 +020080/**
81 * @brief Print the type of an XPath \p set.
82 *
83 * @param[in] set Set to use.
84 * @return Set type string.
85 */
86static const char *
87print_set_type(struct lyxp_set *set)
88{
89 switch (set->type) {
Michal Vasko03ff5a72019-09-11 13:49:33 +020090 case LYXP_SET_NODE_SET:
91 return "node set";
92 case LYXP_SET_SCNODE_SET:
93 return "schema node set";
94 case LYXP_SET_BOOLEAN:
95 return "boolean";
96 case LYXP_SET_NUMBER:
97 return "number";
98 case LYXP_SET_STRING:
99 return "string";
100 }
101
102 return NULL;
103}
104
Michal Vasko24cddf82020-06-01 08:17:01 +0200105const char *
Michal Vasko49fec8e2022-05-24 10:28:33 +0200106lyxp_token2str(enum lyxp_token tok)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200107{
108 switch (tok) {
109 case LYXP_TOKEN_PAR1:
110 return "(";
111 case LYXP_TOKEN_PAR2:
112 return ")";
113 case LYXP_TOKEN_BRACK1:
114 return "[";
115 case LYXP_TOKEN_BRACK2:
116 return "]";
117 case LYXP_TOKEN_DOT:
118 return ".";
119 case LYXP_TOKEN_DDOT:
120 return "..";
121 case LYXP_TOKEN_AT:
122 return "@";
123 case LYXP_TOKEN_COMMA:
124 return ",";
125 case LYXP_TOKEN_NAMETEST:
126 return "NameTest";
127 case LYXP_TOKEN_NODETYPE:
128 return "NodeType";
aPiecekfba75362021-10-07 12:39:48 +0200129 case LYXP_TOKEN_VARREF:
130 return "VariableReference";
Michal Vasko03ff5a72019-09-11 13:49:33 +0200131 case LYXP_TOKEN_FUNCNAME:
132 return "FunctionName";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200133 case LYXP_TOKEN_OPER_LOG:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200134 return "Operator(Logic)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200135 case LYXP_TOKEN_OPER_EQUAL:
136 return "Operator(Equal)";
137 case LYXP_TOKEN_OPER_NEQUAL:
138 return "Operator(Non-equal)";
139 case LYXP_TOKEN_OPER_COMP:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200140 return "Operator(Comparison)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200141 case LYXP_TOKEN_OPER_MATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200142 return "Operator(Math)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200143 case LYXP_TOKEN_OPER_UNI:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200144 return "Operator(Union)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200145 case LYXP_TOKEN_OPER_PATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200146 return "Operator(Path)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200147 case LYXP_TOKEN_OPER_RPATH:
Michal Vasko14676352020-05-29 11:35:55 +0200148 return "Operator(Recursive Path)";
Michal Vasko03ff5a72019-09-11 13:49:33 +0200149 case LYXP_TOKEN_LITERAL:
150 return "Literal";
151 case LYXP_TOKEN_NUMBER:
152 return "Number";
153 default:
154 LOGINT(NULL);
155 return "";
156 }
157}
158
159/**
Michal Vasko49fec8e2022-05-24 10:28:33 +0200160 * @brief Transform string into an axis.
161 *
162 * @param[in] str String to transform.
163 * @param[in] str_len Length of @p str.
164 * @return Transformed axis.
165 */
166static enum lyxp_axis
Michal Vaskodd528af2022-08-08 14:35:07 +0200167str2axis(const char *str, uint32_t str_len)
Michal Vasko49fec8e2022-05-24 10:28:33 +0200168{
169 switch (str_len) {
170 case 4:
171 assert(!strncmp("self", str, str_len));
172 return LYXP_AXIS_SELF;
173 case 5:
174 assert(!strncmp("child", str, str_len));
175 return LYXP_AXIS_CHILD;
176 case 6:
177 assert(!strncmp("parent", str, str_len));
178 return LYXP_AXIS_PARENT;
179 case 8:
180 assert(!strncmp("ancestor", str, str_len));
181 return LYXP_AXIS_ANCESTOR;
182 case 9:
183 if (str[0] == 'a') {
184 assert(!strncmp("attribute", str, str_len));
185 return LYXP_AXIS_ATTRIBUTE;
186 } else if (str[0] == 'f') {
187 assert(!strncmp("following", str, str_len));
188 return LYXP_AXIS_FOLLOWING;
189 } else {
190 assert(!strncmp("preceding", str, str_len));
191 return LYXP_AXIS_PRECEDING;
192 }
193 break;
194 case 10:
195 assert(!strncmp("descendant", str, str_len));
196 return LYXP_AXIS_DESCENDANT;
197 case 16:
198 assert(!strncmp("ancestor-or-self", str, str_len));
199 return LYXP_AXIS_ANCESTOR_OR_SELF;
200 case 17:
201 if (str[0] == 'f') {
202 assert(!strncmp("following-sibling", str, str_len));
203 return LYXP_AXIS_FOLLOWING_SIBLING;
204 } else {
205 assert(!strncmp("preceding-sibling", str, str_len));
206 return LYXP_AXIS_PRECEDING_SIBLING;
207 }
208 break;
209 case 18:
210 assert(!strncmp("descendant-or-self", str, str_len));
211 return LYXP_AXIS_DESCENDANT_OR_SELF;
212 }
213
214 LOGINT(NULL);
215 return 0;
216}
217
218/**
Michal Vasko03ff5a72019-09-11 13:49:33 +0200219 * @brief Print the whole expression \p exp to debug output.
220 *
221 * @param[in] exp Expression to use.
222 */
223static void
Michal Vasko40308e72020-10-20 16:38:40 +0200224print_expr_struct_debug(const struct lyxp_expr *exp)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200225{
Radek Krejcif13b87b2020-12-01 22:02:17 +0100226#define MSG_BUFFER_SIZE 128
227 char tmp[MSG_BUFFER_SIZE];
Michal Vasko1fdd8fa2021-01-08 09:21:45 +0100228 uint32_t i, j;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200229
Radek Krejci52b6d512020-10-12 12:33:17 +0200230 if (!exp || (ly_ll < LY_LLDBG)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200231 return;
232 }
233
234 LOGDBG(LY_LDGXPATH, "expression \"%s\":", exp->expr);
235 for (i = 0; i < exp->used; ++i) {
Michal Vasko49fec8e2022-05-24 10:28:33 +0200236 sprintf(tmp, "\ttoken %s, in expression \"%.*s\"", lyxp_token2str(exp->tokens[i]), exp->tok_len[i],
Michal Vasko69730152020-10-09 16:30:07 +0200237 &exp->expr[exp->tok_pos[i]]);
Michal Vasko23049552021-03-04 15:57:44 +0100238 if (exp->repeat && exp->repeat[i]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200239 sprintf(tmp + strlen(tmp), " (repeat %d", exp->repeat[i][0]);
240 for (j = 1; exp->repeat[i][j]; ++j) {
241 sprintf(tmp + strlen(tmp), ", %d", exp->repeat[i][j]);
242 }
243 strcat(tmp, ")");
244 }
245 LOGDBG(LY_LDGXPATH, tmp);
246 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100247#undef MSG_BUFFER_SIZE
Michal Vasko03ff5a72019-09-11 13:49:33 +0200248}
249
250#ifndef NDEBUG
251
252/**
253 * @brief Print XPath set content to debug output.
254 *
255 * @param[in] set Set to print.
256 */
257static void
258print_set_debug(struct lyxp_set *set)
259{
260 uint32_t i;
261 char *str;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200262 struct lyxp_set_node *item;
263 struct lyxp_set_scnode *sitem;
264
Radek Krejci52b6d512020-10-12 12:33:17 +0200265 if (ly_ll < LY_LLDBG) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200266 return;
267 }
268
269 switch (set->type) {
270 case LYXP_SET_NODE_SET:
271 LOGDBG(LY_LDGXPATH, "set NODE SET:");
272 for (i = 0; i < set->used; ++i) {
273 item = &set->val.nodes[i];
274
275 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +0100276 case LYXP_NODE_NONE:
277 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): NONE", i + 1, item->pos);
278 break;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200279 case LYXP_NODE_ROOT:
280 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT", i + 1, item->pos);
281 break;
282 case LYXP_NODE_ROOT_CONFIG:
283 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT CONFIG", i + 1, item->pos);
284 break;
285 case LYXP_NODE_ELEM:
Michal Vasko9e685082021-01-29 14:49:09 +0100286 if ((item->node->schema->nodetype == LYS_LIST) && (lyd_child(item->node)->schema->nodetype == LYS_LEAF)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200287 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (1st child val: %s)", i + 1, item->pos,
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200288 item->node->schema->name, lyd_get_value(lyd_child(item->node)));
Michal Vasko9e685082021-01-29 14:49:09 +0100289 } else if (item->node->schema->nodetype == LYS_LEAFLIST) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200290 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (val: %s)", i + 1, item->pos,
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200291 item->node->schema->name, lyd_get_value(item->node));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200292 } else {
293 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s", i + 1, item->pos, item->node->schema->name);
294 }
295 break;
296 case LYXP_NODE_TEXT:
297 if (item->node->schema->nodetype & LYS_ANYDATA) {
298 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT <%s>", i + 1, item->pos,
Michal Vasko69730152020-10-09 16:30:07 +0200299 item->node->schema->nodetype == LYS_ANYXML ? "anyxml" : "anydata");
Michal Vasko03ff5a72019-09-11 13:49:33 +0200300 } else {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200301 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT %s", i + 1, item->pos, lyd_get_value(item->node));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200302 }
303 break;
Michal Vasko9f96a052020-03-10 09:41:45 +0100304 case LYXP_NODE_META:
305 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): META %s = %s", i + 1, item->pos, set->val.meta[i].meta->name,
Michal Vasko69730152020-10-09 16:30:07 +0200306 set->val.meta[i].meta->value);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200307 break;
308 }
309 }
310 break;
311
312 case LYXP_SET_SCNODE_SET:
313 LOGDBG(LY_LDGXPATH, "set SCNODE SET:");
314 for (i = 0; i < set->used; ++i) {
315 sitem = &set->val.scnodes[i];
316
317 switch (sitem->type) {
318 case LYXP_NODE_ROOT:
319 LOGDBG(LY_LDGXPATH, "\t%d (%u): ROOT", i + 1, sitem->in_ctx);
320 break;
321 case LYXP_NODE_ROOT_CONFIG:
322 LOGDBG(LY_LDGXPATH, "\t%d (%u): ROOT CONFIG", i + 1, sitem->in_ctx);
323 break;
324 case LYXP_NODE_ELEM:
325 LOGDBG(LY_LDGXPATH, "\t%d (%u): ELEM %s", i + 1, sitem->in_ctx, sitem->scnode->name);
326 break;
327 default:
328 LOGINT(NULL);
329 break;
330 }
331 }
332 break;
333
Michal Vasko03ff5a72019-09-11 13:49:33 +0200334 case LYXP_SET_BOOLEAN:
335 LOGDBG(LY_LDGXPATH, "set BOOLEAN");
Michal Vasko004d3152020-06-11 19:59:22 +0200336 LOGDBG(LY_LDGXPATH, "\t%s", (set->val.bln ? "true" : "false"));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200337 break;
338
339 case LYXP_SET_STRING:
340 LOGDBG(LY_LDGXPATH, "set STRING");
341 LOGDBG(LY_LDGXPATH, "\t%s", set->val.str);
342 break;
343
344 case LYXP_SET_NUMBER:
345 LOGDBG(LY_LDGXPATH, "set NUMBER");
346
347 if (isnan(set->val.num)) {
348 str = strdup("NaN");
349 } else if ((set->val.num == 0) || (set->val.num == -0.0f)) {
350 str = strdup("0");
351 } else if (isinf(set->val.num) && !signbit(set->val.num)) {
352 str = strdup("Infinity");
353 } else if (isinf(set->val.num) && signbit(set->val.num)) {
354 str = strdup("-Infinity");
355 } else if ((long long)set->val.num == set->val.num) {
356 if (asprintf(&str, "%lld", (long long)set->val.num) == -1) {
357 str = NULL;
358 }
359 } else {
360 if (asprintf(&str, "%03.1Lf", set->val.num) == -1) {
361 str = NULL;
362 }
363 }
364 LY_CHECK_ERR_RET(!str, LOGMEM(NULL), );
365
366 LOGDBG(LY_LDGXPATH, "\t%s", str);
367 free(str);
368 }
369}
370
371#endif
372
373/**
374 * @brief Realloc the string \p str.
375 *
376 * @param[in] ctx libyang context for logging.
377 * @param[in] needed How much free space is required.
378 * @param[in,out] str Pointer to the string to use.
379 * @param[in,out] used Used bytes in \p str.
380 * @param[in,out] size Allocated bytes in \p str.
381 * @return LY_ERR
382 */
383static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +0200384cast_string_realloc(const struct ly_ctx *ctx, uint64_t needed, char **str, uint32_t *used, uint32_t *size)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200385{
Michal Vasko2291ab92022-08-08 08:53:12 +0200386 if (*size - (unsigned)*used < needed) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200387 do {
Michal Vaskodd528af2022-08-08 14:35:07 +0200388 if ((UINT32_MAX - *size) < LYXP_STRING_CAST_SIZE_STEP) {
389 LOGERR(ctx, LY_EINVAL, "XPath string length limit (%" PRIu32 ") reached.", UINT32_MAX);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200390 return LY_EINVAL;
391 }
392 *size += LYXP_STRING_CAST_SIZE_STEP;
Michal Vasko2291ab92022-08-08 08:53:12 +0200393 } while (*size - (unsigned)*used < needed);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200394 *str = ly_realloc(*str, *size * sizeof(char));
395 LY_CHECK_ERR_RET(!(*str), LOGMEM(ctx), LY_EMEM);
396 }
397
398 return LY_SUCCESS;
399}
400
401/**
402 * @brief Cast nodes recursively to one string @p str.
403 *
Michal Vasko47da6562022-07-14 15:43:15 +0200404 * @param[in] node Node to cast, NULL if root.
405 * @param[in] set XPath set.
Michal Vasko03ff5a72019-09-11 13:49:33 +0200406 * @param[in] indent Current indent.
407 * @param[in,out] str Resulting string.
408 * @param[in,out] used Used bytes in @p str.
409 * @param[in,out] size Allocated bytes in @p str.
Michal Vasko47da6562022-07-14 15:43:15 +0200410 * @return LY_ERR value.
Michal Vasko03ff5a72019-09-11 13:49:33 +0200411 */
412static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +0200413cast_string_recursive(const struct lyd_node *node, struct lyxp_set *set, uint32_t indent, char **str, uint32_t *used,
414 uint32_t *size)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200415{
Radek Krejci7f769d72020-07-11 23:13:56 +0200416 char *buf, *line, *ptr = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200417 const char *value_str;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200418 const struct lyd_node *child;
Michal Vasko47da6562022-07-14 15:43:15 +0200419 enum lyxp_node_type child_type;
Michal Vasko60ea6352020-06-29 13:39:39 +0200420 struct lyd_node *tree;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200421 struct lyd_node_any *any;
422 LY_ERR rc;
423
Michal Vasko47da6562022-07-14 15:43:15 +0200424 if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && node && (node->schema->flags & LYS_CONFIG_R)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200425 return LY_SUCCESS;
426 }
427
Michal Vasko47da6562022-07-14 15:43:15 +0200428 if (!node) {
429 /* fake container */
430 LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200431 strcpy(*str + (*used - 1), "\n");
432 ++(*used);
433
434 ++indent;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200435
Michal Vasko47da6562022-07-14 15:43:15 +0200436 /* print all the top-level nodes */
437 child = NULL;
438 child_type = 0;
439 while (!moveto_axis_node_next(&child, &child_type, NULL, set->root_type, LYXP_AXIS_CHILD, set)) {
440 LY_CHECK_RET(cast_string_recursive(child, set, indent, str, used, size));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200441 }
442
Michal Vasko47da6562022-07-14 15:43:15 +0200443 /* end fake container */
444 LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200445 strcpy(*str + (*used - 1), "\n");
446 ++(*used);
447
448 --indent;
Michal Vasko47da6562022-07-14 15:43:15 +0200449 } else {
450 switch (node->schema->nodetype) {
451 case LYS_CONTAINER:
452 case LYS_LIST:
453 case LYS_RPC:
454 case LYS_NOTIF:
455 LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
456 strcpy(*str + (*used - 1), "\n");
457 ++(*used);
458
459 for (child = lyd_child(node); child; child = child->next) {
460 LY_CHECK_RET(cast_string_recursive(child, set, indent + 1, str, used, size));
461 }
462
463 break;
464
465 case LYS_LEAF:
466 case LYS_LEAFLIST:
467 value_str = lyd_get_value(node);
468
469 /* print indent */
470 LY_CHECK_RET(cast_string_realloc(set->ctx, indent * 2 + strlen(value_str) + 1, str, used, size));
471 memset(*str + (*used - 1), ' ', indent * 2);
472 *used += indent * 2;
473
474 /* print value */
475 if (*used == 1) {
476 sprintf(*str + (*used - 1), "%s", value_str);
477 *used += strlen(value_str);
478 } else {
479 sprintf(*str + (*used - 1), "%s\n", value_str);
480 *used += strlen(value_str) + 1;
481 }
482
483 break;
484
485 case LYS_ANYXML:
486 case LYS_ANYDATA:
487 any = (struct lyd_node_any *)node;
488 if (!(void *)any->value.tree) {
489 /* no content */
490 buf = strdup("");
491 LY_CHECK_ERR_RET(!buf, LOGMEM(set->ctx), LY_EMEM);
492 } else {
493 struct ly_out *out;
494
495 if (any->value_type == LYD_ANYDATA_LYB) {
496 /* try to parse it into a data tree */
497 if (lyd_parse_data_mem((struct ly_ctx *)set->ctx, any->value.mem, LYD_LYB,
498 LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree) == LY_SUCCESS) {
499 /* successfully parsed */
500 free(any->value.mem);
501 any->value.tree = tree;
502 any->value_type = LYD_ANYDATA_DATATREE;
503 }
504 /* error is covered by the following switch where LYD_ANYDATA_LYB causes failure */
505 }
506
507 switch (any->value_type) {
508 case LYD_ANYDATA_STRING:
509 case LYD_ANYDATA_XML:
510 case LYD_ANYDATA_JSON:
511 buf = strdup(any->value.json);
512 LY_CHECK_ERR_RET(!buf, LOGMEM(set->ctx), LY_EMEM);
513 break;
514 case LYD_ANYDATA_DATATREE:
515 LY_CHECK_RET(ly_out_new_memory(&buf, 0, &out));
516 rc = lyd_print_all(out, any->value.tree, LYD_XML, 0);
517 ly_out_free(out, NULL, 0);
518 LY_CHECK_RET(rc < 0, -rc);
519 break;
520 case LYD_ANYDATA_LYB:
521 LOGERR(set->ctx, LY_EINVAL, "Cannot convert LYB anydata into string.");
522 return LY_EINVAL;
523 }
524 }
525
526 line = strtok_r(buf, "\n", &ptr);
527 do {
528 rc = cast_string_realloc(set->ctx, indent * 2 + strlen(line) + 1, str, used, size);
529 if (rc != LY_SUCCESS) {
530 free(buf);
531 return rc;
532 }
533 memset(*str + (*used - 1), ' ', indent * 2);
534 *used += indent * 2;
535
536 strcpy(*str + (*used - 1), line);
537 *used += strlen(line);
538
539 strcpy(*str + (*used - 1), "\n");
540 *used += 1;
541 } while ((line = strtok_r(NULL, "\n", &ptr)));
542
543 free(buf);
544 break;
545
546 default:
547 LOGINT_RET(set->ctx);
548 }
Michal Vasko03ff5a72019-09-11 13:49:33 +0200549 }
550
551 return LY_SUCCESS;
552}
553
554/**
555 * @brief Cast an element into a string.
556 *
Michal Vasko47da6562022-07-14 15:43:15 +0200557 * @param[in] node Node to cast, NULL if root.
558 * @param[in] set XPath set.
Michal Vasko03ff5a72019-09-11 13:49:33 +0200559 * @param[out] str Element cast to dynamically-allocated string.
560 * @return LY_ERR
561 */
562static LY_ERR
Michal Vasko47da6562022-07-14 15:43:15 +0200563cast_string_elem(const struct lyd_node *node, struct lyxp_set *set, char **str)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200564{
Michal Vaskodd528af2022-08-08 14:35:07 +0200565 uint32_t used, size;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200566 LY_ERR rc;
567
568 *str = malloc(LYXP_STRING_CAST_SIZE_START * sizeof(char));
Michal Vasko47da6562022-07-14 15:43:15 +0200569 LY_CHECK_ERR_RET(!*str, LOGMEM(set->ctx), LY_EMEM);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200570 (*str)[0] = '\0';
571 used = 1;
572 size = LYXP_STRING_CAST_SIZE_START;
573
Michal Vasko47da6562022-07-14 15:43:15 +0200574 rc = cast_string_recursive(node, set, 0, str, &used, &size);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200575 if (rc != LY_SUCCESS) {
576 free(*str);
577 return rc;
578 }
579
580 if (size > used) {
581 *str = ly_realloc(*str, used * sizeof(char));
Michal Vasko47da6562022-07-14 15:43:15 +0200582 LY_CHECK_ERR_RET(!*str, LOGMEM(set->ctx), LY_EMEM);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200583 }
584 return LY_SUCCESS;
585}
586
587/**
588 * @brief Cast a LYXP_SET_NODE_SET set into a string.
589 * Context position aware.
590 *
591 * @param[in] set Set to cast.
Michal Vasko03ff5a72019-09-11 13:49:33 +0200592 * @param[out] str Cast dynamically-allocated string.
593 * @return LY_ERR
594 */
595static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +0100596cast_node_set_to_string(struct lyxp_set *set, char **str)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200597{
Michal Vaskoaa677062021-03-09 13:52:53 +0100598 if (!set->used) {
599 *str = strdup("");
600 if (!*str) {
601 LOGMEM_RET(set->ctx);
602 }
603 return LY_SUCCESS;
604 }
605
Michal Vasko03ff5a72019-09-11 13:49:33 +0200606 switch (set->val.nodes[0].type) {
Michal Vasko2caefc12019-11-14 16:07:56 +0100607 case LYXP_NODE_NONE:
608 /* invalid */
609 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200610 case LYXP_NODE_ROOT:
611 case LYXP_NODE_ROOT_CONFIG:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200612 case LYXP_NODE_ELEM:
613 case LYXP_NODE_TEXT:
Michal Vasko47da6562022-07-14 15:43:15 +0200614 return cast_string_elem(set->val.nodes[0].node, set, str);
Michal Vasko9f96a052020-03-10 09:41:45 +0100615 case LYXP_NODE_META:
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200616 *str = strdup(lyd_get_meta_value(set->val.meta[0].meta));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200617 if (!*str) {
618 LOGMEM_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200619 }
620 return LY_SUCCESS;
621 }
622
623 LOGINT_RET(set->ctx);
624}
625
626/**
627 * @brief Cast a string into an XPath number.
628 *
629 * @param[in] str String to use.
630 * @return Cast number.
631 */
632static long double
633cast_string_to_number(const char *str)
634{
635 long double num;
636 char *ptr;
637
638 errno = 0;
639 num = strtold(str, &ptr);
640 if (errno || *ptr) {
641 num = NAN;
642 }
643 return num;
644}
645
646/**
647 * @brief Callback for checking value equality.
648 *
Michal Vasko62524a92021-02-26 10:08:50 +0100649 * Implementation of ::lyht_value_equal_cb.
Radek Krejci857189e2020-09-01 13:26:36 +0200650 *
Michal Vasko03ff5a72019-09-11 13:49:33 +0200651 * @param[in] val1_p First value.
652 * @param[in] val2_p Second value.
653 * @param[in] mod Whether hash table is being modified.
654 * @param[in] cb_data Callback data.
Radek Krejci857189e2020-09-01 13:26:36 +0200655 * @return Boolean value whether values are equal or not.
Michal Vasko03ff5a72019-09-11 13:49:33 +0200656 */
Radek Krejci857189e2020-09-01 13:26:36 +0200657static ly_bool
658set_values_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
Michal Vasko03ff5a72019-09-11 13:49:33 +0200659{
660 struct lyxp_set_hash_node *val1, *val2;
661
662 val1 = (struct lyxp_set_hash_node *)val1_p;
663 val2 = (struct lyxp_set_hash_node *)val2_p;
664
665 if ((val1->node == val2->node) && (val1->type == val2->type)) {
666 return 1;
667 }
668
669 return 0;
670}
671
672/**
673 * @brief Insert node and its hash into set.
674 *
675 * @param[in] set et to insert to.
676 * @param[in] node Node with hash.
677 * @param[in] type Node type.
678 */
679static void
680set_insert_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type)
681{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200682 LY_ERR r;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200683 uint32_t i, hash;
684 struct lyxp_set_hash_node hnode;
685
686 if (!set->ht && (set->used >= LYD_HT_MIN_ITEMS)) {
687 /* create hash table and add all the nodes */
688 set->ht = lyht_new(1, sizeof(struct lyxp_set_hash_node), set_values_equal_cb, NULL, 1);
689 for (i = 0; i < set->used; ++i) {
690 hnode.node = set->val.nodes[i].node;
691 hnode.type = set->val.nodes[i].type;
692
693 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
694 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
695 hash = dict_hash_multi(hash, NULL, 0);
696
697 r = lyht_insert(set->ht, &hnode, hash, NULL);
698 assert(!r);
699 (void)r;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200700
Michal Vasko9d6befd2019-12-11 14:56:56 +0100701 if (hnode.node == node) {
702 /* it was just added, do not add it twice */
703 node = NULL;
704 }
705 }
706 }
707
708 if (set->ht && node) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200709 /* add the new node into hash table */
710 hnode.node = node;
711 hnode.type = type;
712
713 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
714 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
715 hash = dict_hash_multi(hash, NULL, 0);
716
717 r = lyht_insert(set->ht, &hnode, hash, NULL);
718 assert(!r);
719 (void)r;
720 }
721}
722
723/**
724 * @brief Remove node and its hash from set.
725 *
726 * @param[in] set Set to remove from.
727 * @param[in] node Node to remove.
728 * @param[in] type Node type.
729 */
730static void
731set_remove_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type)
732{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200733 LY_ERR r;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200734 struct lyxp_set_hash_node hnode;
735 uint32_t hash;
736
737 if (set->ht) {
738 hnode.node = node;
739 hnode.type = type;
740
741 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
742 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
743 hash = dict_hash_multi(hash, NULL, 0);
744
745 r = lyht_remove(set->ht, &hnode, hash);
746 assert(!r);
747 (void)r;
748
749 if (!set->ht->used) {
750 lyht_free(set->ht);
751 set->ht = NULL;
752 }
753 }
754}
755
756/**
757 * @brief Check whether node is in set based on its hash.
758 *
759 * @param[in] set Set to search in.
760 * @param[in] node Node to search for.
761 * @param[in] type Node type.
762 * @param[in] skip_idx Index in @p set to skip.
763 * @return LY_ERR
764 */
765static LY_ERR
766set_dup_node_hash_check(const struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type, int skip_idx)
767{
768 struct lyxp_set_hash_node hnode, *match_p;
769 uint32_t hash;
770
771 hnode.node = node;
772 hnode.type = type;
773
774 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
775 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
776 hash = dict_hash_multi(hash, NULL, 0);
777
778 if (!lyht_find(set->ht, &hnode, hash, (void **)&match_p)) {
779 if ((skip_idx > -1) && (set->val.nodes[skip_idx].node == match_p->node) && (set->val.nodes[skip_idx].type == match_p->type)) {
780 /* we found it on the index that should be skipped, find another */
781 hnode = *match_p;
782 if (lyht_find_next(set->ht, &hnode, hash, (void **)&match_p)) {
783 /* none other found */
784 return LY_SUCCESS;
785 }
786 }
787
788 return LY_EEXIST;
789 }
790
791 /* not found */
792 return LY_SUCCESS;
793}
794
Michal Vaskod3678892020-05-21 10:06:58 +0200795void
796lyxp_set_free_content(struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200797{
798 if (!set) {
799 return;
800 }
801
802 if (set->type == LYXP_SET_NODE_SET) {
803 free(set->val.nodes);
804 lyht_free(set->ht);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200805 } else if (set->type == LYXP_SET_SCNODE_SET) {
806 free(set->val.scnodes);
Michal Vaskod3678892020-05-21 10:06:58 +0200807 lyht_free(set->ht);
808 } else {
809 if (set->type == LYXP_SET_STRING) {
810 free(set->val.str);
811 }
812 set->type = LYXP_SET_NODE_SET;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200813 }
Michal Vaskod3678892020-05-21 10:06:58 +0200814
815 set->val.nodes = NULL;
816 set->used = 0;
817 set->size = 0;
818 set->ht = NULL;
819 set->ctx_pos = 0;
aPiecek748da732021-06-01 09:56:43 +0200820 set->ctx_size = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200821}
822
Michal Vasko5e0e6eb2019-11-06 15:47:50 +0100823/**
824 * @brief Free dynamically-allocated set.
825 *
826 * @param[in] set Set to free.
827 */
828static void
Michal Vasko03ff5a72019-09-11 13:49:33 +0200829lyxp_set_free(struct lyxp_set *set)
830{
831 if (!set) {
832 return;
833 }
834
Michal Vaskod3678892020-05-21 10:06:58 +0200835 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200836 free(set);
837}
838
839/**
840 * @brief Initialize set context.
841 *
842 * @param[in] new Set to initialize.
843 * @param[in] set Arbitrary initialized set.
844 */
845static void
Michal Vasko4c7763f2020-07-27 17:40:37 +0200846set_init(struct lyxp_set *new, const struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200847{
848 memset(new, 0, sizeof *new);
Michal Vasko02a77382019-09-12 11:47:35 +0200849 if (set) {
Michal Vasko306e2832022-07-25 09:15:17 +0200850 new->non_child_axis = set->non_child_axis;
Michal Vasko02a77382019-09-12 11:47:35 +0200851 new->ctx = set->ctx;
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200852 new->cur_node = set->cur_node;
Michal Vasko588112f2019-12-10 14:51:53 +0100853 new->root_type = set->root_type;
Michal Vasko6b26e742020-07-17 15:02:10 +0200854 new->context_op = set->context_op;
Michal Vaskof03ed032020-03-04 13:31:44 +0100855 new->tree = set->tree;
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200856 new->cur_mod = set->cur_mod;
Michal Vasko02a77382019-09-12 11:47:35 +0200857 new->format = set->format;
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200858 new->prefix_data = set->prefix_data;
aPiecekfba75362021-10-07 12:39:48 +0200859 new->vars = set->vars;
Michal Vasko02a77382019-09-12 11:47:35 +0200860 }
Michal Vasko03ff5a72019-09-11 13:49:33 +0200861}
862
863/**
864 * @brief Create a deep copy of a set.
865 *
866 * @param[in] set Set to copy.
867 * @return Copy of @p set.
868 */
869static struct lyxp_set *
870set_copy(struct lyxp_set *set)
871{
872 struct lyxp_set *ret;
Michal Vasko1fdd8fa2021-01-08 09:21:45 +0100873 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200874
875 if (!set) {
876 return NULL;
877 }
878
879 ret = malloc(sizeof *ret);
880 LY_CHECK_ERR_RET(!ret, LOGMEM(set->ctx), NULL);
881 set_init(ret, set);
882
883 if (set->type == LYXP_SET_SCNODE_SET) {
884 ret->type = set->type;
885
886 for (i = 0; i < set->used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100887 if ((set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) ||
888 (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START)) {
Radek Krejciaa6b53f2020-08-27 15:19:03 +0200889 uint32_t idx;
Michal Vasko26bbb272022-08-02 14:54:33 +0200890
Michal Vasko7333cb32022-07-29 16:30:29 +0200891 LY_CHECK_ERR_RET(set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
892 set->val.scnodes[i].axis, &idx), lyxp_set_free(ret), NULL);
Michal Vasko3f27c522020-01-06 08:37:49 +0100893 /* coverity seems to think scnodes can be NULL */
Radek Krejciaa6b53f2020-08-27 15:19:03 +0200894 if (!ret->val.scnodes) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200895 lyxp_set_free(ret);
896 return NULL;
897 }
Michal Vaskoba716542019-12-16 10:01:58 +0100898 ret->val.scnodes[idx].in_ctx = set->val.scnodes[i].in_ctx;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200899 }
900 }
901 } else if (set->type == LYXP_SET_NODE_SET) {
902 ret->type = set->type;
Michal Vasko08e9b112021-06-11 15:41:17 +0200903 if (set->used) {
904 ret->val.nodes = malloc(set->used * sizeof *ret->val.nodes);
905 LY_CHECK_ERR_RET(!ret->val.nodes, LOGMEM(set->ctx); free(ret), NULL);
906 memcpy(ret->val.nodes, set->val.nodes, set->used * sizeof *ret->val.nodes);
907 } else {
908 ret->val.nodes = NULL;
909 }
Michal Vasko03ff5a72019-09-11 13:49:33 +0200910
911 ret->used = ret->size = set->used;
912 ret->ctx_pos = set->ctx_pos;
913 ret->ctx_size = set->ctx_size;
Michal Vasko4a04e542021-02-01 08:58:30 +0100914 if (set->ht) {
915 ret->ht = lyht_dup(set->ht);
916 }
Michal Vasko03ff5a72019-09-11 13:49:33 +0200917 } else {
Radek Krejci0f969882020-08-21 16:56:47 +0200918 memcpy(ret, set, sizeof *ret);
919 if (set->type == LYXP_SET_STRING) {
920 ret->val.str = strdup(set->val.str);
921 LY_CHECK_ERR_RET(!ret->val.str, LOGMEM(set->ctx); free(ret), NULL);
922 }
Michal Vasko03ff5a72019-09-11 13:49:33 +0200923 }
924
925 return ret;
926}
927
928/**
929 * @brief Fill XPath set with a string. Any current data are disposed of.
930 *
931 * @param[in] set Set to fill.
932 * @param[in] string String to fill into \p set.
933 * @param[in] str_len Length of \p string. 0 is a valid value!
934 */
935static void
Michal Vaskodd528af2022-08-08 14:35:07 +0200936set_fill_string(struct lyxp_set *set, const char *string, uint32_t str_len)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200937{
Michal Vaskod3678892020-05-21 10:06:58 +0200938 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200939
940 set->type = LYXP_SET_STRING;
941 if ((str_len == 0) && (string[0] != '\0')) {
942 string = "";
943 }
944 set->val.str = strndup(string, str_len);
945}
946
947/**
948 * @brief Fill XPath set with a number. Any current data are disposed of.
949 *
950 * @param[in] set Set to fill.
951 * @param[in] number Number to fill into \p set.
952 */
953static void
954set_fill_number(struct lyxp_set *set, long double number)
955{
Michal Vaskod3678892020-05-21 10:06:58 +0200956 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200957
958 set->type = LYXP_SET_NUMBER;
959 set->val.num = number;
960}
961
962/**
963 * @brief Fill XPath set with a boolean. Any current data are disposed of.
964 *
965 * @param[in] set Set to fill.
966 * @param[in] boolean Boolean to fill into \p set.
967 */
968static void
Radek Krejci857189e2020-09-01 13:26:36 +0200969set_fill_boolean(struct lyxp_set *set, ly_bool boolean)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200970{
Michal Vaskod3678892020-05-21 10:06:58 +0200971 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200972
973 set->type = LYXP_SET_BOOLEAN;
Michal Vasko004d3152020-06-11 19:59:22 +0200974 set->val.bln = boolean;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200975}
976
977/**
978 * @brief Fill XPath set with the value from another set (deep assign).
979 * Any current data are disposed of.
980 *
981 * @param[in] trg Set to fill.
982 * @param[in] src Source set to copy into \p trg.
983 */
984static void
Michal Vasko4c7763f2020-07-27 17:40:37 +0200985set_fill_set(struct lyxp_set *trg, const struct lyxp_set *src)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200986{
987 if (!trg || !src) {
988 return;
989 }
990
991 if (trg->type == LYXP_SET_NODE_SET) {
992 free(trg->val.nodes);
993 } else if (trg->type == LYXP_SET_STRING) {
994 free(trg->val.str);
995 }
996 set_init(trg, src);
997
998 if (src->type == LYXP_SET_SCNODE_SET) {
999 trg->type = LYXP_SET_SCNODE_SET;
1000 trg->used = src->used;
1001 trg->size = src->used;
1002
Michal Vasko08e9b112021-06-11 15:41:17 +02001003 if (trg->size) {
1004 trg->val.scnodes = ly_realloc(trg->val.scnodes, trg->size * sizeof *trg->val.scnodes);
1005 LY_CHECK_ERR_RET(!trg->val.scnodes, LOGMEM(src->ctx); memset(trg, 0, sizeof *trg), );
1006 memcpy(trg->val.scnodes, src->val.scnodes, src->used * sizeof *src->val.scnodes);
1007 } else {
1008 trg->val.scnodes = NULL;
1009 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02001010 } else if (src->type == LYXP_SET_BOOLEAN) {
Michal Vasko004d3152020-06-11 19:59:22 +02001011 set_fill_boolean(trg, src->val.bln);
Michal Vasko44f3d2c2020-08-24 09:49:38 +02001012 } else if (src->type == LYXP_SET_NUMBER) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001013 set_fill_number(trg, src->val.num);
1014 } else if (src->type == LYXP_SET_STRING) {
1015 set_fill_string(trg, src->val.str, strlen(src->val.str));
1016 } else {
1017 if (trg->type == LYXP_SET_NODE_SET) {
1018 free(trg->val.nodes);
1019 } else if (trg->type == LYXP_SET_STRING) {
1020 free(trg->val.str);
1021 }
1022
Michal Vaskod3678892020-05-21 10:06:58 +02001023 assert(src->type == LYXP_SET_NODE_SET);
1024
1025 trg->type = LYXP_SET_NODE_SET;
1026 trg->used = src->used;
1027 trg->size = src->used;
1028 trg->ctx_pos = src->ctx_pos;
1029 trg->ctx_size = src->ctx_size;
1030
Michal Vasko08e9b112021-06-11 15:41:17 +02001031 if (trg->size) {
1032 trg->val.nodes = malloc(trg->size * sizeof *trg->val.nodes);
1033 LY_CHECK_ERR_RET(!trg->val.nodes, LOGMEM(src->ctx); memset(trg, 0, sizeof *trg), );
1034 memcpy(trg->val.nodes, src->val.nodes, src->used * sizeof *src->val.nodes);
1035 } else {
1036 trg->val.nodes = NULL;
1037 }
Michal Vaskod3678892020-05-21 10:06:58 +02001038 if (src->ht) {
1039 trg->ht = lyht_dup(src->ht);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001040 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02001041 trg->ht = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001042 }
1043 }
1044}
1045
1046/**
1047 * @brief Clear context of all schema nodes.
1048 *
1049 * @param[in] set Set to clear.
Michal Vasko1a09b212021-05-06 13:00:10 +02001050 * @param[in] new_ctx New context state for all the nodes currently in the context.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001051 */
1052static void
Michal Vasko1a09b212021-05-06 13:00:10 +02001053set_scnode_clear_ctx(struct lyxp_set *set, int32_t new_ctx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001054{
1055 uint32_t i;
1056
1057 for (i = 0; i < set->used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01001058 if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
Michal Vasko1a09b212021-05-06 13:00:10 +02001059 set->val.scnodes[i].in_ctx = new_ctx;
Radek Krejcif13b87b2020-12-01 22:02:17 +01001060 } else if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START) {
1061 set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_START_USED;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001062 }
1063 }
1064}
1065
1066/**
1067 * @brief Remove a node from a set. Removing last node changes
1068 * set into LYXP_SET_EMPTY. Context position aware.
1069 *
1070 * @param[in] set Set to use.
1071 * @param[in] idx Index from @p set of the node to be removed.
1072 */
1073static void
1074set_remove_node(struct lyxp_set *set, uint32_t idx)
1075{
1076 assert(set && (set->type == LYXP_SET_NODE_SET));
1077 assert(idx < set->used);
1078
1079 set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
1080
1081 --set->used;
Michal Vasko08e9b112021-06-11 15:41:17 +02001082 if (idx < set->used) {
1083 memmove(&set->val.nodes[idx], &set->val.nodes[idx + 1], (set->used - idx) * sizeof *set->val.nodes);
1084 } else if (!set->used) {
Michal Vaskod3678892020-05-21 10:06:58 +02001085 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001086 }
1087}
1088
1089/**
Michal Vasko2caefc12019-11-14 16:07:56 +01001090 * @brief Remove a node from a set by setting its type to LYXP_NODE_NONE.
Michal Vasko57eab132019-09-24 11:46:26 +02001091 *
1092 * @param[in] set Set to use.
1093 * @param[in] idx Index from @p set of the node to be removed.
1094 */
1095static void
Michal Vasko2caefc12019-11-14 16:07:56 +01001096set_remove_node_none(struct lyxp_set *set, uint32_t idx)
Michal Vasko57eab132019-09-24 11:46:26 +02001097{
1098 assert(set && (set->type == LYXP_SET_NODE_SET));
1099 assert(idx < set->used);
1100
Michal Vasko2caefc12019-11-14 16:07:56 +01001101 if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
1102 set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
1103 }
1104 set->val.nodes[idx].type = LYXP_NODE_NONE;
Michal Vasko57eab132019-09-24 11:46:26 +02001105}
1106
1107/**
Michal Vasko2caefc12019-11-14 16:07:56 +01001108 * @brief Remove all LYXP_NODE_NONE nodes from a set. Removing last node changes
Michal Vasko57eab132019-09-24 11:46:26 +02001109 * set into LYXP_SET_EMPTY. Context position aware.
1110 *
1111 * @param[in] set Set to consolidate.
1112 */
1113static void
Michal Vasko2caefc12019-11-14 16:07:56 +01001114set_remove_nodes_none(struct lyxp_set *set)
Michal Vasko57eab132019-09-24 11:46:26 +02001115{
Michal Vasko1fdd8fa2021-01-08 09:21:45 +01001116 uint32_t i, orig_used, end = 0;
1117 int64_t start;
Michal Vasko57eab132019-09-24 11:46:26 +02001118
Michal Vaskod3678892020-05-21 10:06:58 +02001119 assert(set);
Michal Vasko57eab132019-09-24 11:46:26 +02001120
1121 orig_used = set->used;
1122 set->used = 0;
Michal Vaskod989ba02020-08-24 10:59:24 +02001123 for (i = 0; i < orig_used; ) {
Michal Vasko57eab132019-09-24 11:46:26 +02001124 start = -1;
1125 do {
Michal Vasko2caefc12019-11-14 16:07:56 +01001126 if ((set->val.nodes[i].type != LYXP_NODE_NONE) && (start == -1)) {
Michal Vasko57eab132019-09-24 11:46:26 +02001127 start = i;
Michal Vasko2caefc12019-11-14 16:07:56 +01001128 } else if ((start > -1) && (set->val.nodes[i].type == LYXP_NODE_NONE)) {
Michal Vasko57eab132019-09-24 11:46:26 +02001129 end = i;
1130 ++i;
1131 break;
1132 }
1133
1134 ++i;
1135 if (i == orig_used) {
1136 end = i;
1137 }
1138 } while (i < orig_used);
1139
1140 if (start > -1) {
1141 /* move the whole chunk of valid nodes together */
1142 if (set->used != (unsigned)start) {
1143 memmove(&set->val.nodes[set->used], &set->val.nodes[start], (end - start) * sizeof *set->val.nodes);
1144 }
1145 set->used += end - start;
1146 }
1147 }
Michal Vasko57eab132019-09-24 11:46:26 +02001148}
1149
1150/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02001151 * @brief Check for duplicates in a node set.
1152 *
1153 * @param[in] set Set to check.
1154 * @param[in] node Node to look for in @p set.
1155 * @param[in] node_type Type of @p node.
1156 * @param[in] skip_idx Index from @p set to skip.
1157 * @return LY_ERR
1158 */
1159static LY_ERR
1160set_dup_node_check(const struct lyxp_set *set, const struct lyd_node *node, enum lyxp_node_type node_type, int skip_idx)
1161{
1162 uint32_t i;
1163
Michal Vasko2caefc12019-11-14 16:07:56 +01001164 if (set->ht && node) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001165 return set_dup_node_hash_check(set, (struct lyd_node *)node, node_type, skip_idx);
1166 }
1167
1168 for (i = 0; i < set->used; ++i) {
1169 if ((skip_idx > -1) && (i == (unsigned)skip_idx)) {
1170 continue;
1171 }
1172
1173 if ((set->val.nodes[i].node == node) && (set->val.nodes[i].type == node_type)) {
1174 return LY_EEXIST;
1175 }
1176 }
1177
1178 return LY_SUCCESS;
1179}
1180
Radek Krejci857189e2020-09-01 13:26:36 +02001181ly_bool
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001182lyxp_set_scnode_contains(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type, int skip_idx,
1183 uint32_t *index_p)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001184{
1185 uint32_t i;
1186
1187 for (i = 0; i < set->used; ++i) {
1188 if ((skip_idx > -1) && (i == (unsigned)skip_idx)) {
1189 continue;
1190 }
1191
1192 if ((set->val.scnodes[i].scnode == node) && (set->val.scnodes[i].type == node_type)) {
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001193 if (index_p) {
1194 *index_p = i;
1195 }
1196 return 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001197 }
1198 }
1199
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001200 return 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001201}
1202
Michal Vaskoecd62de2019-11-13 12:35:11 +01001203void
1204lyxp_set_scnode_merge(struct lyxp_set *set1, struct lyxp_set *set2)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001205{
1206 uint32_t orig_used, i, j;
1207
Michal Vaskod3678892020-05-21 10:06:58 +02001208 assert((set1->type == LYXP_SET_SCNODE_SET) && (set2->type == LYXP_SET_SCNODE_SET));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001209
Michal Vaskod3678892020-05-21 10:06:58 +02001210 if (!set2->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001211 return;
1212 }
1213
Michal Vaskod3678892020-05-21 10:06:58 +02001214 if (!set1->used) {
aPiecekadc1e4f2021-10-07 11:15:12 +02001215 /* release hidden allocated data (lyxp_set.size) */
1216 lyxp_set_free_content(set1);
1217 /* direct copying of the entire structure */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001218 memcpy(set1, set2, sizeof *set1);
1219 return;
1220 }
1221
1222 if (set1->used + set2->used > set1->size) {
1223 set1->size = set1->used + set2->used;
1224 set1->val.scnodes = ly_realloc(set1->val.scnodes, set1->size * sizeof *set1->val.scnodes);
1225 LY_CHECK_ERR_RET(!set1->val.scnodes, LOGMEM(set1->ctx), );
1226 }
1227
1228 orig_used = set1->used;
1229
1230 for (i = 0; i < set2->used; ++i) {
1231 for (j = 0; j < orig_used; ++j) {
1232 /* detect duplicities */
1233 if (set1->val.scnodes[j].scnode == set2->val.scnodes[i].scnode) {
1234 break;
1235 }
1236 }
1237
Michal Vasko0587bce2020-12-10 12:19:21 +01001238 if (j < orig_used) {
1239 /* node is there, but update its status if needed */
1240 if (set1->val.scnodes[j].in_ctx == LYXP_SET_SCNODE_START_USED) {
1241 set1->val.scnodes[j].in_ctx = set2->val.scnodes[i].in_ctx;
Michal Vasko1a09b212021-05-06 13:00:10 +02001242 } else if ((set1->val.scnodes[j].in_ctx == LYXP_SET_SCNODE_ATOM_NODE) &&
1243 (set2->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
1244 set1->val.scnodes[j].in_ctx = set2->val.scnodes[i].in_ctx;
Michal Vasko0587bce2020-12-10 12:19:21 +01001245 }
1246 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001247 memcpy(&set1->val.scnodes[set1->used], &set2->val.scnodes[i], sizeof *set2->val.scnodes);
1248 ++set1->used;
1249 }
1250 }
1251
Michal Vaskod3678892020-05-21 10:06:58 +02001252 lyxp_set_free_content(set2);
1253 set2->type = LYXP_SET_SCNODE_SET;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001254}
1255
1256/**
1257 * @brief Insert a node into a set. Context position aware.
1258 *
1259 * @param[in] set Set to use.
1260 * @param[in] node Node to insert to @p set.
1261 * @param[in] pos Sort position of @p node. If left 0, it is filled just before sorting.
1262 * @param[in] node_type Node type of @p node.
1263 * @param[in] idx Index in @p set to insert into.
1264 */
1265static void
1266set_insert_node(struct lyxp_set *set, const struct lyd_node *node, uint32_t pos, enum lyxp_node_type node_type, uint32_t idx)
1267{
Michal Vaskod3678892020-05-21 10:06:58 +02001268 assert(set && (set->type == LYXP_SET_NODE_SET));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001269
Michal Vaskod3678892020-05-21 10:06:58 +02001270 if (!set->size) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001271 /* first item */
1272 if (idx) {
1273 /* no real harm done, but it is a bug */
1274 LOGINT(set->ctx);
1275 idx = 0;
1276 }
1277 set->val.nodes = malloc(LYXP_SET_SIZE_START * sizeof *set->val.nodes);
1278 LY_CHECK_ERR_RET(!set->val.nodes, LOGMEM(set->ctx), );
1279 set->type = LYXP_SET_NODE_SET;
1280 set->used = 0;
1281 set->size = LYXP_SET_SIZE_START;
1282 set->ctx_pos = 1;
1283 set->ctx_size = 1;
1284 set->ht = NULL;
1285 } else {
1286 /* not an empty set */
1287 if (set->used == set->size) {
1288
1289 /* set is full */
Michal Vasko871df522022-04-06 12:14:41 +02001290 set->val.nodes = ly_realloc(set->val.nodes, (set->size * LYXP_SET_SIZE_MUL_STEP) * sizeof *set->val.nodes);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001291 LY_CHECK_ERR_RET(!set->val.nodes, LOGMEM(set->ctx), );
Michal Vasko871df522022-04-06 12:14:41 +02001292 set->size *= LYXP_SET_SIZE_MUL_STEP;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001293 }
1294
1295 if (idx > set->used) {
1296 LOGINT(set->ctx);
1297 idx = set->used;
1298 }
1299
1300 /* make space for the new node */
1301 if (idx < set->used) {
1302 memmove(&set->val.nodes[idx + 1], &set->val.nodes[idx], (set->used - idx) * sizeof *set->val.nodes);
1303 }
1304 }
1305
1306 /* finally assign the value */
1307 set->val.nodes[idx].node = (struct lyd_node *)node;
1308 set->val.nodes[idx].type = node_type;
1309 set->val.nodes[idx].pos = pos;
1310 ++set->used;
1311
Michal Vasko2416fdd2021-10-01 11:07:10 +02001312 /* add into hash table */
1313 set_insert_node_hash(set, (struct lyd_node *)node, node_type);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001314}
1315
Michal Vasko7333cb32022-07-29 16:30:29 +02001316/**
1317 * @brief Insert schema node into set.
1318 *
1319 * @param[in] set Set to insert into.
1320 * @param[in] node Node to insert.
1321 * @param[in] node_type Node type of @p node.
1322 * @param[in] axis Axis that @p node was reached on.
1323 * @param[out] index_p Optional pointer to store index if the inserted @p node.
1324 * @return LY_SUCCESS on success.
1325 * @return LY_EMEM on memory allocation failure.
1326 */
1327static LY_ERR
1328set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
1329 enum lyxp_axis axis, uint32_t *index_p)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001330{
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001331 uint32_t index;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001332
1333 assert(set->type == LYXP_SET_SCNODE_SET);
1334
Michal Vasko871df522022-04-06 12:14:41 +02001335 if (!set->size) {
1336 /* first item */
1337 set->val.scnodes = malloc(LYXP_SET_SIZE_START * sizeof *set->val.scnodes);
1338 LY_CHECK_ERR_RET(!set->val.scnodes, LOGMEM(set->ctx), LY_EMEM);
1339 set->type = LYXP_SET_SCNODE_SET;
1340 set->used = 0;
1341 set->size = LYXP_SET_SIZE_START;
1342 set->ctx_pos = 1;
1343 set->ctx_size = 1;
1344 set->ht = NULL;
1345 }
1346
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001347 if (lyxp_set_scnode_contains(set, node, node_type, -1, &index)) {
Michal Vasko7333cb32022-07-29 16:30:29 +02001348 /* BUG if axes differs, this new one is thrown away */
Radek Krejcif13b87b2020-12-01 22:02:17 +01001349 set->val.scnodes[index].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001350 } else {
1351 if (set->used == set->size) {
Michal Vasko871df522022-04-06 12:14:41 +02001352 set->val.scnodes = ly_realloc(set->val.scnodes, (set->size * LYXP_SET_SIZE_MUL_STEP) * sizeof *set->val.scnodes);
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001353 LY_CHECK_ERR_RET(!set->val.scnodes, LOGMEM(set->ctx), LY_EMEM);
Michal Vasko871df522022-04-06 12:14:41 +02001354 set->size *= LYXP_SET_SIZE_MUL_STEP;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001355 }
1356
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001357 index = set->used;
1358 set->val.scnodes[index].scnode = (struct lysc_node *)node;
1359 set->val.scnodes[index].type = node_type;
Radek Krejcif13b87b2020-12-01 22:02:17 +01001360 set->val.scnodes[index].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
Michal Vasko7333cb32022-07-29 16:30:29 +02001361 set->val.scnodes[index].axis = axis;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001362 ++set->used;
1363 }
1364
Radek Krejciaa6b53f2020-08-27 15:19:03 +02001365 if (index_p) {
1366 *index_p = index;
1367 }
1368
1369 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001370}
1371
1372/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02001373 * @brief Set all nodes with ctx 1 to a new unique context value.
1374 *
1375 * @param[in] set Set to modify.
1376 * @return New context value.
1377 */
Michal Vasko5c4e5892019-11-14 12:31:38 +01001378static int32_t
Michal Vasko03ff5a72019-09-11 13:49:33 +02001379set_scnode_new_in_ctx(struct lyxp_set *set)
1380{
Michal Vasko5c4e5892019-11-14 12:31:38 +01001381 uint32_t i;
1382 int32_t ret_ctx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001383
1384 assert(set->type == LYXP_SET_SCNODE_SET);
1385
Radek Krejcif13b87b2020-12-01 22:02:17 +01001386 ret_ctx = LYXP_SET_SCNODE_ATOM_PRED_CTX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001387retry:
1388 for (i = 0; i < set->used; ++i) {
1389 if (set->val.scnodes[i].in_ctx >= ret_ctx) {
1390 ret_ctx = set->val.scnodes[i].in_ctx + 1;
1391 goto retry;
1392 }
1393 }
1394 for (i = 0; i < set->used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01001395 if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001396 set->val.scnodes[i].in_ctx = ret_ctx;
1397 }
1398 }
1399
1400 return ret_ctx;
1401}
1402
1403/**
1404 * @brief Get unique @p node position in the data.
1405 *
1406 * @param[in] node Node to find.
1407 * @param[in] node_type Node type of @p node.
1408 * @param[in] root Root node.
1409 * @param[in] root_type Type of the XPath @p root node.
1410 * @param[in] prev Node that we think is before @p node in DFS from @p root. Can optionally
1411 * be used to increase efficiency and start the DFS from this node.
1412 * @param[in] prev_pos Node @p prev position. Optional, but must be set if @p prev is set.
1413 * @return Node position.
1414 */
1415static uint32_t
1416get_node_pos(const struct lyd_node *node, enum lyxp_node_type node_type, const struct lyd_node *root,
Radek Krejci0f969882020-08-21 16:56:47 +02001417 enum lyxp_node_type root_type, const struct lyd_node **prev, uint32_t *prev_pos)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001418{
Michal Vasko56daf732020-08-10 10:57:18 +02001419 const struct lyd_node *elem = NULL, *top_sibling;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001420 uint32_t pos = 1;
Michal Vaskofb6c9dd2020-11-18 18:18:47 +01001421 ly_bool found = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001422
1423 assert(prev && prev_pos && !root->prev->next);
1424
1425 if ((node_type == LYXP_NODE_ROOT) || (node_type == LYXP_NODE_ROOT_CONFIG)) {
1426 return 0;
1427 }
1428
1429 if (*prev) {
1430 /* start from the previous element instead from the root */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001431 pos = *prev_pos;
Michal Vaskofb6c9dd2020-11-18 18:18:47 +01001432 for (top_sibling = *prev; top_sibling->parent; top_sibling = lyd_parent(top_sibling)) {}
Michal Vasko03ff5a72019-09-11 13:49:33 +02001433 goto dfs_search;
1434 }
1435
Michal Vaskofb6c9dd2020-11-18 18:18:47 +01001436 LY_LIST_FOR(root, top_sibling) {
Michal Vasko56daf732020-08-10 10:57:18 +02001437 LYD_TREE_DFS_BEGIN(top_sibling, elem) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001438dfs_search:
Michal Vaskoa9309bb2021-07-09 09:31:55 +02001439 LYD_TREE_DFS_continue = 0;
1440
Michal Vasko56daf732020-08-10 10:57:18 +02001441 if (*prev && !elem) {
1442 /* resume previous DFS */
1443 elem = LYD_TREE_DFS_next = (struct lyd_node *)*prev;
1444 LYD_TREE_DFS_continue = 0;
1445 }
1446
Michal Vasko482a6822022-05-06 08:56:12 +02001447 if (!elem->schema || ((root_type == LYXP_NODE_ROOT_CONFIG) && (elem->schema->flags & LYS_CONFIG_R))) {
Michal Vasko56daf732020-08-10 10:57:18 +02001448 /* skip */
1449 LYD_TREE_DFS_continue = 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001450 } else {
Michal Vasko56daf732020-08-10 10:57:18 +02001451 if (elem == node) {
Michal Vaskofb6c9dd2020-11-18 18:18:47 +01001452 found = 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001453 break;
1454 }
Michal Vasko56daf732020-08-10 10:57:18 +02001455 ++pos;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001456 }
Michal Vasko56daf732020-08-10 10:57:18 +02001457
1458 LYD_TREE_DFS_END(top_sibling, elem);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001459 }
1460
1461 /* node found */
Michal Vaskofb6c9dd2020-11-18 18:18:47 +01001462 if (found) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001463 break;
1464 }
1465 }
1466
Michal Vaskofb6c9dd2020-11-18 18:18:47 +01001467 if (!found) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001468 if (!(*prev)) {
1469 /* we went from root and failed to find it, cannot be */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001470 LOGINT(LYD_CTX(node));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001471 return 0;
1472 } else {
Michal Vasko56daf732020-08-10 10:57:18 +02001473 /* start the search again from the beginning */
1474 *prev = root;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001475
Michal Vasko56daf732020-08-10 10:57:18 +02001476 top_sibling = root;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001477 pos = 1;
1478 goto dfs_search;
1479 }
1480 }
1481
1482 /* remember the last found node for next time */
1483 *prev = node;
1484 *prev_pos = pos;
1485
1486 return pos;
1487}
1488
1489/**
1490 * @brief Assign (fill) missing node positions.
1491 *
1492 * @param[in] set Set to fill positions in.
1493 * @param[in] root Context root node.
1494 * @param[in] root_type Context root type.
1495 * @return LY_ERR
1496 */
1497static LY_ERR
1498set_assign_pos(struct lyxp_set *set, const struct lyd_node *root, enum lyxp_node_type root_type)
1499{
1500 const struct lyd_node *prev = NULL, *tmp_node;
1501 uint32_t i, tmp_pos = 0;
1502
1503 for (i = 0; i < set->used; ++i) {
1504 if (!set->val.nodes[i].pos) {
1505 tmp_node = NULL;
1506 switch (set->val.nodes[i].type) {
Michal Vasko9f96a052020-03-10 09:41:45 +01001507 case LYXP_NODE_META:
1508 tmp_node = set->val.meta[i].meta->parent;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001509 if (!tmp_node) {
1510 LOGINT_RET(root->schema->module->ctx);
1511 }
Radek Krejcif13b87b2020-12-01 22:02:17 +01001512 /* fall through */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001513 case LYXP_NODE_ELEM:
1514 case LYXP_NODE_TEXT:
1515 if (!tmp_node) {
1516 tmp_node = set->val.nodes[i].node;
1517 }
1518 set->val.nodes[i].pos = get_node_pos(tmp_node, set->val.nodes[i].type, root, root_type, &prev, &tmp_pos);
1519 break;
1520 default:
1521 /* all roots have position 0 */
1522 break;
1523 }
1524 }
1525 }
1526
1527 return LY_SUCCESS;
1528}
1529
1530/**
Michal Vasko9f96a052020-03-10 09:41:45 +01001531 * @brief Get unique @p meta position in the parent metadata.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001532 *
Michal Vasko9f96a052020-03-10 09:41:45 +01001533 * @param[in] meta Metadata to use.
1534 * @return Metadata position.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001535 */
Michal Vaskodd528af2022-08-08 14:35:07 +02001536static uint32_t
Michal Vasko9f96a052020-03-10 09:41:45 +01001537get_meta_pos(struct lyd_meta *meta)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001538{
Michal Vaskodd528af2022-08-08 14:35:07 +02001539 uint32_t pos = 0;
Michal Vasko9f96a052020-03-10 09:41:45 +01001540 struct lyd_meta *meta2;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001541
Michal Vasko9f96a052020-03-10 09:41:45 +01001542 for (meta2 = meta->parent->meta; meta2 && (meta2 != meta); meta2 = meta2->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001543 ++pos;
1544 }
1545
Michal Vasko9f96a052020-03-10 09:41:45 +01001546 assert(meta2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001547 return pos;
1548}
1549
1550/**
1551 * @brief Compare 2 nodes in respect to XPath document order.
1552 *
1553 * @param[in] item1 1st node.
1554 * @param[in] item2 2nd node.
1555 * @return If 1st > 2nd returns 1, 1st == 2nd returns 0, and 1st < 2nd returns -1.
1556 */
1557static int
1558set_sort_compare(struct lyxp_set_node *item1, struct lyxp_set_node *item2)
1559{
Michal Vasko9f96a052020-03-10 09:41:45 +01001560 uint32_t meta_pos1 = 0, meta_pos2 = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001561
1562 if (item1->pos < item2->pos) {
1563 return -1;
1564 }
1565
1566 if (item1->pos > item2->pos) {
1567 return 1;
1568 }
1569
1570 /* node positions are equal, the fun case */
1571
1572 /* 1st ELEM - == - 2nd TEXT, 1st TEXT - == - 2nd ELEM */
1573 /* special case since text nodes are actually saved as their parents */
1574 if ((item1->node == item2->node) && (item1->type != item2->type)) {
1575 if (item1->type == LYXP_NODE_ELEM) {
1576 assert(item2->type == LYXP_NODE_TEXT);
1577 return -1;
1578 } else {
1579 assert((item1->type == LYXP_NODE_TEXT) && (item2->type == LYXP_NODE_ELEM));
1580 return 1;
1581 }
1582 }
1583
Michal Vasko9f96a052020-03-10 09:41:45 +01001584 /* we need meta positions now */
1585 if (item1->type == LYXP_NODE_META) {
1586 meta_pos1 = get_meta_pos((struct lyd_meta *)item1->node);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001587 }
Michal Vasko9f96a052020-03-10 09:41:45 +01001588 if (item2->type == LYXP_NODE_META) {
1589 meta_pos2 = get_meta_pos((struct lyd_meta *)item2->node);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001590 }
1591
Michal Vasko9f96a052020-03-10 09:41:45 +01001592 /* 1st ROOT - 2nd ROOT, 1st ELEM - 2nd ELEM, 1st TEXT - 2nd TEXT, 1st META - =pos= - 2nd META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001593 /* check for duplicates */
1594 if (item1->node == item2->node) {
Michal Vasko9f96a052020-03-10 09:41:45 +01001595 assert((item1->type == item2->type) && ((item1->type != LYXP_NODE_META) || (meta_pos1 == meta_pos2)));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001596 return 0;
1597 }
1598
Michal Vasko9f96a052020-03-10 09:41:45 +01001599 /* 1st ELEM - 2nd TEXT, 1st ELEM - any pos - 2nd META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001600 /* elem is always first, 2nd node is after it */
1601 if (item1->type == LYXP_NODE_ELEM) {
1602 assert(item2->type != LYXP_NODE_ELEM);
1603 return -1;
1604 }
1605
Michal Vasko9f96a052020-03-10 09:41:45 +01001606 /* 1st TEXT - 2nd ELEM, 1st TEXT - any pos - 2nd META, 1st META - any pos - 2nd ELEM, 1st META - >pos> - 2nd META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001607 /* 2nd is before 1st */
Michal Vasko69730152020-10-09 16:30:07 +02001608 if (((item1->type == LYXP_NODE_TEXT) &&
1609 ((item2->type == LYXP_NODE_ELEM) || (item2->type == LYXP_NODE_META))) ||
1610 ((item1->type == LYXP_NODE_META) && (item2->type == LYXP_NODE_ELEM)) ||
1611 (((item1->type == LYXP_NODE_META) && (item2->type == LYXP_NODE_META)) &&
1612 (meta_pos1 > meta_pos2))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001613 return 1;
1614 }
1615
Michal Vasko9f96a052020-03-10 09:41:45 +01001616 /* 1st META - any pos - 2nd TEXT, 1st META <pos< - 2nd META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001617 /* 2nd is after 1st */
1618 return -1;
1619}
1620
1621/**
1622 * @brief Set cast for comparisons.
1623 *
Michal Vasko8abcecc2022-07-28 09:55:01 +02001624 * @param[in,out] trg Target set to cast source into.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001625 * @param[in] src Source set.
1626 * @param[in] type Target set type.
1627 * @param[in] src_idx Source set node index.
Michal Vasko8abcecc2022-07-28 09:55:01 +02001628 * @return LY_SUCCESS on success.
1629 * @return LY_ERR value on error.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001630 */
1631static LY_ERR
Michal Vasko8abcecc2022-07-28 09:55:01 +02001632set_comp_cast(struct lyxp_set *trg, const struct lyxp_set *src, enum lyxp_set_type type, uint32_t src_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001633{
1634 assert(src->type == LYXP_SET_NODE_SET);
1635
1636 set_init(trg, src);
1637
1638 /* insert node into target set */
1639 set_insert_node(trg, src->val.nodes[src_idx].node, src->val.nodes[src_idx].pos, src->val.nodes[src_idx].type, 0);
1640
1641 /* cast target set appropriately */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001642 return lyxp_set_cast(trg, type);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001643}
1644
Michal Vasko4c7763f2020-07-27 17:40:37 +02001645/**
1646 * @brief Set content canonization for comparisons.
1647 *
Michal Vasko8abcecc2022-07-28 09:55:01 +02001648 * @param[in,out] set Set to canonize.
Michal Vasko4c7763f2020-07-27 17:40:37 +02001649 * @param[in] xp_node Source XPath node/meta to use for canonization.
Michal Vasko8abcecc2022-07-28 09:55:01 +02001650 * @return LY_SUCCESS on success.
1651 * @return LY_ERR value on error.
Michal Vasko4c7763f2020-07-27 17:40:37 +02001652 */
1653static LY_ERR
Michal Vasko8abcecc2022-07-28 09:55:01 +02001654set_comp_canonize(struct lyxp_set *set, const struct lyxp_set_node *xp_node)
Michal Vasko4c7763f2020-07-27 17:40:37 +02001655{
Michal Vaskofeca4fb2020-10-05 08:58:40 +02001656 const struct lysc_type *type = NULL;
Michal Vasko4c7763f2020-07-27 17:40:37 +02001657 struct lyd_value val;
1658 struct ly_err_item *err = NULL;
Michal Vasko4c7763f2020-07-27 17:40:37 +02001659 LY_ERR rc;
1660
1661 /* is there anything to canonize even? */
Michal Vasko8abcecc2022-07-28 09:55:01 +02001662 if (set->type == LYXP_SET_STRING) {
Michal Vasko4c7763f2020-07-27 17:40:37 +02001663 /* do we have a type to use for canonization? */
1664 if ((xp_node->type == LYXP_NODE_ELEM) && (xp_node->node->schema->nodetype & LYD_NODE_TERM)) {
1665 type = ((struct lyd_node_term *)xp_node->node)->value.realtype;
1666 } else if (xp_node->type == LYXP_NODE_META) {
1667 type = ((struct lyd_meta *)xp_node->node)->value.realtype;
1668 }
1669 }
1670 if (!type) {
Michal Vasko8abcecc2022-07-28 09:55:01 +02001671 /* no canonization needed/possible */
1672 return LY_SUCCESS;
Michal Vasko4c7763f2020-07-27 17:40:37 +02001673 }
1674
Michal Vasko8abcecc2022-07-28 09:55:01 +02001675 /* check for built-in types without required canonization */
1676 if ((type->basetype == LY_TYPE_STRING) && (type->plugin->store == lyplg_type_store_string)) {
1677 /* string */
1678 return LY_SUCCESS;
1679 }
1680 if ((type->basetype == LY_TYPE_BOOL) && (type->plugin->store == lyplg_type_store_boolean)) {
1681 /* boolean */
1682 return LY_SUCCESS;
1683 }
1684 if ((type->basetype == LY_TYPE_ENUM) && (type->plugin->store == lyplg_type_store_enum)) {
1685 /* enumeration */
1686 return LY_SUCCESS;
Michal Vasko4c7763f2020-07-27 17:40:37 +02001687 }
1688
Michal Vasko8abcecc2022-07-28 09:55:01 +02001689 /* print canonized string, ignore errors, the value may not satisfy schema constraints */
1690 rc = type->plugin->store(set->ctx, type, set->val.str, strlen(set->val.str), 0, set->format, set->prefix_data,
Michal Vasko405cc9e2020-12-01 12:01:27 +01001691 LYD_HINT_DATA, xp_node->node->schema, &val, NULL, &err);
Michal Vasko4c7763f2020-07-27 17:40:37 +02001692 ly_err_free(err);
1693 if (rc) {
Michal Vasko8abcecc2022-07-28 09:55:01 +02001694 /* invalid value, function store automaticaly dealloc value when fail */
1695 return LY_SUCCESS;
Michal Vasko4c7763f2020-07-27 17:40:37 +02001696 }
1697
Michal Vasko8abcecc2022-07-28 09:55:01 +02001698 /* use the canonized string value */
1699 free(set->val.str);
1700 set->val.str = strdup(lyd_value_get_canonical(set->ctx, &val));
1701 type->plugin->free(set->ctx, &val);
1702 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), LY_EMEM);
Michal Vasko4c7763f2020-07-27 17:40:37 +02001703
Michal Vasko4c7763f2020-07-27 17:40:37 +02001704 return LY_SUCCESS;
1705}
1706
Michal Vasko03ff5a72019-09-11 13:49:33 +02001707/**
1708 * @brief Bubble sort @p set into XPath document order.
Michal Vasko49fec8e2022-05-24 10:28:33 +02001709 * Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001710 *
1711 * @param[in] set Set to sort.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001712 * @return How many times the whole set was traversed - 1 (if set was sorted, returns 0).
1713 */
1714static int
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001715set_sort(struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001716{
1717 uint32_t i, j;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001718 int ret = 0, cmp;
Radek Krejci857189e2020-09-01 13:26:36 +02001719 ly_bool inverted, change;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001720 const struct lyd_node *root;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001721 struct lyxp_set_node item;
1722 struct lyxp_set_hash_node hnode;
1723 uint64_t hash;
1724
Michal Vasko3cf8fbf2020-05-27 15:21:21 +02001725 if ((set->type != LYXP_SET_NODE_SET) || (set->used < 2)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001726 return 0;
1727 }
1728
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001729 /* find first top-level node to be used as anchor for positions */
Michal Vasko4a7d4d62021-12-13 17:05:06 +01001730 for (root = set->tree; root->parent; root = lyd_parent(root)) {}
Michal Vaskod989ba02020-08-24 10:59:24 +02001731 for ( ; root->prev->next; root = root->prev) {}
Michal Vasko03ff5a72019-09-11 13:49:33 +02001732
1733 /* fill positions */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001734 if (set_assign_pos(set, root, set->root_type)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001735 return -1;
1736 }
1737
Michal Vasko88a9e802022-05-24 10:50:28 +02001738#ifndef NDEBUG
Michal Vasko03ff5a72019-09-11 13:49:33 +02001739 LOGDBG(LY_LDGXPATH, "SORT BEGIN");
1740 print_set_debug(set);
Michal Vasko88a9e802022-05-24 10:50:28 +02001741#endif
Michal Vasko03ff5a72019-09-11 13:49:33 +02001742
1743 for (i = 0; i < set->used; ++i) {
1744 inverted = 0;
1745 change = 0;
1746
1747 for (j = 1; j < set->used - i; ++j) {
1748 /* compare node positions */
1749 if (inverted) {
1750 cmp = set_sort_compare(&set->val.nodes[j], &set->val.nodes[j - 1]);
1751 } else {
1752 cmp = set_sort_compare(&set->val.nodes[j - 1], &set->val.nodes[j]);
1753 }
1754
1755 /* swap if needed */
1756 if ((inverted && (cmp < 0)) || (!inverted && (cmp > 0))) {
1757 change = 1;
1758
1759 item = set->val.nodes[j - 1];
1760 set->val.nodes[j - 1] = set->val.nodes[j];
1761 set->val.nodes[j] = item;
1762 } else {
1763 /* whether node_pos1 should be smaller than node_pos2 or the other way around */
1764 inverted = !inverted;
1765 }
1766 }
1767
1768 ++ret;
1769
1770 if (!change) {
1771 break;
1772 }
1773 }
1774
Michal Vasko88a9e802022-05-24 10:50:28 +02001775#ifndef NDEBUG
Michal Vasko03ff5a72019-09-11 13:49:33 +02001776 LOGDBG(LY_LDGXPATH, "SORT END %d", ret);
1777 print_set_debug(set);
Michal Vasko88a9e802022-05-24 10:50:28 +02001778#endif
Michal Vasko03ff5a72019-09-11 13:49:33 +02001779
1780 /* check node hashes */
1781 if (set->used >= LYD_HT_MIN_ITEMS) {
1782 assert(set->ht);
1783 for (i = 0; i < set->used; ++i) {
1784 hnode.node = set->val.nodes[i].node;
1785 hnode.type = set->val.nodes[i].type;
1786
1787 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
1788 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
1789 hash = dict_hash_multi(hash, NULL, 0);
1790
1791 assert(!lyht_find(set->ht, &hnode, hash, NULL));
1792 }
1793 }
1794
1795 return ret - 1;
1796}
1797
Michal Vasko03ff5a72019-09-11 13:49:33 +02001798/**
1799 * @brief Merge 2 sorted sets into one.
1800 *
1801 * @param[in,out] trg Set to merge into. Duplicates are removed.
1802 * @param[in] src Set to be merged into @p trg. It is cast to #LYXP_SET_EMPTY on success.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001803 * @return LY_ERR
1804 */
1805static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001806set_sorted_merge(struct lyxp_set *trg, struct lyxp_set *src)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001807{
1808 uint32_t i, j, k, count, dup_count;
1809 int cmp;
1810 const struct lyd_node *root;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001811
Michal Vaskod3678892020-05-21 10:06:58 +02001812 if ((trg->type != LYXP_SET_NODE_SET) || (src->type != LYXP_SET_NODE_SET)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001813 return LY_EINVAL;
1814 }
1815
Michal Vaskod3678892020-05-21 10:06:58 +02001816 if (!src->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001817 return LY_SUCCESS;
Michal Vaskod3678892020-05-21 10:06:58 +02001818 } else if (!trg->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001819 set_fill_set(trg, src);
Michal Vaskod3678892020-05-21 10:06:58 +02001820 lyxp_set_free_content(src);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001821 return LY_SUCCESS;
1822 }
1823
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001824 /* find first top-level node to be used as anchor for positions */
Michal Vasko0143b682021-12-13 17:13:09 +01001825 for (root = trg->tree; root->parent; root = lyd_parent(root)) {}
Michal Vaskod989ba02020-08-24 10:59:24 +02001826 for ( ; root->prev->next; root = root->prev) {}
Michal Vasko03ff5a72019-09-11 13:49:33 +02001827
1828 /* fill positions */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001829 if (set_assign_pos(trg, root, trg->root_type) || set_assign_pos(src, root, src->root_type)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001830 return LY_EINT;
1831 }
1832
1833#ifndef NDEBUG
1834 LOGDBG(LY_LDGXPATH, "MERGE target");
1835 print_set_debug(trg);
1836 LOGDBG(LY_LDGXPATH, "MERGE source");
1837 print_set_debug(src);
1838#endif
1839
1840 /* make memory for the merge (duplicates are not detected yet, so space
1841 * will likely be wasted on them, too bad) */
1842 if (trg->size - trg->used < src->used) {
1843 trg->size = trg->used + src->used;
1844
1845 trg->val.nodes = ly_realloc(trg->val.nodes, trg->size * sizeof *trg->val.nodes);
1846 LY_CHECK_ERR_RET(!trg->val.nodes, LOGMEM(src->ctx), LY_EMEM);
1847 }
1848
1849 i = 0;
1850 j = 0;
1851 count = 0;
1852 dup_count = 0;
1853 do {
1854 cmp = set_sort_compare(&src->val.nodes[i], &trg->val.nodes[j]);
1855 if (!cmp) {
1856 if (!count) {
1857 /* duplicate, just skip it */
1858 ++i;
1859 ++j;
1860 } else {
1861 /* we are copying something already, so let's copy the duplicate too,
1862 * we are hoping that afterwards there are some more nodes to
1863 * copy and this way we can copy them all together */
1864 ++count;
1865 ++dup_count;
1866 ++i;
1867 ++j;
1868 }
1869 } else if (cmp < 0) {
1870 /* inserting src node into trg, just remember it for now */
1871 ++count;
1872 ++i;
1873
1874 /* insert the hash now */
1875 set_insert_node_hash(trg, src->val.nodes[i - 1].node, src->val.nodes[i - 1].type);
1876 } else if (count) {
1877copy_nodes:
1878 /* time to actually copy the nodes, we have found the largest block of nodes */
1879 memmove(&trg->val.nodes[j + (count - dup_count)],
1880 &trg->val.nodes[j],
1881 (trg->used - j) * sizeof *trg->val.nodes);
1882 memcpy(&trg->val.nodes[j - dup_count], &src->val.nodes[i - count], count * sizeof *src->val.nodes);
1883
1884 trg->used += count - dup_count;
1885 /* do not change i, except the copying above, we are basically doing exactly what is in the else branch below */
1886 j += count - dup_count;
1887
1888 count = 0;
1889 dup_count = 0;
1890 } else {
1891 ++j;
1892 }
1893 } while ((i < src->used) && (j < trg->used));
1894
1895 if ((i < src->used) || count) {
1896 /* insert all the hashes first */
1897 for (k = i; k < src->used; ++k) {
1898 set_insert_node_hash(trg, src->val.nodes[k].node, src->val.nodes[k].type);
1899 }
1900
1901 /* loop ended, but we need to copy something at trg end */
1902 count += src->used - i;
1903 i = src->used;
1904 goto copy_nodes;
1905 }
1906
1907 /* we are inserting hashes before the actual node insert, which causes
1908 * situations when there were initially not enough items for a hash table,
1909 * but even after some were inserted, hash table was not created (during
1910 * insertion the number of items is not updated yet) */
1911 if (!trg->ht && (trg->used >= LYD_HT_MIN_ITEMS)) {
1912 set_insert_node_hash(trg, NULL, 0);
1913 }
1914
1915#ifndef NDEBUG
1916 LOGDBG(LY_LDGXPATH, "MERGE result");
1917 print_set_debug(trg);
1918#endif
1919
Michal Vaskod3678892020-05-21 10:06:58 +02001920 lyxp_set_free_content(src);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001921 return LY_SUCCESS;
1922}
1923
Michal Vasko14676352020-05-29 11:35:55 +02001924LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02001925lyxp_check_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t tok_idx, enum lyxp_token want_tok)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001926{
Michal Vasko004d3152020-06-11 19:59:22 +02001927 if (exp->used == tok_idx) {
Michal Vasko14676352020-05-29 11:35:55 +02001928 if (ctx) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01001929 LOGVAL(ctx, LY_VCODE_XP_EOF);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001930 }
Michal Vasko14676352020-05-29 11:35:55 +02001931 return LY_EINCOMPLETE;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001932 }
1933
Michal Vasko004d3152020-06-11 19:59:22 +02001934 if (want_tok && (exp->tokens[tok_idx] != want_tok)) {
Michal Vasko14676352020-05-29 11:35:55 +02001935 if (ctx) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02001936 LOGVAL(ctx, LY_VCODE_XP_INTOK2, lyxp_token2str(exp->tokens[tok_idx]),
1937 &exp->expr[exp->tok_pos[tok_idx]], lyxp_token2str(want_tok));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001938 }
Michal Vasko14676352020-05-29 11:35:55 +02001939 return LY_ENOT;
1940 }
1941
1942 return LY_SUCCESS;
1943}
1944
Michal Vasko004d3152020-06-11 19:59:22 +02001945LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02001946lyxp_next_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_token want_tok)
Michal Vasko004d3152020-06-11 19:59:22 +02001947{
1948 LY_CHECK_RET(lyxp_check_token(ctx, exp, *tok_idx, want_tok));
1949
1950 /* skip the token */
1951 ++(*tok_idx);
1952
1953 return LY_SUCCESS;
1954}
1955
Michal Vasko14676352020-05-29 11:35:55 +02001956/* just like lyxp_check_token() but tests for 2 tokens */
1957static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02001958exp_check_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t tok_idx, enum lyxp_token want_tok1,
Radek Krejci0f969882020-08-21 16:56:47 +02001959 enum lyxp_token want_tok2)
Michal Vasko14676352020-05-29 11:35:55 +02001960{
Michal Vasko004d3152020-06-11 19:59:22 +02001961 if (exp->used == tok_idx) {
Michal Vasko14676352020-05-29 11:35:55 +02001962 if (ctx) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01001963 LOGVAL(ctx, LY_VCODE_XP_EOF);
Michal Vasko14676352020-05-29 11:35:55 +02001964 }
1965 return LY_EINCOMPLETE;
1966 }
1967
Michal Vasko004d3152020-06-11 19:59:22 +02001968 if ((exp->tokens[tok_idx] != want_tok1) && (exp->tokens[tok_idx] != want_tok2)) {
Michal Vasko14676352020-05-29 11:35:55 +02001969 if (ctx) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02001970 LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[tok_idx]),
Michal Vasko0b468e62020-10-19 09:33:04 +02001971 &exp->expr[exp->tok_pos[tok_idx]]);
Michal Vasko14676352020-05-29 11:35:55 +02001972 }
1973 return LY_ENOT;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001974 }
1975
1976 return LY_SUCCESS;
1977}
1978
Michal Vasko4911eeb2021-06-28 11:23:05 +02001979LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02001980lyxp_next_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_token want_tok1,
Michal Vasko4911eeb2021-06-28 11:23:05 +02001981 enum lyxp_token want_tok2)
1982{
1983 LY_CHECK_RET(exp_check_token2(ctx, exp, *tok_idx, want_tok1, want_tok2));
1984
1985 /* skip the token */
1986 ++(*tok_idx);
1987
1988 return LY_SUCCESS;
1989}
1990
Michal Vasko03ff5a72019-09-11 13:49:33 +02001991/**
1992 * @brief Stack operation push on the repeat array.
1993 *
1994 * @param[in] exp Expression to use.
Michal Vasko004d3152020-06-11 19:59:22 +02001995 * @param[in] tok_idx Position in the expresion \p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001996 * @param[in] repeat_op_idx Index from \p exp of the operator token. This value is pushed.
1997 */
1998static void
Michal Vaskodd528af2022-08-08 14:35:07 +02001999exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, uint32_t repeat_op_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002000{
Michal Vaskodd528af2022-08-08 14:35:07 +02002001 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002002
Michal Vasko004d3152020-06-11 19:59:22 +02002003 if (exp->repeat[tok_idx]) {
Radek Krejci1e008d22020-08-17 11:37:37 +02002004 for (i = 0; exp->repeat[tok_idx][i]; ++i) {}
Michal Vasko004d3152020-06-11 19:59:22 +02002005 exp->repeat[tok_idx] = realloc(exp->repeat[tok_idx], (i + 2) * sizeof *exp->repeat[tok_idx]);
2006 LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
2007 exp->repeat[tok_idx][i] = repeat_op_idx;
2008 exp->repeat[tok_idx][i + 1] = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002009 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02002010 exp->repeat[tok_idx] = calloc(2, sizeof *exp->repeat[tok_idx]);
2011 LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
2012 exp->repeat[tok_idx][0] = repeat_op_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002013 }
2014}
2015
2016/**
2017 * @brief Reparse Predicate. Logs directly on error.
2018 *
2019 * [7] Predicate ::= '[' Expr ']'
2020 *
2021 * @param[in] ctx Context for logging.
2022 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002023 * @param[in] tok_idx Position in the expression @p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002024 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002025 * @return LY_ERR
2026 */
2027static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002028reparse_predicate(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002029{
2030 LY_ERR rc;
2031
Michal Vasko004d3152020-06-11 19:59:22 +02002032 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002033 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002034 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002035
aPiecekbf968d92021-05-27 14:35:05 +02002036 rc = reparse_or_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002037 LY_CHECK_RET(rc);
2038
Michal Vasko004d3152020-06-11 19:59:22 +02002039 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002040 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002041 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002042
2043 return LY_SUCCESS;
2044}
2045
2046/**
2047 * @brief Reparse RelativeLocationPath. Logs directly on error.
2048 *
2049 * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
2050 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
2051 * [6] NodeTest ::= NameTest | NodeType '(' ')'
2052 *
2053 * @param[in] ctx Context for logging.
2054 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002055 * @param[in] tok_idx Position in the expression \p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002056 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002057 * @return LY_ERR (LY_EINCOMPLETE on forward reference)
2058 */
2059static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002060reparse_relative_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002061{
2062 LY_ERR rc;
2063
Michal Vasko004d3152020-06-11 19:59:22 +02002064 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002065 LY_CHECK_RET(rc);
2066
2067 goto step;
2068 do {
2069 /* '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02002070 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002071
Michal Vasko004d3152020-06-11 19:59:22 +02002072 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002073 LY_CHECK_RET(rc);
2074step:
2075 /* Step */
Michal Vasko004d3152020-06-11 19:59:22 +02002076 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002077 case LYXP_TOKEN_DOT:
Michal Vasko004d3152020-06-11 19:59:22 +02002078 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002079 break;
2080
2081 case LYXP_TOKEN_DDOT:
Michal Vasko004d3152020-06-11 19:59:22 +02002082 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002083 break;
2084
Michal Vasko49fec8e2022-05-24 10:28:33 +02002085 case LYXP_TOKEN_AXISNAME:
2086 ++(*tok_idx);
2087
2088 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_DCOLON);
2089 LY_CHECK_RET(rc);
2090
2091 /* fall through */
Michal Vasko03ff5a72019-09-11 13:49:33 +02002092 case LYXP_TOKEN_AT:
Michal Vasko004d3152020-06-11 19:59:22 +02002093 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002094
Michal Vasko004d3152020-06-11 19:59:22 +02002095 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002096 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002097 if ((exp->tokens[*tok_idx] != LYXP_TOKEN_NAMETEST) && (exp->tokens[*tok_idx] != LYXP_TOKEN_NODETYPE)) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02002098 LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002099 return LY_EVALID;
2100 }
Michal Vasko49fec8e2022-05-24 10:28:33 +02002101 if (exp->tokens[*tok_idx] == LYXP_TOKEN_NODETYPE) {
2102 goto reparse_nodetype;
2103 }
Radek Krejci0f969882020-08-21 16:56:47 +02002104 /* fall through */
Michal Vasko03ff5a72019-09-11 13:49:33 +02002105 case LYXP_TOKEN_NAMETEST:
Michal Vasko004d3152020-06-11 19:59:22 +02002106 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002107 goto reparse_predicate;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002108
2109 case LYXP_TOKEN_NODETYPE:
Michal Vasko49fec8e2022-05-24 10:28:33 +02002110reparse_nodetype:
Michal Vasko004d3152020-06-11 19:59:22 +02002111 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002112
2113 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02002114 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002115 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002116 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002117
2118 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02002119 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002120 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002121 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002122
2123reparse_predicate:
2124 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02002125 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
aPiecekbf968d92021-05-27 14:35:05 +02002126 rc = reparse_predicate(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002127 LY_CHECK_RET(rc);
2128 }
2129 break;
2130 default:
Michal Vasko49fec8e2022-05-24 10:28:33 +02002131 LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002132 return LY_EVALID;
2133 }
Michal Vasko004d3152020-06-11 19:59:22 +02002134 } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
Michal Vasko03ff5a72019-09-11 13:49:33 +02002135
2136 return LY_SUCCESS;
2137}
2138
2139/**
2140 * @brief Reparse AbsoluteLocationPath. Logs directly on error.
2141 *
2142 * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
2143 *
2144 * @param[in] ctx Context for logging.
2145 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002146 * @param[in] tok_idx Position in the expression \p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002147 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002148 * @return LY_ERR
2149 */
2150static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002151reparse_absolute_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002152{
2153 LY_ERR rc;
2154
Michal Vasko004d3152020-06-11 19:59:22 +02002155 LY_CHECK_RET(exp_check_token2(ctx, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
Michal Vasko03ff5a72019-09-11 13:49:33 +02002156
2157 /* '/' RelativeLocationPath? */
Michal Vasko004d3152020-06-11 19:59:22 +02002158 if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002159 /* '/' */
Michal Vasko004d3152020-06-11 19:59:22 +02002160 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002161
Michal Vasko004d3152020-06-11 19:59:22 +02002162 if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002163 return LY_SUCCESS;
2164 }
Michal Vasko004d3152020-06-11 19:59:22 +02002165 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002166 case LYXP_TOKEN_DOT:
2167 case LYXP_TOKEN_DDOT:
Michal Vasko49fec8e2022-05-24 10:28:33 +02002168 case LYXP_TOKEN_AXISNAME:
Michal Vasko03ff5a72019-09-11 13:49:33 +02002169 case LYXP_TOKEN_AT:
2170 case LYXP_TOKEN_NAMETEST:
2171 case LYXP_TOKEN_NODETYPE:
aPiecekbf968d92021-05-27 14:35:05 +02002172 rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002173 LY_CHECK_RET(rc);
Radek Krejci0f969882020-08-21 16:56:47 +02002174 /* fall through */
Michal Vasko03ff5a72019-09-11 13:49:33 +02002175 default:
2176 break;
2177 }
2178
Michal Vasko03ff5a72019-09-11 13:49:33 +02002179 } else {
Radek Krejcif6a11002020-08-21 13:29:07 +02002180 /* '//' RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02002181 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002182
aPiecekbf968d92021-05-27 14:35:05 +02002183 rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002184 LY_CHECK_RET(rc);
2185 }
2186
2187 return LY_SUCCESS;
2188}
2189
2190/**
2191 * @brief Reparse FunctionCall. Logs directly on error.
2192 *
2193 * [9] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
2194 *
2195 * @param[in] ctx Context for logging.
2196 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002197 * @param[in] tok_idx Position in the expression @p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002198 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002199 * @return LY_ERR
2200 */
2201static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002202reparse_function_call(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002203{
Radek Krejci1deb5be2020-08-26 16:43:36 +02002204 int8_t min_arg_count = -1;
Michal Vaskodd528af2022-08-08 14:35:07 +02002205 uint32_t arg_count, max_arg_count = 0, func_tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002206 LY_ERR rc;
2207
Michal Vasko004d3152020-06-11 19:59:22 +02002208 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002209 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002210 func_tok_idx = *tok_idx;
2211 switch (exp->tok_len[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002212 case 3:
Michal Vasko004d3152020-06-11 19:59:22 +02002213 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002214 min_arg_count = 1;
2215 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002216 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002217 min_arg_count = 1;
2218 max_arg_count = 1;
2219 }
2220 break;
2221 case 4:
Michal Vasko004d3152020-06-11 19:59:22 +02002222 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002223 min_arg_count = 1;
2224 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002225 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002226 min_arg_count = 0;
2227 max_arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002228 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002229 min_arg_count = 0;
2230 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002231 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002232 min_arg_count = 0;
2233 max_arg_count = 0;
2234 }
2235 break;
2236 case 5:
Michal Vasko004d3152020-06-11 19:59:22 +02002237 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002238 min_arg_count = 1;
2239 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002240 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002241 min_arg_count = 0;
2242 max_arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002243 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002244 min_arg_count = 1;
2245 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002246 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002247 min_arg_count = 1;
2248 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002249 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002250 min_arg_count = 1;
2251 max_arg_count = 1;
2252 }
2253 break;
2254 case 6:
Michal Vasko004d3152020-06-11 19:59:22 +02002255 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002256 min_arg_count = 2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002257 max_arg_count = UINT32_MAX;
Michal Vasko004d3152020-06-11 19:59:22 +02002258 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002259 min_arg_count = 0;
2260 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002261 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002262 min_arg_count = 0;
2263 max_arg_count = 1;
2264 }
2265 break;
2266 case 7:
Michal Vasko004d3152020-06-11 19:59:22 +02002267 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002268 min_arg_count = 1;
2269 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002270 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002271 min_arg_count = 1;
2272 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002273 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002274 min_arg_count = 0;
2275 max_arg_count = 0;
2276 }
2277 break;
2278 case 8:
Michal Vasko004d3152020-06-11 19:59:22 +02002279 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002280 min_arg_count = 2;
2281 max_arg_count = 2;
Michal Vasko004d3152020-06-11 19:59:22 +02002282 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002283 min_arg_count = 0;
2284 max_arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002285 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002286 min_arg_count = 2;
2287 max_arg_count = 2;
2288 }
2289 break;
2290 case 9:
Michal Vasko004d3152020-06-11 19:59:22 +02002291 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002292 min_arg_count = 2;
2293 max_arg_count = 3;
Michal Vasko004d3152020-06-11 19:59:22 +02002294 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002295 min_arg_count = 3;
2296 max_arg_count = 3;
2297 }
2298 break;
2299 case 10:
Michal Vasko004d3152020-06-11 19:59:22 +02002300 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002301 min_arg_count = 0;
2302 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002303 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002304 min_arg_count = 1;
2305 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002306 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002307 min_arg_count = 2;
2308 max_arg_count = 2;
2309 }
2310 break;
2311 case 11:
Michal Vasko004d3152020-06-11 19:59:22 +02002312 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002313 min_arg_count = 2;
2314 max_arg_count = 2;
2315 }
2316 break;
2317 case 12:
Michal Vasko004d3152020-06-11 19:59:22 +02002318 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002319 min_arg_count = 2;
2320 max_arg_count = 2;
2321 }
2322 break;
2323 case 13:
Michal Vasko004d3152020-06-11 19:59:22 +02002324 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002325 min_arg_count = 0;
2326 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002327 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002328 min_arg_count = 0;
2329 max_arg_count = 1;
2330 }
2331 break;
2332 case 15:
Michal Vasko004d3152020-06-11 19:59:22 +02002333 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002334 min_arg_count = 0;
2335 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002336 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002337 min_arg_count = 2;
2338 max_arg_count = 2;
2339 }
2340 break;
2341 case 16:
Michal Vasko004d3152020-06-11 19:59:22 +02002342 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002343 min_arg_count = 2;
2344 max_arg_count = 2;
2345 }
2346 break;
2347 case 20:
Michal Vasko004d3152020-06-11 19:59:22 +02002348 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002349 min_arg_count = 2;
2350 max_arg_count = 2;
2351 }
2352 break;
2353 }
2354 if (min_arg_count == -1) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01002355 LOGVAL(ctx, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002356 return LY_EINVAL;
2357 }
Michal Vasko004d3152020-06-11 19:59:22 +02002358 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002359
2360 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02002361 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002362 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002363 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002364
2365 /* ( Expr ( ',' Expr )* )? */
2366 arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002367 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002368 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002369 if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002370 ++arg_count;
aPiecekbf968d92021-05-27 14:35:05 +02002371 rc = reparse_or_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002372 LY_CHECK_RET(rc);
2373 }
Michal Vasko004d3152020-06-11 19:59:22 +02002374 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
2375 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002376
2377 ++arg_count;
aPiecekbf968d92021-05-27 14:35:05 +02002378 rc = reparse_or_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002379 LY_CHECK_RET(rc);
2380 }
2381
2382 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02002383 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002384 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002385 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002386
Radek Krejci857189e2020-09-01 13:26:36 +02002387 if ((arg_count < (uint32_t)min_arg_count) || (arg_count > max_arg_count)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01002388 LOGVAL(ctx, LY_VCODE_XP_INARGCOUNT, arg_count, exp->tok_len[func_tok_idx], &exp->expr[exp->tok_pos[func_tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002389 return LY_EVALID;
2390 }
2391
2392 return LY_SUCCESS;
2393}
2394
2395/**
2396 * @brief Reparse PathExpr. Logs directly on error.
2397 *
2398 * [10] PathExpr ::= LocationPath | PrimaryExpr Predicate*
2399 * | PrimaryExpr Predicate* '/' RelativeLocationPath
2400 * | PrimaryExpr Predicate* '//' RelativeLocationPath
2401 * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
aPiecekfba75362021-10-07 12:39:48 +02002402 * [8] PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
Michal Vasko03ff5a72019-09-11 13:49:33 +02002403 *
2404 * @param[in] ctx Context for logging.
2405 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002406 * @param[in] tok_idx Position in the expression @p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002407 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002408 * @return LY_ERR
2409 */
2410static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002411reparse_path_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002412{
2413 LY_ERR rc;
2414
Michal Vasko004d3152020-06-11 19:59:22 +02002415 if (lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE)) {
Michal Vasko14676352020-05-29 11:35:55 +02002416 return LY_EVALID;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002417 }
2418
Michal Vasko004d3152020-06-11 19:59:22 +02002419 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002420 case LYXP_TOKEN_PAR1:
2421 /* '(' Expr ')' Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02002422 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002423
aPiecekbf968d92021-05-27 14:35:05 +02002424 rc = reparse_or_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002425 LY_CHECK_RET(rc);
2426
Michal Vasko004d3152020-06-11 19:59:22 +02002427 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002428 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002429 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002430 goto predicate;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002431 case LYXP_TOKEN_DOT:
2432 case LYXP_TOKEN_DDOT:
Michal Vasko49fec8e2022-05-24 10:28:33 +02002433 case LYXP_TOKEN_AXISNAME:
Michal Vasko03ff5a72019-09-11 13:49:33 +02002434 case LYXP_TOKEN_AT:
2435 case LYXP_TOKEN_NAMETEST:
2436 case LYXP_TOKEN_NODETYPE:
2437 /* RelativeLocationPath */
aPiecekbf968d92021-05-27 14:35:05 +02002438 rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002439 LY_CHECK_RET(rc);
2440 break;
aPiecekfba75362021-10-07 12:39:48 +02002441 case LYXP_TOKEN_VARREF:
2442 /* VariableReference */
2443 ++(*tok_idx);
2444 goto predicate;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002445 case LYXP_TOKEN_FUNCNAME:
2446 /* FunctionCall */
aPiecekbf968d92021-05-27 14:35:05 +02002447 rc = reparse_function_call(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002448 LY_CHECK_RET(rc);
2449 goto predicate;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002450 case LYXP_TOKEN_OPER_PATH:
2451 case LYXP_TOKEN_OPER_RPATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +02002452 /* AbsoluteLocationPath */
aPiecekbf968d92021-05-27 14:35:05 +02002453 rc = reparse_absolute_location_path(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002454 LY_CHECK_RET(rc);
2455 break;
2456 case LYXP_TOKEN_LITERAL:
2457 /* Literal */
Michal Vasko004d3152020-06-11 19:59:22 +02002458 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002459 goto predicate;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002460 case LYXP_TOKEN_NUMBER:
2461 /* Number */
Michal Vasko004d3152020-06-11 19:59:22 +02002462 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002463 goto predicate;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002464 default:
Michal Vasko49fec8e2022-05-24 10:28:33 +02002465 LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002466 return LY_EVALID;
2467 }
2468
2469 return LY_SUCCESS;
2470
2471predicate:
2472 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02002473 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
aPiecekbf968d92021-05-27 14:35:05 +02002474 rc = reparse_predicate(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002475 LY_CHECK_RET(rc);
2476 }
2477
2478 /* ('/' or '//') RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02002479 if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002480
2481 /* '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02002482 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002483
aPiecekbf968d92021-05-27 14:35:05 +02002484 rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002485 LY_CHECK_RET(rc);
2486 }
2487
2488 return LY_SUCCESS;
2489}
2490
2491/**
2492 * @brief Reparse UnaryExpr. Logs directly on error.
2493 *
2494 * [17] UnaryExpr ::= UnionExpr | '-' UnaryExpr
2495 * [18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
2496 *
2497 * @param[in] ctx Context for logging.
2498 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002499 * @param[in] tok_idx Position in the expression @p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002500 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002501 * @return LY_ERR
2502 */
2503static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002504reparse_unary_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002505{
Michal Vaskodd528af2022-08-08 14:35:07 +02002506 uint32_t prev_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002507 LY_ERR rc;
2508
2509 /* ('-')* */
Michal Vasko004d3152020-06-11 19:59:22 +02002510 prev_exp = *tok_idx;
Michal Vasko69730152020-10-09 16:30:07 +02002511 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) &&
2512 (exp->expr[exp->tok_pos[*tok_idx]] == '-')) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002513 exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNARY);
Michal Vasko004d3152020-06-11 19:59:22 +02002514 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002515 }
2516
2517 /* PathExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002518 prev_exp = *tok_idx;
aPiecekbf968d92021-05-27 14:35:05 +02002519 rc = reparse_path_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002520 LY_CHECK_RET(rc);
2521
2522 /* ('|' PathExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002523 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_UNI)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002524 exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNION);
Michal Vasko004d3152020-06-11 19:59:22 +02002525 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002526
aPiecekbf968d92021-05-27 14:35:05 +02002527 rc = reparse_path_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002528 LY_CHECK_RET(rc);
2529 }
2530
2531 return LY_SUCCESS;
2532}
2533
2534/**
2535 * @brief Reparse AdditiveExpr. Logs directly on error.
2536 *
2537 * [15] AdditiveExpr ::= MultiplicativeExpr
2538 * | AdditiveExpr '+' MultiplicativeExpr
2539 * | AdditiveExpr '-' MultiplicativeExpr
2540 * [16] MultiplicativeExpr ::= UnaryExpr
2541 * | MultiplicativeExpr '*' UnaryExpr
2542 * | MultiplicativeExpr 'div' UnaryExpr
2543 * | MultiplicativeExpr 'mod' UnaryExpr
2544 *
2545 * @param[in] ctx Context for logging.
2546 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002547 * @param[in] tok_idx Position in the expression @p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002548 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002549 * @return LY_ERR
2550 */
2551static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002552reparse_additive_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002553{
Michal Vaskodd528af2022-08-08 14:35:07 +02002554 uint32_t prev_add_exp, prev_mul_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002555 LY_ERR rc;
2556
Michal Vasko004d3152020-06-11 19:59:22 +02002557 prev_add_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002558 goto reparse_multiplicative_expr;
2559
2560 /* ('+' / '-' MultiplicativeExpr)* */
Michal Vasko69730152020-10-09 16:30:07 +02002561 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) &&
2562 ((exp->expr[exp->tok_pos[*tok_idx]] == '+') || (exp->expr[exp->tok_pos[*tok_idx]] == '-'))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002563 exp_repeat_push(exp, prev_add_exp, LYXP_EXPR_ADDITIVE);
Michal Vasko004d3152020-06-11 19:59:22 +02002564 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002565
2566reparse_multiplicative_expr:
2567 /* UnaryExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002568 prev_mul_exp = *tok_idx;
aPiecekbf968d92021-05-27 14:35:05 +02002569 rc = reparse_unary_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002570 LY_CHECK_RET(rc);
2571
2572 /* ('*' / 'div' / 'mod' UnaryExpr)* */
Michal Vasko69730152020-10-09 16:30:07 +02002573 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) &&
2574 ((exp->expr[exp->tok_pos[*tok_idx]] == '*') || (exp->tok_len[*tok_idx] == 3))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002575 exp_repeat_push(exp, prev_mul_exp, LYXP_EXPR_MULTIPLICATIVE);
Michal Vasko004d3152020-06-11 19:59:22 +02002576 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002577
aPiecekbf968d92021-05-27 14:35:05 +02002578 rc = reparse_unary_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002579 LY_CHECK_RET(rc);
2580 }
2581 }
2582
2583 return LY_SUCCESS;
2584}
2585
2586/**
2587 * @brief Reparse EqualityExpr. Logs directly on error.
2588 *
2589 * [13] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
2590 * | EqualityExpr '!=' RelationalExpr
2591 * [14] RelationalExpr ::= AdditiveExpr
2592 * | RelationalExpr '<' AdditiveExpr
2593 * | RelationalExpr '>' AdditiveExpr
2594 * | RelationalExpr '<=' AdditiveExpr
2595 * | RelationalExpr '>=' AdditiveExpr
2596 *
2597 * @param[in] ctx Context for logging.
2598 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002599 * @param[in] tok_idx Position in the expression @p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002600 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002601 * @return LY_ERR
2602 */
2603static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002604reparse_equality_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002605{
Michal Vaskodd528af2022-08-08 14:35:07 +02002606 uint32_t prev_eq_exp, prev_rel_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002607 LY_ERR rc;
2608
Michal Vasko004d3152020-06-11 19:59:22 +02002609 prev_eq_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002610 goto reparse_additive_expr;
2611
2612 /* ('=' / '!=' RelationalExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002613 while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_EQUAL, LYXP_TOKEN_OPER_NEQUAL)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002614 exp_repeat_push(exp, prev_eq_exp, LYXP_EXPR_EQUALITY);
Michal Vasko004d3152020-06-11 19:59:22 +02002615 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002616
2617reparse_additive_expr:
2618 /* AdditiveExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002619 prev_rel_exp = *tok_idx;
aPiecekbf968d92021-05-27 14:35:05 +02002620 rc = reparse_additive_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002621 LY_CHECK_RET(rc);
2622
2623 /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002624 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_COMP)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002625 exp_repeat_push(exp, prev_rel_exp, LYXP_EXPR_RELATIONAL);
Michal Vasko004d3152020-06-11 19:59:22 +02002626 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002627
aPiecekbf968d92021-05-27 14:35:05 +02002628 rc = reparse_additive_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002629 LY_CHECK_RET(rc);
2630 }
2631 }
2632
2633 return LY_SUCCESS;
2634}
2635
2636/**
2637 * @brief Reparse OrExpr. Logs directly on error.
2638 *
2639 * [11] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
2640 * [12] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
2641 *
2642 * @param[in] ctx Context for logging.
2643 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002644 * @param[in] tok_idx Position in the expression @p exp.
aPiecekbf968d92021-05-27 14:35:05 +02002645 * @param[in] depth Current number of nested expressions.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002646 * @return LY_ERR
2647 */
2648static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002649reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002650{
Michal Vaskodd528af2022-08-08 14:35:07 +02002651 uint32_t prev_or_exp, prev_and_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002652 LY_ERR rc;
2653
aPiecekbf968d92021-05-27 14:35:05 +02002654 ++depth;
2655 LY_CHECK_ERR_RET(depth > LYXP_MAX_BLOCK_DEPTH, LOGVAL(ctx, LY_VCODE_XP_DEPTH), LY_EINVAL);
2656
Michal Vasko004d3152020-06-11 19:59:22 +02002657 prev_or_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002658 goto reparse_equality_expr;
2659
2660 /* ('or' AndExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002661 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*tok_idx] == 2)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002662 exp_repeat_push(exp, prev_or_exp, LYXP_EXPR_OR);
Michal Vasko004d3152020-06-11 19:59:22 +02002663 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002664
2665reparse_equality_expr:
2666 /* EqualityExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002667 prev_and_exp = *tok_idx;
aPiecekbf968d92021-05-27 14:35:05 +02002668 rc = reparse_equality_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002669 LY_CHECK_RET(rc);
2670
2671 /* ('and' EqualityExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002672 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*tok_idx] == 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002673 exp_repeat_push(exp, prev_and_exp, LYXP_EXPR_AND);
Michal Vasko004d3152020-06-11 19:59:22 +02002674 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002675
aPiecekbf968d92021-05-27 14:35:05 +02002676 rc = reparse_equality_expr(ctx, exp, tok_idx, depth);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002677 LY_CHECK_RET(rc);
2678 }
2679 }
2680
2681 return LY_SUCCESS;
2682}
Radek Krejcib1646a92018-11-02 16:08:26 +01002683
2684/**
2685 * @brief Parse NCName.
2686 *
2687 * @param[in] ncname Name to parse.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002688 * @return Length of @p ncname valid bytes.
Radek Krejcib1646a92018-11-02 16:08:26 +01002689 */
Michal Vasko49fec8e2022-05-24 10:28:33 +02002690static ssize_t
Radek Krejcib1646a92018-11-02 16:08:26 +01002691parse_ncname(const char *ncname)
2692{
Radek Krejci1deb5be2020-08-26 16:43:36 +02002693 uint32_t uc;
Radek Krejcid4270262019-01-07 15:07:25 +01002694 size_t size;
Michal Vasko49fec8e2022-05-24 10:28:33 +02002695 ssize_t len = 0;
Radek Krejcib1646a92018-11-02 16:08:26 +01002696
2697 LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), 0);
2698 if (!is_xmlqnamestartchar(uc) || (uc == ':')) {
2699 return len;
2700 }
2701
2702 do {
2703 len += size;
Radek Krejci9a564c92019-01-07 14:53:57 +01002704 if (!*ncname) {
2705 break;
2706 }
Radek Krejcid4270262019-01-07 15:07:25 +01002707 LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), -len);
Radek Krejcib1646a92018-11-02 16:08:26 +01002708 } while (is_xmlqnamechar(uc) && (uc != ':'));
2709
2710 return len;
2711}
2712
2713/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02002714 * @brief Add @p token into the expression @p exp.
Radek Krejcib1646a92018-11-02 16:08:26 +01002715 *
Michal Vasko03ff5a72019-09-11 13:49:33 +02002716 * @param[in] ctx Context for logging.
Radek Krejcib1646a92018-11-02 16:08:26 +01002717 * @param[in] exp Expression to use.
2718 * @param[in] token Token to add.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002719 * @param[in] tok_pos Token position in the XPath expression.
Radek Krejcib1646a92018-11-02 16:08:26 +01002720 * @param[in] tok_len Token length in the XPath expression.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002721 * @return LY_ERR
Radek Krejcib1646a92018-11-02 16:08:26 +01002722 */
2723static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02002724exp_add_token(const struct ly_ctx *ctx, struct lyxp_expr *exp, enum lyxp_token token, uint32_t tok_pos, uint32_t tok_len)
Radek Krejcib1646a92018-11-02 16:08:26 +01002725{
2726 uint32_t prev;
2727
2728 if (exp->used == exp->size) {
2729 prev = exp->size;
2730 exp->size += LYXP_EXPR_SIZE_STEP;
2731 if (prev > exp->size) {
2732 LOGINT(ctx);
2733 return LY_EINT;
2734 }
2735
2736 exp->tokens = ly_realloc(exp->tokens, exp->size * sizeof *exp->tokens);
2737 LY_CHECK_ERR_RET(!exp->tokens, LOGMEM(ctx), LY_EMEM);
2738 exp->tok_pos = ly_realloc(exp->tok_pos, exp->size * sizeof *exp->tok_pos);
2739 LY_CHECK_ERR_RET(!exp->tok_pos, LOGMEM(ctx), LY_EMEM);
2740 exp->tok_len = ly_realloc(exp->tok_len, exp->size * sizeof *exp->tok_len);
2741 LY_CHECK_ERR_RET(!exp->tok_len, LOGMEM(ctx), LY_EMEM);
2742 }
2743
2744 exp->tokens[exp->used] = token;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002745 exp->tok_pos[exp->used] = tok_pos;
Radek Krejcib1646a92018-11-02 16:08:26 +01002746 exp->tok_len[exp->used] = tok_len;
2747 ++exp->used;
2748 return LY_SUCCESS;
2749}
2750
2751void
Michal Vasko14676352020-05-29 11:35:55 +02002752lyxp_expr_free(const struct ly_ctx *ctx, struct lyxp_expr *expr)
Radek Krejcib1646a92018-11-02 16:08:26 +01002753{
Michal Vaskodd528af2022-08-08 14:35:07 +02002754 uint32_t i;
Radek Krejcib1646a92018-11-02 16:08:26 +01002755
2756 if (!expr) {
2757 return;
2758 }
2759
2760 lydict_remove(ctx, expr->expr);
2761 free(expr->tokens);
2762 free(expr->tok_pos);
2763 free(expr->tok_len);
2764 if (expr->repeat) {
2765 for (i = 0; i < expr->used; ++i) {
2766 free(expr->repeat[i]);
2767 }
2768 }
2769 free(expr->repeat);
2770 free(expr);
2771}
2772
Michal Vasko49fec8e2022-05-24 10:28:33 +02002773/**
2774 * @brief Parse Axis name.
2775 *
2776 * @param[in] str String to parse.
2777 * @param[in] str_len Length of @p str.
2778 * @return LY_SUCCESS if an axis.
2779 * @return LY_ENOT otherwise.
2780 */
2781static LY_ERR
2782expr_parse_axis(const char *str, size_t str_len)
2783{
2784 switch (str_len) {
2785 case 4:
2786 if (!strncmp("self", str, str_len)) {
2787 return LY_SUCCESS;
2788 }
2789 break;
2790 case 5:
2791 if (!strncmp("child", str, str_len)) {
2792 return LY_SUCCESS;
2793 }
2794 break;
2795 case 6:
2796 if (!strncmp("parent", str, str_len)) {
2797 return LY_SUCCESS;
2798 }
2799 break;
2800 case 8:
2801 if (!strncmp("ancestor", str, str_len)) {
2802 return LY_SUCCESS;
2803 }
2804 break;
2805 case 9:
2806 if (!strncmp("attribute", str, str_len)) {
2807 return LY_SUCCESS;
2808 } else if (!strncmp("following", str, str_len)) {
2809 return LY_SUCCESS;
2810 } else if (!strncmp("namespace", str, str_len)) {
2811 LOGERR(NULL, LY_EINVAL, "Axis \"namespace\" not supported.");
2812 return LY_ENOT;
2813 } else if (!strncmp("preceding", str, str_len)) {
2814 return LY_SUCCESS;
2815 }
2816 break;
2817 case 10:
2818 if (!strncmp("descendant", str, str_len)) {
2819 return LY_SUCCESS;
2820 }
2821 break;
2822 case 16:
2823 if (!strncmp("ancestor-or-self", str, str_len)) {
2824 return LY_SUCCESS;
2825 }
2826 break;
2827 case 17:
2828 if (!strncmp("following-sibling", str, str_len)) {
2829 return LY_SUCCESS;
2830 } else if (!strncmp("preceding-sibling", str, str_len)) {
2831 return LY_SUCCESS;
2832 }
2833 break;
2834 case 18:
2835 if (!strncmp("descendant-or-self", str, str_len)) {
2836 return LY_SUCCESS;
2837 }
2838 break;
2839 }
2840
2841 return LY_ENOT;
2842}
2843
Radek Krejcif03a9e22020-09-18 20:09:31 +02002844LY_ERR
2845lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len, ly_bool reparse, struct lyxp_expr **expr_p)
Radek Krejcib1646a92018-11-02 16:08:26 +01002846{
Radek Krejcif03a9e22020-09-18 20:09:31 +02002847 LY_ERR ret = LY_SUCCESS;
2848 struct lyxp_expr *expr;
Radek Krejcid4270262019-01-07 15:07:25 +01002849 size_t parsed = 0, tok_len;
Radek Krejcib1646a92018-11-02 16:08:26 +01002850 enum lyxp_token tok_type;
Michal Vasko49fec8e2022-05-24 10:28:33 +02002851 ly_bool prev_func_check = 0, prev_ntype_check = 0, has_axis;
Michal Vaskodd528af2022-08-08 14:35:07 +02002852 uint32_t tok_idx = 0;
Michal Vasko49fec8e2022-05-24 10:28:33 +02002853 ssize_t ncname_len;
Radek Krejcib1646a92018-11-02 16:08:26 +01002854
Radek Krejcif03a9e22020-09-18 20:09:31 +02002855 assert(expr_p);
2856
2857 if (!expr_str[0]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01002858 LOGVAL(ctx, LY_VCODE_XP_EOF);
Radek Krejcif03a9e22020-09-18 20:09:31 +02002859 return LY_EVALID;
Michal Vasko004d3152020-06-11 19:59:22 +02002860 }
2861
2862 if (!expr_len) {
Radek Krejcif03a9e22020-09-18 20:09:31 +02002863 expr_len = strlen(expr_str);
Michal Vasko004d3152020-06-11 19:59:22 +02002864 }
Michal Vaskodd528af2022-08-08 14:35:07 +02002865 if (expr_len > UINT32_MAX) {
2866 LOGVAL(ctx, LYVE_XPATH, "XPath expression cannot be longer than %" PRIu32 " characters.", UINT32_MAX);
Radek Krejcif03a9e22020-09-18 20:09:31 +02002867 return LY_EVALID;
Radek Krejcib1646a92018-11-02 16:08:26 +01002868 }
2869
2870 /* init lyxp_expr structure */
Radek Krejcif03a9e22020-09-18 20:09:31 +02002871 expr = calloc(1, sizeof *expr);
2872 LY_CHECK_ERR_GOTO(!expr, LOGMEM(ctx); ret = LY_EMEM, error);
2873 LY_CHECK_GOTO(ret = lydict_insert(ctx, expr_str, expr_len, &expr->expr), error);
2874 expr->used = 0;
2875 expr->size = LYXP_EXPR_SIZE_START;
2876 expr->tokens = malloc(expr->size * sizeof *expr->tokens);
2877 LY_CHECK_ERR_GOTO(!expr->tokens, LOGMEM(ctx); ret = LY_EMEM, error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002878
Radek Krejcif03a9e22020-09-18 20:09:31 +02002879 expr->tok_pos = malloc(expr->size * sizeof *expr->tok_pos);
2880 LY_CHECK_ERR_GOTO(!expr->tok_pos, LOGMEM(ctx); ret = LY_EMEM, error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002881
Radek Krejcif03a9e22020-09-18 20:09:31 +02002882 expr->tok_len = malloc(expr->size * sizeof *expr->tok_len);
2883 LY_CHECK_ERR_GOTO(!expr->tok_len, LOGMEM(ctx); ret = LY_EMEM, error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002884
Michal Vasko004d3152020-06-11 19:59:22 +02002885 /* make expr 0-terminated */
Radek Krejcif03a9e22020-09-18 20:09:31 +02002886 expr_str = expr->expr;
Michal Vasko004d3152020-06-11 19:59:22 +02002887
Radek Krejcif03a9e22020-09-18 20:09:31 +02002888 while (is_xmlws(expr_str[parsed])) {
Radek Krejcib1646a92018-11-02 16:08:26 +01002889 ++parsed;
2890 }
2891
2892 do {
Radek Krejcif03a9e22020-09-18 20:09:31 +02002893 if (expr_str[parsed] == '(') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002894
2895 /* '(' */
2896 tok_len = 1;
2897 tok_type = LYXP_TOKEN_PAR1;
2898
Michal Vasko49fec8e2022-05-24 10:28:33 +02002899 if (prev_ntype_check && expr->used && (expr->tokens[expr->used - 1] == LYXP_TOKEN_NAMETEST) &&
2900 (((expr->tok_len[expr->used - 1] == 4) &&
2901 (!strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "node", 4) ||
2902 !strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "text", 4))) ||
2903 ((expr->tok_len[expr->used - 1] == 7) &&
2904 !strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "comment", 7)))) {
2905 /* it is NodeType after all */
2906 expr->tokens[expr->used - 1] = LYXP_TOKEN_NODETYPE;
2907
2908 prev_ntype_check = 0;
2909 prev_func_check = 0;
2910 } else if (prev_func_check && expr->used && (expr->tokens[expr->used - 1] == LYXP_TOKEN_NAMETEST)) {
2911 /* it is FunctionName after all */
2912 expr->tokens[expr->used - 1] = LYXP_TOKEN_FUNCNAME;
2913
2914 prev_ntype_check = 0;
2915 prev_func_check = 0;
Radek Krejcib1646a92018-11-02 16:08:26 +01002916 }
2917
Radek Krejcif03a9e22020-09-18 20:09:31 +02002918 } else if (expr_str[parsed] == ')') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002919
2920 /* ')' */
2921 tok_len = 1;
2922 tok_type = LYXP_TOKEN_PAR2;
2923
Radek Krejcif03a9e22020-09-18 20:09:31 +02002924 } else if (expr_str[parsed] == '[') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002925
2926 /* '[' */
2927 tok_len = 1;
2928 tok_type = LYXP_TOKEN_BRACK1;
2929
Radek Krejcif03a9e22020-09-18 20:09:31 +02002930 } else if (expr_str[parsed] == ']') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002931
2932 /* ']' */
2933 tok_len = 1;
2934 tok_type = LYXP_TOKEN_BRACK2;
2935
Radek Krejcif03a9e22020-09-18 20:09:31 +02002936 } else if (!strncmp(&expr_str[parsed], "..", 2)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01002937
2938 /* '..' */
2939 tok_len = 2;
2940 tok_type = LYXP_TOKEN_DDOT;
2941
Radek Krejcif03a9e22020-09-18 20:09:31 +02002942 } else if ((expr_str[parsed] == '.') && (!isdigit(expr_str[parsed + 1]))) {
Radek Krejcib1646a92018-11-02 16:08:26 +01002943
2944 /* '.' */
2945 tok_len = 1;
2946 tok_type = LYXP_TOKEN_DOT;
2947
Radek Krejcif03a9e22020-09-18 20:09:31 +02002948 } else if (expr_str[parsed] == '@') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002949
2950 /* '@' */
2951 tok_len = 1;
2952 tok_type = LYXP_TOKEN_AT;
2953
Radek Krejcif03a9e22020-09-18 20:09:31 +02002954 } else if (expr_str[parsed] == ',') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002955
2956 /* ',' */
2957 tok_len = 1;
2958 tok_type = LYXP_TOKEN_COMMA;
2959
Radek Krejcif03a9e22020-09-18 20:09:31 +02002960 } else if (expr_str[parsed] == '\'') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002961
2962 /* Literal with ' */
Radek Krejcif03a9e22020-09-18 20:09:31 +02002963 for (tok_len = 1; (expr_str[parsed + tok_len] != '\0') && (expr_str[parsed + tok_len] != '\''); ++tok_len) {}
2964 LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == '\0',
Radek Krejci2efc45b2020-12-22 16:25:44 +01002965 LOGVAL(ctx, LY_VCODE_XP_EOE, expr_str[parsed], &expr_str[parsed]); ret = LY_EVALID,
Michal Vasko69730152020-10-09 16:30:07 +02002966 error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002967 ++tok_len;
2968 tok_type = LYXP_TOKEN_LITERAL;
2969
Radek Krejcif03a9e22020-09-18 20:09:31 +02002970 } else if (expr_str[parsed] == '\"') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002971
2972 /* Literal with " */
Radek Krejcif03a9e22020-09-18 20:09:31 +02002973 for (tok_len = 1; (expr_str[parsed + tok_len] != '\0') && (expr_str[parsed + tok_len] != '\"'); ++tok_len) {}
2974 LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == '\0',
Radek Krejci2efc45b2020-12-22 16:25:44 +01002975 LOGVAL(ctx, LY_VCODE_XP_EOE, expr_str[parsed], &expr_str[parsed]); ret = LY_EVALID,
Michal Vasko69730152020-10-09 16:30:07 +02002976 error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002977 ++tok_len;
2978 tok_type = LYXP_TOKEN_LITERAL;
2979
Radek Krejcif03a9e22020-09-18 20:09:31 +02002980 } else if ((expr_str[parsed] == '.') || (isdigit(expr_str[parsed]))) {
Radek Krejcib1646a92018-11-02 16:08:26 +01002981
2982 /* Number */
Radek Krejcif03a9e22020-09-18 20:09:31 +02002983 for (tok_len = 0; isdigit(expr_str[parsed + tok_len]); ++tok_len) {}
2984 if (expr_str[parsed + tok_len] == '.') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002985 ++tok_len;
Radek Krejcif03a9e22020-09-18 20:09:31 +02002986 for ( ; isdigit(expr_str[parsed + tok_len]); ++tok_len) {}
Radek Krejcib1646a92018-11-02 16:08:26 +01002987 }
2988 tok_type = LYXP_TOKEN_NUMBER;
2989
aPiecekfba75362021-10-07 12:39:48 +02002990 } else if (expr_str[parsed] == '$') {
2991
2992 /* VariableReference */
2993 parsed++;
Michal Vasko49fec8e2022-05-24 10:28:33 +02002994 ncname_len = parse_ncname(&expr_str[parsed]);
aPiecekfba75362021-10-07 12:39:48 +02002995 LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
2996 parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
2997 tok_len = ncname_len;
2998 LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == ':',
2999 LOGVAL(ctx, LYVE_XPATH, "Variable with prefix is not supported."); ret = LY_EVALID,
3000 error);
3001 tok_type = LYXP_TOKEN_VARREF;
3002
Radek Krejcif03a9e22020-09-18 20:09:31 +02003003 } else if (expr_str[parsed] == '/') {
Radek Krejcib1646a92018-11-02 16:08:26 +01003004
3005 /* Operator '/', '//' */
Radek Krejcif03a9e22020-09-18 20:09:31 +02003006 if (!strncmp(&expr_str[parsed], "//", 2)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003007 tok_len = 2;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003008 tok_type = LYXP_TOKEN_OPER_RPATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01003009 } else {
3010 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003011 tok_type = LYXP_TOKEN_OPER_PATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01003012 }
Radek Krejcib1646a92018-11-02 16:08:26 +01003013
Radek Krejcif03a9e22020-09-18 20:09:31 +02003014 } else if (!strncmp(&expr_str[parsed], "!=", 2)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003015
Michal Vasko3e48bf32020-06-01 08:39:07 +02003016 /* Operator '!=' */
Radek Krejcib1646a92018-11-02 16:08:26 +01003017 tok_len = 2;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003018 tok_type = LYXP_TOKEN_OPER_NEQUAL;
3019
Radek Krejcif03a9e22020-09-18 20:09:31 +02003020 } else if (!strncmp(&expr_str[parsed], "<=", 2) || !strncmp(&expr_str[parsed], ">=", 2)) {
Michal Vasko3e48bf32020-06-01 08:39:07 +02003021
3022 /* Operator '<=', '>=' */
3023 tok_len = 2;
3024 tok_type = LYXP_TOKEN_OPER_COMP;
Radek Krejcib1646a92018-11-02 16:08:26 +01003025
Radek Krejcif03a9e22020-09-18 20:09:31 +02003026 } else if (expr_str[parsed] == '|') {
Radek Krejcib1646a92018-11-02 16:08:26 +01003027
3028 /* Operator '|' */
3029 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003030 tok_type = LYXP_TOKEN_OPER_UNI;
Radek Krejcib1646a92018-11-02 16:08:26 +01003031
Radek Krejcif03a9e22020-09-18 20:09:31 +02003032 } else if ((expr_str[parsed] == '+') || (expr_str[parsed] == '-')) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003033
3034 /* Operator '+', '-' */
3035 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003036 tok_type = LYXP_TOKEN_OPER_MATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01003037
Radek Krejcif03a9e22020-09-18 20:09:31 +02003038 } else if (expr_str[parsed] == '=') {
Radek Krejcib1646a92018-11-02 16:08:26 +01003039
Michal Vasko3e48bf32020-06-01 08:39:07 +02003040 /* Operator '=' */
Radek Krejcib1646a92018-11-02 16:08:26 +01003041 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003042 tok_type = LYXP_TOKEN_OPER_EQUAL;
3043
Radek Krejcif03a9e22020-09-18 20:09:31 +02003044 } else if ((expr_str[parsed] == '<') || (expr_str[parsed] == '>')) {
Michal Vasko3e48bf32020-06-01 08:39:07 +02003045
3046 /* Operator '<', '>' */
3047 tok_len = 1;
3048 tok_type = LYXP_TOKEN_OPER_COMP;
Radek Krejcib1646a92018-11-02 16:08:26 +01003049
Michal Vasko69730152020-10-09 16:30:07 +02003050 } else if (expr->used && (expr->tokens[expr->used - 1] != LYXP_TOKEN_AT) &&
3051 (expr->tokens[expr->used - 1] != LYXP_TOKEN_PAR1) &&
3052 (expr->tokens[expr->used - 1] != LYXP_TOKEN_BRACK1) &&
3053 (expr->tokens[expr->used - 1] != LYXP_TOKEN_COMMA) &&
3054 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_LOG) &&
3055 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_EQUAL) &&
3056 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_NEQUAL) &&
3057 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_COMP) &&
3058 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_MATH) &&
3059 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_UNI) &&
3060 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_PATH) &&
3061 (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_RPATH)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003062
3063 /* Operator '*', 'or', 'and', 'mod', or 'div' */
Radek Krejcif03a9e22020-09-18 20:09:31 +02003064 if (expr_str[parsed] == '*') {
Radek Krejcib1646a92018-11-02 16:08:26 +01003065 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003066 tok_type = LYXP_TOKEN_OPER_MATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01003067
Radek Krejcif03a9e22020-09-18 20:09:31 +02003068 } else if (!strncmp(&expr_str[parsed], "or", 2)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003069 tok_len = 2;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003070 tok_type = LYXP_TOKEN_OPER_LOG;
Radek Krejcib1646a92018-11-02 16:08:26 +01003071
Radek Krejcif03a9e22020-09-18 20:09:31 +02003072 } else if (!strncmp(&expr_str[parsed], "and", 3)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003073 tok_len = 3;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003074 tok_type = LYXP_TOKEN_OPER_LOG;
Radek Krejcib1646a92018-11-02 16:08:26 +01003075
Radek Krejcif03a9e22020-09-18 20:09:31 +02003076 } else if (!strncmp(&expr_str[parsed], "mod", 3) || !strncmp(&expr_str[parsed], "div", 3)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003077 tok_len = 3;
Michal Vasko3e48bf32020-06-01 08:39:07 +02003078 tok_type = LYXP_TOKEN_OPER_MATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01003079
Michal Vasko49fec8e2022-05-24 10:28:33 +02003080 } else if (prev_ntype_check || prev_func_check) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01003081 LOGVAL(ctx, LYVE_XPATH, "Invalid character 0x%x ('%c'), perhaps \"%.*s\" is supposed to be a function call.",
Michal Vasko69730152020-10-09 16:30:07 +02003082 expr_str[parsed], expr_str[parsed], expr->tok_len[expr->used - 1], &expr->expr[expr->tok_pos[expr->used - 1]]);
Radek Krejcif03a9e22020-09-18 20:09:31 +02003083 ret = LY_EVALID;
Radek Krejcib1646a92018-11-02 16:08:26 +01003084 goto error;
3085 } else {
Michal Vasko774ce402021-04-14 15:35:06 +02003086 LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str);
Radek Krejcif03a9e22020-09-18 20:09:31 +02003087 ret = LY_EVALID;
Radek Krejcib1646a92018-11-02 16:08:26 +01003088 goto error;
3089 }
Radek Krejcib1646a92018-11-02 16:08:26 +01003090 } else {
3091
Michal Vasko49fec8e2022-05-24 10:28:33 +02003092 /* (AxisName '::')? ((NCName ':')? '*' | QName) or NodeType/FunctionName */
3093 if (expr_str[parsed] == '*') {
3094 ncname_len = 1;
3095 } else {
3096 ncname_len = parse_ncname(&expr_str[parsed]);
3097 LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
3098 parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
3099 }
Radek Krejcib1646a92018-11-02 16:08:26 +01003100 tok_len = ncname_len;
3101
Michal Vasko49fec8e2022-05-24 10:28:33 +02003102 has_axis = 0;
3103 if (!strncmp(&expr_str[parsed + tok_len], "::", 2)) {
3104 /* axis */
3105 LY_CHECK_ERR_GOTO(expr_parse_axis(&expr_str[parsed], ncname_len),
3106 LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str); ret = LY_EVALID, error);
3107 tok_type = LYXP_TOKEN_AXISNAME;
3108
3109 LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
3110 parsed += tok_len;
3111
3112 /* '::' */
3113 tok_len = 2;
3114 tok_type = LYXP_TOKEN_DCOLON;
3115
3116 LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
3117 parsed += tok_len;
3118
3119 if (expr_str[parsed] == '*') {
3120 ncname_len = 1;
3121 } else {
3122 ncname_len = parse_ncname(&expr_str[parsed]);
3123 LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
3124 parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
3125 }
3126 tok_len = ncname_len;
3127
3128 has_axis = 1;
3129 }
3130
Radek Krejcif03a9e22020-09-18 20:09:31 +02003131 if (expr_str[parsed + tok_len] == ':') {
Radek Krejcib1646a92018-11-02 16:08:26 +01003132 ++tok_len;
Radek Krejcif03a9e22020-09-18 20:09:31 +02003133 if (expr_str[parsed + tok_len] == '*') {
Radek Krejcib1646a92018-11-02 16:08:26 +01003134 ++tok_len;
3135 } else {
Radek Krejcif03a9e22020-09-18 20:09:31 +02003136 ncname_len = parse_ncname(&expr_str[parsed + tok_len]);
Michal Vaskoe2be5462021-08-04 10:49:42 +02003137 LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
Michal Vasko774ce402021-04-14 15:35:06 +02003138 parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
Radek Krejcib1646a92018-11-02 16:08:26 +01003139 tok_len += ncname_len;
3140 }
Michal Vasko49fec8e2022-05-24 10:28:33 +02003141 /* remove old flags to prevent ambiguities */
3142 prev_ntype_check = 0;
3143 prev_func_check = 0;
Radek Krejcib1646a92018-11-02 16:08:26 +01003144 tok_type = LYXP_TOKEN_NAMETEST;
3145 } else {
Michal Vasko49fec8e2022-05-24 10:28:33 +02003146 /* if not '*', there is no prefix so it can still be NodeType/FunctionName, we can't finally decide now */
3147 prev_ntype_check = (expr_str[parsed] == '*') ? 0 : 1;
3148 prev_func_check = (prev_ntype_check && !has_axis) ? 1 : 0;
Radek Krejcib1646a92018-11-02 16:08:26 +01003149 tok_type = LYXP_TOKEN_NAMETEST;
3150 }
3151 }
3152
3153 /* store the token, move on to the next one */
Radek Krejcif03a9e22020-09-18 20:09:31 +02003154 LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
Radek Krejcib1646a92018-11-02 16:08:26 +01003155 parsed += tok_len;
Radek Krejcif03a9e22020-09-18 20:09:31 +02003156 while (is_xmlws(expr_str[parsed])) {
Radek Krejcib1646a92018-11-02 16:08:26 +01003157 ++parsed;
3158 }
3159
Radek Krejcif03a9e22020-09-18 20:09:31 +02003160 } while (expr_str[parsed]);
Radek Krejcib1646a92018-11-02 16:08:26 +01003161
Michal Vasko004d3152020-06-11 19:59:22 +02003162 if (reparse) {
3163 /* prealloc repeat */
Radek Krejcif03a9e22020-09-18 20:09:31 +02003164 expr->repeat = calloc(expr->size, sizeof *expr->repeat);
3165 LY_CHECK_ERR_GOTO(!expr->repeat, LOGMEM(ctx); ret = LY_EMEM, error);
Radek Krejcib1646a92018-11-02 16:08:26 +01003166
Michal Vasko004d3152020-06-11 19:59:22 +02003167 /* fill repeat */
aPiecekbf968d92021-05-27 14:35:05 +02003168 LY_CHECK_ERR_GOTO(reparse_or_expr(ctx, expr, &tok_idx, 0), ret = LY_EVALID, error);
Radek Krejcif03a9e22020-09-18 20:09:31 +02003169 if (expr->used > tok_idx) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01003170 LOGVAL(ctx, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of an XPath expression.",
Michal Vasko69730152020-10-09 16:30:07 +02003171 &expr->expr[expr->tok_pos[tok_idx]]);
Radek Krejcif03a9e22020-09-18 20:09:31 +02003172 ret = LY_EVALID;
Michal Vasko004d3152020-06-11 19:59:22 +02003173 goto error;
3174 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02003175 }
3176
Radek Krejcif03a9e22020-09-18 20:09:31 +02003177 print_expr_struct_debug(expr);
3178 *expr_p = expr;
3179 return LY_SUCCESS;
Radek Krejcib1646a92018-11-02 16:08:26 +01003180
3181error:
Radek Krejcif03a9e22020-09-18 20:09:31 +02003182 lyxp_expr_free(ctx, expr);
3183 return ret;
Radek Krejcib1646a92018-11-02 16:08:26 +01003184}
3185
Michal Vasko1734be92020-09-22 08:55:10 +02003186LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003187lyxp_expr_dup(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t start_idx, uint32_t end_idx,
Michal Vaskoe33134a2022-07-29 14:54:40 +02003188 struct lyxp_expr **dup_p)
Michal Vasko004d3152020-06-11 19:59:22 +02003189{
Michal Vasko1734be92020-09-22 08:55:10 +02003190 LY_ERR ret = LY_SUCCESS;
3191 struct lyxp_expr *dup = NULL;
Michal Vaskodd528af2022-08-08 14:35:07 +02003192 uint32_t used = 0, i, j, k, expr_len;
Michal Vaskoe33134a2022-07-29 14:54:40 +02003193 const char *expr_start;
3194
3195 assert((!start_idx && !end_idx) || ((start_idx < exp->used) && (end_idx < exp->used) && (start_idx <= end_idx)));
Michal Vasko004d3152020-06-11 19:59:22 +02003196
Michal Vasko7f45cf22020-10-01 12:49:44 +02003197 if (!exp) {
3198 goto cleanup;
3199 }
3200
Michal Vaskoe33134a2022-07-29 14:54:40 +02003201 if (!start_idx && !end_idx) {
3202 end_idx = exp->used - 1;
3203 }
3204
3205 expr_start = exp->expr + exp->tok_pos[start_idx];
3206 expr_len = (exp->tok_pos[end_idx] + exp->tok_len[end_idx]) - exp->tok_pos[start_idx];
3207
Michal Vasko004d3152020-06-11 19:59:22 +02003208 dup = calloc(1, sizeof *dup);
Michal Vasko1734be92020-09-22 08:55:10 +02003209 LY_CHECK_ERR_GOTO(!dup, LOGMEM(ctx); ret = LY_EMEM, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +02003210
Michal Vasko08e9b112021-06-11 15:41:17 +02003211 if (exp->used) {
Michal Vaskoe33134a2022-07-29 14:54:40 +02003212 used = (end_idx - start_idx) + 1;
3213
3214 dup->tokens = malloc(used * sizeof *dup->tokens);
Michal Vasko08e9b112021-06-11 15:41:17 +02003215 LY_CHECK_ERR_GOTO(!dup->tokens, LOGMEM(ctx); ret = LY_EMEM, cleanup);
Michal Vaskoe33134a2022-07-29 14:54:40 +02003216 memcpy(dup->tokens, exp->tokens + start_idx, used * sizeof *dup->tokens);
Michal Vasko004d3152020-06-11 19:59:22 +02003217
Michal Vaskoe33134a2022-07-29 14:54:40 +02003218 dup->tok_pos = malloc(used * sizeof *dup->tok_pos);
Michal Vasko08e9b112021-06-11 15:41:17 +02003219 LY_CHECK_ERR_GOTO(!dup->tok_pos, LOGMEM(ctx); ret = LY_EMEM, cleanup);
Michal Vaskoe33134a2022-07-29 14:54:40 +02003220 memcpy(dup->tok_pos, exp->tok_pos + start_idx, used * sizeof *dup->tok_pos);
Michal Vasko004d3152020-06-11 19:59:22 +02003221
Michal Vaskoe33134a2022-07-29 14:54:40 +02003222 if (start_idx) {
3223 /* fix the indices in the expression */
3224 for (i = 0; i < used; ++i) {
3225 dup->tok_pos[i] -= expr_start - exp->expr;
3226 }
3227 }
3228
3229 dup->tok_len = malloc(used * sizeof *dup->tok_len);
Michal Vasko08e9b112021-06-11 15:41:17 +02003230 LY_CHECK_ERR_GOTO(!dup->tok_len, LOGMEM(ctx); ret = LY_EMEM, cleanup);
Michal Vaskoe33134a2022-07-29 14:54:40 +02003231 memcpy(dup->tok_len, exp->tok_len + start_idx, used * sizeof *dup->tok_len);
Michal Vasko004d3152020-06-11 19:59:22 +02003232
Michal Vasko79a7a872022-06-17 09:00:48 +02003233 if (exp->repeat) {
Michal Vaskoe33134a2022-07-29 14:54:40 +02003234 dup->repeat = malloc(used * sizeof *dup->repeat);
Michal Vasko79a7a872022-06-17 09:00:48 +02003235 LY_CHECK_ERR_GOTO(!dup->repeat, LOGMEM(ctx); ret = LY_EMEM, cleanup);
Michal Vaskoe33134a2022-07-29 14:54:40 +02003236 for (i = start_idx; i <= end_idx; ++i) {
Michal Vasko79a7a872022-06-17 09:00:48 +02003237 if (!exp->repeat[i]) {
Michal Vaskoe33134a2022-07-29 14:54:40 +02003238 dup->repeat[i - start_idx] = NULL;
Michal Vasko79a7a872022-06-17 09:00:48 +02003239 } else {
3240 for (j = 0; exp->repeat[i][j]; ++j) {}
3241 /* the ending 0 as well */
3242 ++j;
Michal Vasko004d3152020-06-11 19:59:22 +02003243
Michal Vaskoe33134a2022-07-29 14:54:40 +02003244 dup->repeat[i - start_idx] = malloc(j * sizeof **dup->repeat);
3245 LY_CHECK_ERR_GOTO(!dup->repeat[i - start_idx], LOGMEM(ctx); ret = LY_EMEM, cleanup);
3246 memcpy(dup->repeat[i - start_idx], exp->repeat[i], j * sizeof **dup->repeat);
3247 dup->repeat[i - start_idx][j - 1] = 0;
3248
3249 if (start_idx) {
3250 /* fix the indices in the tokens */
3251 for (k = 0; k < j; ++k) {
3252 dup->repeat[i - start_idx][k] -= start_idx;
3253 }
3254 }
Michal Vasko79a7a872022-06-17 09:00:48 +02003255 }
Michal Vasko08e9b112021-06-11 15:41:17 +02003256 }
Michal Vasko004d3152020-06-11 19:59:22 +02003257 }
3258 }
3259
Michal Vaskoe33134a2022-07-29 14:54:40 +02003260 dup->used = used;
3261 dup->size = used;
3262
3263 /* copy only subexpression */
3264 LY_CHECK_GOTO(ret = lydict_insert(ctx, expr_start, expr_len, &dup->expr), cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +02003265
Michal Vasko1734be92020-09-22 08:55:10 +02003266cleanup:
3267 if (ret) {
3268 lyxp_expr_free(ctx, dup);
3269 } else {
3270 *dup_p = dup;
3271 }
3272 return ret;
Michal Vasko004d3152020-06-11 19:59:22 +02003273}
3274
Michal Vasko03ff5a72019-09-11 13:49:33 +02003275/**
3276 * @brief Get the last-added schema node that is currently in the context.
3277 *
3278 * @param[in] set Set to search in.
3279 * @return Last-added schema context node, NULL if no node is in context.
3280 */
3281static struct lysc_node *
3282warn_get_scnode_in_ctx(struct lyxp_set *set)
3283{
3284 uint32_t i;
3285
3286 if (!set || (set->type != LYXP_SET_SCNODE_SET)) {
3287 return NULL;
3288 }
3289
3290 i = set->used;
3291 do {
3292 --i;
Radek Krejcif13b87b2020-12-01 22:02:17 +01003293 if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003294 /* if there are more, simply return the first found (last added) */
3295 return set->val.scnodes[i].scnode;
3296 }
3297 } while (i);
3298
3299 return NULL;
3300}
3301
3302/**
3303 * @brief Test whether a type is numeric - integer type or decimal64.
3304 *
3305 * @param[in] type Type to test.
Radek Krejci857189e2020-09-01 13:26:36 +02003306 * @return Boolean value whether @p type is numeric type or not.
Michal Vasko03ff5a72019-09-11 13:49:33 +02003307 */
Radek Krejci857189e2020-09-01 13:26:36 +02003308static ly_bool
Michal Vasko03ff5a72019-09-11 13:49:33 +02003309warn_is_numeric_type(struct lysc_type *type)
3310{
3311 struct lysc_type_union *uni;
Radek Krejci857189e2020-09-01 13:26:36 +02003312 ly_bool ret;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003313 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003314
3315 switch (type->basetype) {
3316 case LY_TYPE_DEC64:
3317 case LY_TYPE_INT8:
3318 case LY_TYPE_UINT8:
3319 case LY_TYPE_INT16:
3320 case LY_TYPE_UINT16:
3321 case LY_TYPE_INT32:
3322 case LY_TYPE_UINT32:
3323 case LY_TYPE_INT64:
3324 case LY_TYPE_UINT64:
3325 return 1;
3326 case LY_TYPE_UNION:
3327 uni = (struct lysc_type_union *)type;
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003328 LY_ARRAY_FOR(uni->types, u) {
3329 ret = warn_is_numeric_type(uni->types[u]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003330 if (ret) {
3331 /* found a suitable type */
Radek Krejci857189e2020-09-01 13:26:36 +02003332 return ret;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003333 }
3334 }
3335 /* did not find any suitable type */
3336 return 0;
3337 case LY_TYPE_LEAFREF:
3338 return warn_is_numeric_type(((struct lysc_type_leafref *)type)->realtype);
3339 default:
3340 return 0;
3341 }
3342}
3343
3344/**
3345 * @brief Test whether a type is string-like - no integers, decimal64 or binary.
3346 *
3347 * @param[in] type Type to test.
Radek Krejci857189e2020-09-01 13:26:36 +02003348 * @return Boolean value whether @p type's basetype is string type or not.
Michal Vasko03ff5a72019-09-11 13:49:33 +02003349 */
Radek Krejci857189e2020-09-01 13:26:36 +02003350static ly_bool
Michal Vasko03ff5a72019-09-11 13:49:33 +02003351warn_is_string_type(struct lysc_type *type)
3352{
3353 struct lysc_type_union *uni;
Radek Krejci857189e2020-09-01 13:26:36 +02003354 ly_bool ret;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003355 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003356
3357 switch (type->basetype) {
3358 case LY_TYPE_BITS:
3359 case LY_TYPE_ENUM:
3360 case LY_TYPE_IDENT:
3361 case LY_TYPE_INST:
3362 case LY_TYPE_STRING:
3363 return 1;
3364 case LY_TYPE_UNION:
3365 uni = (struct lysc_type_union *)type;
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003366 LY_ARRAY_FOR(uni->types, u) {
3367 ret = warn_is_string_type(uni->types[u]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003368 if (ret) {
3369 /* found a suitable type */
Radek Krejci857189e2020-09-01 13:26:36 +02003370 return ret;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003371 }
3372 }
3373 /* did not find any suitable type */
3374 return 0;
3375 case LY_TYPE_LEAFREF:
3376 return warn_is_string_type(((struct lysc_type_leafref *)type)->realtype);
3377 default:
3378 return 0;
3379 }
3380}
3381
3382/**
3383 * @brief Test whether a type is one specific type.
3384 *
3385 * @param[in] type Type to test.
3386 * @param[in] base Expected type.
Radek Krejci857189e2020-09-01 13:26:36 +02003387 * @return Boolean value whether the given @p type is of the specific basetype @p base.
Michal Vasko03ff5a72019-09-11 13:49:33 +02003388 */
Radek Krejci857189e2020-09-01 13:26:36 +02003389static ly_bool
Michal Vasko03ff5a72019-09-11 13:49:33 +02003390warn_is_specific_type(struct lysc_type *type, LY_DATA_TYPE base)
3391{
3392 struct lysc_type_union *uni;
Radek Krejci857189e2020-09-01 13:26:36 +02003393 ly_bool ret;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003394 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003395
3396 if (type->basetype == base) {
3397 return 1;
3398 } else if (type->basetype == LY_TYPE_UNION) {
3399 uni = (struct lysc_type_union *)type;
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003400 LY_ARRAY_FOR(uni->types, u) {
3401 ret = warn_is_specific_type(uni->types[u], base);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003402 if (ret) {
3403 /* found a suitable type */
Radek Krejci857189e2020-09-01 13:26:36 +02003404 return ret;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003405 }
3406 }
3407 /* did not find any suitable type */
3408 return 0;
3409 } else if (type->basetype == LY_TYPE_LEAFREF) {
3410 return warn_is_specific_type(((struct lysc_type_leafref *)type)->realtype, base);
3411 }
3412
3413 return 0;
3414}
3415
3416/**
3417 * @brief Get next type of a (union) type.
3418 *
3419 * @param[in] type Base type.
3420 * @param[in] prev_type Previously returned type.
3421 * @return Next type or NULL.
3422 */
3423static struct lysc_type *
3424warn_is_equal_type_next_type(struct lysc_type *type, struct lysc_type *prev_type)
3425{
3426 struct lysc_type_union *uni;
Radek Krejci857189e2020-09-01 13:26:36 +02003427 ly_bool found = 0;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003428 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003429
3430 switch (type->basetype) {
3431 case LY_TYPE_UNION:
3432 uni = (struct lysc_type_union *)type;
3433 if (!prev_type) {
3434 return uni->types[0];
3435 }
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003436 LY_ARRAY_FOR(uni->types, u) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003437 if (found) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003438 return uni->types[u];
Michal Vasko03ff5a72019-09-11 13:49:33 +02003439 }
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003440 if (prev_type == uni->types[u]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003441 found = 1;
3442 }
3443 }
3444 return NULL;
3445 default:
3446 if (prev_type) {
3447 assert(type == prev_type);
3448 return NULL;
3449 } else {
3450 return type;
3451 }
3452 }
3453}
3454
3455/**
3456 * @brief Test whether 2 types have a common type.
3457 *
3458 * @param[in] type1 First type.
3459 * @param[in] type2 Second type.
3460 * @return 1 if they do, 0 otherwise.
3461 */
3462static int
3463warn_is_equal_type(struct lysc_type *type1, struct lysc_type *type2)
3464{
3465 struct lysc_type *t1, *rt1, *t2, *rt2;
3466
3467 t1 = NULL;
3468 while ((t1 = warn_is_equal_type_next_type(type1, t1))) {
3469 if (t1->basetype == LY_TYPE_LEAFREF) {
3470 rt1 = ((struct lysc_type_leafref *)t1)->realtype;
3471 } else {
3472 rt1 = t1;
3473 }
3474
3475 t2 = NULL;
3476 while ((t2 = warn_is_equal_type_next_type(type2, t2))) {
3477 if (t2->basetype == LY_TYPE_LEAFREF) {
3478 rt2 = ((struct lysc_type_leafref *)t2)->realtype;
3479 } else {
3480 rt2 = t2;
3481 }
3482
3483 if (rt2->basetype == rt1->basetype) {
3484 /* match found */
3485 return 1;
3486 }
3487 }
3488 }
3489
3490 return 0;
3491}
3492
3493/**
Michal Vaskoaa956522021-11-11 10:45:34 +01003494 * @brief Print warning with information about the XPath subexpression that caused previous warning.
3495 *
3496 * @param[in] ctx Context for logging.
3497 * @param[in] tok_pos Index of the subexpression in the whole expression.
3498 * @param[in] subexpr Subexpression start.
3499 * @param[in] subexpr_len Length of @p subexpr to print.
3500 * @param[in] cur_scnode Expression context node.
3501 */
3502static void
Michal Vaskodd528af2022-08-08 14:35:07 +02003503warn_subexpr_log(const struct ly_ctx *ctx, uint32_t tok_pos, const char *subexpr, int subexpr_len,
Michal Vaskoaa956522021-11-11 10:45:34 +01003504 const struct lysc_node *cur_scnode)
3505{
3506 char *path;
3507
3508 path = lysc_path(cur_scnode, LYSC_PATH_LOG, NULL, 0);
Michal Vaskodd528af2022-08-08 14:35:07 +02003509 LOGWRN(ctx, "Previous warning generated by XPath subexpression[%" PRIu32 "] \"%.*s\" with context node \"%s\".",
Michal Vaskoaa956522021-11-11 10:45:34 +01003510 tok_pos, subexpr_len, subexpr, path);
3511 free(path);
3512}
3513
3514/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02003515 * @brief Check both operands of comparison operators.
3516 *
3517 * @param[in] ctx Context for errors.
3518 * @param[in] set1 First operand set.
3519 * @param[in] set2 Second operand set.
3520 * @param[in] numbers_only Whether accept only numbers or other types are fine too (for '=' and '!=').
3521 * @param[in] expr Start of the expression to print with the warning.
3522 * @param[in] tok_pos Token position.
3523 */
3524static void
Michal Vaskoaa956522021-11-11 10:45:34 +01003525warn_operands(struct ly_ctx *ctx, struct lyxp_set *set1, struct lyxp_set *set2, ly_bool numbers_only, const char *expr,
Michal Vaskodd528af2022-08-08 14:35:07 +02003526 uint32_t tok_pos)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003527{
3528 struct lysc_node_leaf *node1, *node2;
Radek Krejci857189e2020-09-01 13:26:36 +02003529 ly_bool leaves = 1, warning = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003530
3531 node1 = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set1);
3532 node2 = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set2);
3533
3534 if (!node1 && !node2) {
3535 /* no node-sets involved, nothing to do */
3536 return;
3537 }
3538
3539 if (node1) {
3540 if (!(node1->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3541 LOGWRN(ctx, "Node type %s \"%s\" used as operand.", lys_nodetype2str(node1->nodetype), node1->name);
3542 warning = 1;
3543 leaves = 0;
3544 } else if (numbers_only && !warn_is_numeric_type(node1->type)) {
3545 LOGWRN(ctx, "Node \"%s\" is not of a numeric type, but used where it was expected.", node1->name);
3546 warning = 1;
3547 }
3548 }
3549
3550 if (node2) {
3551 if (!(node2->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3552 LOGWRN(ctx, "Node type %s \"%s\" used as operand.", lys_nodetype2str(node2->nodetype), node2->name);
3553 warning = 1;
3554 leaves = 0;
3555 } else if (numbers_only && !warn_is_numeric_type(node2->type)) {
3556 LOGWRN(ctx, "Node \"%s\" is not of a numeric type, but used where it was expected.", node2->name);
3557 warning = 1;
3558 }
3559 }
3560
3561 if (node1 && node2 && leaves && !numbers_only) {
Michal Vasko69730152020-10-09 16:30:07 +02003562 if ((warn_is_numeric_type(node1->type) && !warn_is_numeric_type(node2->type)) ||
3563 (!warn_is_numeric_type(node1->type) && warn_is_numeric_type(node2->type)) ||
3564 (!warn_is_numeric_type(node1->type) && !warn_is_numeric_type(node2->type) &&
3565 !warn_is_equal_type(node1->type, node2->type))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003566 LOGWRN(ctx, "Incompatible types of operands \"%s\" and \"%s\" for comparison.", node1->name, node2->name);
3567 warning = 1;
3568 }
3569 }
3570
3571 if (warning) {
Michal Vaskoaa956522021-11-11 10:45:34 +01003572 warn_subexpr_log(ctx, tok_pos, expr + tok_pos, 20, set1->cur_scnode);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003573 }
3574}
3575
3576/**
3577 * @brief Check that a value is valid for a leaf. If not applicable, does nothing.
3578 *
3579 * @param[in] exp Parsed XPath expression.
3580 * @param[in] set Set with the leaf/leaf-list.
3581 * @param[in] val_exp Index of the value (literal/number) in @p exp.
3582 * @param[in] equal_exp Index of the start of the equality expression in @p exp.
3583 * @param[in] last_equal_exp Index of the end of the equality expression in @p exp.
3584 */
3585static void
Michal Vaskodd528af2022-08-08 14:35:07 +02003586warn_equality_value(const struct lyxp_expr *exp, struct lyxp_set *set, uint32_t val_exp, uint32_t equal_exp,
3587 uint32_t last_equal_exp)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003588{
3589 struct lysc_node *scnode;
3590 struct lysc_type *type;
3591 char *value;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02003592 struct lyd_value storage;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003593 LY_ERR rc;
3594 struct ly_err_item *err = NULL;
3595
Michal Vasko69730152020-10-09 16:30:07 +02003596 if ((scnode = warn_get_scnode_in_ctx(set)) && (scnode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) &&
3597 ((exp->tokens[val_exp] == LYXP_TOKEN_LITERAL) || (exp->tokens[val_exp] == LYXP_TOKEN_NUMBER))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003598 /* check that the node can have the specified value */
3599 if (exp->tokens[val_exp] == LYXP_TOKEN_LITERAL) {
3600 value = strndup(exp->expr + exp->tok_pos[val_exp] + 1, exp->tok_len[val_exp] - 2);
3601 } else {
3602 value = strndup(exp->expr + exp->tok_pos[val_exp], exp->tok_len[val_exp]);
3603 }
3604 if (!value) {
3605 LOGMEM(set->ctx);
3606 return;
3607 }
3608
3609 if ((((struct lysc_node_leaf *)scnode)->type->basetype == LY_TYPE_IDENT) && !strchr(value, ':')) {
3610 LOGWRN(set->ctx, "Identityref \"%s\" comparison with identity \"%s\" without prefix, consider adding"
Michal Vasko69730152020-10-09 16:30:07 +02003611 " a prefix or best using \"derived-from(-or-self)()\" functions.", scnode->name, value);
Michal Vaskoaa956522021-11-11 10:45:34 +01003612 warn_subexpr_log(set->ctx, exp->tok_pos[equal_exp], exp->expr + exp->tok_pos[equal_exp],
Radek Krejci0f969882020-08-21 16:56:47 +02003613 (exp->tok_pos[last_equal_exp] - exp->tok_pos[equal_exp]) + exp->tok_len[last_equal_exp],
Michal Vaskoaa956522021-11-11 10:45:34 +01003614 set->cur_scnode);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003615 }
3616
3617 type = ((struct lysc_node_leaf *)scnode)->type;
3618 if (type->basetype != LY_TYPE_IDENT) {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02003619 rc = type->plugin->store(set->ctx, type, value, strlen(value), 0, set->format, set->prefix_data,
Michal Vasko405cc9e2020-12-01 12:01:27 +01003620 LYD_HINT_DATA, scnode, &storage, NULL, &err);
Michal Vaskobf42e832020-11-23 16:59:42 +01003621 if (rc == LY_EINCOMPLETE) {
3622 rc = LY_SUCCESS;
3623 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02003624
3625 if (err) {
3626 LOGWRN(set->ctx, "Invalid value \"%s\" which does not fit the type (%s).", value, err->msg);
3627 ly_err_free(err);
3628 } else if (rc != LY_SUCCESS) {
3629 LOGWRN(set->ctx, "Invalid value \"%s\" which does not fit the type.", value);
3630 }
3631 if (rc != LY_SUCCESS) {
Michal Vaskoaa956522021-11-11 10:45:34 +01003632 warn_subexpr_log(set->ctx, exp->tok_pos[equal_exp], exp->expr + exp->tok_pos[equal_exp],
Radek Krejci0f969882020-08-21 16:56:47 +02003633 (exp->tok_pos[last_equal_exp] - exp->tok_pos[equal_exp]) + exp->tok_len[last_equal_exp],
Michal Vaskoaa956522021-11-11 10:45:34 +01003634 set->cur_scnode);
Michal Vaskoba99a3e2020-08-18 15:50:05 +02003635 } else {
3636 type->plugin->free(set->ctx, &storage);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003637 }
3638 }
3639 free(value);
3640 }
3641}
3642
3643/*
3644 * XPath functions
3645 */
3646
3647/**
3648 * @brief Execute the YANG 1.1 bit-is-set(node-set, string) function. Returns LYXP_SET_BOOLEAN
3649 * depending on whether the first node bit value from the second argument is set.
3650 *
3651 * @param[in] args Array of arguments.
3652 * @param[in] arg_count Count of elements in @p args.
3653 * @param[in,out] set Context and result set at the same time.
3654 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003655 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003656 */
3657static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003658xpath_bit_is_set(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003659{
3660 struct lyd_node_term *leaf;
3661 struct lysc_node_leaf *sleaf;
Michal Vasko2588b952021-07-29 07:43:26 +02003662 struct lyd_value_bits *bits;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003663 LY_ERR rc = LY_SUCCESS;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003664 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003665
3666 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003667 if (args[0]->type != LYXP_SET_SCNODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003668 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko5676f4e2021-04-06 17:14:45 +02003669 } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3670 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3671 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
3672 sleaf->name);
3673 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_BITS)) {
3674 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"bits\".", __func__, sleaf->name);
3675 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02003676 }
3677
3678 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
3679 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003680 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
3681 sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003682 } else if (!warn_is_string_type(sleaf->type)) {
3683 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003684 }
3685 }
Michal Vasko1a09b212021-05-06 13:00:10 +02003686 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003687 return rc;
3688 }
3689
Michal Vaskod3678892020-05-21 10:06:58 +02003690 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01003691 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "bit-is-set(node-set, string)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02003692 return LY_EVALID;
3693 }
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003694 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003695 LY_CHECK_RET(rc);
3696
3697 set_fill_boolean(set, 0);
Michal Vaskod3678892020-05-21 10:06:58 +02003698 if (args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003699 leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
Michal Vasko2588b952021-07-29 07:43:26 +02003700 if ((leaf->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && (leaf->value.realtype->basetype == LY_TYPE_BITS)) {
3701 LYD_VALUE_GET(&leaf->value, bits);
3702 LY_ARRAY_FOR(bits->items, u) {
3703 if (!strcmp(bits->items[u]->name, args[1]->val.str)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003704 set_fill_boolean(set, 1);
3705 break;
3706 }
3707 }
3708 }
3709 }
3710
3711 return LY_SUCCESS;
3712}
3713
3714/**
3715 * @brief Execute the XPath boolean(object) function. Returns LYXP_SET_BOOLEAN
3716 * with the argument converted to boolean.
3717 *
3718 * @param[in] args Array of arguments.
3719 * @param[in] arg_count Count of elements in @p args.
3720 * @param[in,out] set Context and result set at the same time.
3721 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003722 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003723 */
3724static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003725xpath_boolean(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003726{
3727 LY_ERR rc;
3728
3729 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02003730 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003731 return LY_SUCCESS;
3732 }
3733
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003734 rc = lyxp_set_cast(args[0], LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003735 LY_CHECK_RET(rc);
3736 set_fill_set(set, args[0]);
3737
3738 return LY_SUCCESS;
3739}
3740
3741/**
3742 * @brief Execute the XPath ceiling(number) function. Returns LYXP_SET_NUMBER
3743 * with the first argument rounded up to the nearest integer.
3744 *
3745 * @param[in] args Array of arguments.
3746 * @param[in] arg_count Count of elements in @p args.
3747 * @param[in,out] set Context and result set at the same time.
3748 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003749 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003750 */
3751static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003752xpath_ceiling(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003753{
3754 struct lysc_node_leaf *sleaf;
3755 LY_ERR rc = LY_SUCCESS;
3756
3757 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003758 if (args[0]->type != LYXP_SET_SCNODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003759 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko5676f4e2021-04-06 17:14:45 +02003760 } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3761 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3762 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
3763 sleaf->name);
3764 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
3765 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
3766 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02003767 }
Michal Vasko1a09b212021-05-06 13:00:10 +02003768 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003769 return rc;
3770 }
3771
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003772 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003773 LY_CHECK_RET(rc);
3774 if ((long long)args[0]->val.num != args[0]->val.num) {
3775 set_fill_number(set, ((long long)args[0]->val.num) + 1);
3776 } else {
3777 set_fill_number(set, args[0]->val.num);
3778 }
3779
3780 return LY_SUCCESS;
3781}
3782
3783/**
3784 * @brief Execute the XPath concat(string, string, string*) function.
3785 * Returns LYXP_SET_STRING with the concatenation of all the arguments.
3786 *
3787 * @param[in] args Array of arguments.
3788 * @param[in] arg_count Count of elements in @p args.
3789 * @param[in,out] set Context and result set at the same time.
3790 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003791 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003792 */
3793static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003794xpath_concat(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003795{
Michal Vaskodd528af2022-08-08 14:35:07 +02003796 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003797 char *str = NULL;
3798 size_t used = 1;
3799 LY_ERR rc = LY_SUCCESS;
3800 struct lysc_node_leaf *sleaf;
3801
3802 if (options & LYXP_SCNODE_ALL) {
3803 for (i = 0; i < arg_count; ++i) {
3804 if ((args[i]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[i]))) {
3805 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3806 LOGWRN(set->ctx, "Argument #%u of %s is a %s node \"%s\".",
Michal Vasko69730152020-10-09 16:30:07 +02003807 i + 1, __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003808 } else if (!warn_is_string_type(sleaf->type)) {
Radek Krejci70124c82020-08-14 22:17:03 +02003809 LOGWRN(set->ctx, "Argument #%u of %s is node \"%s\", not of string-type.", i + 1, __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003810 }
3811 }
3812 }
Michal Vasko1a09b212021-05-06 13:00:10 +02003813 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003814 return rc;
3815 }
3816
3817 for (i = 0; i < arg_count; ++i) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003818 rc = lyxp_set_cast(args[i], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003819 if (rc != LY_SUCCESS) {
3820 free(str);
3821 return rc;
3822 }
3823
3824 str = ly_realloc(str, (used + strlen(args[i]->val.str)) * sizeof(char));
3825 LY_CHECK_ERR_RET(!str, LOGMEM(set->ctx), LY_EMEM);
3826 strcpy(str + used - 1, args[i]->val.str);
3827 used += strlen(args[i]->val.str);
3828 }
3829
3830 /* free, kind of */
Michal Vaskod3678892020-05-21 10:06:58 +02003831 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003832 set->type = LYXP_SET_STRING;
3833 set->val.str = str;
3834
3835 return LY_SUCCESS;
3836}
3837
3838/**
3839 * @brief Execute the XPath contains(string, string) function.
3840 * Returns LYXP_SET_BOOLEAN whether the second argument can
3841 * be found in the first or not.
3842 *
3843 * @param[in] args Array of arguments.
3844 * @param[in] arg_count Count of elements in @p args.
3845 * @param[in,out] set Context and result set at the same time.
3846 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003847 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003848 */
3849static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003850xpath_contains(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003851{
3852 struct lysc_node_leaf *sleaf;
3853 LY_ERR rc = LY_SUCCESS;
3854
3855 if (options & LYXP_SCNODE_ALL) {
3856 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3857 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003858 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
3859 sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003860 } else if (!warn_is_string_type(sleaf->type)) {
3861 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003862 }
3863 }
3864
3865 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
3866 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003867 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
3868 sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003869 } else if (!warn_is_string_type(sleaf->type)) {
3870 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003871 }
3872 }
Michal Vasko1a09b212021-05-06 13:00:10 +02003873 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003874 return rc;
3875 }
3876
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003877 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003878 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003879 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003880 LY_CHECK_RET(rc);
3881
3882 if (strstr(args[0]->val.str, args[1]->val.str)) {
3883 set_fill_boolean(set, 1);
3884 } else {
3885 set_fill_boolean(set, 0);
3886 }
3887
3888 return LY_SUCCESS;
3889}
3890
3891/**
3892 * @brief Execute the XPath count(node-set) function. Returns LYXP_SET_NUMBER
3893 * with the size of the node-set from the argument.
3894 *
3895 * @param[in] args Array of arguments.
3896 * @param[in] arg_count Count of elements in @p args.
3897 * @param[in,out] set Context and result set at the same time.
3898 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003899 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003900 */
3901static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003902xpath_count(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003903{
Michal Vasko03ff5a72019-09-11 13:49:33 +02003904 LY_ERR rc = LY_SUCCESS;
3905
3906 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003907 if (args[0]->type != LYXP_SET_SCNODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003908 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003909 }
Michal Vasko1a09b212021-05-06 13:00:10 +02003910 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003911 return rc;
3912 }
3913
Michal Vasko03ff5a72019-09-11 13:49:33 +02003914 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01003915 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "count(node-set)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02003916 return LY_EVALID;
3917 }
3918
3919 set_fill_number(set, args[0]->used);
3920 return LY_SUCCESS;
3921}
3922
3923/**
3924 * @brief Execute the XPath current() function. Returns LYXP_SET_NODE_SET
3925 * with the context with the intial node.
3926 *
3927 * @param[in] args Array of arguments.
3928 * @param[in] arg_count Count of elements in @p args.
3929 * @param[in,out] set Context and result set at the same time.
3930 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003931 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003932 */
3933static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003934xpath_current(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003935{
3936 if (arg_count || args) {
Radek Krejcie87c7dc2021-06-02 21:25:42 +02003937 LOGVAL(set->ctx, LY_VCODE_XP_INARGCOUNT, arg_count, LY_PRI_LENSTR("current()"));
Michal Vasko03ff5a72019-09-11 13:49:33 +02003938 return LY_EVALID;
3939 }
3940
3941 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02003942 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003943
Michal Vasko296dfaf2021-12-13 16:57:42 +01003944 if (set->cur_scnode) {
Michal Vasko7333cb32022-07-29 16:30:29 +02003945 LY_CHECK_RET(set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
Michal Vasko296dfaf2021-12-13 16:57:42 +01003946 } else {
3947 /* root node */
Michal Vasko7333cb32022-07-29 16:30:29 +02003948 LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
Michal Vasko296dfaf2021-12-13 16:57:42 +01003949 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02003950 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02003951 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003952
Michal Vasko296dfaf2021-12-13 16:57:42 +01003953 if (set->cur_node) {
3954 /* position is filled later */
3955 set_insert_node(set, set->cur_node, 0, LYXP_NODE_ELEM, 0);
3956 } else {
3957 /* root node */
3958 set_insert_node(set, NULL, 0, set->root_type, 0);
3959 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02003960 }
3961
3962 return LY_SUCCESS;
3963}
3964
3965/**
3966 * @brief Execute the YANG 1.1 deref(node-set) function. Returns LYXP_SET_NODE_SET with either
3967 * leafref or instance-identifier target node(s).
3968 *
3969 * @param[in] args Array of arguments.
3970 * @param[in] arg_count Count of elements in @p args.
3971 * @param[in,out] set Context and result set at the same time.
3972 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003973 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003974 */
3975static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02003976xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02003977{
3978 struct lyd_node_term *leaf;
Michal Vasko42e497c2020-01-06 08:38:25 +01003979 struct lysc_node_leaf *sleaf = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +02003980 struct lysc_type_leafref *lref;
Michal Vaskoae9e4cb2019-09-25 08:43:05 +02003981 const struct lysc_node *target;
Michal Vasko004d3152020-06-11 19:59:22 +02003982 struct ly_path *p;
3983 struct lyd_node *node;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003984 char *errmsg = NULL;
Michal Vasko00cbf532020-06-15 13:58:47 +02003985 uint8_t oper;
Michal Vasko741bb562021-06-24 11:59:50 +02003986 LY_ERR r;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003987
3988 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003989 if (args[0]->type != LYXP_SET_SCNODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003990 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko5676f4e2021-04-06 17:14:45 +02003991 } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
Michal Vasko423ba3f2021-07-19 13:08:50 +02003992 if (!(sleaf->nodetype & LYD_NODE_TERM)) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003993 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
3994 sleaf->name);
Michal Vaskoed725d72021-06-23 12:03:45 +02003995 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_LEAFREF) &&
3996 !warn_is_specific_type(sleaf->type, LY_TYPE_INST)) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02003997 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"leafref\" nor \"instance-identifier\".",
3998 __func__, sleaf->name);
3999 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004000 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004001 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko423ba3f2021-07-19 13:08:50 +02004002 if (sleaf && (sleaf->nodetype & LYD_NODE_TERM) && (sleaf->type->basetype == LY_TYPE_LEAFREF)) {
Michal Vasko004d3152020-06-11 19:59:22 +02004003 lref = (struct lysc_type_leafref *)sleaf->type;
Michal Vaskod1e53b92021-01-28 13:11:06 +01004004 oper = (sleaf->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
Michal Vasko004d3152020-06-11 19:59:22 +02004005
4006 /* it was already evaluated on schema, it must succeed */
Michal Vasko741bb562021-06-24 11:59:50 +02004007 r = ly_path_compile_leafref(set->ctx, &sleaf->node, NULL, lref->path, oper, LY_PATH_TARGET_MANY,
Michal Vasko24fc4d12021-07-12 14:41:20 +02004008 LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, &p);
Michal Vasko741bb562021-06-24 11:59:50 +02004009 if (!r) {
4010 /* get the target node */
4011 target = p[LY_ARRAY_COUNT(p) - 1].node;
4012 ly_path_free(set->ctx, p);
Michal Vasko004d3152020-06-11 19:59:22 +02004013
Michal Vasko7333cb32022-07-29 16:30:29 +02004014 LY_CHECK_RET(set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
Michal Vasko741bb562021-06-24 11:59:50 +02004015 } /* else the target was found before but is disabled so it was removed */
Michal Vaskoae9e4cb2019-09-25 08:43:05 +02004016 }
4017
Michal Vasko741bb562021-06-24 11:59:50 +02004018 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004019 }
4020
Michal Vaskod3678892020-05-21 10:06:58 +02004021 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004022 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "deref(node-set)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004023 return LY_EVALID;
4024 }
4025
Michal Vaskod3678892020-05-21 10:06:58 +02004026 lyxp_set_free_content(set);
4027 if (args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004028 leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
4029 sleaf = (struct lysc_node_leaf *)leaf->schema;
4030 if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
4031 if (sleaf->type->basetype == LY_TYPE_LEAFREF) {
4032 /* find leafref target */
Radek Krejci0b013302021-03-29 15:22:32 +02004033 if (lyplg_type_resolve_leafref((struct lysc_type_leafref *)sleaf->type, &leaf->node, &leaf->value, set->tree,
Michal Vasko9e685082021-01-29 14:49:09 +01004034 &node, &errmsg)) {
Michal Vasko004d3152020-06-11 19:59:22 +02004035 LOGERR(set->ctx, LY_EVALID, errmsg);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004036 free(errmsg);
Michal Vasko004d3152020-06-11 19:59:22 +02004037 return LY_EVALID;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004038 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004039 } else {
4040 assert(sleaf->type->basetype == LY_TYPE_INST);
Michal Vasko004d3152020-06-11 19:59:22 +02004041 if (ly_path_eval(leaf->value.target, set->tree, &node)) {
Michal Vaskoba99a3e2020-08-18 15:50:05 +02004042 LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02004043 lyd_get_value(&leaf->node));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004044 return LY_EVALID;
4045 }
4046 }
Michal Vasko004d3152020-06-11 19:59:22 +02004047
4048 /* insert it */
4049 set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004050 }
4051 }
4052
4053 return LY_SUCCESS;
4054}
4055
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004056static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +02004057xpath_derived_(struct lyxp_set **args, struct lyxp_set *set, uint32_t options, ly_bool self_match, const char *func)
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004058{
Michal Vaskofe1af042022-07-29 14:58:59 +02004059 uint32_t i, id_len;
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004060 LY_ARRAY_COUNT_TYPE u;
4061 struct lyd_node_term *leaf;
4062 struct lysc_node_leaf *sleaf;
4063 struct lyd_meta *meta;
Michal Vasko93923692021-05-07 15:28:02 +02004064 struct lyd_value *val;
4065 const struct lys_module *mod;
4066 const char *id_name;
Michal Vasko93923692021-05-07 15:28:02 +02004067 struct lysc_ident *id;
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004068 LY_ERR rc = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +02004069 ly_bool found;
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004070
4071 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02004072 if (args[0]->type != LYXP_SET_SCNODE_SET) {
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004073 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", func);
Michal Vasko5676f4e2021-04-06 17:14:45 +02004074 } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4075 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4076 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", func, lys_nodetype2str(sleaf->nodetype),
4077 sleaf->name);
4078 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_IDENT)) {
4079 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"identityref\".", func, sleaf->name);
4080 }
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004081 }
4082
4083 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
4084 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4085 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", func, lys_nodetype2str(sleaf->nodetype),
Michal Vasko69730152020-10-09 16:30:07 +02004086 sleaf->name);
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004087 } else if (!warn_is_string_type(sleaf->type)) {
4088 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", func, sleaf->name);
4089 }
4090 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004091 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004092 return rc;
4093 }
4094
4095 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004096 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "derived-from(-or-self)(node-set, string)");
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004097 return LY_EVALID;
4098 }
4099 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
4100 LY_CHECK_RET(rc);
4101
Michal Vasko93923692021-05-07 15:28:02 +02004102 /* parse the identity */
4103 id_name = args[1]->val.str;
4104 id_len = strlen(id_name);
4105 rc = moveto_resolve_model(&id_name, &id_len, set, set->cur_node ? set->cur_node->schema : NULL, &mod);
4106 LY_CHECK_RET(rc);
4107 if (!mod) {
4108 LOGVAL(set->ctx, LYVE_XPATH, "Identity \"%.*s\" without a prefix.", (int)id_len, id_name);
4109 return LY_EVALID;
4110 }
4111
4112 /* find the identity */
4113 found = 0;
4114 LY_ARRAY_FOR(mod->identities, u) {
4115 if (!ly_strncmp(mod->identities[u].name, id_name, id_len)) {
4116 /* we have match */
4117 found = 1;
4118 break;
4119 }
4120 }
4121 if (!found) {
4122 LOGVAL(set->ctx, LYVE_XPATH, "Identity \"%.*s\" not found in module \"%s\".", (int)id_len, id_name, mod->name);
4123 return LY_EVALID;
4124 }
4125 id = &mod->identities[u];
4126
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004127 set_fill_boolean(set, 0);
4128 found = 0;
4129 for (i = 0; i < args[0]->used; ++i) {
4130 if ((args[0]->val.nodes[i].type != LYXP_NODE_ELEM) && (args[0]->val.nodes[i].type != LYXP_NODE_META)) {
4131 continue;
4132 }
4133
4134 if (args[0]->val.nodes[i].type == LYXP_NODE_ELEM) {
4135 leaf = (struct lyd_node_term *)args[0]->val.nodes[i].node;
4136 sleaf = (struct lysc_node_leaf *)leaf->schema;
4137 val = &leaf->value;
4138 if (!(sleaf->nodetype & LYD_NODE_TERM) || (leaf->value.realtype->basetype != LY_TYPE_IDENT)) {
4139 /* uninteresting */
4140 continue;
4141 }
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004142 } else {
4143 meta = args[0]->val.meta[i].meta;
4144 val = &meta->value;
4145 if (val->realtype->basetype != LY_TYPE_IDENT) {
4146 /* uninteresting */
4147 continue;
4148 }
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004149 }
4150
Michal Vasko93923692021-05-07 15:28:02 +02004151 /* check the identity itself */
4152 if (self_match && (id == val->ident)) {
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004153 set_fill_boolean(set, 1);
4154 found = 1;
4155 }
Michal Vasko93923692021-05-07 15:28:02 +02004156 if (!found && !lyplg_type_identity_isderived(id, val->ident)) {
4157 set_fill_boolean(set, 1);
4158 found = 1;
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004159 }
4160
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004161 if (found) {
4162 break;
4163 }
4164 }
4165
4166 return LY_SUCCESS;
4167}
4168
Michal Vasko03ff5a72019-09-11 13:49:33 +02004169/**
4170 * @brief Execute the YANG 1.1 derived-from(node-set, string) function. Returns LYXP_SET_BOOLEAN depending
4171 * on whether the first argument nodes contain a node of an identity derived from the second
4172 * argument identity.
4173 *
4174 * @param[in] args Array of arguments.
4175 * @param[in] arg_count Count of elements in @p args.
4176 * @param[in,out] set Context and result set at the same time.
4177 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004178 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004179 */
4180static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004181xpath_derived_from(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004182{
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004183 return xpath_derived_(args, set, options, 0, __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004184}
4185
4186/**
4187 * @brief Execute the YANG 1.1 derived-from-or-self(node-set, string) function. Returns LYXP_SET_BOOLEAN depending
4188 * on whether the first argument nodes contain a node of an identity that either is or is derived from
4189 * the second argument identity.
4190 *
4191 * @param[in] args Array of arguments.
4192 * @param[in] arg_count Count of elements in @p args.
4193 * @param[in,out] set Context and result set at the same time.
4194 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004195 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004196 */
4197static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004198xpath_derived_from_or_self(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004199{
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02004200 return xpath_derived_(args, set, options, 1, __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004201}
4202
4203/**
4204 * @brief Execute the YANG 1.1 enum-value(node-set) function. Returns LYXP_SET_NUMBER
4205 * with the integer value of the first node's enum value, otherwise NaN.
4206 *
4207 * @param[in] args Array of arguments.
4208 * @param[in] arg_count Count of elements in @p args.
4209 * @param[in,out] set Context and result set at the same time.
4210 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004211 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004212 */
4213static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004214xpath_enum_value(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004215{
4216 struct lyd_node_term *leaf;
4217 struct lysc_node_leaf *sleaf;
4218 LY_ERR rc = LY_SUCCESS;
4219
4220 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02004221 if (args[0]->type != LYXP_SET_SCNODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004222 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko5676f4e2021-04-06 17:14:45 +02004223 } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4224 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4225 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
4226 sleaf->name);
4227 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_ENUM)) {
4228 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"enumeration\".", __func__, sleaf->name);
4229 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004230 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004231 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004232 return rc;
4233 }
4234
Michal Vaskod3678892020-05-21 10:06:58 +02004235 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004236 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "enum-value(node-set)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004237 return LY_EVALID;
4238 }
4239
4240 set_fill_number(set, NAN);
Michal Vaskod3678892020-05-21 10:06:58 +02004241 if (args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004242 leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
4243 sleaf = (struct lysc_node_leaf *)leaf->schema;
4244 if ((sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && (sleaf->type->basetype == LY_TYPE_ENUM)) {
4245 set_fill_number(set, leaf->value.enum_item->value);
4246 }
4247 }
4248
4249 return LY_SUCCESS;
4250}
4251
4252/**
4253 * @brief Execute the XPath false() function. Returns LYXP_SET_BOOLEAN
4254 * with false value.
4255 *
4256 * @param[in] args Array of arguments.
4257 * @param[in] arg_count Count of elements in @p args.
4258 * @param[in,out] set Context and result set at the same time.
4259 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004260 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004261 */
4262static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004263xpath_false(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004264{
4265 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004266 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004267 return LY_SUCCESS;
4268 }
4269
4270 set_fill_boolean(set, 0);
4271 return LY_SUCCESS;
4272}
4273
4274/**
4275 * @brief Execute the XPath floor(number) function. Returns LYXP_SET_NUMBER
4276 * with the first argument floored (truncated).
4277 *
4278 * @param[in] args Array of arguments.
4279 * @param[in] arg_count Count of elements in @p args.
4280 * @param[in,out] set Context and result set at the same time.
4281 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004282 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004283 */
4284static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004285xpath_floor(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t UNUSED(options))
Michal Vasko03ff5a72019-09-11 13:49:33 +02004286{
4287 LY_ERR rc;
4288
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004289 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004290 LY_CHECK_RET(rc);
4291 if (isfinite(args[0]->val.num)) {
4292 set_fill_number(set, (long long)args[0]->val.num);
4293 }
4294
4295 return LY_SUCCESS;
4296}
4297
4298/**
4299 * @brief Execute the XPath lang(string) function. Returns LYXP_SET_BOOLEAN
4300 * whether the language of the text matches the one from the argument.
4301 *
4302 * @param[in] args Array of arguments.
4303 * @param[in] arg_count Count of elements in @p args.
4304 * @param[in,out] set Context and result set at the same time.
4305 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004306 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004307 */
4308static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004309xpath_lang(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004310{
4311 const struct lyd_node *node;
4312 struct lysc_node_leaf *sleaf;
Michal Vasko9f96a052020-03-10 09:41:45 +01004313 struct lyd_meta *meta = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004314 const char *val;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004315 LY_ERR rc = LY_SUCCESS;
4316
4317 if (options & LYXP_SCNODE_ALL) {
4318 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4319 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004320 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
4321 sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004322 } else if (!warn_is_string_type(sleaf->type)) {
4323 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004324 }
4325 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004326 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004327 return rc;
4328 }
4329
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004330 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004331 LY_CHECK_RET(rc);
4332
Michal Vasko03ff5a72019-09-11 13:49:33 +02004333 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004334 LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "lang(string)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004335 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004336 } else if (!set->used) {
4337 set_fill_boolean(set, 0);
4338 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004339 }
4340
4341 switch (set->val.nodes[0].type) {
4342 case LYXP_NODE_ELEM:
4343 case LYXP_NODE_TEXT:
4344 node = set->val.nodes[0].node;
4345 break;
Michal Vasko9f96a052020-03-10 09:41:45 +01004346 case LYXP_NODE_META:
4347 node = set->val.meta[0].meta->parent;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004348 break;
4349 default:
4350 /* nothing to do with roots */
4351 set_fill_boolean(set, 0);
4352 return LY_SUCCESS;
4353 }
4354
Michal Vasko9f96a052020-03-10 09:41:45 +01004355 /* find lang metadata */
Michal Vasko9e685082021-01-29 14:49:09 +01004356 for ( ; node; node = lyd_parent(node)) {
Michal Vasko9f96a052020-03-10 09:41:45 +01004357 for (meta = node->meta; meta; meta = meta->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004358 /* annotations */
Michal Vasko9f96a052020-03-10 09:41:45 +01004359 if (meta->name && !strcmp(meta->name, "lang") && !strcmp(meta->annotation->module->name, "xml")) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004360 break;
4361 }
4362 }
4363
Michal Vasko9f96a052020-03-10 09:41:45 +01004364 if (meta) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004365 break;
4366 }
4367 }
4368
4369 /* compare languages */
Michal Vasko9f96a052020-03-10 09:41:45 +01004370 if (!meta) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004371 set_fill_boolean(set, 0);
4372 } else {
Radek Krejci1deb5be2020-08-26 16:43:36 +02004373 uint64_t i;
4374
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02004375 val = lyd_get_meta_value(meta);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004376 for (i = 0; args[0]->val.str[i]; ++i) {
4377 if (tolower(args[0]->val.str[i]) != tolower(val[i])) {
4378 set_fill_boolean(set, 0);
4379 break;
4380 }
4381 }
4382 if (!args[0]->val.str[i]) {
4383 if (!val[i] || (val[i] == '-')) {
4384 set_fill_boolean(set, 1);
4385 } else {
4386 set_fill_boolean(set, 0);
4387 }
4388 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004389 }
4390
4391 return LY_SUCCESS;
4392}
4393
4394/**
4395 * @brief Execute the XPath last() function. Returns LYXP_SET_NUMBER
4396 * with the context size.
4397 *
4398 * @param[in] args Array of arguments.
4399 * @param[in] arg_count Count of elements in @p args.
4400 * @param[in,out] set Context and result set at the same time.
4401 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004402 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004403 */
4404static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004405xpath_last(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004406{
4407 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004408 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004409 return LY_SUCCESS;
4410 }
4411
Michal Vasko03ff5a72019-09-11 13:49:33 +02004412 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004413 LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "last()");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004414 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004415 } else if (!set->used) {
4416 set_fill_number(set, 0);
4417 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004418 }
4419
4420 set_fill_number(set, set->ctx_size);
4421 return LY_SUCCESS;
4422}
4423
4424/**
4425 * @brief Execute the XPath local-name(node-set?) function. Returns LYXP_SET_STRING
4426 * with the node name without namespace from the argument or the context.
4427 *
4428 * @param[in] args Array of arguments.
4429 * @param[in] arg_count Count of elements in @p args.
4430 * @param[in,out] set Context and result set at the same time.
4431 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004432 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004433 */
4434static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004435xpath_local_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004436{
4437 struct lyxp_set_node *item;
Michal Vasko69730152020-10-09 16:30:07 +02004438
Michal Vasko03ff5a72019-09-11 13:49:33 +02004439 /* suppress unused variable warning */
4440 (void)options;
4441
4442 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004443 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004444 return LY_SUCCESS;
4445 }
4446
4447 if (arg_count) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004448 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004449 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]),
Michal Vasko5d24f6c2020-10-13 13:49:06 +02004450 "local-name(node-set?)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004451 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004452 } else if (!args[0]->used) {
4453 set_fill_string(set, "", 0);
4454 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004455 }
4456
4457 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004458 assert(!set_sort(args[0]));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004459
4460 item = &args[0]->val.nodes[0];
4461 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004462 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004463 LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "local-name(node-set?)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004464 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004465 } else if (!set->used) {
4466 set_fill_string(set, "", 0);
4467 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004468 }
4469
4470 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004471 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004472
4473 item = &set->val.nodes[0];
4474 }
4475
4476 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +01004477 case LYXP_NODE_NONE:
4478 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004479 case LYXP_NODE_ROOT:
4480 case LYXP_NODE_ROOT_CONFIG:
4481 case LYXP_NODE_TEXT:
4482 set_fill_string(set, "", 0);
4483 break;
4484 case LYXP_NODE_ELEM:
4485 set_fill_string(set, item->node->schema->name, strlen(item->node->schema->name));
4486 break;
Michal Vasko9f96a052020-03-10 09:41:45 +01004487 case LYXP_NODE_META:
4488 set_fill_string(set, ((struct lyd_meta *)item->node)->name, strlen(((struct lyd_meta *)item->node)->name));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004489 break;
4490 }
4491
4492 return LY_SUCCESS;
4493}
4494
4495/**
4496 * @brief Execute the XPath name(node-set?) function. Returns LYXP_SET_STRING
4497 * with the node name fully qualified (with namespace) from the argument or the context.
4498 *
4499 * @param[in] args Array of arguments.
4500 * @param[in] arg_count Count of elements in @p args.
4501 * @param[in,out] set Context and result set at the same time.
4502 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004503 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004504 */
4505static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004506xpath_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004507{
4508 struct lyxp_set_node *item;
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02004509 struct lys_module *mod = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004510 char *str;
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02004511 const char *name = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004512
4513 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004514 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004515 return LY_SUCCESS;
4516 }
4517
4518 if (arg_count) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004519 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004520 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "name(node-set?)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004521 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004522 } else if (!args[0]->used) {
4523 set_fill_string(set, "", 0);
4524 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004525 }
4526
4527 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004528 assert(!set_sort(args[0]));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004529
4530 item = &args[0]->val.nodes[0];
4531 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004532 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004533 LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "name(node-set?)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004534 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004535 } else if (!set->used) {
4536 set_fill_string(set, "", 0);
4537 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004538 }
4539
4540 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004541 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004542
4543 item = &set->val.nodes[0];
4544 }
4545
4546 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +01004547 case LYXP_NODE_NONE:
4548 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004549 case LYXP_NODE_ROOT:
4550 case LYXP_NODE_ROOT_CONFIG:
4551 case LYXP_NODE_TEXT:
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02004552 /* keep NULL */
Michal Vasko03ff5a72019-09-11 13:49:33 +02004553 break;
4554 case LYXP_NODE_ELEM:
4555 mod = item->node->schema->module;
4556 name = item->node->schema->name;
4557 break;
Michal Vasko9f96a052020-03-10 09:41:45 +01004558 case LYXP_NODE_META:
4559 mod = ((struct lyd_meta *)item->node)->annotation->module;
4560 name = ((struct lyd_meta *)item->node)->name;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004561 break;
4562 }
4563
4564 if (mod && name) {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02004565 int rc = asprintf(&str, "%s:%s", ly_get_prefix(mod, set->format, set->prefix_data), name);
Michal Vasko26bbb272022-08-02 14:54:33 +02004566
Michal Vasko03ff5a72019-09-11 13:49:33 +02004567 LY_CHECK_ERR_RET(rc == -1, LOGMEM(set->ctx), LY_EMEM);
4568 set_fill_string(set, str, strlen(str));
4569 free(str);
4570 } else {
4571 set_fill_string(set, "", 0);
4572 }
4573
4574 return LY_SUCCESS;
4575}
4576
4577/**
4578 * @brief Execute the XPath namespace-uri(node-set?) function. Returns LYXP_SET_STRING
4579 * with the namespace of the node from the argument or the context.
4580 *
4581 * @param[in] args Array of arguments.
4582 * @param[in] arg_count Count of elements in @p args.
4583 * @param[in,out] set Context and result set at the same time.
4584 * @param[in] options XPath options.
4585 * @return LY_ERR (LY_EINVAL for wrong arguments on schema)
4586 */
4587static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004588xpath_namespace_uri(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004589{
4590 struct lyxp_set_node *item;
4591 struct lys_module *mod;
Michal Vasko69730152020-10-09 16:30:07 +02004592
Michal Vasko03ff5a72019-09-11 13:49:33 +02004593 /* suppress unused variable warning */
4594 (void)options;
4595
4596 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004597 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004598 return LY_SUCCESS;
4599 }
4600
4601 if (arg_count) {
Michal Vaskod3678892020-05-21 10:06:58 +02004602 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004603 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]),
Michal Vasko69730152020-10-09 16:30:07 +02004604 "namespace-uri(node-set?)");
Michal Vaskod3678892020-05-21 10:06:58 +02004605 return LY_EVALID;
4606 } else if (!args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004607 set_fill_string(set, "", 0);
4608 return LY_SUCCESS;
4609 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004610
4611 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004612 assert(!set_sort(args[0]));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004613
4614 item = &args[0]->val.nodes[0];
4615 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004616 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004617 LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "namespace-uri(node-set?)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004618 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004619 } else if (!set->used) {
4620 set_fill_string(set, "", 0);
4621 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004622 }
4623
4624 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004625 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004626
4627 item = &set->val.nodes[0];
4628 }
4629
4630 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +01004631 case LYXP_NODE_NONE:
4632 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004633 case LYXP_NODE_ROOT:
4634 case LYXP_NODE_ROOT_CONFIG:
4635 case LYXP_NODE_TEXT:
4636 set_fill_string(set, "", 0);
4637 break;
4638 case LYXP_NODE_ELEM:
Michal Vasko9f96a052020-03-10 09:41:45 +01004639 case LYXP_NODE_META:
Michal Vasko03ff5a72019-09-11 13:49:33 +02004640 if (item->type == LYXP_NODE_ELEM) {
4641 mod = item->node->schema->module;
Michal Vasko9f96a052020-03-10 09:41:45 +01004642 } else { /* LYXP_NODE_META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02004643 /* annotations */
Michal Vasko9f96a052020-03-10 09:41:45 +01004644 mod = ((struct lyd_meta *)item->node)->annotation->module;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004645 }
4646
4647 set_fill_string(set, mod->ns, strlen(mod->ns));
4648 break;
4649 }
4650
4651 return LY_SUCCESS;
4652}
4653
4654/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02004655 * @brief Execute the XPath normalize-space(string?) function. Returns LYXP_SET_STRING
4656 * with normalized value (no leading, trailing, double white spaces) of the node
4657 * from the argument or the context.
4658 *
4659 * @param[in] args Array of arguments.
4660 * @param[in] arg_count Count of elements in @p args.
4661 * @param[in,out] set Context and result set at the same time.
4662 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004663 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004664 */
4665static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004666xpath_normalize_space(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004667{
Michal Vaskodd528af2022-08-08 14:35:07 +02004668 uint32_t i, new_used;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004669 char *new;
Radek Krejci857189e2020-09-01 13:26:36 +02004670 ly_bool have_spaces = 0, space_before = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004671 struct lysc_node_leaf *sleaf;
4672 LY_ERR rc = LY_SUCCESS;
4673
4674 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004675 if (arg_count && (args[0]->type == LYXP_SET_SCNODE_SET) &&
4676 (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004677 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004678 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
4679 sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004680 } else if (!warn_is_string_type(sleaf->type)) {
4681 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004682 }
4683 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004684 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004685 return rc;
4686 }
4687
4688 if (arg_count) {
4689 set_fill_set(set, args[0]);
4690 }
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004691 rc = lyxp_set_cast(set, LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004692 LY_CHECK_RET(rc);
4693
4694 /* is there any normalization necessary? */
4695 for (i = 0; set->val.str[i]; ++i) {
4696 if (is_xmlws(set->val.str[i])) {
4697 if ((i == 0) || space_before || (!set->val.str[i + 1])) {
4698 have_spaces = 1;
4699 break;
4700 }
4701 space_before = 1;
4702 } else {
4703 space_before = 0;
4704 }
4705 }
4706
4707 /* yep, there is */
4708 if (have_spaces) {
4709 /* it's enough, at least one character will go, makes space for ending '\0' */
4710 new = malloc(strlen(set->val.str) * sizeof(char));
4711 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
4712 new_used = 0;
4713
4714 space_before = 0;
4715 for (i = 0; set->val.str[i]; ++i) {
4716 if (is_xmlws(set->val.str[i])) {
4717 if ((i == 0) || space_before) {
4718 space_before = 1;
4719 continue;
4720 } else {
4721 space_before = 1;
4722 }
4723 } else {
4724 space_before = 0;
4725 }
4726
4727 new[new_used] = (space_before ? ' ' : set->val.str[i]);
4728 ++new_used;
4729 }
4730
4731 /* at worst there is one trailing space now */
4732 if (new_used && is_xmlws(new[new_used - 1])) {
4733 --new_used;
4734 }
4735
4736 new = ly_realloc(new, (new_used + 1) * sizeof(char));
4737 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
4738 new[new_used] = '\0';
4739
4740 free(set->val.str);
4741 set->val.str = new;
4742 }
4743
4744 return LY_SUCCESS;
4745}
4746
4747/**
4748 * @brief Execute the XPath not(boolean) function. Returns LYXP_SET_BOOLEAN
4749 * with the argument converted to boolean and logically inverted.
4750 *
4751 * @param[in] args Array of arguments.
4752 * @param[in] arg_count Count of elements in @p args.
4753 * @param[in,out] set Context and result set at the same time.
4754 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004755 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004756 */
4757static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004758xpath_not(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004759{
4760 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004761 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004762 return LY_SUCCESS;
4763 }
4764
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004765 lyxp_set_cast(args[0], LYXP_SET_BOOLEAN);
Michal Vasko004d3152020-06-11 19:59:22 +02004766 if (args[0]->val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004767 set_fill_boolean(set, 0);
4768 } else {
4769 set_fill_boolean(set, 1);
4770 }
4771
4772 return LY_SUCCESS;
4773}
4774
4775/**
4776 * @brief Execute the XPath number(object?) function. Returns LYXP_SET_NUMBER
4777 * with the number representation of either the argument or the context.
4778 *
4779 * @param[in] args Array of arguments.
4780 * @param[in] arg_count Count of elements in @p args.
4781 * @param[in,out] set Context and result set at the same time.
4782 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004783 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004784 */
4785static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004786xpath_number(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004787{
4788 LY_ERR rc;
4789
4790 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004791 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004792 return LY_SUCCESS;
4793 }
4794
4795 if (arg_count) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004796 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004797 LY_CHECK_RET(rc);
4798 set_fill_set(set, args[0]);
4799 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004800 rc = lyxp_set_cast(set, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004801 LY_CHECK_RET(rc);
4802 }
4803
4804 return LY_SUCCESS;
4805}
4806
4807/**
4808 * @brief Execute the XPath position() function. Returns LYXP_SET_NUMBER
4809 * with the context position.
4810 *
4811 * @param[in] args Array of arguments.
4812 * @param[in] arg_count Count of elements in @p args.
4813 * @param[in,out] set Context and result set at the same time.
4814 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004815 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004816 */
4817static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004818xpath_position(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004819{
4820 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02004821 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004822 return LY_SUCCESS;
4823 }
4824
Michal Vasko03ff5a72019-09-11 13:49:33 +02004825 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01004826 LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "position()");
Michal Vasko03ff5a72019-09-11 13:49:33 +02004827 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004828 } else if (!set->used) {
4829 set_fill_number(set, 0);
4830 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004831 }
4832
4833 set_fill_number(set, set->ctx_pos);
4834
4835 /* UNUSED in 'Release' build type */
4836 (void)options;
4837 return LY_SUCCESS;
4838}
4839
4840/**
4841 * @brief Execute the YANG 1.1 re-match(string, string) function. Returns LYXP_SET_BOOLEAN
4842 * depending on whether the second argument regex matches the first argument string. For details refer to
4843 * YANG 1.1 RFC section 10.2.1.
4844 *
4845 * @param[in] args Array of arguments.
4846 * @param[in] arg_count Count of elements in @p args.
4847 * @param[in,out] set Context and result set at the same time.
4848 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004849 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004850 */
4851static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004852xpath_re_match(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004853{
4854 struct lysc_pattern **patterns = NULL, **pattern;
4855 struct lysc_node_leaf *sleaf;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004856 LY_ERR rc = LY_SUCCESS;
4857 struct ly_err_item *err;
4858
4859 if (options & LYXP_SCNODE_ALL) {
4860 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4861 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4862 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004863 } else if (!warn_is_string_type(sleaf->type)) {
4864 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004865 }
4866 }
4867
4868 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
4869 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4870 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004871 } else if (!warn_is_string_type(sleaf->type)) {
4872 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004873 }
4874 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004875 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004876 return rc;
4877 }
4878
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004879 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004880 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004881 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004882 LY_CHECK_RET(rc);
4883
4884 LY_ARRAY_NEW_RET(set->ctx, patterns, pattern, LY_EMEM);
Radek Iša45802b52021-02-09 09:21:58 +01004885 *pattern = calloc(1, sizeof **pattern);
Radek Krejciddace2c2021-01-08 11:30:56 +01004886 LOG_LOCSET(NULL, set->cur_node, NULL, NULL);
Radek Krejci2efc45b2020-12-22 16:25:44 +01004887 rc = lys_compile_type_pattern_check(set->ctx, args[1]->val.str, &(*pattern)->code);
Michal Vasko4a7d4d62021-12-13 17:05:06 +01004888 if (set->cur_node) {
4889 LOG_LOCBACK(0, 1, 0, 0);
4890 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004891 if (rc != LY_SUCCESS) {
4892 LY_ARRAY_FREE(patterns);
4893 return rc;
4894 }
4895
Radek Krejci0b013302021-03-29 15:22:32 +02004896 rc = lyplg_type_validate_patterns(patterns, args[0]->val.str, strlen(args[0]->val.str), &err);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004897 pcre2_code_free((*pattern)->code);
4898 free(*pattern);
4899 LY_ARRAY_FREE(patterns);
4900 if (rc && (rc != LY_EVALID)) {
Michal Vasko177d0ed2020-11-23 16:43:03 +01004901 ly_err_print(set->ctx, err);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004902 ly_err_free(err);
4903 return rc;
4904 }
4905
4906 if (rc == LY_EVALID) {
4907 ly_err_free(err);
4908 set_fill_boolean(set, 0);
4909 } else {
4910 set_fill_boolean(set, 1);
4911 }
4912
4913 return LY_SUCCESS;
4914}
4915
4916/**
4917 * @brief Execute the XPath round(number) function. Returns LYXP_SET_NUMBER
4918 * with the rounded first argument. For details refer to
4919 * http://www.w3.org/TR/1999/REC-xpath-19991116/#function-round.
4920 *
4921 * @param[in] args Array of arguments.
4922 * @param[in] arg_count Count of elements in @p args.
4923 * @param[in,out] set Context and result set at the same time.
4924 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004925 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004926 */
4927static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004928xpath_round(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004929{
4930 struct lysc_node_leaf *sleaf;
4931 LY_ERR rc = LY_SUCCESS;
4932
4933 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5676f4e2021-04-06 17:14:45 +02004934 if (args[0]->type != LYXP_SET_SCNODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004935 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko5676f4e2021-04-06 17:14:45 +02004936 } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4937 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4938 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
4939 sleaf->name);
4940 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
4941 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
4942 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004943 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004944 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004945 return rc;
4946 }
4947
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004948 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004949 LY_CHECK_RET(rc);
4950
4951 /* cover only the cases where floor can't be used */
4952 if ((args[0]->val.num == -0.0f) || ((args[0]->val.num < 0) && (args[0]->val.num >= -0.5))) {
4953 set_fill_number(set, -0.0f);
4954 } else {
4955 args[0]->val.num += 0.5;
4956 rc = xpath_floor(args, 1, args[0], options);
4957 LY_CHECK_RET(rc);
4958 set_fill_number(set, args[0]->val.num);
4959 }
4960
4961 return LY_SUCCESS;
4962}
4963
4964/**
4965 * @brief Execute the XPath starts-with(string, string) function.
4966 * Returns LYXP_SET_BOOLEAN whether the second argument is
4967 * the prefix of the first or not.
4968 *
4969 * @param[in] args Array of arguments.
4970 * @param[in] arg_count Count of elements in @p args.
4971 * @param[in,out] set Context and result set at the same time.
4972 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004973 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004974 */
4975static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02004976xpath_starts_with(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02004977{
4978 struct lysc_node_leaf *sleaf;
4979 LY_ERR rc = LY_SUCCESS;
4980
4981 if (options & LYXP_SCNODE_ALL) {
4982 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4983 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4984 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004985 } else if (!warn_is_string_type(sleaf->type)) {
4986 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004987 }
4988 }
4989
4990 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
4991 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4992 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004993 } else if (!warn_is_string_type(sleaf->type)) {
4994 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004995 }
4996 }
Michal Vasko1a09b212021-05-06 13:00:10 +02004997 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004998 return rc;
4999 }
5000
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005001 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005002 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005003 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005004 LY_CHECK_RET(rc);
5005
5006 if (strncmp(args[0]->val.str, args[1]->val.str, strlen(args[1]->val.str))) {
5007 set_fill_boolean(set, 0);
5008 } else {
5009 set_fill_boolean(set, 1);
5010 }
5011
5012 return LY_SUCCESS;
5013}
5014
5015/**
5016 * @brief Execute the XPath string(object?) function. Returns LYXP_SET_STRING
5017 * with the string representation of either the argument or the context.
5018 *
5019 * @param[in] args Array of arguments.
5020 * @param[in] arg_count Count of elements in @p args.
5021 * @param[in,out] set Context and result set at the same time.
5022 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005023 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005024 */
5025static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005026xpath_string(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005027{
5028 LY_ERR rc;
5029
5030 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02005031 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005032 return LY_SUCCESS;
5033 }
5034
5035 if (arg_count) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005036 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005037 LY_CHECK_RET(rc);
5038 set_fill_set(set, args[0]);
5039 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005040 rc = lyxp_set_cast(set, LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005041 LY_CHECK_RET(rc);
5042 }
5043
5044 return LY_SUCCESS;
5045}
5046
5047/**
5048 * @brief Execute the XPath string-length(string?) function. Returns LYXP_SET_NUMBER
5049 * with the length of the string in either the argument or the context.
5050 *
5051 * @param[in] args Array of arguments.
5052 * @param[in] arg_count Count of elements in @p args.
5053 * @param[in,out] set Context and result set at the same time.
5054 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005055 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005056 */
5057static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005058xpath_string_length(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005059{
5060 struct lysc_node_leaf *sleaf;
5061 LY_ERR rc = LY_SUCCESS;
5062
5063 if (options & LYXP_SCNODE_ALL) {
5064 if (arg_count && (args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
5065 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5066 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005067 } else if (!warn_is_string_type(sleaf->type)) {
5068 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005069 }
5070 }
5071 if (!arg_count && (set->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set))) {
5072 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5073 LOGWRN(set->ctx, "Argument #0 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005074 } else if (!warn_is_string_type(sleaf->type)) {
5075 LOGWRN(set->ctx, "Argument #0 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005076 }
5077 }
Michal Vasko1a09b212021-05-06 13:00:10 +02005078 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005079 return rc;
5080 }
5081
5082 if (arg_count) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005083 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005084 LY_CHECK_RET(rc);
5085 set_fill_number(set, strlen(args[0]->val.str));
5086 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005087 rc = lyxp_set_cast(set, LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005088 LY_CHECK_RET(rc);
5089 set_fill_number(set, strlen(set->val.str));
5090 }
5091
5092 return LY_SUCCESS;
5093}
5094
5095/**
5096 * @brief Execute the XPath substring(string, number, number?) function.
5097 * Returns LYXP_SET_STRING substring of the first argument starting
5098 * on the second argument index ending on the third argument index,
5099 * indexed from 1. For exact definition refer to
5100 * http://www.w3.org/TR/1999/REC-xpath-19991116/#function-substring.
5101 *
5102 * @param[in] args Array of arguments.
5103 * @param[in] arg_count Count of elements in @p args.
5104 * @param[in,out] set Context and result set at the same time.
5105 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005106 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005107 */
5108static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005109xpath_substring(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005110{
Michal Vasko6db996e2022-07-28 10:28:04 +02005111 int64_t start;
5112 int32_t len;
Michal Vaskodd528af2022-08-08 14:35:07 +02005113 uint32_t str_start, str_len, pos;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005114 struct lysc_node_leaf *sleaf;
5115 LY_ERR rc = LY_SUCCESS;
5116
5117 if (options & LYXP_SCNODE_ALL) {
5118 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
5119 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5120 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005121 } else if (!warn_is_string_type(sleaf->type)) {
5122 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005123 }
5124 }
5125
5126 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
5127 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5128 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005129 } else if (!warn_is_numeric_type(sleaf->type)) {
5130 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005131 }
5132 }
5133
Michal Vasko69730152020-10-09 16:30:07 +02005134 if ((arg_count == 3) && (args[2]->type == LYXP_SET_SCNODE_SET) &&
5135 (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[2]))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005136 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5137 LOGWRN(set->ctx, "Argument #3 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005138 } else if (!warn_is_numeric_type(sleaf->type)) {
5139 LOGWRN(set->ctx, "Argument #3 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005140 }
5141 }
Michal Vasko1a09b212021-05-06 13:00:10 +02005142 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005143 return rc;
5144 }
5145
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005146 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005147 LY_CHECK_RET(rc);
5148
5149 /* start */
5150 if (xpath_round(&args[1], 1, args[1], options)) {
5151 return -1;
5152 }
5153 if (isfinite(args[1]->val.num)) {
5154 start = args[1]->val.num - 1;
5155 } else if (isinf(args[1]->val.num) && signbit(args[1]->val.num)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +02005156 start = INT32_MIN;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005157 } else {
Radek Krejci1deb5be2020-08-26 16:43:36 +02005158 start = INT32_MAX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005159 }
5160
5161 /* len */
5162 if (arg_count == 3) {
5163 rc = xpath_round(&args[2], 1, args[2], options);
5164 LY_CHECK_RET(rc);
Radek Krejci1deb5be2020-08-26 16:43:36 +02005165 if (isnan(args[2]->val.num) || signbit(args[2]->val.num)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005166 len = 0;
Radek Krejci1deb5be2020-08-26 16:43:36 +02005167 } else if (isfinite(args[2]->val.num)) {
5168 len = args[2]->val.num;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005169 } else {
Radek Krejci1deb5be2020-08-26 16:43:36 +02005170 len = INT32_MAX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005171 }
5172 } else {
Radek Krejci1deb5be2020-08-26 16:43:36 +02005173 len = INT32_MAX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005174 }
5175
5176 /* find matching character positions */
5177 str_start = 0;
5178 str_len = 0;
5179 for (pos = 0; args[0]->val.str[pos]; ++pos) {
5180 if (pos < start) {
5181 ++str_start;
5182 } else if (pos < start + len) {
5183 ++str_len;
5184 } else {
5185 break;
5186 }
5187 }
5188
5189 set_fill_string(set, args[0]->val.str + str_start, str_len);
5190 return LY_SUCCESS;
5191}
5192
5193/**
5194 * @brief Execute the XPath substring-after(string, string) function.
5195 * Returns LYXP_SET_STRING with the string succeeding the occurance
5196 * of the second argument in the first or an empty string.
5197 *
5198 * @param[in] args Array of arguments.
5199 * @param[in] arg_count Count of elements in @p args.
5200 * @param[in,out] set Context and result set at the same time.
5201 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005202 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005203 */
5204static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005205xpath_substring_after(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005206{
5207 char *ptr;
5208 struct lysc_node_leaf *sleaf;
5209 LY_ERR rc = LY_SUCCESS;
5210
5211 if (options & LYXP_SCNODE_ALL) {
5212 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
5213 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5214 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005215 } else if (!warn_is_string_type(sleaf->type)) {
5216 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005217 }
5218 }
5219
5220 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
5221 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5222 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005223 } else if (!warn_is_string_type(sleaf->type)) {
5224 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005225 }
5226 }
Michal Vasko1a09b212021-05-06 13:00:10 +02005227 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005228 return rc;
5229 }
5230
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005231 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005232 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005233 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005234 LY_CHECK_RET(rc);
5235
5236 ptr = strstr(args[0]->val.str, args[1]->val.str);
5237 if (ptr) {
5238 set_fill_string(set, ptr + strlen(args[1]->val.str), strlen(ptr + strlen(args[1]->val.str)));
5239 } else {
5240 set_fill_string(set, "", 0);
5241 }
5242
5243 return LY_SUCCESS;
5244}
5245
5246/**
5247 * @brief Execute the XPath substring-before(string, string) function.
5248 * Returns LYXP_SET_STRING with the string preceding the occurance
5249 * of the second argument in the first or an empty string.
5250 *
5251 * @param[in] args Array of arguments.
5252 * @param[in] arg_count Count of elements in @p args.
5253 * @param[in,out] set Context and result set at the same time.
5254 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005255 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005256 */
5257static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005258xpath_substring_before(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005259{
5260 char *ptr;
5261 struct lysc_node_leaf *sleaf;
5262 LY_ERR rc = LY_SUCCESS;
5263
5264 if (options & LYXP_SCNODE_ALL) {
5265 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
5266 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5267 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005268 } else if (!warn_is_string_type(sleaf->type)) {
5269 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005270 }
5271 }
5272
5273 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
5274 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5275 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005276 } else if (!warn_is_string_type(sleaf->type)) {
5277 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005278 }
5279 }
Michal Vasko1a09b212021-05-06 13:00:10 +02005280 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005281 return rc;
5282 }
5283
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005284 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005285 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005286 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005287 LY_CHECK_RET(rc);
5288
5289 ptr = strstr(args[0]->val.str, args[1]->val.str);
5290 if (ptr) {
5291 set_fill_string(set, args[0]->val.str, ptr - args[0]->val.str);
5292 } else {
5293 set_fill_string(set, "", 0);
5294 }
5295
5296 return LY_SUCCESS;
5297}
5298
5299/**
5300 * @brief Execute the XPath sum(node-set) function. Returns LYXP_SET_NUMBER
5301 * with the sum of all the nodes in the context.
5302 *
5303 * @param[in] args Array of arguments.
5304 * @param[in] arg_count Count of elements in @p args.
5305 * @param[in,out] set Context and result set at the same time.
5306 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005307 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005308 */
5309static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005310xpath_sum(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005311{
5312 long double num;
5313 char *str;
Michal Vasko1fdd8fa2021-01-08 09:21:45 +01005314 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005315 struct lyxp_set set_item;
5316 struct lysc_node_leaf *sleaf;
5317 LY_ERR rc = LY_SUCCESS;
5318
5319 if (options & LYXP_SCNODE_ALL) {
5320 if (args[0]->type == LYXP_SET_SCNODE_SET) {
5321 for (i = 0; i < args[0]->used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01005322 if (args[0]->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005323 sleaf = (struct lysc_node_leaf *)args[0]->val.scnodes[i].scnode;
5324 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5325 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__,
Michal Vasko69730152020-10-09 16:30:07 +02005326 lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005327 } else if (!warn_is_numeric_type(sleaf->type)) {
5328 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005329 }
5330 }
5331 }
5332 }
Michal Vasko1a09b212021-05-06 13:00:10 +02005333 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005334 return rc;
5335 }
5336
5337 set_fill_number(set, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005338
5339 if (args[0]->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01005340 LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "sum(node-set)");
Michal Vasko03ff5a72019-09-11 13:49:33 +02005341 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02005342 } else if (!args[0]->used) {
5343 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005344 }
5345
Michal Vasko5c4e5892019-11-14 12:31:38 +01005346 set_init(&set_item, set);
5347
Michal Vasko03ff5a72019-09-11 13:49:33 +02005348 set_item.type = LYXP_SET_NODE_SET;
Michal Vasko41decbf2021-11-02 11:50:21 +01005349 set_item.val.nodes = calloc(1, sizeof *set_item.val.nodes);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005350 LY_CHECK_ERR_RET(!set_item.val.nodes, LOGMEM(set->ctx), LY_EMEM);
5351
5352 set_item.used = 1;
5353 set_item.size = 1;
5354
5355 for (i = 0; i < args[0]->used; ++i) {
5356 set_item.val.nodes[0] = args[0]->val.nodes[i];
5357
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005358 rc = cast_node_set_to_string(&set_item, &str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005359 LY_CHECK_RET(rc);
5360 num = cast_string_to_number(str);
5361 free(str);
5362 set->val.num += num;
5363 }
5364
5365 free(set_item.val.nodes);
5366
5367 return LY_SUCCESS;
5368}
5369
5370/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02005371 * @brief Execute the XPath translate(string, string, string) function.
5372 * Returns LYXP_SET_STRING with the first argument with the characters
5373 * from the second argument replaced by those on the corresponding
5374 * positions in the third argument.
5375 *
5376 * @param[in] args Array of arguments.
5377 * @param[in] arg_count Count of elements in @p args.
5378 * @param[in,out] set Context and result set at the same time.
5379 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005380 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005381 */
5382static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005383xpath_translate(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005384{
Michal Vaskodd528af2022-08-08 14:35:07 +02005385 uint32_t i, j, new_used;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005386 char *new;
Radek Krejci857189e2020-09-01 13:26:36 +02005387 ly_bool have_removed;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005388 struct lysc_node_leaf *sleaf;
5389 LY_ERR rc = LY_SUCCESS;
5390
5391 if (options & LYXP_SCNODE_ALL) {
5392 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
5393 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5394 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005395 } else if (!warn_is_string_type(sleaf->type)) {
5396 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005397 }
5398 }
5399
5400 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
5401 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5402 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005403 } else if (!warn_is_string_type(sleaf->type)) {
5404 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005405 }
5406 }
5407
5408 if ((args[2]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[2]))) {
5409 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5410 LOGWRN(set->ctx, "Argument #3 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005411 } else if (!warn_is_string_type(sleaf->type)) {
5412 LOGWRN(set->ctx, "Argument #3 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005413 }
5414 }
Michal Vasko1a09b212021-05-06 13:00:10 +02005415 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005416 return rc;
5417 }
5418
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005419 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005420 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005421 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005422 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005423 rc = lyxp_set_cast(args[2], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005424 LY_CHECK_RET(rc);
5425
5426 new = malloc((strlen(args[0]->val.str) + 1) * sizeof(char));
5427 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
5428 new_used = 0;
5429
5430 have_removed = 0;
5431 for (i = 0; args[0]->val.str[i]; ++i) {
Radek Krejci857189e2020-09-01 13:26:36 +02005432 ly_bool found = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005433
5434 for (j = 0; args[1]->val.str[j]; ++j) {
5435 if (args[0]->val.str[i] == args[1]->val.str[j]) {
5436 /* removing this char */
5437 if (j >= strlen(args[2]->val.str)) {
5438 have_removed = 1;
5439 found = 1;
5440 break;
5441 }
5442 /* replacing this char */
5443 new[new_used] = args[2]->val.str[j];
5444 ++new_used;
5445 found = 1;
5446 break;
5447 }
5448 }
5449
5450 /* copying this char */
5451 if (!found) {
5452 new[new_used] = args[0]->val.str[i];
5453 ++new_used;
5454 }
5455 }
5456
5457 if (have_removed) {
5458 new = ly_realloc(new, (new_used + 1) * sizeof(char));
5459 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
5460 }
5461 new[new_used] = '\0';
5462
Michal Vaskod3678892020-05-21 10:06:58 +02005463 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005464 set->type = LYXP_SET_STRING;
5465 set->val.str = new;
5466
5467 return LY_SUCCESS;
5468}
5469
5470/**
5471 * @brief Execute the XPath true() function. Returns LYXP_SET_BOOLEAN
5472 * with true value.
5473 *
5474 * @param[in] args Array of arguments.
5475 * @param[in] arg_count Count of elements in @p args.
5476 * @param[in,out] set Context and result set at the same time.
5477 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005478 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005479 */
5480static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02005481xpath_true(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005482{
5483 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02005484 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005485 return LY_SUCCESS;
5486 }
5487
5488 set_fill_boolean(set, 1);
5489 return LY_SUCCESS;
5490}
5491
Michal Vasko03ff5a72019-09-11 13:49:33 +02005492/**
Michal Vasko49fec8e2022-05-24 10:28:33 +02005493 * @brief Execute the XPath node() processing instruction (node type). Returns LYXP_SET_NODE_SET
5494 * with only nodes from the context.
5495 *
5496 * @param[in,out] set Context and result set at the same time.
5497 * @param[in] axis Axis to search on.
5498 * @param[in] options XPath options.
5499 * @return LY_ERR
5500 */
5501static LY_ERR
5502xpath_pi_node(struct lyxp_set *set, enum lyxp_axis axis, uint32_t options)
5503{
5504 if (options & LYXP_SCNODE_ALL) {
5505 return moveto_scnode(set, NULL, NULL, axis, options);
5506 }
5507
5508 if (set->type != LYXP_SET_NODE_SET) {
5509 lyxp_set_free_content(set);
5510 return LY_SUCCESS;
5511 }
5512
5513 /* just like moving to a node with no restrictions */
5514 return moveto_node(set, NULL, NULL, axis, options);
5515}
5516
5517/**
5518 * @brief Execute the XPath text() processing instruction (node type). Returns LYXP_SET_NODE_SET
5519 * with the text content of the nodes in the context.
5520 *
5521 * @param[in,out] set Context and result set at the same time.
5522 * @param[in] axis Axis to search on.
5523 * @param[in] options XPath options.
5524 * @return LY_ERR
5525 */
5526static LY_ERR
5527xpath_pi_text(struct lyxp_set *set, enum lyxp_axis axis, uint32_t options)
5528{
5529 uint32_t i;
5530
5531 if (options & LYXP_SCNODE_ALL) {
5532 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
5533 return LY_SUCCESS;
5534 }
5535
5536 if (set->type != LYXP_SET_NODE_SET) {
5537 LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "text()");
5538 return LY_EVALID;
5539 }
5540
5541 if (axis != LYXP_AXIS_CHILD) {
5542 /* even following and preceding axescan return text nodes, but whatever */
5543 lyxp_set_free_content(set);
5544 return LY_SUCCESS;
5545 }
5546
5547 for (i = 0; i < set->used; ++i) {
5548 switch (set->val.nodes[i].type) {
5549 case LYXP_NODE_NONE:
5550 LOGINT_RET(set->ctx);
5551 case LYXP_NODE_ELEM:
5552 if (set->val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
5553 set->val.nodes[i].type = LYXP_NODE_TEXT;
5554 break;
5555 }
5556 /* fall through */
5557 case LYXP_NODE_ROOT:
5558 case LYXP_NODE_ROOT_CONFIG:
5559 case LYXP_NODE_TEXT:
5560 case LYXP_NODE_META:
5561 set_remove_node_none(set, i);
5562 break;
5563 }
5564 }
5565 set_remove_nodes_none(set);
5566
5567 return LY_SUCCESS;
5568}
5569
5570/**
Michal Vasko6346ece2019-09-24 13:12:53 +02005571 * @brief Skip prefix and return corresponding model if there is a prefix. Logs directly.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005572 *
Michal Vasko2104e9f2020-03-06 08:23:25 +01005573 * XPath @p set is expected to be a (sc)node set!
5574 *
Michal Vasko6346ece2019-09-24 13:12:53 +02005575 * @param[in,out] qname Qualified node name. If includes prefix, it is skipped.
5576 * @param[in,out] qname_len Length of @p qname, is updated accordingly.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005577 * @param[in] set Set with general XPath context.
5578 * @param[in] ctx_scnode Context node to inherit module for unprefixed node for ::LY_PREF_JSON.
Michal Vasko6346ece2019-09-24 13:12:53 +02005579 * @param[out] moveto_mod Expected module of a matching node.
5580 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005581 */
Michal Vasko6346ece2019-09-24 13:12:53 +02005582static LY_ERR
Michal Vaskofe1af042022-07-29 14:58:59 +02005583moveto_resolve_model(const char **qname, uint32_t *qname_len, const struct lyxp_set *set,
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005584 const struct lysc_node *ctx_scnode, const struct lys_module **moveto_mod)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005585{
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02005586 const struct lys_module *mod = NULL;
Michal Vasko6346ece2019-09-24 13:12:53 +02005587 const char *ptr;
Radek Krejci1deb5be2020-08-26 16:43:36 +02005588 size_t pref_len;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005589
Michal Vasko2104e9f2020-03-06 08:23:25 +01005590 assert((set->type == LYXP_SET_NODE_SET) || (set->type == LYXP_SET_SCNODE_SET));
5591
Michal Vasko6346ece2019-09-24 13:12:53 +02005592 if ((ptr = ly_strnchr(*qname, ':', *qname_len))) {
5593 /* specific module */
5594 pref_len = ptr - *qname;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005595 mod = ly_resolve_prefix(set->ctx, *qname, pref_len, set->format, set->prefix_data);
Michal Vasko6346ece2019-09-24 13:12:53 +02005596
Michal Vasko004d3152020-06-11 19:59:22 +02005597 /* check for errors and non-implemented modules, as they are not valid */
Juraj Vijtiukd75faa62019-11-26 14:10:10 +01005598 if (!mod || !mod->implemented) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01005599 LOGVAL(set->ctx, LY_VCODE_XP_INMOD, pref_len, *qname);
Michal Vasko6346ece2019-09-24 13:12:53 +02005600 return LY_EVALID;
5601 }
Juraj Vijtiukd75faa62019-11-26 14:10:10 +01005602
Michal Vasko6346ece2019-09-24 13:12:53 +02005603 *qname += pref_len + 1;
5604 *qname_len -= pref_len + 1;
5605 } else if (((*qname)[0] == '*') && (*qname_len == 1)) {
5606 /* all modules - special case */
Michal Vasko03ff5a72019-09-11 13:49:33 +02005607 mod = NULL;
Michal Vasko6346ece2019-09-24 13:12:53 +02005608 } else {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005609 switch (set->format) {
Radek Krejci8df109d2021-04-23 12:19:08 +02005610 case LY_VALUE_SCHEMA:
5611 case LY_VALUE_SCHEMA_RESOLVED:
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005612 /* current module */
5613 mod = set->cur_mod;
5614 break;
Radek Krejci224d4b42021-04-23 13:54:59 +02005615 case LY_VALUE_CANON:
Radek Krejci8df109d2021-04-23 12:19:08 +02005616 case LY_VALUE_JSON:
Radek Krejcif9943642021-04-26 10:18:21 +02005617 case LY_VALUE_LYB:
Michal Vaskoddd76592022-01-17 13:34:48 +01005618 case LY_VALUE_STR_NS:
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005619 /* inherit parent (context node) module */
5620 if (ctx_scnode) {
5621 mod = ctx_scnode->module;
5622 } else {
5623 mod = NULL;
5624 }
5625 break;
Radek Krejci8df109d2021-04-23 12:19:08 +02005626 case LY_VALUE_XML:
Michal Vasko52143d12021-04-14 15:36:39 +02005627 /* all nodes need to be prefixed */
5628 LOGVAL(set->ctx, LYVE_DATA, "Non-prefixed node \"%.*s\" in XML xpath found.", *qname_len, *qname);
5629 return LY_EVALID;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005630 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005631 }
5632
Michal Vasko6346ece2019-09-24 13:12:53 +02005633 *moveto_mod = mod;
5634 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005635}
5636
5637/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02005638 * @brief Move context @p set to the root. Handles absolute path.
5639 * Result is LYXP_SET_NODE_SET.
5640 *
5641 * @param[in,out] set Set to use.
5642 * @param[in] options Xpath options.
Michal Vaskob0099a92020-08-31 14:55:23 +02005643 * @return LY_ERR value.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005644 */
Michal Vaskob0099a92020-08-31 14:55:23 +02005645static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02005646moveto_root(struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005647{
aPiecek8b0cc152021-05-31 16:40:31 +02005648 assert(!(options & LYXP_SKIP_EXPR));
Michal Vasko03ff5a72019-09-11 13:49:33 +02005649
5650 if (options & LYXP_SCNODE_ALL) {
Michal Vasko1a09b212021-05-06 13:00:10 +02005651 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko7333cb32022-07-29 16:30:29 +02005652 LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
Michal Vasko03ff5a72019-09-11 13:49:33 +02005653 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02005654 set->type = LYXP_SET_NODE_SET;
5655 set->used = 0;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005656 set_insert_node(set, NULL, 0, set->root_type, 0);
Michal Vasko306e2832022-07-25 09:15:17 +02005657 set->non_child_axis = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005658 }
Michal Vaskob0099a92020-08-31 14:55:23 +02005659
5660 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005661}
5662
5663/**
5664 * @brief Check @p node as a part of NameTest processing.
5665 *
5666 * @param[in] node Node to check.
Michal Vasko49fec8e2022-05-24 10:28:33 +02005667 * @param[in] node_type Node type of @p node.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005668 * @param[in] set Set to read general context from.
Michal Vaskod3678892020-05-21 10:06:58 +02005669 * @param[in] node_name Node name in the dictionary to move to, NULL for any node.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005670 * @param[in] moveto_mod Expected module of the node, NULL for no prefix.
Michal Vaskocdad7122020-11-09 21:04:44 +01005671 * @param[in] options XPath options.
Michal Vasko6346ece2019-09-24 13:12:53 +02005672 * @return LY_ERR (LY_ENOT if node does not match, LY_EINCOMPLETE on unresolved when,
Michal Vaskocd2c88a2022-06-07 10:54:34 +02005673 * LY_EINVAL if neither node nor any children match)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005674 */
5675static LY_ERR
Michal Vasko49fec8e2022-05-24 10:28:33 +02005676moveto_node_check(const struct lyd_node *node, enum lyxp_node_type node_type, const struct lyxp_set *set,
5677 const char *node_name, const struct lys_module *moveto_mod, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005678{
Michal Vasko49fec8e2022-05-24 10:28:33 +02005679 if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
5680 assert(node_type == set->root_type);
5681
5682 if (node_name || moveto_mod) {
5683 /* root will not match a specific node */
5684 return LY_ENOT;
5685 }
5686 return LY_SUCCESS;
5687 } else if (node_type != LYXP_NODE_ELEM) {
5688 /* other types will not match */
5689 return LY_ENOT;
5690 }
5691
Michal Vaskodca9f122021-07-16 13:56:22 +02005692 if (!node->schema) {
5693 /* opaque node never matches */
5694 return LY_ENOT;
5695 }
5696
Michal Vasko03ff5a72019-09-11 13:49:33 +02005697 /* module check */
Michal Vasko19089f02022-06-07 11:02:11 +02005698 if (moveto_mod) {
5699 if (!(node->flags & LYD_EXT) && (node->schema->module != moveto_mod)) {
5700 return LY_ENOT;
5701 } else if ((node->flags & LYD_EXT) && strcmp(node->schema->module->name, moveto_mod->name)) {
5702 return LY_ENOT;
5703 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005704 }
5705
Michal Vasko5c4e5892019-11-14 12:31:38 +01005706 /* context check */
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005707 if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (node->schema->flags & LYS_CONFIG_R)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005708 return LY_EINVAL;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005709 } else if (set->context_op && (node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) &&
5710 (node->schema != set->context_op)) {
Michal Vasko6b26e742020-07-17 15:02:10 +02005711 return LY_EINVAL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005712 }
5713
5714 /* name check */
Michal Vasko19089f02022-06-07 11:02:11 +02005715 if (node_name) {
5716 if (!(node->flags & LYD_EXT) && (node->schema->name != node_name)) {
5717 return LY_ENOT;
5718 } else if ((node->flags & LYD_EXT) && strcmp(node->schema->name, node_name)) {
5719 return LY_ENOT;
5720 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005721 }
5722
Michal Vaskoa1424542019-11-14 16:08:52 +01005723 /* when check */
Michal Vaskod5cfa6e2020-11-23 16:56:08 +01005724 if (!(options & LYXP_IGNORE_WHEN) && lysc_has_when(node->schema) && !(node->flags & LYD_WHEN_TRUE)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005725 return LY_EINCOMPLETE;
Michal Vaskoa1424542019-11-14 16:08:52 +01005726 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005727
5728 /* match */
5729 return LY_SUCCESS;
5730}
5731
5732/**
Michal Vasko49fec8e2022-05-24 10:28:33 +02005733 * @brief Get the next node in a forward DFS.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005734 *
Michal Vasko49fec8e2022-05-24 10:28:33 +02005735 * @param[in] iter Last returned node.
5736 * @param[in] stop Node to stop the search on and not return.
5737 * @return Next node, NULL if there are no more.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005738 */
Michal Vasko49fec8e2022-05-24 10:28:33 +02005739static const struct lyd_node *
5740moveto_axis_node_next_dfs_forward(const struct lyd_node *iter, const struct lyd_node *stop)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005741{
Michal Vasko49fec8e2022-05-24 10:28:33 +02005742 const struct lyd_node *next = NULL;
5743
5744 /* 1) child */
5745 next = lyd_child(iter);
5746 if (!next) {
5747 if (iter == stop) {
5748 /* reached stop, no more descendants */
5749 return NULL;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005750 }
Michal Vasko49fec8e2022-05-24 10:28:33 +02005751 /* 2) child next sibling */
5752 next = iter->next;
5753 }
5754 while (!next) {
5755 iter = lyd_parent(iter);
5756 if ((!stop && !iter) || (stop && (lyd_parent(iter) == lyd_parent(stop)))) {
5757 return NULL;
5758 }
5759 next = iter->next;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02005760 }
5761
Michal Vasko49fec8e2022-05-24 10:28:33 +02005762 return next;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005763}
5764
5765/**
Michal Vasko49fec8e2022-05-24 10:28:33 +02005766 * @brief Get the next node in a backward DFS.
5767 *
5768 * @param[in] iter Last returned node.
5769 * @param[in] stop Node to stop the search on and not return.
5770 * @return Next node, NULL if there are no more.
5771 */
5772static const struct lyd_node *
5773moveto_axis_node_next_dfs_backward(const struct lyd_node *iter, const struct lyd_node *stop)
5774{
5775 const struct lyd_node *next = NULL;
5776
5777 /* 1) previous sibling innermost last child */
5778 next = iter->prev->next ? iter->prev : NULL;
5779 while (next && lyd_child(next)) {
5780 next = lyd_child(next);
5781 next = next->prev;
5782 }
5783
5784 if (!next) {
5785 /* 2) parent */
5786 iter = lyd_parent(iter);
5787 if ((!stop && !iter) || (stop && (lyd_parent(iter) == lyd_parent(stop)))) {
5788 return NULL;
5789 }
5790 next = iter;
5791 }
5792
5793 return next;
5794}
5795
5796/**
5797 * @brief Get the first node on an axis for a context node.
5798 *
5799 * @param[in,out] iter NULL, updated to the next node.
5800 * @param[in,out] iter_type Node type 0 of @p iter, updated to the node type of the next node.
5801 * @param[in] node Context node.
5802 * @param[in] node_type Type of @p node.
5803 * @param[in] axis Axis to use.
5804 * @param[in] set XPath set with the general context.
5805 * @return LY_SUCCESS on success.
5806 * @return LY_ENOTFOUND if no next node found.
5807 */
5808static LY_ERR
5809moveto_axis_node_next_first(const struct lyd_node **iter, enum lyxp_node_type *iter_type, const struct lyd_node *node,
5810 enum lyxp_node_type node_type, enum lyxp_axis axis, struct lyxp_set *set)
5811{
5812 const struct lyd_node *next = NULL;
5813 enum lyxp_node_type next_type = 0;
5814
5815 assert(!*iter);
5816 assert(!*iter_type);
5817
5818 switch (axis) {
5819 case LYXP_AXIS_ANCESTOR_OR_SELF:
5820 case LYXP_AXIS_DESCENDANT_OR_SELF:
5821 case LYXP_AXIS_SELF:
5822 /* return the context node */
5823 next = node;
5824 next_type = node_type;
5825 break;
5826
5827 case LYXP_AXIS_ANCESTOR:
5828 case LYXP_AXIS_PARENT:
5829 if (node_type == LYXP_NODE_ELEM) {
5830 next = lyd_parent(node);
5831 next_type = next ? LYXP_NODE_ELEM : set->root_type;
5832 } else if (node_type == LYXP_NODE_TEXT) {
5833 next = node;
5834 next_type = LYXP_NODE_ELEM;
5835 } else if (node_type == LYXP_NODE_META) {
5836 next = ((struct lyd_meta *)node)->parent;
5837 next_type = LYXP_NODE_ELEM;
5838 } /* else root does not have a parent */
5839 break;
5840
5841 case LYXP_AXIS_CHILD:
5842 if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
5843 assert(!node);
5844
5845 /* search in all the trees */
5846 next = set->tree;
5847 next_type = next ? LYXP_NODE_ELEM : 0;
5848 } else {
5849 /* search in children */
5850 next = lyd_child(node);
5851 next_type = next ? LYXP_NODE_ELEM : 0;
5852 }
5853 break;
5854
5855 case LYXP_AXIS_DESCENDANT:
5856 if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
5857 /* top-level nodes */
5858 next = set->tree;
5859 next_type = LYXP_NODE_ELEM;
5860 } else if (node_type == LYXP_NODE_ELEM) {
5861 /* start from the context node */
5862 next = moveto_axis_node_next_dfs_forward(node, node);
5863 next_type = next ? LYXP_NODE_ELEM : 0;
5864 } /* else no children */
5865 break;
5866
5867 case LYXP_AXIS_FOLLOWING:
5868 case LYXP_AXIS_FOLLOWING_SIBLING:
5869 if (node_type == LYXP_NODE_ELEM) {
5870 /* first next sibling */
5871 next = node->next;
5872 next_type = next ? LYXP_NODE_ELEM : 0;
5873 } /* else no sibling */
5874 break;
5875
5876 case LYXP_AXIS_PRECEDING:
5877 if ((node_type == LYXP_NODE_ELEM) && node->prev->next) {
5878 /* skip ancestors */
5879 next = moveto_axis_node_next_dfs_backward(node, NULL);
5880 assert(next);
5881 next_type = LYXP_NODE_ELEM;
5882 } /* else no sibling */
5883 break;
5884
5885 case LYXP_AXIS_PRECEDING_SIBLING:
5886 if (node_type == LYXP_NODE_ELEM) {
5887 /* first previous sibling */
5888 next = node->prev->next ? node->prev : NULL;
5889 next_type = next ? LYXP_NODE_ELEM : 0;
5890 } /* else no sibling */
5891 break;
5892
5893 case LYXP_AXIS_ATTRIBUTE:
5894 /* handled specially */
5895 assert(0);
5896 LOGINT(set->ctx);
5897 break;
5898 }
5899
5900 *iter = next;
5901 *iter_type = next_type;
5902 return next_type ? LY_SUCCESS : LY_ENOTFOUND;
5903}
5904
5905/**
5906 * @brief Iterate over all nodes on an axis for a context node.
5907 *
5908 * @param[in,out] iter Last returned node, start with NULL, updated to the next node.
5909 * @param[in,out] iter_type Node type of @p iter, start with 0, updated to the node type of the next node.
5910 * @param[in] node Context node.
5911 * @param[in] node_type Type of @p node.
5912 * @param[in] axis Axis to use.
5913 * @param[in] set XPath set with the general context.
5914 * @return LY_SUCCESS on success.
5915 * @return LY_ENOTFOUND if no next node found.
5916 */
5917static LY_ERR
5918moveto_axis_node_next(const struct lyd_node **iter, enum lyxp_node_type *iter_type, const struct lyd_node *node,
5919 enum lyxp_node_type node_type, enum lyxp_axis axis, struct lyxp_set *set)
5920{
5921 const struct lyd_node *next = NULL;
5922 enum lyxp_node_type next_type = 0;
5923
5924 if (!*iter_type) {
5925 /* first returned node */
5926 return moveto_axis_node_next_first(iter, iter_type, node, node_type, axis, set);
5927 }
5928
5929 switch (axis) {
5930 case LYXP_AXIS_ANCESTOR_OR_SELF:
5931 if ((*iter == node) && (*iter_type == node_type)) {
5932 /* fake first ancestor, we returned self before */
5933 *iter = NULL;
5934 *iter_type = 0;
5935 return moveto_axis_node_next_first(iter, iter_type, node, node_type, LYXP_AXIS_ANCESTOR, set);
5936 } /* else continue ancestor */
5937
5938 /* fallthrough */
5939 case LYXP_AXIS_ANCESTOR:
5940 if (*iter_type == LYXP_NODE_ELEM) {
5941 /* iter parent */
5942 next = lyd_parent(*iter);
5943 next_type = next ? LYXP_NODE_ELEM : set->root_type;
5944 } /* else root, no ancestors */
5945 break;
5946
5947 case LYXP_AXIS_CHILD:
5948 assert(*iter_type == LYXP_NODE_ELEM);
5949
5950 /* next sibling (child) */
5951 next = (*iter)->next;
5952 next_type = next ? LYXP_NODE_ELEM : 0;
5953 break;
5954
5955 case LYXP_AXIS_DESCENDANT_OR_SELF:
5956 if ((*iter == node) && (*iter_type == node_type)) {
5957 /* fake first descendant, we returned self before */
5958 *iter = NULL;
5959 *iter_type = 0;
5960 return moveto_axis_node_next_first(iter, iter_type, node, node_type, LYXP_AXIS_DESCENDANT, set);
5961 } /* else continue descendant */
5962
5963 /* fallthrough */
5964 case LYXP_AXIS_DESCENDANT:
5965 assert(*iter_type == LYXP_NODE_ELEM);
5966 next = moveto_axis_node_next_dfs_forward(*iter, node);
5967 next_type = next ? LYXP_NODE_ELEM : 0;
5968 break;
5969
5970 case LYXP_AXIS_FOLLOWING:
5971 assert(*iter_type == LYXP_NODE_ELEM);
5972 next = moveto_axis_node_next_dfs_forward(*iter, NULL);
5973 next_type = next ? LYXP_NODE_ELEM : 0;
5974 break;
5975
5976 case LYXP_AXIS_FOLLOWING_SIBLING:
5977 assert(*iter_type == LYXP_NODE_ELEM);
5978
5979 /* next sibling */
5980 next = (*iter)->next;
5981 next_type = next ? LYXP_NODE_ELEM : 0;
5982 break;
5983
5984 case LYXP_AXIS_PARENT:
5985 case LYXP_AXIS_SELF:
5986 /* parent/self was returned before */
5987 break;
5988
5989 case LYXP_AXIS_PRECEDING:
5990 assert(*iter_type == LYXP_NODE_ELEM);
5991 next = moveto_axis_node_next_dfs_backward(*iter, NULL);
5992 next_type = next ? LYXP_NODE_ELEM : 0;
5993 break;
5994
5995 case LYXP_AXIS_PRECEDING_SIBLING:
5996 assert(*iter_type == LYXP_NODE_ELEM);
5997
5998 /* previous sibling */
5999 next = (*iter)->prev->next ? (*iter)->prev : NULL;
6000 next_type = next ? LYXP_NODE_ELEM : 0;
6001 break;
6002
6003 case LYXP_AXIS_ATTRIBUTE:
6004 /* handled specially */
6005 assert(0);
6006 LOGINT(set->ctx);
6007 break;
6008 }
6009
6010 *iter = next;
6011 *iter_type = next_type;
6012 return next_type ? LY_SUCCESS : LY_ENOTFOUND;
6013}
6014
6015/**
6016 * @brief Move context @p set to a node. Result is LYXP_SET_NODE_SET. Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006017 *
6018 * @param[in,out] set Set to use.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02006019 * @param[in] moveto_mod Matching node module, NULL for no prefix.
Michal Vaskod3678892020-05-21 10:06:58 +02006020 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vasko49fec8e2022-05-24 10:28:33 +02006021 * @param[in] axis Axis to search on.
Michal Vaskocdad7122020-11-09 21:04:44 +01006022 * @param[in] options XPath options.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006023 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6024 */
6025static LY_ERR
Michal Vasko49fec8e2022-05-24 10:28:33 +02006026moveto_node(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, enum lyxp_axis axis,
6027 uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006028{
Michal Vasko230cf972021-12-02 12:31:00 +01006029 LY_ERR r, rc = LY_SUCCESS;
Michal Vasko49fec8e2022-05-24 10:28:33 +02006030 const struct lyd_node *iter;
6031 enum lyxp_node_type iter_type;
Michal Vasko230cf972021-12-02 12:31:00 +01006032 struct lyxp_set result;
Michal Vasko49fec8e2022-05-24 10:28:33 +02006033 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006034
aPiecek8b0cc152021-05-31 16:40:31 +02006035 if (options & LYXP_SKIP_EXPR) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006036 return LY_SUCCESS;
6037 }
6038
6039 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01006040 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006041 return LY_EVALID;
6042 }
6043
Michal Vasko230cf972021-12-02 12:31:00 +01006044 /* init result set */
6045 set_init(&result, set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006046
Michal Vasko49fec8e2022-05-24 10:28:33 +02006047 for (i = 0; i < set->used; ++i) {
6048 /* iterate over all the nodes on the axis of the node */
6049 iter = NULL;
6050 iter_type = 0;
6051 while (!moveto_axis_node_next(&iter, &iter_type, set->val.nodes[i].node, set->val.nodes[i].type, axis, set)) {
6052 r = moveto_node_check(iter, iter_type, set, ncname, moveto_mod, options);
6053 if (r == LY_EINCOMPLETE) {
Michal Vasko230cf972021-12-02 12:31:00 +01006054 rc = r;
6055 goto cleanup;
Michal Vasko49fec8e2022-05-24 10:28:33 +02006056 } else if (r) {
6057 continue;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006058 }
Michal Vasko49fec8e2022-05-24 10:28:33 +02006059
6060 /* check for duplicates if they are possible */
6061 switch (axis) {
6062 case LYXP_AXIS_ANCESTOR:
6063 case LYXP_AXIS_ANCESTOR_OR_SELF:
6064 case LYXP_AXIS_DESCENDANT:
6065 case LYXP_AXIS_DESCENDANT_OR_SELF:
6066 case LYXP_AXIS_FOLLOWING:
6067 case LYXP_AXIS_FOLLOWING_SIBLING:
6068 case LYXP_AXIS_PARENT:
6069 case LYXP_AXIS_PRECEDING:
6070 case LYXP_AXIS_PRECEDING_SIBLING:
Michal Vasko306e2832022-07-25 09:15:17 +02006071 result.non_child_axis = 1;
Michal Vasko49fec8e2022-05-24 10:28:33 +02006072 if (set_dup_node_check(&result, iter, iter_type, -1)) {
6073 continue;
6074 }
6075 break;
6076 case LYXP_AXIS_CHILD:
6077 case LYXP_AXIS_SELF:
6078 break;
6079 case LYXP_AXIS_ATTRIBUTE:
6080 /* handled specially */
6081 assert(0);
6082 LOGINT(set->ctx);
6083 break;
6084 }
6085
6086 /* matching node */
6087 set_insert_node(&result, iter, 0, iter_type, result.used);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006088 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02006089 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02006090
Michal Vasko230cf972021-12-02 12:31:00 +01006091 /* move result to the set */
6092 lyxp_set_free_content(set);
6093 *set = result;
6094 result.type = LYXP_SET_NUMBER;
Michal Vasko49fec8e2022-05-24 10:28:33 +02006095
Michal Vasko306e2832022-07-25 09:15:17 +02006096 /* sort the final set if the document order could have been broken */
6097 if (set->non_child_axis) {
6098 set_sort(set);
6099 } else {
6100 assert(!set_sort(set));
6101 }
Michal Vasko230cf972021-12-02 12:31:00 +01006102
6103cleanup:
6104 lyxp_set_free_content(&result);
6105 return rc;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006106}
6107
6108/**
Michal Vasko49fec8e2022-05-24 10:28:33 +02006109 * @brief Move context @p set to child nodes using hashes. Result is LYXP_SET_NODE_SET. Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006110 *
6111 * @param[in,out] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02006112 * @param[in] scnode Matching node schema.
Michal Vasko004d3152020-06-11 19:59:22 +02006113 * @param[in] predicates If @p scnode is ::LYS_LIST or ::LYS_LEAFLIST, the predicates specifying a single instance.
Michal Vaskocdad7122020-11-09 21:04:44 +01006114 * @param[in] options XPath options.
Michal Vaskod3678892020-05-21 10:06:58 +02006115 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6116 */
6117static LY_ERR
Michal Vasko49fec8e2022-05-24 10:28:33 +02006118moveto_node_hash_child(struct lyxp_set *set, const struct lysc_node *scnode, const struct ly_path_predicate *predicates,
Michal Vaskocdad7122020-11-09 21:04:44 +01006119 uint32_t options)
Michal Vaskod3678892020-05-21 10:06:58 +02006120{
Michal Vaskoddd76592022-01-17 13:34:48 +01006121 LY_ERR ret = LY_SUCCESS, r;
Michal Vaskod3678892020-05-21 10:06:58 +02006122 uint32_t i;
Michal Vaskod3678892020-05-21 10:06:58 +02006123 const struct lyd_node *siblings;
Michal Vasko230cf972021-12-02 12:31:00 +01006124 struct lyxp_set result;
Michal Vasko004d3152020-06-11 19:59:22 +02006125 struct lyd_node *sub, *inst = NULL;
Michal Vaskod3678892020-05-21 10:06:58 +02006126
Michal Vasko004d3152020-06-11 19:59:22 +02006127 assert(scnode && (!(scnode->nodetype & (LYS_LIST | LYS_LEAFLIST)) || predicates));
Michal Vaskod3678892020-05-21 10:06:58 +02006128
Michal Vasko50aaa072021-12-02 13:11:56 +01006129 /* init result set */
6130 set_init(&result, set);
6131
aPiecek8b0cc152021-05-31 16:40:31 +02006132 if (options & LYXP_SKIP_EXPR) {
Michal Vasko004d3152020-06-11 19:59:22 +02006133 goto cleanup;
Michal Vaskod3678892020-05-21 10:06:58 +02006134 }
6135
6136 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01006137 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko004d3152020-06-11 19:59:22 +02006138 ret = LY_EVALID;
6139 goto cleanup;
Michal Vaskod3678892020-05-21 10:06:58 +02006140 }
6141
6142 /* context check for all the nodes since we have the schema node */
6143 if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (scnode->flags & LYS_CONFIG_R)) {
6144 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02006145 goto cleanup;
Michal Vasko69730152020-10-09 16:30:07 +02006146 } else if (set->context_op && (scnode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) &&
6147 (scnode != set->context_op)) {
Michal Vasko6b26e742020-07-17 15:02:10 +02006148 lyxp_set_free_content(set);
6149 goto cleanup;
Michal Vasko004d3152020-06-11 19:59:22 +02006150 }
6151
6152 /* create specific data instance if needed */
6153 if (scnode->nodetype == LYS_LIST) {
6154 LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, &inst), cleanup);
6155 } else if (scnode->nodetype == LYS_LEAFLIST) {
6156 LY_CHECK_GOTO(ret = lyd_create_term2(scnode, &predicates[0].value, &inst), cleanup);
Michal Vaskod3678892020-05-21 10:06:58 +02006157 }
6158
Michal Vasko230cf972021-12-02 12:31:00 +01006159 for (i = 0; i < set->used; ++i) {
Michal Vaskod3678892020-05-21 10:06:58 +02006160 siblings = NULL;
6161
6162 if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
6163 assert(!set->val.nodes[i].node);
6164
6165 /* search in all the trees */
6166 siblings = set->tree;
6167 } else if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
6168 /* search in children */
Radek Krejcia1c1e542020-09-29 16:06:52 +02006169 siblings = lyd_child(set->val.nodes[i].node);
Michal Vaskod3678892020-05-21 10:06:58 +02006170 }
6171
6172 /* find the node using hashes */
Michal Vasko004d3152020-06-11 19:59:22 +02006173 if (inst) {
Michal Vaskoddd76592022-01-17 13:34:48 +01006174 r = lyd_find_sibling_first(siblings, inst, &sub);
Michal Vaskod3678892020-05-21 10:06:58 +02006175 } else {
Michal Vaskoddd76592022-01-17 13:34:48 +01006176 r = lyd_find_sibling_val(siblings, scnode, NULL, 0, &sub);
Michal Vaskod3678892020-05-21 10:06:58 +02006177 }
Michal Vaskoddd76592022-01-17 13:34:48 +01006178 LY_CHECK_ERR_GOTO(r && (r != LY_ENOTFOUND), ret = r, cleanup);
Michal Vaskod3678892020-05-21 10:06:58 +02006179
6180 /* when check */
Michal Vaskod5cfa6e2020-11-23 16:56:08 +01006181 if (!(options & LYXP_IGNORE_WHEN) && sub && lysc_has_when(sub->schema) && !(sub->flags & LYD_WHEN_TRUE)) {
Michal Vasko004d3152020-06-11 19:59:22 +02006182 ret = LY_EINCOMPLETE;
6183 goto cleanup;
Michal Vaskod3678892020-05-21 10:06:58 +02006184 }
6185
6186 if (sub) {
6187 /* pos filled later */
Michal Vasko230cf972021-12-02 12:31:00 +01006188 set_insert_node(&result, sub, 0, LYXP_NODE_ELEM, result.used);
Michal Vaskod3678892020-05-21 10:06:58 +02006189 }
6190 }
6191
Michal Vasko230cf972021-12-02 12:31:00 +01006192 /* move result to the set */
6193 lyxp_set_free_content(set);
6194 *set = result;
6195 result.type = LYXP_SET_NUMBER;
6196 assert(!set_sort(set));
6197
Michal Vasko004d3152020-06-11 19:59:22 +02006198cleanup:
Michal Vasko230cf972021-12-02 12:31:00 +01006199 lyxp_set_free_content(&result);
Michal Vasko004d3152020-06-11 19:59:22 +02006200 lyd_free_tree(inst);
6201 return ret;
Michal Vaskod3678892020-05-21 10:06:58 +02006202}
6203
6204/**
Michal Vasko49fec8e2022-05-24 10:28:33 +02006205 * @brief Check @p node as a part of schema NameTest processing.
6206 *
6207 * @param[in] node Schema node to check.
6208 * @param[in] ctx_scnode Context node.
6209 * @param[in] set Set to read general context from.
6210 * @param[in] node_name Node name in the dictionary to move to, NULL for any nodes.
6211 * @param[in] moveto_mod Expected module of the node, NULL for no prefix.
6212 * @return LY_ERR (LY_ENOT if node does not match, LY_EINVAL if neither node nor any children match)
6213 */
6214static LY_ERR
6215moveto_scnode_check(const struct lysc_node *node, const struct lysc_node *ctx_scnode, const struct lyxp_set *set,
6216 const char *node_name, const struct lys_module *moveto_mod)
6217{
6218 if (!moveto_mod && node_name) {
6219 switch (set->format) {
6220 case LY_VALUE_SCHEMA:
6221 case LY_VALUE_SCHEMA_RESOLVED:
6222 /* use current module */
6223 moveto_mod = set->cur_mod;
6224 break;
6225 case LY_VALUE_JSON:
6226 case LY_VALUE_LYB:
6227 case LY_VALUE_STR_NS:
6228 /* inherit module of the context node, if any */
6229 if (ctx_scnode) {
6230 moveto_mod = ctx_scnode->module;
6231 }
6232 break;
6233 case LY_VALUE_CANON:
6234 case LY_VALUE_XML:
6235 /* not defined */
6236 LOGINT(set->ctx);
6237 return LY_EINVAL;
6238 }
6239 }
6240
6241 if (!node) {
6242 /* root will not match a specific node */
6243 if (node_name || moveto_mod) {
6244 return LY_ENOT;
6245 }
6246 return LY_SUCCESS;
6247 }
6248
6249 /* module check */
6250 if (moveto_mod && (node->module != moveto_mod)) {
6251 return LY_ENOT;
6252 }
6253
6254 /* context check */
6255 if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (node->flags & LYS_CONFIG_R)) {
6256 return LY_EINVAL;
6257 } else if (set->context_op && (node->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node != set->context_op)) {
6258 return LY_EINVAL;
6259 }
6260
6261 /* name check */
6262 if (node_name && (node->name != node_name)) {
6263 return LY_ENOT;
6264 }
6265
6266 /* match */
6267 return LY_SUCCESS;
6268}
6269
6270/**
6271 * @brief Get the next node in a forward schema node DFS.
6272 *
6273 * @param[in] iter Last returned node.
6274 * @param[in] stop Node to stop the search on and not return.
6275 * @param[in] getnext_opts Options for ::lys_getnext().
6276 * @return Next node, NULL if there are no more.
6277 */
6278static const struct lysc_node *
6279moveto_axis_scnode_next_dfs_forward(const struct lysc_node *iter, const struct lysc_node *stop, uint32_t getnext_opts)
6280{
6281 const struct lysc_node *next = NULL;
6282
6283 next = lysc_node_child(iter);
6284 if (!next) {
6285 /* no children, try siblings */
Michal Vasko34a22fe2022-06-15 07:58:55 +02006286 if ((iter == stop) || !lysc_data_parent(iter)) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02006287 /* we are done, no next element to process */
6288 return NULL;
6289 }
6290
6291 next = lys_getnext(iter, lysc_data_parent(iter), NULL, getnext_opts);
6292 }
6293 while (!next && iter) {
6294 /* parent is already processed, go to its sibling */
6295 iter = iter->parent;
Michal Vasko34a22fe2022-06-15 07:58:55 +02006296 if ((iter == stop) || !lysc_data_parent(iter)) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02006297 /* we are done, no next element to process */
6298 return NULL;
6299 }
6300 next = lys_getnext(iter, lysc_data_parent(iter), NULL, getnext_opts);
6301 }
6302
6303 return next;
6304}
6305
6306/**
6307 * @brief Consider schema node based on its in_ctx enum value.
6308 *
6309 * @param[in,out] in_ctx In_ctx enum of the schema node, may be updated.
6310 * @param[in] axis Axis to use.
6311 * @return LY_SUCCESS on success.
6312 * @return LY_ENOT if the node should not be returned.
6313 */
6314static LY_ERR
6315moveto_axis_scnode_next_in_ctx(int32_t *in_ctx, enum lyxp_axis axis)
6316{
6317 switch (axis) {
6318 case LYXP_AXIS_SELF:
6319 if ((*in_ctx == LYXP_SET_SCNODE_START) || (*in_ctx == LYXP_SET_SCNODE_ATOM_CTX)) {
6320 /* additionally put the start node into context */
6321 *in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
6322 return LY_SUCCESS;
6323 }
6324 break;
6325 case LYXP_AXIS_PARENT:
6326 case LYXP_AXIS_ANCESTOR_OR_SELF:
6327 case LYXP_AXIS_ANCESTOR:
6328 case LYXP_AXIS_DESCENDANT_OR_SELF:
6329 case LYXP_AXIS_DESCENDANT:
6330 case LYXP_AXIS_FOLLOWING:
6331 case LYXP_AXIS_FOLLOWING_SIBLING:
6332 case LYXP_AXIS_PRECEDING:
6333 case LYXP_AXIS_PRECEDING_SIBLING:
6334 case LYXP_AXIS_CHILD:
6335 if (*in_ctx == LYXP_SET_SCNODE_START) {
6336 /* remember that context node was used */
6337 *in_ctx = LYXP_SET_SCNODE_START_USED;
6338 return LY_SUCCESS;
6339 } else if (*in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
6340 /* traversed */
6341 *in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
6342 return LY_SUCCESS;
6343 }
6344 break;
6345 case LYXP_AXIS_ATTRIBUTE:
6346 /* unreachable */
6347 assert(0);
6348 LOGINT(NULL);
6349 break;
6350 }
6351
6352 return LY_ENOT;
6353}
6354
6355/**
6356 * @brief Get previous sibling for a schema node.
6357 *
6358 * @param[in] scnode Schema node.
6359 * @param[in] getnext_opts Options for ::lys_getnext().
6360 * @return Previous sibling, NULL if none.
6361 */
6362static const struct lysc_node *
6363moveto_axis_scnode_preceding_sibling(const struct lysc_node *scnode, uint32_t getnext_opts)
6364{
6365 const struct lysc_node *next = NULL, *prev = NULL;
6366
6367 while ((next = lys_getnext(next, lysc_data_parent(scnode), scnode->module->compiled, getnext_opts))) {
6368 if (next == scnode) {
6369 break;
6370 }
6371
6372 prev = next;
6373 }
6374
6375 return prev;
6376}
6377
6378/**
6379 * @brief Get the first schema node on an axis for a context node.
6380 *
6381 * @param[in,out] iter Last returned node, start with NULL, updated to the next node.
6382 * @param[in,out] iter_type Node type of @p iter, start with 0, updated to the node type of the next node.
6383 * @param[in,out] iter_mod Internal module iterator, do not change.
6384 * @param[in,out] iter_mod_idx Internal module index iterator, do not change.
6385 * @param[in] scnode Context node.
6386 * @param[in] node_type Type of @p scnode.
6387 * @param[in] in_ctx In_ctx enum of @p scnode.
6388 * @param[in] axis Axis to use.
6389 * @param[in] set XPath set with the general context.
6390 * @param[in] getnext_opts Options for ::lys_getnext().
6391 * @return LY_SUCCESS on success.
6392 * @return LY_ENOTFOUND if no next node found.
6393 */
6394static LY_ERR
6395moveto_axis_scnode_next_first(const struct lysc_node **iter, enum lyxp_node_type *iter_type, const struct lys_module **iter_mod,
6396 uint32_t *iter_mod_idx, const struct lysc_node *scnode, enum lyxp_node_type node_type, enum lyxp_axis axis,
6397 struct lyxp_set *set, uint32_t getnext_opts)
6398{
6399 const struct lysc_node *next = NULL;
6400 enum lyxp_node_type next_type = 0;
6401
6402 assert(!*iter);
6403 assert(!*iter_type);
6404
6405 *iter_mod = NULL;
6406 *iter_mod_idx = 0;
6407
6408 switch (axis) {
6409 case LYXP_AXIS_ANCESTOR_OR_SELF:
6410 case LYXP_AXIS_DESCENDANT_OR_SELF:
6411 case LYXP_AXIS_SELF:
6412 if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT) || (node_type == LYXP_NODE_ELEM)) {
6413 /* just return the node */
6414 next = scnode;
6415 next_type = node_type;
6416 }
6417 break;
6418
6419 case LYXP_AXIS_ANCESTOR:
6420 case LYXP_AXIS_PARENT:
6421 if (node_type == LYXP_NODE_ELEM) {
6422 next = lysc_data_parent(scnode);
6423 next_type = next ? LYXP_NODE_ELEM : set->root_type;
6424 } /* else no parent */
6425 break;
6426
6427 case LYXP_AXIS_DESCENDANT:
6428 case LYXP_AXIS_CHILD:
6429 if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
6430 /* it can actually be in any module, it's all <running>, and even if it's moveto_mod (if set),
6431 * it can be in a top-level augment */
6432 while ((*iter_mod = ly_ctx_get_module_iter(set->ctx, iter_mod_idx))) {
6433 /* module may not be implemented or not compiled yet */
6434 if (!(*iter_mod)->compiled) {
6435 continue;
6436 }
6437
6438 /* get next node */
6439 if ((next = lys_getnext(NULL, NULL, (*iter_mod)->compiled, getnext_opts))) {
6440 next_type = LYXP_NODE_ELEM;
6441 break;
6442 }
6443 }
6444 } else if (node_type == LYXP_NODE_ELEM) {
6445 /* get next node */
6446 next = lys_getnext(NULL, scnode, NULL, getnext_opts);
6447 next_type = next ? LYXP_NODE_ELEM : 0;
6448 }
6449 break;
6450
6451 case LYXP_AXIS_FOLLOWING:
6452 case LYXP_AXIS_FOLLOWING_SIBLING:
6453 if (node_type == LYXP_NODE_ELEM) {
6454 /* first next sibling */
6455 next = lys_getnext(scnode, lysc_data_parent(scnode), scnode->module->compiled, getnext_opts);
6456 next_type = next ? LYXP_NODE_ELEM : 0;
6457 } /* else no sibling */
6458 break;
6459
6460 case LYXP_AXIS_PRECEDING:
6461 case LYXP_AXIS_PRECEDING_SIBLING:
6462 if (node_type == LYXP_NODE_ELEM) {
6463 /* first parent sibling */
6464 next = lys_getnext(NULL, lysc_data_parent(scnode), scnode->module->compiled, getnext_opts);
6465 if (next == scnode) {
6466 /* no preceding sibling */
6467 next = NULL;
6468 }
6469 next_type = next ? LYXP_NODE_ELEM : 0;
6470 } /* else no sibling */
6471 break;
6472
6473 case LYXP_AXIS_ATTRIBUTE:
6474 /* unreachable */
6475 assert(0);
6476 LOGINT(set->ctx);
6477 break;
6478 }
6479
6480 *iter = next;
6481 *iter_type = next_type;
6482 return next_type ? LY_SUCCESS : LY_ENOTFOUND;
6483}
6484
6485/**
6486 * @brief Iterate over all schema nodes on an axis for a context node.
6487 *
6488 * @param[in,out] iter Last returned node, start with NULL, updated to the next node.
6489 * @param[in,out] iter_type Node type of @p iter, start with 0, updated to the node type of the next node.
6490 * @param[in,out] iter_mod Internal module iterator, do not change.
6491 * @param[in,out] iter_mod_idx Internal module index iterator, do not change.
6492 * @param[in] scnode Context node.
6493 * @param[in] node_type Type of @p scnode.
6494 * @param[in] axis Axis to use.
6495 * @param[in] set XPath set with the general context.
6496 * @param[in] getnext_opts Options for ::lys_getnext().
6497 * @return LY_SUCCESS on success.
6498 * @return LY_ENOTFOUND if no next node found.
6499 */
6500static LY_ERR
6501moveto_axis_scnode_next(const struct lysc_node **iter, enum lyxp_node_type *iter_type, const struct lys_module **iter_mod,
6502 uint32_t *iter_mod_idx, const struct lysc_node *scnode, enum lyxp_node_type node_type, enum lyxp_axis axis,
6503 struct lyxp_set *set, uint32_t getnext_opts)
6504{
6505 const struct lysc_node *next = NULL, *dfs_stop;
6506 enum lyxp_node_type next_type = 0;
6507
6508 if (!*iter_type) {
6509 /* first returned node */
6510 return moveto_axis_scnode_next_first(iter, iter_type, iter_mod, iter_mod_idx, scnode, node_type, axis, set,
6511 getnext_opts);
6512 }
6513
6514 switch (axis) {
6515 case LYXP_AXIS_PARENT:
6516 case LYXP_AXIS_SELF:
6517 /* parent/self was returned before */
6518 break;
6519
6520 case LYXP_AXIS_ANCESTOR_OR_SELF:
6521 if ((*iter == scnode) && (*iter_type == node_type)) {
6522 /* fake first ancestor, we returned self before */
6523 *iter = NULL;
6524 *iter_type = 0;
6525 return moveto_axis_scnode_next_first(iter, iter_type, iter_mod, iter_mod_idx, scnode, node_type,
6526 LYXP_AXIS_ANCESTOR, set, getnext_opts);
6527 } /* else continue ancestor */
6528
6529 /* fallthrough */
6530 case LYXP_AXIS_ANCESTOR:
6531 if (*iter_type == LYXP_NODE_ELEM) {
6532 next = lysc_data_parent(*iter);
6533 next_type = next ? LYXP_NODE_ELEM : set->root_type;
6534 } /* else no ancestor */
6535 break;
6536
6537 case LYXP_AXIS_DESCENDANT_OR_SELF:
6538 if ((*iter == scnode) && (*iter_type == node_type)) {
6539 /* fake first descendant, we returned self before */
6540 *iter = NULL;
6541 *iter_type = 0;
6542 return moveto_axis_scnode_next_first(iter, iter_type, iter_mod, iter_mod_idx, scnode, node_type,
6543 LYXP_AXIS_DESCENDANT, set, getnext_opts);
6544 } /* else DFS until context node */
6545 dfs_stop = scnode;
6546
6547 /* fallthrough */
6548 case LYXP_AXIS_DESCENDANT:
6549 if (axis == LYXP_AXIS_DESCENDANT) {
6550 /* DFS until the context node */
6551 dfs_stop = scnode;
6552 }
6553
6554 /* fallthrough */
6555 case LYXP_AXIS_PRECEDING:
6556 if (axis == LYXP_AXIS_PRECEDING) {
6557 /* DFS until the previous sibling */
6558 dfs_stop = moveto_axis_scnode_preceding_sibling(scnode, getnext_opts);
6559 assert(dfs_stop);
6560
6561 if (*iter == dfs_stop) {
6562 /* we are done */
6563 break;
6564 }
6565 }
6566
6567 /* fallthrough */
6568 case LYXP_AXIS_FOLLOWING:
6569 if (axis == LYXP_AXIS_FOLLOWING) {
6570 /* DFS through the whole module */
6571 dfs_stop = NULL;
6572 }
6573
6574 /* nested nodes */
6575 assert(*iter);
6576 next = moveto_axis_scnode_next_dfs_forward(*iter, dfs_stop, getnext_opts);
6577 if (next) {
6578 next_type = LYXP_NODE_ELEM;
6579 break;
6580 } /* else get next top-level node just like a child */
6581
6582 /* fallthrough */
6583 case LYXP_AXIS_CHILD:
6584 case LYXP_AXIS_FOLLOWING_SIBLING:
6585 if (!*iter_mod) {
6586 /* nodes from a single module */
6587 if ((next = lys_getnext(*iter, lysc_data_parent(*iter), (*iter)->module->compiled, getnext_opts))) {
6588 next_type = LYXP_NODE_ELEM;
6589 break;
6590 }
6591
6592 assert(scnode);
6593 if (!lysc_data_parent(scnode)) {
6594 /* iterating over top-level nodes, find next */
6595 while (lysc_data_parent(*iter)) {
6596 *iter = lysc_data_parent(*iter);
6597 }
6598 if ((next = lys_getnext(*iter, NULL, (*iter)->module->compiled, getnext_opts))) {
6599 next_type = LYXP_NODE_ELEM;
6600 break;
6601 }
6602 }
6603 }
6604
6605 while (*iter_mod) {
6606 /* module top-level nodes */
6607 if ((next = lys_getnext(*iter, NULL, (*iter_mod)->compiled, getnext_opts))) {
6608 next_type = LYXP_NODE_ELEM;
6609 break;
6610 }
6611
6612 /* get next module */
6613 while ((*iter_mod = ly_ctx_get_module_iter(set->ctx, iter_mod_idx))) {
6614 /* module may not be implemented or not compiled yet */
6615 if ((*iter_mod)->compiled) {
6616 break;
6617 }
6618 }
6619
6620 /* new module, start over */
6621 *iter = NULL;
6622 }
6623 break;
6624
6625 case LYXP_AXIS_PRECEDING_SIBLING:
6626 assert(*iter);
6627
6628 /* next parent sibling until scnode */
6629 next = lys_getnext(*iter, lysc_data_parent(*iter), (*iter)->module->compiled, getnext_opts);
6630 if (next == scnode) {
6631 /* no previous sibling */
6632 next = NULL;
6633 }
6634 next_type = next ? LYXP_NODE_ELEM : 0;
6635 break;
6636
6637 case LYXP_AXIS_ATTRIBUTE:
6638 /* unreachable */
6639 assert(0);
6640 LOGINT(set->ctx);
6641 break;
6642 }
6643
6644 *iter = next;
6645 *iter_type = next_type;
6646 return next_type ? LY_SUCCESS : LY_ENOTFOUND;
6647}
6648
6649/**
Michal Vaskod3678892020-05-21 10:06:58 +02006650 * @brief Move context @p set to a schema node. Result is LYXP_SET_SCNODE_SET (or LYXP_SET_EMPTY).
6651 *
6652 * @param[in,out] set Set to use.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02006653 * @param[in] moveto_mod Matching node module, NULL for no prefix.
Michal Vaskod3678892020-05-21 10:06:58 +02006654 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vasko49fec8e2022-05-24 10:28:33 +02006655 * @param[in] axis Axis to search on.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006656 * @param[in] options XPath options.
6657 * @return LY_ERR
6658 */
6659static LY_ERR
Michal Vasko49fec8e2022-05-24 10:28:33 +02006660moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, enum lyxp_axis axis,
6661 uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006662{
Radek Krejci857189e2020-09-01 13:26:36 +02006663 ly_bool temp_ctx = 0;
Michal Vasko49fec8e2022-05-24 10:28:33 +02006664 uint32_t getnext_opts, orig_used, i, mod_idx, idx;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02006665 const struct lys_module *mod;
Michal Vasko49fec8e2022-05-24 10:28:33 +02006666 const struct lysc_node *iter;
6667 enum lyxp_node_type iter_type;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006668
aPiecek8b0cc152021-05-31 16:40:31 +02006669 if (options & LYXP_SKIP_EXPR) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006670 return LY_SUCCESS;
6671 }
6672
6673 if (set->type != LYXP_SET_SCNODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01006674 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006675 return LY_EVALID;
6676 }
6677
Michal Vaskocafad9d2019-11-07 15:20:03 +01006678 /* getnext opts */
Michal Vasko7b1ad1a2020-11-02 15:41:27 +01006679 getnext_opts = 0;
Michal Vaskocafad9d2019-11-07 15:20:03 +01006680 if (options & LYXP_SCNODE_OUTPUT) {
6681 getnext_opts |= LYS_GETNEXT_OUTPUT;
6682 }
6683
Michal Vasko03ff5a72019-09-11 13:49:33 +02006684 orig_used = set->used;
6685 for (i = 0; i < orig_used; ++i) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02006686 /* update in_ctx first */
6687 if (moveto_axis_scnode_next_in_ctx(&set->val.scnodes[i].in_ctx, axis)) {
6688 /* not usable, skip */
6689 continue;
6690 }
Radek Krejciaa6b53f2020-08-27 15:19:03 +02006691
Michal Vasko49fec8e2022-05-24 10:28:33 +02006692 iter = NULL;
6693 iter_type = 0;
6694 while (!moveto_axis_scnode_next(&iter, &iter_type, &mod, &mod_idx, set->val.scnodes[i].scnode,
6695 set->val.scnodes[i].type, axis, set, getnext_opts)) {
6696 if (moveto_scnode_check(iter, NULL, set, ncname, moveto_mod)) {
Michal Vasko5c4e5892019-11-14 12:31:38 +01006697 continue;
6698 }
6699
Michal Vasko49fec8e2022-05-24 10:28:33 +02006700 /* insert */
Michal Vasko7333cb32022-07-29 16:30:29 +02006701 LY_CHECK_RET(set_scnode_insert_node(set, iter, iter_type, axis, &idx));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006702
Michal Vasko49fec8e2022-05-24 10:28:33 +02006703 /* we need to prevent these nodes from being considered in this moveto */
6704 if ((idx < orig_used) && (idx > i)) {
6705 set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_NEW_CTX;
6706 temp_ctx = 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006707 }
6708 }
6709 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02006710
6711 /* correct temporary in_ctx values */
6712 if (temp_ctx) {
6713 for (i = 0; i < orig_used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01006714 if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_NEW_CTX) {
6715 set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006716 }
6717 }
6718 }
6719
6720 return LY_SUCCESS;
6721}
6722
6723/**
Michal Vasko49fec8e2022-05-24 10:28:33 +02006724 * @brief Move context @p set to a child node and all its descendants. Result is LYXP_SET_NODE_SET.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006725 * Context position aware.
6726 *
6727 * @param[in] set Set to use.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02006728 * @param[in] moveto_mod Matching node module, NULL for no prefix.
Michal Vaskod3678892020-05-21 10:06:58 +02006729 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vaskocdad7122020-11-09 21:04:44 +01006730 * @param[in] options XPath options.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006731 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6732 */
6733static LY_ERR
Michal Vasko49fec8e2022-05-24 10:28:33 +02006734moveto_node_alldesc_child(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006735{
6736 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006737 const struct lyd_node *next, *elem, *start;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006738 struct lyxp_set ret_set;
6739 LY_ERR rc;
6740
aPiecek8b0cc152021-05-31 16:40:31 +02006741 if (options & LYXP_SKIP_EXPR) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006742 return LY_SUCCESS;
6743 }
6744
6745 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01006746 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006747 return LY_EVALID;
6748 }
6749
Michal Vasko9f96a052020-03-10 09:41:45 +01006750 /* replace the original nodes (and throws away all text and meta nodes, root is replaced by a child) */
Michal Vasko49fec8e2022-05-24 10:28:33 +02006751 rc = xpath_pi_node(set, LYXP_AXIS_CHILD, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006752 LY_CHECK_RET(rc);
6753
Michal Vasko6346ece2019-09-24 13:12:53 +02006754 /* this loop traverses all the nodes in the set and adds/keeps only those that match qname */
Michal Vasko03ff5a72019-09-11 13:49:33 +02006755 set_init(&ret_set, set);
6756 for (i = 0; i < set->used; ++i) {
6757
6758 /* TREE DFS */
6759 start = set->val.nodes[i].node;
6760 for (elem = next = start; elem; elem = next) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02006761 rc = moveto_node_check(elem, LYXP_NODE_ELEM, set, ncname, moveto_mod, options);
Michal Vasko6346ece2019-09-24 13:12:53 +02006762 if (!rc) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006763 /* add matching node into result set */
6764 set_insert_node(&ret_set, elem, 0, LYXP_NODE_ELEM, ret_set.used);
6765 if (set_dup_node_check(set, elem, LYXP_NODE_ELEM, i)) {
6766 /* the node is a duplicate, we'll process it later in the set */
6767 goto skip_children;
6768 }
Michal Vasko6346ece2019-09-24 13:12:53 +02006769 } else if (rc == LY_EINCOMPLETE) {
Michal Vasko6346ece2019-09-24 13:12:53 +02006770 return rc;
6771 } else if (rc == LY_EINVAL) {
6772 goto skip_children;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006773 }
6774
6775 /* TREE DFS NEXT ELEM */
6776 /* select element for the next run - children first */
Radek Krejcia1c1e542020-09-29 16:06:52 +02006777 next = lyd_child(elem);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006778 if (!next) {
6779skip_children:
6780 /* no children, so try siblings, but only if it's not the start,
6781 * that is considered to be the root and it's siblings are not traversed */
6782 if (elem != start) {
6783 next = elem->next;
6784 } else {
6785 break;
6786 }
6787 }
6788 while (!next) {
6789 /* no siblings, go back through the parents */
Michal Vasko9e685082021-01-29 14:49:09 +01006790 if (lyd_parent(elem) == start) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006791 /* we are done, no next element to process */
6792 break;
6793 }
6794 /* parent is already processed, go to its sibling */
Michal Vasko9e685082021-01-29 14:49:09 +01006795 elem = lyd_parent(elem);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006796 next = elem->next;
6797 }
6798 }
6799 }
6800
6801 /* make the temporary set the current one */
6802 ret_set.ctx_pos = set->ctx_pos;
6803 ret_set.ctx_size = set->ctx_size;
Michal Vaskod3678892020-05-21 10:06:58 +02006804 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006805 memcpy(set, &ret_set, sizeof *set);
Michal Vasko306e2832022-07-25 09:15:17 +02006806 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006807
6808 return LY_SUCCESS;
6809}
6810
6811/**
Michal Vasko49fec8e2022-05-24 10:28:33 +02006812 * @brief Move context @p set to a child schema node and all its descendants starting from a node.
6813 * Result is LYXP_SET_NODE_SET.
6814 *
6815 * @param[in] set Set to use.
6816 * @param[in] start Start node whose subtree to add.
6817 * @param[in] start_idx Index of @p start in @p set.
6818 * @param[in] moveto_mod Matching node module, NULL for no prefix.
6819 * @param[in] ncname Matching node name in the dictionary, NULL for any.
6820 * @param[in] options XPath options.
6821 * @return LY_ERR value.
6822 */
6823static LY_ERR
6824moveto_scnode_dfs(struct lyxp_set *set, const struct lysc_node *start, uint32_t start_idx,
6825 const struct lys_module *moveto_mod, const char *ncname, uint32_t options)
6826{
6827 const struct lysc_node *next, *elem;
6828 uint32_t idx;
6829 LY_ERR rc;
6830
6831 /* TREE DFS */
6832 for (elem = next = start; elem; elem = next) {
6833 if ((elem == start) || (elem->nodetype & (LYS_CHOICE | LYS_CASE))) {
6834 /* schema-only nodes, skip root */
6835 goto next_iter;
6836 }
6837
6838 rc = moveto_scnode_check(elem, start, set, ncname, moveto_mod);
6839 if (!rc) {
6840 if (lyxp_set_scnode_contains(set, elem, LYXP_NODE_ELEM, start_idx, &idx)) {
6841 set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
6842 if (idx > start_idx) {
6843 /* we will process it later in the set */
6844 goto skip_children;
6845 }
6846 } else {
Michal Vasko7333cb32022-07-29 16:30:29 +02006847 LY_CHECK_RET(set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
Michal Vasko49fec8e2022-05-24 10:28:33 +02006848 }
6849 } else if (rc == LY_EINVAL) {
6850 goto skip_children;
6851 }
6852
6853next_iter:
6854 /* TREE DFS NEXT ELEM */
6855 /* select element for the next run - children first */
6856 next = lysc_node_child(elem);
6857 if (next && (next->nodetype == LYS_INPUT) && (options & LYXP_SCNODE_OUTPUT)) {
6858 next = next->next;
6859 } else if (next && (next->nodetype == LYS_OUTPUT) && !(options & LYXP_SCNODE_OUTPUT)) {
6860 next = next->next;
6861 }
6862 if (!next) {
6863skip_children:
6864 /* no children, so try siblings, but only if it's not the start,
6865 * that is considered to be the root and it's siblings are not traversed */
6866 if (elem != start) {
6867 next = elem->next;
6868 } else {
6869 break;
6870 }
6871 }
6872 while (!next) {
6873 /* no siblings, go back through the parents */
6874 if (elem->parent == start) {
6875 /* we are done, no next element to process */
6876 break;
6877 }
6878 /* parent is already processed, go to its sibling */
6879 elem = elem->parent;
6880 next = elem->next;
6881 }
6882 }
6883
6884 return LY_SUCCESS;
6885}
6886
6887/**
6888 * @brief Move context @p set to a child schema node and all its descendants. Result is LYXP_SET_NODE_SET.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006889 *
6890 * @param[in] set Set to use.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02006891 * @param[in] moveto_mod Matching node module, NULL for no prefix.
Michal Vaskod3678892020-05-21 10:06:58 +02006892 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006893 * @param[in] options XPath options.
Michal Vasko49fec8e2022-05-24 10:28:33 +02006894 * @return LY_ERR value.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006895 */
6896static LY_ERR
Michal Vasko49fec8e2022-05-24 10:28:33 +02006897moveto_scnode_alldesc_child(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006898{
Michal Vasko49fec8e2022-05-24 10:28:33 +02006899 uint32_t i, orig_used, mod_idx;
6900 const struct lys_module *mod;
6901 const struct lysc_node *root;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006902
aPiecek8b0cc152021-05-31 16:40:31 +02006903 if (options & LYXP_SKIP_EXPR) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006904 return LY_SUCCESS;
6905 }
6906
6907 if (set->type != LYXP_SET_SCNODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01006908 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006909 return LY_EVALID;
6910 }
6911
Michal Vasko03ff5a72019-09-11 13:49:33 +02006912 orig_used = set->used;
6913 for (i = 0; i < orig_used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01006914 if (set->val.scnodes[i].in_ctx != LYXP_SET_SCNODE_ATOM_CTX) {
6915 if (set->val.scnodes[i].in_ctx != LYXP_SET_SCNODE_START) {
Michal Vasko5c4e5892019-11-14 12:31:38 +01006916 continue;
6917 }
6918
6919 /* remember context node */
Radek Krejcif13b87b2020-12-01 22:02:17 +01006920 set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_START_USED;
Michal Vaskoec4df482019-12-16 10:02:18 +01006921 } else {
Michal Vasko1a09b212021-05-06 13:00:10 +02006922 set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006923 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02006924
Michal Vasko49fec8e2022-05-24 10:28:33 +02006925 if ((set->val.scnodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.scnodes[i].type == LYXP_NODE_ROOT)) {
6926 /* traverse all top-level nodes in all the modules */
6927 mod_idx = 0;
6928 while ((mod = ly_ctx_get_module_iter(set->ctx, &mod_idx))) {
6929 /* module may not be implemented or not compiled yet */
6930 if (!mod->compiled) {
6931 continue;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006932 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02006933
Michal Vasko49fec8e2022-05-24 10:28:33 +02006934 root = NULL;
6935 /* no getnext opts needed */
6936 while ((root = lys_getnext(root, NULL, mod->compiled, 0))) {
6937 LY_CHECK_RET(moveto_scnode_dfs(set, root, i, moveto_mod, ncname, options));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006938 }
6939 }
Michal Vasko49fec8e2022-05-24 10:28:33 +02006940
6941 } else if (set->val.scnodes[i].type == LYXP_NODE_ELEM) {
6942 /* add all the descendants recursively */
6943 LY_CHECK_RET(moveto_scnode_dfs(set, set->val.scnodes[i].scnode, i, moveto_mod, ncname, options));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006944 }
6945 }
6946
6947 return LY_SUCCESS;
6948}
6949
6950/**
Michal Vasko61ac2f62020-05-25 12:39:51 +02006951 * @brief Move context @p set to an attribute. Result is LYXP_SET_NODE_SET.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006952 * Indirectly context position aware.
6953 *
6954 * @param[in,out] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02006955 * @param[in] mod Matching metadata module, NULL for any.
6956 * @param[in] ncname Matching metadata name in the dictionary, NULL for any.
aPiecek8b0cc152021-05-31 16:40:31 +02006957 * @param[in] options XPath options.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006958 * @return LY_ERR
6959 */
6960static LY_ERR
aPiecek8b0cc152021-05-31 16:40:31 +02006961moveto_attr(struct lyxp_set *set, const struct lys_module *mod, const char *ncname, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006962{
Michal Vasko9f96a052020-03-10 09:41:45 +01006963 struct lyd_meta *sub;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006964
aPiecek8b0cc152021-05-31 16:40:31 +02006965 if (options & LYXP_SKIP_EXPR) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006966 return LY_SUCCESS;
6967 }
6968
6969 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01006970 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006971 return LY_EVALID;
6972 }
6973
Radek Krejci1deb5be2020-08-26 16:43:36 +02006974 for (uint32_t i = 0; i < set->used; ) {
Radek Krejci857189e2020-09-01 13:26:36 +02006975 ly_bool replaced = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006976
6977 /* only attributes of an elem (not dummy) can be in the result, skip all the rest;
6978 * our attributes are always qualified */
Michal Vasko5c4e5892019-11-14 12:31:38 +01006979 if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
Michal Vasko9f96a052020-03-10 09:41:45 +01006980 for (sub = set->val.nodes[i].node->meta; sub; sub = sub->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006981
6982 /* check "namespace" */
Michal Vaskod3678892020-05-21 10:06:58 +02006983 if (mod && (sub->annotation->module != mod)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006984 continue;
6985 }
6986
Michal Vaskod3678892020-05-21 10:06:58 +02006987 if (!ncname || (sub->name == ncname)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006988 /* match */
6989 if (!replaced) {
Michal Vasko9f96a052020-03-10 09:41:45 +01006990 set->val.meta[i].meta = sub;
6991 set->val.meta[i].type = LYXP_NODE_META;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006992 /* pos does not change */
6993 replaced = 1;
6994 } else {
Michal Vasko9f96a052020-03-10 09:41:45 +01006995 set_insert_node(set, (struct lyd_node *)sub, set->val.nodes[i].pos, LYXP_NODE_META, i + 1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006996 }
6997 ++i;
6998 }
6999 }
7000 }
7001
7002 if (!replaced) {
7003 /* no match */
7004 set_remove_node(set, i);
7005 }
7006 }
7007
7008 return LY_SUCCESS;
7009}
7010
7011/**
7012 * @brief Move context @p set1 to union with @p set2. @p set2 is emptied afterwards.
Michal Vasko61ac2f62020-05-25 12:39:51 +02007013 * Result is LYXP_SET_NODE_SET. Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007014 *
7015 * @param[in,out] set1 Set to use for the result.
7016 * @param[in] set2 Set that is copied to @p set1.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007017 * @return LY_ERR
7018 */
7019static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007020moveto_union(struct lyxp_set *set1, struct lyxp_set *set2)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007021{
7022 LY_ERR rc;
7023
Michal Vaskod3678892020-05-21 10:06:58 +02007024 if ((set1->type != LYXP_SET_NODE_SET) || (set2->type != LYXP_SET_NODE_SET)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01007025 LOGVAL(set1->ctx, LY_VCODE_XP_INOP_2, "union", print_set_type(set1), print_set_type(set2));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007026 return LY_EVALID;
7027 }
7028
7029 /* set2 is empty or both set1 and set2 */
Michal Vaskod3678892020-05-21 10:06:58 +02007030 if (!set2->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007031 return LY_SUCCESS;
7032 }
7033
Michal Vaskod3678892020-05-21 10:06:58 +02007034 if (!set1->used) {
aPiecekadc1e4f2021-10-07 11:15:12 +02007035 /* release hidden allocated data (lyxp_set.size) */
7036 lyxp_set_free_content(set1);
7037 /* direct copying of the entire structure */
Michal Vasko03ff5a72019-09-11 13:49:33 +02007038 memcpy(set1, set2, sizeof *set1);
7039 /* dynamic memory belongs to set1 now, do not free */
Michal Vaskod3678892020-05-21 10:06:58 +02007040 memset(set2, 0, sizeof *set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007041 return LY_SUCCESS;
7042 }
7043
7044 /* we assume sets are sorted */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007045 assert(!set_sort(set1) && !set_sort(set2));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007046
7047 /* sort, remove duplicates */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007048 rc = set_sorted_merge(set1, set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007049 LY_CHECK_RET(rc);
7050
7051 /* final set must be sorted */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007052 assert(!set_sort(set1));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007053
7054 return LY_SUCCESS;
7055}
7056
7057/**
Michal Vasko61ac2f62020-05-25 12:39:51 +02007058 * @brief Move context @p set to an attribute in any of the descendants. Result is LYXP_SET_NODE_SET.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007059 * Context position aware.
7060 *
7061 * @param[in,out] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02007062 * @param[in] mod Matching metadata module, NULL for any.
7063 * @param[in] ncname Matching metadata name in the dictionary, NULL for any.
Michal Vaskocdad7122020-11-09 21:04:44 +01007064 * @param[in] options XPath options.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007065 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7066 */
7067static int
Michal Vaskocdad7122020-11-09 21:04:44 +01007068moveto_attr_alldesc(struct lyxp_set *set, const struct lys_module *mod, const char *ncname, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007069{
Michal Vasko9f96a052020-03-10 09:41:45 +01007070 struct lyd_meta *sub;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007071 struct lyxp_set *set_all_desc = NULL;
7072 LY_ERR rc;
7073
aPiecek8b0cc152021-05-31 16:40:31 +02007074 if (options & LYXP_SKIP_EXPR) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007075 return LY_SUCCESS;
7076 }
7077
7078 if (set->type != LYXP_SET_NODE_SET) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01007079 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007080 return LY_EVALID;
7081 }
7082
Michal Vasko03ff5a72019-09-11 13:49:33 +02007083 /* can be optimized similarly to moveto_node_alldesc() and save considerable amount of memory,
7084 * but it likely won't be used much, so it's a waste of time */
7085 /* copy the context */
7086 set_all_desc = set_copy(set);
7087 /* get all descendant nodes (the original context nodes are removed) */
Michal Vasko49fec8e2022-05-24 10:28:33 +02007088 rc = moveto_node_alldesc_child(set_all_desc, NULL, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007089 if (rc != LY_SUCCESS) {
7090 lyxp_set_free(set_all_desc);
7091 return rc;
7092 }
7093 /* prepend the original context nodes */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007094 rc = moveto_union(set, set_all_desc);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007095 if (rc != LY_SUCCESS) {
7096 lyxp_set_free(set_all_desc);
7097 return rc;
7098 }
7099 lyxp_set_free(set_all_desc);
7100
Radek Krejci1deb5be2020-08-26 16:43:36 +02007101 for (uint32_t i = 0; i < set->used; ) {
Radek Krejci857189e2020-09-01 13:26:36 +02007102 ly_bool replaced = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007103
7104 /* only attributes of an elem can be in the result, skip all the rest,
7105 * we have all attributes qualified in lyd tree */
7106 if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
Michal Vasko9f96a052020-03-10 09:41:45 +01007107 for (sub = set->val.nodes[i].node->meta; sub; sub = sub->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007108 /* check "namespace" */
Michal Vaskod3678892020-05-21 10:06:58 +02007109 if (mod && (sub->annotation->module != mod)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007110 continue;
7111 }
7112
Michal Vaskod3678892020-05-21 10:06:58 +02007113 if (!ncname || (sub->name == ncname)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007114 /* match */
7115 if (!replaced) {
Michal Vasko9f96a052020-03-10 09:41:45 +01007116 set->val.meta[i].meta = sub;
7117 set->val.meta[i].type = LYXP_NODE_META;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007118 /* pos does not change */
7119 replaced = 1;
7120 } else {
Michal Vasko9f96a052020-03-10 09:41:45 +01007121 set_insert_node(set, (struct lyd_node *)sub, set->val.meta[i].pos, LYXP_NODE_META, i + 1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007122 }
7123 ++i;
7124 }
7125 }
7126 }
7127
7128 if (!replaced) {
7129 /* no match */
7130 set_remove_node(set, i);
7131 }
7132 }
7133
7134 return LY_SUCCESS;
7135}
7136
7137/**
Michal Vasko8abcecc2022-07-28 09:55:01 +02007138 * @brief Move context @p set1 single item to the result of a comparison.
7139 *
7140 * @param[in] set1 First set with the item to compare.
7141 * @param[in] idx1 Index of the item in @p set1.
7142 * @param[in] set2 Second set.
7143 * @param[in] op Comparison operator to process.
7144 * @param[in] switch_operands Whether to switch sets as operands; whether it is `set1 op set2` or `set2 op set1`.
7145 * @param[out] result Result of the comparison.
7146 * @return LY_ERR value.
7147 */
7148static LY_ERR
7149moveto_op_comp_item(const struct lyxp_set *set1, uint32_t idx1, struct lyxp_set *set2, const char *op,
7150 ly_bool switch_operands, ly_bool *result)
7151{
7152 struct lyxp_set tmp1 = {0};
7153 LY_ERR rc = LY_SUCCESS;
7154
7155 assert(set1->type == LYXP_SET_NODE_SET);
7156
7157 /* cast set1 */
7158 switch (set2->type) {
7159 case LYXP_SET_NUMBER:
7160 rc = set_comp_cast(&tmp1, set1, LYXP_SET_NUMBER, idx1);
7161 break;
7162 case LYXP_SET_BOOLEAN:
7163 rc = set_comp_cast(&tmp1, set1, LYXP_SET_BOOLEAN, idx1);
7164 break;
7165 default:
7166 rc = set_comp_cast(&tmp1, set1, LYXP_SET_STRING, idx1);
7167 break;
7168 }
7169 LY_CHECK_GOTO(rc, cleanup);
7170
7171 /* canonize set2 */
7172 LY_CHECK_GOTO(rc = set_comp_canonize(set2, &set1->val.nodes[idx1]), cleanup);
7173
7174 /* compare recursively and store the result */
7175 if (switch_operands) {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007176 LY_CHECK_GOTO(rc = moveto_op_comp(set2, &tmp1, op, result), cleanup);
Michal Vasko8abcecc2022-07-28 09:55:01 +02007177 } else {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007178 LY_CHECK_GOTO(rc = moveto_op_comp(&tmp1, set2, op, result), cleanup);
Michal Vasko8abcecc2022-07-28 09:55:01 +02007179 }
7180
7181cleanup:
7182 lyxp_set_free_content(&tmp1);
7183 return rc;
7184}
7185
7186/**
7187 * @brief Move context @p set1 to the result of a comparison. Handles '=', '!=', '<=', '<', '>=', or '>'.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007188 * Result is LYXP_SET_BOOLEAN. Indirectly context position aware.
7189 *
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007190 * @param[in] set1 Set acting as the first operand for @p op.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007191 * @param[in] set2 Set acting as the second operand for @p op.
7192 * @param[in] op Comparison operator to process.
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007193 * @param[out] result Result of the comparison.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007194 * @return LY_ERR
7195 */
7196static LY_ERR
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007197moveto_op_comp(struct lyxp_set *set1, struct lyxp_set *set2, const char *op, ly_bool *result)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007198{
7199 /*
7200 * NODE SET + NODE SET = NODE SET + STRING /(1 NODE SET) 2 STRING
7201 * NODE SET + STRING = STRING + STRING /1 STRING (2 STRING)
7202 * NODE SET + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
7203 * NODE SET + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
7204 * STRING + NODE SET = STRING + STRING /(1 STRING) 2 STRING
7205 * NUMBER + NODE SET = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
7206 * BOOLEAN + NODE SET = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
7207 *
7208 * '=' or '!='
7209 * BOOLEAN + BOOLEAN
7210 * BOOLEAN + STRING = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
7211 * BOOLEAN + NUMBER = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
7212 * STRING + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
7213 * NUMBER + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
7214 * NUMBER + NUMBER
7215 * NUMBER + STRING = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
7216 * STRING + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
7217 * STRING + STRING
7218 *
7219 * '<=', '<', '>=', '>'
7220 * NUMBER + NUMBER
7221 * BOOLEAN + BOOLEAN = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
7222 * BOOLEAN + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
7223 * BOOLEAN + STRING = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
7224 * NUMBER + STRING = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
7225 * STRING + STRING = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
7226 * STRING + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
7227 * NUMBER + BOOLEAN = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
7228 * STRING + BOOLEAN = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
7229 */
Michal Vasko8abcecc2022-07-28 09:55:01 +02007230 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007231 LY_ERR rc;
7232
Michal Vasko03ff5a72019-09-11 13:49:33 +02007233 /* iterative evaluation with node-sets */
7234 if ((set1->type == LYXP_SET_NODE_SET) || (set2->type == LYXP_SET_NODE_SET)) {
7235 if (set1->type == LYXP_SET_NODE_SET) {
7236 for (i = 0; i < set1->used; ++i) {
Michal Vasko8abcecc2022-07-28 09:55:01 +02007237 /* evaluate for the single item */
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007238 LY_CHECK_RET(moveto_op_comp_item(set1, i, set2, op, 0, result));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007239
7240 /* lazy evaluation until true */
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007241 if (*result) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007242 return LY_SUCCESS;
7243 }
7244 }
7245 } else {
7246 for (i = 0; i < set2->used; ++i) {
Michal Vasko8abcecc2022-07-28 09:55:01 +02007247 /* evaluate for the single item */
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007248 LY_CHECK_RET(moveto_op_comp_item(set2, i, set1, op, 1, result));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007249
7250 /* lazy evaluation until true */
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007251 if (*result) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007252 return LY_SUCCESS;
7253 }
7254 }
7255 }
7256
Michal Vasko8abcecc2022-07-28 09:55:01 +02007257 /* false for all the nodes */
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007258 *result = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007259 return LY_SUCCESS;
7260 }
7261
7262 /* first convert properly */
7263 if ((op[0] == '=') || (op[0] == '!')) {
7264 if ((set1->type == LYXP_SET_BOOLEAN) || (set2->type == LYXP_SET_BOOLEAN)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007265 lyxp_set_cast(set1, LYXP_SET_BOOLEAN);
7266 lyxp_set_cast(set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007267 } else if ((set1->type == LYXP_SET_NUMBER) || (set2->type == LYXP_SET_NUMBER)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007268 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007269 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007270 rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007271 LY_CHECK_RET(rc);
7272 } /* else we have 2 strings */
7273 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007274 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007275 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007276 rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007277 LY_CHECK_RET(rc);
7278 }
7279
7280 assert(set1->type == set2->type);
7281
7282 /* compute result */
7283 if (op[0] == '=') {
7284 if (set1->type == LYXP_SET_BOOLEAN) {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007285 *result = (set1->val.bln == set2->val.bln);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007286 } else if (set1->type == LYXP_SET_NUMBER) {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007287 *result = (set1->val.num == set2->val.num);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007288 } else {
7289 assert(set1->type == LYXP_SET_STRING);
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007290 *result = strcmp(set1->val.str, set2->val.str) ? 0 : 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007291 }
7292 } else if (op[0] == '!') {
7293 if (set1->type == LYXP_SET_BOOLEAN) {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007294 *result = (set1->val.bln != set2->val.bln);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007295 } else if (set1->type == LYXP_SET_NUMBER) {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007296 *result = (set1->val.num != set2->val.num);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007297 } else {
7298 assert(set1->type == LYXP_SET_STRING);
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007299 *result = strcmp(set1->val.str, set2->val.str) ? 1 : 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007300 }
7301 } else {
7302 assert(set1->type == LYXP_SET_NUMBER);
7303 if (op[0] == '<') {
7304 if (op[1] == '=') {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007305 *result = (set1->val.num <= set2->val.num);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007306 } else {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007307 *result = (set1->val.num < set2->val.num);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007308 }
7309 } else {
7310 if (op[1] == '=') {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007311 *result = (set1->val.num >= set2->val.num);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007312 } else {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02007313 *result = (set1->val.num > set2->val.num);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007314 }
7315 }
7316 }
7317
Michal Vasko03ff5a72019-09-11 13:49:33 +02007318 return LY_SUCCESS;
7319}
7320
7321/**
7322 * @brief Move context @p set to the result of a basic operation. Handles '+', '-', unary '-', '*', 'div',
7323 * or 'mod'. Result is LYXP_SET_NUMBER. Indirectly context position aware.
7324 *
7325 * @param[in,out] set1 Set to use for the result.
7326 * @param[in] set2 Set acting as the second operand for @p op.
7327 * @param[in] op Operator to process.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007328 * @return LY_ERR
7329 */
7330static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007331moveto_op_math(struct lyxp_set *set1, struct lyxp_set *set2, const char *op)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007332{
7333 LY_ERR rc;
7334
7335 /* unary '-' */
7336 if (!set2 && (op[0] == '-')) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007337 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007338 LY_CHECK_RET(rc);
7339 set1->val.num *= -1;
7340 lyxp_set_free(set2);
7341 return LY_SUCCESS;
7342 }
7343
7344 assert(set1 && set2);
7345
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007346 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007347 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007348 rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007349 LY_CHECK_RET(rc);
7350
7351 switch (op[0]) {
7352 /* '+' */
7353 case '+':
7354 set1->val.num += set2->val.num;
7355 break;
7356
7357 /* '-' */
7358 case '-':
7359 set1->val.num -= set2->val.num;
7360 break;
7361
7362 /* '*' */
7363 case '*':
7364 set1->val.num *= set2->val.num;
7365 break;
7366
7367 /* 'div' */
7368 case 'd':
7369 set1->val.num /= set2->val.num;
7370 break;
7371
7372 /* 'mod' */
7373 case 'm':
7374 set1->val.num = ((long long)set1->val.num) % ((long long)set2->val.num);
7375 break;
7376
7377 default:
7378 LOGINT_RET(set1->ctx);
7379 }
7380
7381 return LY_SUCCESS;
7382}
7383
Michal Vasko03ff5a72019-09-11 13:49:33 +02007384/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02007385 * @brief Evaluate Predicate. Logs directly on error.
7386 *
Michal Vaskod3678892020-05-21 10:06:58 +02007387 * [9] Predicate ::= '[' Expr ']'
Michal Vasko03ff5a72019-09-11 13:49:33 +02007388 *
7389 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007390 * @param[in] tok_idx Position in the expression @p exp.
aPiecek8b0cc152021-05-31 16:40:31 +02007391 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007392 * @param[in] options XPath options.
Michal Vasko49fec8e2022-05-24 10:28:33 +02007393 * @param[in] axis Axis to search on.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007394 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7395 */
7396static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02007397eval_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options, enum lyxp_axis axis)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007398{
7399 LY_ERR rc;
Michal Vaskodd528af2022-08-08 14:35:07 +02007400 uint32_t i, orig_exp, orig_pos, orig_size;
Michal Vasko5c4e5892019-11-14 12:31:38 +01007401 int32_t pred_in_ctx;
Michal Vasko88a9e802022-05-24 10:50:28 +02007402 ly_bool reverse_axis = 0;
aPiecekfff4dca2021-10-07 10:59:53 +02007403 struct lyxp_set set2 = {0};
Michal Vasko03ff5a72019-09-11 13:49:33 +02007404
7405 /* '[' */
aPiecek8b0cc152021-05-31 16:40:31 +02007406 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02007407 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02007408 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007409
aPiecek8b0cc152021-05-31 16:40:31 +02007410 if (options & LYXP_SKIP_EXPR) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007411only_parse:
aPiecek8b0cc152021-05-31 16:40:31 +02007412 rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007413 LY_CHECK_RET(rc);
7414 } else if (set->type == LYXP_SET_NODE_SET) {
7415 /* we (possibly) need the set sorted, it can affect the result (if the predicate result is a number) */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007416 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007417
7418 /* empty set, nothing to evaluate */
7419 if (!set->used) {
7420 goto only_parse;
7421 }
7422
Michal Vasko49fec8e2022-05-24 10:28:33 +02007423 /* decide forward or reverse axis */
7424 switch (axis) {
7425 case LYXP_AXIS_ANCESTOR:
7426 case LYXP_AXIS_ANCESTOR_OR_SELF:
7427 case LYXP_AXIS_PRECEDING:
7428 case LYXP_AXIS_PRECEDING_SIBLING:
7429 reverse_axis = 1;
7430 break;
7431 case LYXP_AXIS_DESCENDANT:
7432 case LYXP_AXIS_DESCENDANT_OR_SELF:
7433 case LYXP_AXIS_FOLLOWING:
7434 case LYXP_AXIS_FOLLOWING_SIBLING:
7435 case LYXP_AXIS_PARENT:
7436 case LYXP_AXIS_CHILD:
7437 case LYXP_AXIS_SELF:
7438 case LYXP_AXIS_ATTRIBUTE:
7439 reverse_axis = 0;
7440 break;
7441 }
7442
Michal Vasko004d3152020-06-11 19:59:22 +02007443 orig_exp = *tok_idx;
Michal Vasko49fec8e2022-05-24 10:28:33 +02007444 orig_pos = reverse_axis ? set->used + 1 : 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007445 orig_size = set->used;
Michal Vasko39dbf352020-05-21 10:08:59 +02007446 for (i = 0; i < set->used; ++i) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007447 set_init(&set2, set);
7448 set_insert_node(&set2, set->val.nodes[i].node, set->val.nodes[i].pos, set->val.nodes[i].type, 0);
Michal Vasko49fec8e2022-05-24 10:28:33 +02007449
7450 /* remember the node context position for position() and context size for last() */
7451 orig_pos += reverse_axis ? -1 : 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007452
7453 set2.ctx_pos = orig_pos;
7454 set2.ctx_size = orig_size;
Michal Vasko004d3152020-06-11 19:59:22 +02007455 *tok_idx = orig_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007456
Michal Vasko004d3152020-06-11 19:59:22 +02007457 rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007458 if (rc != LY_SUCCESS) {
Michal Vaskod3678892020-05-21 10:06:58 +02007459 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007460 return rc;
7461 }
7462
Michal Vasko49fec8e2022-05-24 10:28:33 +02007463 /* number is a proximity position */
Michal Vasko03ff5a72019-09-11 13:49:33 +02007464 if (set2.type == LYXP_SET_NUMBER) {
7465 if ((long long)set2.val.num == orig_pos) {
7466 set2.val.num = 1;
7467 } else {
7468 set2.val.num = 0;
7469 }
7470 }
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007471 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007472
7473 /* predicate satisfied or not? */
Michal Vasko004d3152020-06-11 19:59:22 +02007474 if (!set2.val.bln) {
Michal Vasko2caefc12019-11-14 16:07:56 +01007475 set_remove_node_none(set, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007476 }
7477 }
Michal Vasko2caefc12019-11-14 16:07:56 +01007478 set_remove_nodes_none(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007479
7480 } else if (set->type == LYXP_SET_SCNODE_SET) {
7481 for (i = 0; i < set->used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01007482 if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007483 /* there is a currently-valid node */
7484 break;
7485 }
7486 }
7487 /* empty set, nothing to evaluate */
7488 if (i == set->used) {
7489 goto only_parse;
7490 }
7491
Michal Vasko004d3152020-06-11 19:59:22 +02007492 orig_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007493
Michal Vasko03ff5a72019-09-11 13:49:33 +02007494 /* set special in_ctx to all the valid snodes */
7495 pred_in_ctx = set_scnode_new_in_ctx(set);
7496
7497 /* use the valid snodes one-by-one */
7498 for (i = 0; i < set->used; ++i) {
7499 if (set->val.scnodes[i].in_ctx != pred_in_ctx) {
7500 continue;
7501 }
Radek Krejcif13b87b2020-12-01 22:02:17 +01007502 set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007503
Michal Vasko004d3152020-06-11 19:59:22 +02007504 *tok_idx = orig_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007505
Michal Vasko004d3152020-06-11 19:59:22 +02007506 rc = eval_expr_select(exp, tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007507 LY_CHECK_RET(rc);
7508
7509 set->val.scnodes[i].in_ctx = pred_in_ctx;
7510 }
7511
7512 /* restore the state as it was before the predicate */
7513 for (i = 0; i < set->used; ++i) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01007514 if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
Michal Vasko1a09b212021-05-06 13:00:10 +02007515 set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007516 } else if (set->val.scnodes[i].in_ctx == pred_in_ctx) {
Radek Krejcif13b87b2020-12-01 22:02:17 +01007517 set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007518 }
7519 }
7520
7521 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02007522 set2.type = LYXP_SET_NODE_SET;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007523 set_fill_set(&set2, set);
7524
Michal Vasko004d3152020-06-11 19:59:22 +02007525 rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007526 if (rc != LY_SUCCESS) {
Michal Vaskod3678892020-05-21 10:06:58 +02007527 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007528 return rc;
7529 }
7530
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007531 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko004d3152020-06-11 19:59:22 +02007532 if (!set2.val.bln) {
Michal Vaskod3678892020-05-21 10:06:58 +02007533 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007534 }
Michal Vaskod3678892020-05-21 10:06:58 +02007535 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007536 }
7537
7538 /* ']' */
Michal Vasko004d3152020-06-11 19:59:22 +02007539 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
aPiecek8b0cc152021-05-31 16:40:31 +02007540 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02007541 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02007542 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007543
7544 return LY_SUCCESS;
7545}
7546
7547/**
Michal Vaskod3678892020-05-21 10:06:58 +02007548 * @brief Evaluate Literal. Logs directly on error.
7549 *
7550 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007551 * @param[in] tok_idx Position in the expression @p exp.
Michal Vaskod3678892020-05-21 10:06:58 +02007552 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7553 */
7554static void
Michal Vaskodd528af2022-08-08 14:35:07 +02007555eval_literal(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set)
Michal Vaskod3678892020-05-21 10:06:58 +02007556{
7557 if (set) {
Michal Vasko004d3152020-06-11 19:59:22 +02007558 if (exp->tok_len[*tok_idx] == 2) {
Michal Vaskod3678892020-05-21 10:06:58 +02007559 set_fill_string(set, "", 0);
7560 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007561 set_fill_string(set, &exp->expr[exp->tok_pos[*tok_idx] + 1], exp->tok_len[*tok_idx] - 2);
Michal Vaskod3678892020-05-21 10:06:58 +02007562 }
7563 }
7564 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02007565 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02007566 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02007567}
7568
7569/**
Michal Vasko3d969ff2022-07-29 15:02:08 +02007570 * @brief Check that a nametest in a predicate matches a key node.
7571 *
7572 * @param[in] nametest Nametest to check.
7573 * @param[in] len Length of @p nametest.
7574 * @param[in] ctx_scnode Found schema node as the context for the predicate.
7575 * @param[in] set Context set.
7576 * @param[in] key Expected key node.
7577 * @return LY_SUCCESS on success,
7578 * @return LY_ENOT if a predicate could not be compiled.
7579 * @return LY_ERR on any error.
7580 */
7581static LY_ERR
7582eval_name_test_try_compile_predicate_key(const char *nametest, uint32_t len, const struct lysc_node *ctx_scnode,
7583 const struct lyxp_set *set, const struct lysc_node *key)
7584{
7585 const struct lys_module *mod;
7586
7587 /* prefix (module) */
7588 LY_CHECK_RET(moveto_resolve_model(&nametest, &len, set, ctx_scnode, &mod));
7589 if (mod != key->module) {
7590 return LY_ENOT;
7591 }
7592
7593 /* node name */
7594 if (ly_strncmp(key->name, nametest, len)) {
7595 return LY_ENOT;
7596 }
7597
7598 return LY_SUCCESS;
7599}
7600
7601/**
7602 * @brief Append a simple predicate for the node.
7603 *
7604 * @param[in] exp Full parsed XPath expression.
7605 * @param[in] tok_idx Predicate start index in @p exp.
7606 * @param[in] end_tok_idx Predicate end index in @p exp.
7607 * @param[in] ctx_scnode Found schema node as the context for the predicate.
7608 * @param[in] set Context set.
7609 * @param[in] pred_node Node with the value referenced in the predicate.
7610 * @param[in,out] pred Predicate to append to.
7611 * @param[in,out] pred_len Length of @p pred, is updated.
7612 * @return LY_SUCCESS on success,
7613 * @return LY_ENOT if a predicate could not be compiled.
7614 * @return LY_ERR on any error.
7615 */
7616static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02007617eval_name_test_try_compile_predicate_append(const struct lyxp_expr *exp, uint32_t tok_idx, uint32_t end_tok_idx,
Michal Vasko3d969ff2022-07-29 15:02:08 +02007618 const struct lysc_node *ctx_scnode, const struct lyxp_set *set, const struct lysc_node *pred_node, char **pred,
7619 uint32_t *pred_len)
7620{
7621 LY_ERR rc = LY_SUCCESS;
7622 uint32_t i;
7623 const struct lyd_node *siblings;
7624 struct lyd_node *ctx_node;
Michal Vasko5fb92a22022-08-02 09:21:37 +02007625 const struct lysc_node *sparent, *cur_scnode;
Michal Vasko3d969ff2022-07-29 15:02:08 +02007626 struct lyxp_expr *val_exp = NULL;
7627 struct lyxp_set set2 = {0};
7628 char quot;
7629
7630 /* duplicate the value expression */
7631 LY_CHECK_GOTO(rc = lyxp_expr_dup(set->ctx, exp, tok_idx, end_tok_idx, &val_exp), cleanup);
7632
7633 /* get its atoms */
Michal Vasko5fb92a22022-08-02 09:21:37 +02007634 cur_scnode = set->cur_node ? set->cur_node->schema : NULL;
7635 LY_CHECK_GOTO(rc = lyxp_atomize(set->ctx, val_exp, set->cur_mod, set->format, set->prefix_data, cur_scnode,
7636 ctx_scnode, &set2, LYXP_SCNODE), cleanup);
Michal Vasko3d969ff2022-07-29 15:02:08 +02007637
7638 /* check whether we can compile a single predicate (evaluation result value is always the same) */
7639 for (i = 0; i < set2.used; ++i) {
7640 if ((set2.val.scnodes[i].type != LYXP_NODE_ELEM) || (set2.val.scnodes[i].in_ctx < LYXP_SET_SCNODE_ATOM_NODE)) {
7641 /* skip root and context node */
7642 continue;
7643 }
7644
Michal Vasko5fb92a22022-08-02 09:21:37 +02007645 /* 1) context node descendants are traversed - do best-effort detection of the value dependency on the
7646 * context node instance */
7647 if ((set2.val.scnodes[i].axis == LYXP_AXIS_CHILD) && (set2.val.scnodes[i].scnode->parent == ctx_scnode)) {
7648 /* 1.1) context node child was accessed on the child axis, certain dependency */
Michal Vasko3d969ff2022-07-29 15:02:08 +02007649 rc = LY_ENOT;
7650 goto cleanup;
7651 }
Michal Vasko5fb92a22022-08-02 09:21:37 +02007652 if ((set2.val.scnodes[i].axis == LYXP_AXIS_DESCENDANT) || (set2.val.scnodes[i].axis == LYXP_AXIS_DESCENDANT_OR_SELF)) {
7653 for (sparent = set2.val.scnodes[i].scnode->parent; sparent && (sparent != ctx_scnode); sparent = sparent->parent) {}
7654 if (sparent) {
7655 /* 1.2) context node descendant was accessed on the descendant axis, probable dependency */
7656 rc = LY_ENOT;
7657 goto cleanup;
7658 }
7659 }
Michal Vasko3d969ff2022-07-29 15:02:08 +02007660
Michal Vasko5fb92a22022-08-02 09:21:37 +02007661 /* 2) multi-instance nodes (list or leaf-list) are traversed - all the instances need to be considered,
7662 * but the current node can be safely ignored, it is always the same data instance */
7663 if ((set2.val.scnodes[i].scnode->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (cur_scnode != set2.val.scnodes[i].scnode)) {
Michal Vasko3d969ff2022-07-29 15:02:08 +02007664 rc = LY_ENOT;
7665 goto cleanup;
7666 }
Michal Vasko3d969ff2022-07-29 15:02:08 +02007667 }
7668
7669 /* get any data instance of the context node, we checked it makes no difference */
7670 siblings = set->val.nodes[0].node ? lyd_child(set->val.nodes[0].node) : set->tree;
7671 LY_CHECK_GOTO(rc = lyd_find_sibling_schema(siblings, ctx_scnode, &ctx_node), cleanup);
7672
7673 /* evaluate the value subexpression with the root context node */
7674 lyxp_set_free_content(&set2);
7675 LY_CHECK_GOTO(rc = lyxp_eval(set->ctx, val_exp, set->cur_mod, set->format, set->prefix_data, set->cur_node,
7676 ctx_node, set->tree, NULL, &set2, 0), cleanup);
7677
7678 /* cast it into a string */
7679 LY_CHECK_GOTO(rc = lyxp_set_cast(&set2, LYXP_SET_STRING), cleanup);
7680
7681 /* append the JSON predicate */
7682 *pred = ly_realloc(*pred, *pred_len + 1 + strlen(pred_node->name) + 2 + strlen(set2.val.str) + 3);
7683 LY_CHECK_ERR_GOTO(!*pred, LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
7684 quot = strchr(set2.val.str, '\'') ? '\"' : '\'';
7685 *pred_len += sprintf(*pred + *pred_len, "[%s=%c%s%c]", pred_node->name, quot, set2.val.str, quot);
7686
7687cleanup:
7688 lyxp_expr_free(set->ctx, val_exp);
7689 lyxp_set_free_content(&set2);
7690 return rc;
7691}
7692
7693/**
Michal Vasko004d3152020-06-11 19:59:22 +02007694 * @brief Try to compile list or leaf-list predicate in the known format to be used for hash-based instance search.
Michal Vaskod3678892020-05-21 10:06:58 +02007695 *
Michal Vasko004d3152020-06-11 19:59:22 +02007696 * @param[in] exp Full parsed XPath expression.
7697 * @param[in,out] tok_idx Index in @p exp at the beginning of the predicate, is updated on success.
Michal Vasko3d969ff2022-07-29 15:02:08 +02007698 * @param[in] ctx_scnode Found schema node as the context for the predicate.
7699 * @param[in] set Context set.
Michal Vasko004d3152020-06-11 19:59:22 +02007700 * @param[out] predicates Parsed predicates.
7701 * @param[out] pred_type Type of @p predicates.
7702 * @return LY_SUCCESS on success,
Michal Vasko3d969ff2022-07-29 15:02:08 +02007703 * @return LY_ENOT if a predicate could not be compiled.
Michal Vasko004d3152020-06-11 19:59:22 +02007704 * @return LY_ERR on any error.
Michal Vaskod3678892020-05-21 10:06:58 +02007705 */
7706static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02007707eval_name_test_try_compile_predicates(const struct lyxp_expr *exp, uint32_t *tok_idx, const struct lysc_node *ctx_scnode,
Michal Vasko3d969ff2022-07-29 15:02:08 +02007708 const struct lyxp_set *set, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
Michal Vaskod3678892020-05-21 10:06:58 +02007709{
Michal Vasko3d969ff2022-07-29 15:02:08 +02007710 LY_ERR rc = LY_SUCCESS;
Michal Vaskodd528af2022-08-08 14:35:07 +02007711 uint32_t e_idx, val_start_idx, pred_idx = 0, prev_lo, pred_len = 0;
Michal Vaskod3678892020-05-21 10:06:58 +02007712 const struct lysc_node *key;
Michal Vasko3d969ff2022-07-29 15:02:08 +02007713 char *pred = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +02007714 struct lyxp_expr *exp2 = NULL;
Michal Vaskod3678892020-05-21 10:06:58 +02007715
Michal Vasko3d969ff2022-07-29 15:02:08 +02007716 assert(ctx_scnode->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vaskod3678892020-05-21 10:06:58 +02007717
Michal Vasko3d969ff2022-07-29 15:02:08 +02007718 /* turn logging off */
7719 prev_lo = ly_log_options(0);
7720
7721 if (ctx_scnode->nodetype == LYS_LIST) {
7722 /* check for predicates "[key1=...][key2=...]..." */
7723
Michal Vasko004d3152020-06-11 19:59:22 +02007724 /* get key count */
Michal Vasko3d969ff2022-07-29 15:02:08 +02007725 if (ctx_scnode->flags & LYS_KEYLESS) {
7726 rc = LY_ENOT;
7727 goto cleanup;
Michal Vasko004d3152020-06-11 19:59:22 +02007728 }
Michal Vaskod3678892020-05-21 10:06:58 +02007729
Michal Vasko004d3152020-06-11 19:59:22 +02007730 /* learn where the predicates end */
7731 e_idx = *tok_idx;
Michal Vasko3d969ff2022-07-29 15:02:08 +02007732 for (key = lysc_node_child(ctx_scnode); key && (key->flags & LYS_KEY); key = key->next) {
Michal Vasko004d3152020-06-11 19:59:22 +02007733 /* '[' */
7734 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
Michal Vasko3d969ff2022-07-29 15:02:08 +02007735 rc = LY_ENOT;
7736 goto cleanup;
Michal Vasko004d3152020-06-11 19:59:22 +02007737 }
7738 ++e_idx;
7739
Michal Vasko3354d272021-04-06 09:40:06 +02007740 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_NAMETEST)) {
Michal Vasko3d969ff2022-07-29 15:02:08 +02007741 /* not a key */
7742 rc = LY_ENOT;
7743 goto cleanup;
Michal Vasko3354d272021-04-06 09:40:06 +02007744 }
7745
Michal Vasko3d969ff2022-07-29 15:02:08 +02007746 /* check key */
7747 LY_CHECK_GOTO(rc = eval_name_test_try_compile_predicate_key(exp->expr + exp->tok_pos[e_idx],
7748 exp->tok_len[e_idx], ctx_scnode, set, key), cleanup);
7749
7750 ++e_idx;
7751
7752 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_EQUAL)) {
7753 /* not '=' */
7754 rc = LY_ENOT;
7755 goto cleanup;
7756 }
7757 ++e_idx;
7758
7759 /* value start */
7760 val_start_idx = e_idx;
7761
Michal Vasko004d3152020-06-11 19:59:22 +02007762 /* ']' */
7763 while (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
Michal Vaskob8ee3562022-08-02 10:43:17 +02007764 if (!lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_LOG)) {
7765 /* higher priority than '=' */
7766 rc = LY_ENOT;
7767 goto cleanup;
7768 }
Michal Vasko004d3152020-06-11 19:59:22 +02007769 ++e_idx;
7770 }
Michal Vasko004d3152020-06-11 19:59:22 +02007771
Michal Vasko3d969ff2022-07-29 15:02:08 +02007772 /* try to evaluate the value */
7773 LY_CHECK_GOTO(rc = eval_name_test_try_compile_predicate_append(exp, val_start_idx, e_idx - 1, ctx_scnode,
7774 set, key, &pred, &pred_len), cleanup);
7775
7776 ++e_idx;
Michal Vasko004d3152020-06-11 19:59:22 +02007777 }
Michal Vasko004d3152020-06-11 19:59:22 +02007778 } else {
Michal Vasko3d969ff2022-07-29 15:02:08 +02007779 /* check for predicate "[.=...]" */
7780
Michal Vasko004d3152020-06-11 19:59:22 +02007781 /* learn just where this single predicate ends */
7782 e_idx = *tok_idx;
7783
Michal Vaskod3678892020-05-21 10:06:58 +02007784 /* '[' */
Michal Vasko004d3152020-06-11 19:59:22 +02007785 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
Michal Vasko3d969ff2022-07-29 15:02:08 +02007786 rc = LY_ENOT;
7787 goto cleanup;
Michal Vasko004d3152020-06-11 19:59:22 +02007788 }
7789 ++e_idx;
Michal Vaskod3678892020-05-21 10:06:58 +02007790
Michal Vasko3354d272021-04-06 09:40:06 +02007791 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_DOT)) {
Michal Vasko3d969ff2022-07-29 15:02:08 +02007792 /* not the node value */
7793 rc = LY_ENOT;
7794 goto cleanup;
Michal Vasko3354d272021-04-06 09:40:06 +02007795 }
Michal Vasko3d969ff2022-07-29 15:02:08 +02007796 ++e_idx;
7797
7798 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_EQUAL)) {
7799 /* not '=' */
7800 rc = LY_ENOT;
7801 goto cleanup;
7802 }
7803 ++e_idx;
7804
7805 /* value start */
7806 val_start_idx = e_idx;
Michal Vasko3354d272021-04-06 09:40:06 +02007807
Michal Vaskod3678892020-05-21 10:06:58 +02007808 /* ']' */
Michal Vasko004d3152020-06-11 19:59:22 +02007809 while (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
Michal Vaskob8ee3562022-08-02 10:43:17 +02007810 if (!lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_LOG)) {
7811 /* higher priority than '=' */
7812 rc = LY_ENOT;
7813 goto cleanup;
7814 }
Michal Vasko004d3152020-06-11 19:59:22 +02007815 ++e_idx;
7816 }
Michal Vasko3d969ff2022-07-29 15:02:08 +02007817
7818 /* try to evaluate the value */
7819 LY_CHECK_GOTO(rc = eval_name_test_try_compile_predicate_append(exp, val_start_idx, e_idx - 1, ctx_scnode, set,
7820 ctx_scnode, &pred, &pred_len), cleanup);
7821
Michal Vasko004d3152020-06-11 19:59:22 +02007822 ++e_idx;
Michal Vaskod3678892020-05-21 10:06:58 +02007823 }
7824
Michal Vasko004d3152020-06-11 19:59:22 +02007825 /* parse the predicate(s) */
Michal Vasko3d969ff2022-07-29 15:02:08 +02007826 LY_CHECK_GOTO(rc = ly_path_parse_predicate(set->ctx, ctx_scnode, pred, pred_len, LY_PATH_PREFIX_OPTIONAL,
7827 LY_PATH_PRED_SIMPLE, &exp2), cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +02007828
7829 /* compile */
Michal Vasko3d969ff2022-07-29 15:02:08 +02007830 rc = ly_path_compile_predicate(set->ctx, set->cur_node ? set->cur_node->schema : NULL, set->cur_mod, ctx_scnode, exp2,
7831 &pred_idx, LY_VALUE_JSON, NULL, predicates, pred_type);
7832 LY_CHECK_GOTO(rc, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +02007833
7834 /* success, the predicate must include all the needed information for hash-based search */
7835 *tok_idx = e_idx;
7836
7837cleanup:
7838 ly_log_options(prev_lo);
Michal Vasko3d969ff2022-07-29 15:02:08 +02007839 lyxp_expr_free(set->ctx, exp2);
7840 free(pred);
7841 return rc;
Michal Vaskod3678892020-05-21 10:06:58 +02007842}
7843
7844/**
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007845 * @brief Search for/check the next schema node that could be the only matching schema node meaning the
7846 * data node(s) could be found using a single hash-based search.
7847 *
Michal Vaskoc71c37b2021-01-11 13:40:02 +01007848 * @param[in] ctx libyang context.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007849 * @param[in] node Next context node to check.
7850 * @param[in] name Expected node name.
7851 * @param[in] name_len Length of @p name.
Michal Vaskoc71c37b2021-01-11 13:40:02 +01007852 * @param[in] moveto_mod Expected node module, can be NULL for JSON format with no prefix.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007853 * @param[in] root_type XPath root type.
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007854 * @param[in] format Prefix format.
7855 * @param[in,out] found Previously found node, is updated.
7856 * @return LY_SUCCESS on success,
7857 * @return LY_ENOT if the whole check failed and hashes cannot be used.
7858 */
7859static LY_ERR
Michal Vaskoc71c37b2021-01-11 13:40:02 +01007860eval_name_test_with_predicate_get_scnode(const struct ly_ctx *ctx, const struct lyd_node *node, const char *name,
Michal Vaskodd528af2022-08-08 14:35:07 +02007861 uint32_t name_len, const struct lys_module *moveto_mod, enum lyxp_node_type root_type, LY_VALUE_FORMAT format,
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007862 const struct lysc_node **found)
7863{
7864 const struct lysc_node *scnode;
7865 const struct lys_module *mod;
7866 uint32_t idx = 0;
7867
Radek Krejci8df109d2021-04-23 12:19:08 +02007868 assert((format == LY_VALUE_JSON) || moveto_mod);
Michal Vaskoc71c37b2021-01-11 13:40:02 +01007869
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007870continue_search:
Michal Vasko7d1d0912020-10-16 08:38:30 +02007871 scnode = NULL;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007872 if (!node) {
Radek Krejci8df109d2021-04-23 12:19:08 +02007873 if ((format == LY_VALUE_JSON) && !moveto_mod) {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007874 /* search all modules for a single match */
Michal Vaskoc71c37b2021-01-11 13:40:02 +01007875 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
Michal Vasko35a3b1d2021-07-14 09:34:16 +02007876 if (!mod->implemented) {
7877 continue;
7878 }
7879
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007880 scnode = lys_find_child(NULL, mod, name, name_len, 0, 0);
7881 if (scnode) {
7882 /* we have found a match */
7883 break;
7884 }
7885 }
7886
Michal Vasko7d1d0912020-10-16 08:38:30 +02007887 if (!scnode) {
7888 /* all modules searched */
7889 idx = 0;
7890 }
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007891 } else {
7892 /* search in top-level */
7893 scnode = lys_find_child(NULL, moveto_mod, name, name_len, 0, 0);
7894 }
7895 } else if (!*found || (lysc_data_parent(*found) != node->schema)) {
Radek Krejci8df109d2021-04-23 12:19:08 +02007896 if ((format == LY_VALUE_JSON) && !moveto_mod) {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007897 /* we must adjust the module to inherit the one from the context node */
7898 moveto_mod = node->schema->module;
7899 }
7900
7901 /* search in children, do not repeat the same search */
7902 scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
Michal Vasko7d1d0912020-10-16 08:38:30 +02007903 } /* else skip redundant search */
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007904
7905 /* additional context check */
7906 if (scnode && (root_type == LYXP_NODE_ROOT_CONFIG) && (scnode->flags & LYS_CONFIG_R)) {
7907 scnode = NULL;
7908 }
7909
7910 if (scnode) {
7911 if (*found) {
7912 /* we found a schema node with the same name but at different level, give up, too complicated
7913 * (more hash-based searches would be required, not supported) */
7914 return LY_ENOT;
7915 } else {
7916 /* remember the found schema node and continue to make sure it can be used */
7917 *found = scnode;
7918 }
7919 }
7920
7921 if (idx) {
7922 /* continue searching all the following models */
7923 goto continue_search;
7924 }
7925
7926 return LY_SUCCESS;
7927}
7928
7929/**
Michal Vasko4ad69e72021-10-26 16:25:55 +02007930 * @brief Generate message when no matching schema nodes were found for a path segment.
7931 *
7932 * @param[in] set XPath set.
7933 * @param[in] scparent Previous schema parent in the context, if only one.
7934 * @param[in] ncname XPath NCName being evaluated.
7935 * @param[in] ncname_len Length of @p ncname.
7936 * @param[in] expr Whole XPath expression.
7937 * @param[in] options XPath options.
7938 */
7939static void
7940eval_name_test_scnode_no_match_msg(struct lyxp_set *set, const struct lyxp_set_scnode *scparent, const char *ncname,
Michal Vaskodd528af2022-08-08 14:35:07 +02007941 uint32_t ncname_len, const char *expr, uint32_t options)
Michal Vasko4ad69e72021-10-26 16:25:55 +02007942{
7943 const char *format;
7944 char *path = NULL, *ppath = NULL;
7945
7946 path = lysc_path(set->cur_scnode, LYSC_PATH_LOG, NULL, 0);
7947 if (scparent) {
7948 /* generate path for the parent */
7949 if (scparent->type == LYXP_NODE_ELEM) {
7950 ppath = lysc_path(scparent->scnode, LYSC_PATH_LOG, NULL, 0);
7951 } else if (scparent->type == LYXP_NODE_ROOT) {
7952 ppath = strdup("<root>");
7953 } else if (scparent->type == LYXP_NODE_ROOT_CONFIG) {
7954 ppath = strdup("<config-root>");
7955 }
7956 }
7957 if (ppath) {
7958 format = "Schema node \"%.*s\" for parent \"%s\" not found; in expr \"%.*s\" with context node \"%s\".";
7959 if (options & LYXP_SCNODE_ERROR) {
7960 LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
7961 } else {
7962 LOGWRN(set->ctx, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
7963 }
7964 } else {
7965 format = "Schema node \"%.*s\" not found; in expr \"%.*s\" with context node \"%s\".";
7966 if (options & LYXP_SCNODE_ERROR) {
7967 LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
7968 } else {
7969 LOGWRN(set->ctx, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
7970 }
7971 }
7972 free(path);
7973 free(ppath);
7974}
7975
7976/**
Michal Vaskod3678892020-05-21 10:06:58 +02007977 * @brief Evaluate NameTest and any following Predicates. Logs directly on error.
7978 *
7979 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
7980 * [6] NodeTest ::= NameTest | NodeType '(' ')'
7981 * [7] NameTest ::= '*' | NCName ':' '*' | QName
7982 *
7983 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007984 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko49fec8e2022-05-24 10:28:33 +02007985 * @param[in] axis What axis to search on.
Michal Vaskod3678892020-05-21 10:06:58 +02007986 * @param[in] all_desc Whether to search all the descendants or children only.
aPiecek8b0cc152021-05-31 16:40:31 +02007987 * @param[in,out] set Context and result set.
Michal Vaskod3678892020-05-21 10:06:58 +02007988 * @param[in] options XPath options.
Michal Vaskoa036a6b2021-10-12 16:05:23 +02007989 * @return LY_ERR (LY_EINCOMPLETE on unresolved when, LY_ENOT for not found schema node)
Michal Vaskod3678892020-05-21 10:06:58 +02007990 */
7991static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02007992eval_name_test_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_axis axis, ly_bool all_desc,
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007993 struct lyxp_set *set, uint32_t options)
Michal Vaskod3678892020-05-21 10:06:58 +02007994{
Michal Vaskoa036a6b2021-10-12 16:05:23 +02007995 LY_ERR rc = LY_SUCCESS, r;
Michal Vasko004d3152020-06-11 19:59:22 +02007996 const char *ncname, *ncname_dict = NULL;
Michal Vasko56c008c2022-07-29 14:57:47 +02007997 uint32_t i, ncname_len;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02007998 const struct lys_module *moveto_mod = NULL;
Michal Vaskoc71c37b2021-01-11 13:40:02 +01007999 const struct lysc_node *scnode = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +02008000 struct ly_path_predicate *predicates = NULL;
8001 enum ly_path_pred_type pred_type = 0;
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008002 int scnode_skip_pred = 0;
Michal Vaskod3678892020-05-21 10:06:58 +02008003
aPiecek8b0cc152021-05-31 16:40:31 +02008004 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008005 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008006 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02008007
aPiecek8b0cc152021-05-31 16:40:31 +02008008 if (options & LYXP_SKIP_EXPR) {
Michal Vaskod3678892020-05-21 10:06:58 +02008009 goto moveto;
8010 }
8011
Michal Vasko004d3152020-06-11 19:59:22 +02008012 ncname = &exp->expr[exp->tok_pos[*tok_idx - 1]];
8013 ncname_len = exp->tok_len[*tok_idx - 1];
Michal Vaskod3678892020-05-21 10:06:58 +02008014
Michal Vaskoc71c37b2021-01-11 13:40:02 +01008015 if ((ncname[0] == '*') && (ncname_len == 1)) {
8016 /* all nodes will match */
8017 goto moveto;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02008018 }
8019
Michal Vaskoc71c37b2021-01-11 13:40:02 +01008020 /* parse (and skip) module name */
8021 rc = moveto_resolve_model(&ncname, &ncname_len, set, NULL, &moveto_mod);
Michal Vaskod3678892020-05-21 10:06:58 +02008022 LY_CHECK_GOTO(rc, cleanup);
8023
Michal Vasko49fec8e2022-05-24 10:28:33 +02008024 if ((ncname[0] == '*') && (ncname_len == 1)) {
8025 /* all nodes from the module will match */
8026 goto moveto;
8027 }
8028
8029 if (((set->format == LY_VALUE_JSON) || moveto_mod) && (axis == LYXP_AXIS_CHILD) && !all_desc &&
8030 (set->type == LYXP_SET_NODE_SET)) {
Michal Vaskod3678892020-05-21 10:06:58 +02008031 /* find the matching schema node in some parent in the context */
Michal Vasko56c008c2022-07-29 14:57:47 +02008032 for (i = 0; i < set->used; ++i) {
Michal Vaskoc71c37b2021-01-11 13:40:02 +01008033 if (eval_name_test_with_predicate_get_scnode(set->ctx, set->val.nodes[i].node, ncname, ncname_len,
8034 moveto_mod, set->root_type, set->format, &scnode)) {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02008035 /* check failed */
8036 scnode = NULL;
8037 break;
Michal Vaskod3678892020-05-21 10:06:58 +02008038 }
8039 }
8040
Michal Vasko004d3152020-06-11 19:59:22 +02008041 if (scnode && (scnode->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
8042 /* try to create the predicates */
Michal Vasko3d969ff2022-07-29 15:02:08 +02008043 if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set, &predicates, &pred_type)) {
Michal Vasko004d3152020-06-11 19:59:22 +02008044 /* hashes cannot be used */
Michal Vaskod3678892020-05-21 10:06:58 +02008045 scnode = NULL;
8046 }
8047 }
8048 }
8049
Michal Vaskoc71c37b2021-01-11 13:40:02 +01008050 if (!scnode) {
8051 /* we are not able to match based on a schema node and not all the modules match ("*"),
Michal Vasko004d3152020-06-11 19:59:22 +02008052 * use dictionary for efficient comparison */
Radek Krejci011e4aa2020-09-04 15:22:31 +02008053 LY_CHECK_GOTO(rc = lydict_insert(set->ctx, ncname, ncname_len, &ncname_dict), cleanup);
Michal Vaskod3678892020-05-21 10:06:58 +02008054 }
8055
8056moveto:
8057 /* move to the attribute(s), data node(s), or schema node(s) */
Michal Vasko49fec8e2022-05-24 10:28:33 +02008058 if (axis == LYXP_AXIS_ATTRIBUTE) {
aPiecek8b0cc152021-05-31 16:40:31 +02008059 if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
Michal Vasko1a09b212021-05-06 13:00:10 +02008060 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vaskod3678892020-05-21 10:06:58 +02008061 } else {
8062 if (all_desc) {
Michal Vaskocdad7122020-11-09 21:04:44 +01008063 rc = moveto_attr_alldesc(set, moveto_mod, ncname_dict, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008064 } else {
aPiecek8b0cc152021-05-31 16:40:31 +02008065 rc = moveto_attr(set, moveto_mod, ncname_dict, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008066 }
8067 LY_CHECK_GOTO(rc, cleanup);
8068 }
8069 } else {
aPiecek8b0cc152021-05-31 16:40:31 +02008070 if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008071 const struct lyxp_set_scnode *scparent = NULL;
Michal Vasko56c008c2022-07-29 14:57:47 +02008072 ly_bool found = 0;
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008073
8074 /* remember parent if there is only one, to print in the warning */
8075 for (i = 0; i < set->used; ++i) {
8076 if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
8077 if (!scparent) {
8078 /* remember the context node */
8079 scparent = &set->val.scnodes[i];
8080 } else {
8081 /* several context nodes, no reasonable error possible */
8082 scparent = NULL;
8083 break;
8084 }
8085 }
8086 }
Radek Krejci1deb5be2020-08-26 16:43:36 +02008087
Michal Vasko49fec8e2022-05-24 10:28:33 +02008088 if (all_desc && (axis == LYXP_AXIS_CHILD)) {
8089 /* efficient evaluation that does not add all the descendants into the set */
8090 rc = moveto_scnode_alldesc_child(set, moveto_mod, ncname_dict, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008091 } else {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008092 if (all_desc) {
8093 /* "//" == "/descendant-or-self::node()/" */
8094 rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
8095 LY_CHECK_GOTO(rc, cleanup);
8096 }
8097 rc = moveto_scnode(set, moveto_mod, ncname_dict, axis, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008098 }
8099 LY_CHECK_GOTO(rc, cleanup);
8100
Michal Vasko56c008c2022-07-29 14:57:47 +02008101 i = set->used;
8102 do {
8103 --i;
Michal Vasko1a09b212021-05-06 13:00:10 +02008104 if (set->val.scnodes[i].in_ctx > LYXP_SET_SCNODE_ATOM_NODE) {
Michal Vasko56c008c2022-07-29 14:57:47 +02008105 found = 1;
Michal Vaskod3678892020-05-21 10:06:58 +02008106 break;
8107 }
Michal Vasko56c008c2022-07-29 14:57:47 +02008108 } while (i);
8109 if (!found) {
Michal Vasko4ad69e72021-10-26 16:25:55 +02008110 /* generate message */
8111 eval_name_test_scnode_no_match_msg(set, scparent, ncname, ncname_len, exp->expr, options);
8112
8113 if (options & LYXP_SCNODE_ERROR) {
8114 /* error */
8115 rc = LY_EVALID;
8116 goto cleanup;
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008117 }
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008118
8119 /* skip the predicates and the rest of this path to not generate invalid warnings */
8120 rc = LY_ENOT;
8121 scnode_skip_pred = 1;
Michal Vaskod3678892020-05-21 10:06:58 +02008122 }
8123 } else {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008124 if (all_desc && (axis == LYXP_AXIS_CHILD)) {
8125 /* efficient evaluation */
8126 rc = moveto_node_alldesc_child(set, moveto_mod, ncname_dict, options);
8127 } else if (scnode && (axis == LYXP_AXIS_CHILD)) {
8128 /* we can find the child nodes using hashes */
8129 rc = moveto_node_hash_child(set, scnode, predicates, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008130 } else {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008131 if (all_desc) {
8132 /* "//" == "/descendant-or-self::node()/" */
8133 rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
8134 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskod3678892020-05-21 10:06:58 +02008135 }
Michal Vasko49fec8e2022-05-24 10:28:33 +02008136 rc = moveto_node(set, moveto_mod, ncname_dict, axis, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008137 }
8138 LY_CHECK_GOTO(rc, cleanup);
8139 }
8140 }
8141
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008142 if (scnode_skip_pred) {
8143 /* skip predicates */
8144 options |= LYXP_SKIP_EXPR;
8145 }
8146
Michal Vaskod3678892020-05-21 10:06:58 +02008147 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02008148 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008149 r = eval_predicate(exp, tok_idx, set, options, axis);
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008150 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
Michal Vaskod3678892020-05-21 10:06:58 +02008151 }
8152
8153cleanup:
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008154 if (scnode_skip_pred) {
8155 /* restore options */
8156 options &= ~LYXP_SKIP_EXPR;
8157 }
aPiecek8b0cc152021-05-31 16:40:31 +02008158 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko004d3152020-06-11 19:59:22 +02008159 lydict_remove(set->ctx, ncname_dict);
Michal Vaskof7e16e22020-10-21 09:24:39 +02008160 ly_path_predicates_free(set->ctx, pred_type, predicates);
Michal Vaskodb51a8d2020-05-27 15:22:29 +02008161 }
Michal Vaskod3678892020-05-21 10:06:58 +02008162 return rc;
8163}
8164
8165/**
8166 * @brief Evaluate NodeType and any following Predicates. Logs directly on error.
8167 *
8168 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
8169 * [6] NodeTest ::= NameTest | NodeType '(' ')'
8170 * [8] NodeType ::= 'text' | 'node'
8171 *
8172 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008173 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko49fec8e2022-05-24 10:28:33 +02008174 * @param[in] axis Axis to search on.
8175 * @param[in] all_desc Whether to search all the descendants or axis only.
aPiecek8b0cc152021-05-31 16:40:31 +02008176 * @param[in,out] set Context and result set.
Michal Vaskod3678892020-05-21 10:06:58 +02008177 * @param[in] options XPath options.
8178 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8179 */
8180static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008181eval_node_type_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_axis axis, ly_bool all_desc,
Radek Krejci1deb5be2020-08-26 16:43:36 +02008182 struct lyxp_set *set, uint32_t options)
Michal Vaskod3678892020-05-21 10:06:58 +02008183{
8184 LY_ERR rc;
8185
Michal Vaskod3678892020-05-21 10:06:58 +02008186 (void)all_desc;
8187
aPiecek8b0cc152021-05-31 16:40:31 +02008188 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko004d3152020-06-11 19:59:22 +02008189 assert(exp->tok_len[*tok_idx] == 4);
Michal Vasko49fec8e2022-05-24 10:28:33 +02008190 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "node", 4)) {
8191 rc = xpath_pi_node(set, axis, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008192 } else {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008193 assert(!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "text", 4));
8194 rc = xpath_pi_text(set, axis, options);
Michal Vaskod3678892020-05-21 10:06:58 +02008195 }
Michal Vasko49fec8e2022-05-24 10:28:33 +02008196 LY_CHECK_RET(rc);
Michal Vaskod3678892020-05-21 10:06:58 +02008197 }
aPiecek8b0cc152021-05-31 16:40:31 +02008198 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008199 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008200 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02008201
8202 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02008203 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
aPiecek8b0cc152021-05-31 16:40:31 +02008204 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008205 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008206 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02008207
8208 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02008209 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
aPiecek8b0cc152021-05-31 16:40:31 +02008210 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008211 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008212 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02008213
8214 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02008215 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008216 rc = eval_predicate(exp, tok_idx, set, options, axis);
Michal Vaskod3678892020-05-21 10:06:58 +02008217 LY_CHECK_RET(rc);
8218 }
8219
8220 return LY_SUCCESS;
8221}
8222
8223/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02008224 * @brief Evaluate RelativeLocationPath. Logs directly on error.
8225 *
8226 * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
8227 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
Michal Vaskod3678892020-05-21 10:06:58 +02008228 * [6] NodeTest ::= NameTest | NodeType '(' ')'
Michal Vasko03ff5a72019-09-11 13:49:33 +02008229 *
8230 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008231 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008232 * @param[in] all_desc Whether to search all the descendants or children only.
aPiecek8b0cc152021-05-31 16:40:31 +02008233 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008234 * @param[in] options XPath options.
8235 * @return LY_ERR (YL_EINCOMPLETE on unresolved when)
8236 */
8237static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008238eval_relative_location_path(const struct lyxp_expr *exp, uint32_t *tok_idx, ly_bool all_desc, struct lyxp_set *set,
Michal Vasko40308e72020-10-20 16:38:40 +02008239 uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008240{
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008241 LY_ERR rc = LY_SUCCESS;
Michal Vasko49fec8e2022-05-24 10:28:33 +02008242 enum lyxp_axis axis;
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008243 int scnode_skip_path = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008244
8245 goto step;
8246 do {
8247 /* evaluate '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02008248 if (exp->tok_len[*tok_idx] == 1) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008249 all_desc = 0;
8250 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02008251 assert(exp->tok_len[*tok_idx] == 2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008252 all_desc = 1;
8253 }
aPiecek8b0cc152021-05-31 16:40:31 +02008254 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008255 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008256 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008257
8258step:
Michal Vasko49fec8e2022-05-24 10:28:33 +02008259 /* AxisSpecifier */
8260 if (exp->tokens[*tok_idx] == LYXP_TOKEN_AXISNAME) {
8261 axis = str2axis(exp->expr + exp->tok_pos[*tok_idx], exp->tok_len[*tok_idx]);
Michal Vaskod3678892020-05-21 10:06:58 +02008262
aPiecek8b0cc152021-05-31 16:40:31 +02008263 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008264 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
8265 ++(*tok_idx);
8266
8267 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_DCOLON);
8268 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
8269 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
8270 ++(*tok_idx);
8271 } else if (exp->tokens[*tok_idx] == LYXP_TOKEN_AT) {
8272 axis = LYXP_AXIS_ATTRIBUTE;
8273
8274 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
8275 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008276 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02008277 } else {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008278 /* default */
8279 axis = LYXP_AXIS_CHILD;
Michal Vaskod3678892020-05-21 10:06:58 +02008280 }
8281
Michal Vasko49fec8e2022-05-24 10:28:33 +02008282 /* NodeTest Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02008283 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008284 case LYXP_TOKEN_DOT:
8285 /* evaluate '.' */
Michal Vasko49fec8e2022-05-24 10:28:33 +02008286 if (!(options & LYXP_SKIP_EXPR)) {
8287 if (((options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_SCNODE_SET)) ||
8288 (!(options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_NODE_SET))) {
8289 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
8290 rc = LY_EVALID;
8291 goto cleanup;
8292 }
8293
8294 if (all_desc) {
8295 rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
8296 LY_CHECK_GOTO(rc, cleanup);
8297 }
8298 rc = xpath_pi_node(set, LYXP_AXIS_SELF, options);
8299 LY_CHECK_GOTO(rc, cleanup);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008300 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02008301 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008302 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008303 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008304 break;
8305
8306 case LYXP_TOKEN_DDOT:
8307 /* evaluate '..' */
Michal Vasko49fec8e2022-05-24 10:28:33 +02008308 if (!(options & LYXP_SKIP_EXPR)) {
8309 if (((options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_SCNODE_SET)) ||
8310 (!(options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_NODE_SET))) {
8311 LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
8312 rc = LY_EVALID;
8313 goto cleanup;
8314 }
8315
8316 if (all_desc) {
8317 rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
8318 LY_CHECK_GOTO(rc, cleanup);
8319 }
8320 rc = xpath_pi_node(set, LYXP_AXIS_PARENT, options);
8321 LY_CHECK_GOTO(rc, cleanup);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008322 }
aPiecek8b0cc152021-05-31 16:40:31 +02008323 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008324 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008325 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008326 break;
8327
Michal Vasko03ff5a72019-09-11 13:49:33 +02008328 case LYXP_TOKEN_NAMETEST:
Michal Vaskod3678892020-05-21 10:06:58 +02008329 /* evaluate NameTest Predicate* */
Michal Vasko49fec8e2022-05-24 10:28:33 +02008330 rc = eval_name_test_with_predicate(exp, tok_idx, axis, all_desc, set, options);
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008331 if (rc == LY_ENOT) {
8332 assert(options & LYXP_SCNODE_ALL);
8333 /* skip the rest of this path */
8334 rc = LY_SUCCESS;
8335 scnode_skip_path = 1;
8336 options |= LYXP_SKIP_EXPR;
8337 }
8338 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskod3678892020-05-21 10:06:58 +02008339 break;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008340
Michal Vaskod3678892020-05-21 10:06:58 +02008341 case LYXP_TOKEN_NODETYPE:
8342 /* evaluate NodeType Predicate* */
Michal Vasko49fec8e2022-05-24 10:28:33 +02008343 rc = eval_node_type_with_predicate(exp, tok_idx, axis, all_desc, set, options);
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008344 LY_CHECK_GOTO(rc, cleanup);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008345 break;
8346
8347 default:
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008348 LOGINT(set->ctx);
8349 rc = LY_EINT;
8350 goto cleanup;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008351 }
Michal Vasko004d3152020-06-11 19:59:22 +02008352 } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008353
Michal Vaskoa036a6b2021-10-12 16:05:23 +02008354cleanup:
8355 if (scnode_skip_path) {
8356 options &= ~LYXP_SKIP_EXPR;
8357 }
8358 return rc;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008359}
8360
8361/**
8362 * @brief Evaluate AbsoluteLocationPath. Logs directly on error.
8363 *
8364 * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
8365 *
8366 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008367 * @param[in] tok_idx Position in the expression @p exp.
aPiecek8b0cc152021-05-31 16:40:31 +02008368 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008369 * @param[in] options XPath options.
8370 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8371 */
8372static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008373eval_absolute_location_path(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008374{
Radek Krejci857189e2020-09-01 13:26:36 +02008375 ly_bool all_desc;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008376
aPiecek8b0cc152021-05-31 16:40:31 +02008377 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008378 /* no matter what tokens follow, we need to be at the root */
Michal Vaskob0099a92020-08-31 14:55:23 +02008379 LY_CHECK_RET(moveto_root(set, options));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008380 }
8381
8382 /* '/' RelativeLocationPath? */
Michal Vasko004d3152020-06-11 19:59:22 +02008383 if (exp->tok_len[*tok_idx] == 1) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008384 /* evaluate '/' - deferred */
8385 all_desc = 0;
aPiecek8b0cc152021-05-31 16:40:31 +02008386 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008387 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008388 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008389
Michal Vasko004d3152020-06-11 19:59:22 +02008390 if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008391 return LY_SUCCESS;
8392 }
Michal Vasko004d3152020-06-11 19:59:22 +02008393 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008394 case LYXP_TOKEN_DOT:
8395 case LYXP_TOKEN_DDOT:
Michal Vasko49fec8e2022-05-24 10:28:33 +02008396 case LYXP_TOKEN_AXISNAME:
Michal Vasko03ff5a72019-09-11 13:49:33 +02008397 case LYXP_TOKEN_AT:
8398 case LYXP_TOKEN_NAMETEST:
8399 case LYXP_TOKEN_NODETYPE:
Michal Vaskob0099a92020-08-31 14:55:23 +02008400 LY_CHECK_RET(eval_relative_location_path(exp, tok_idx, all_desc, set, options));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008401 break;
8402 default:
8403 break;
8404 }
8405
Michal Vasko03ff5a72019-09-11 13:49:33 +02008406 } else {
Radek Krejcif6a11002020-08-21 13:29:07 +02008407 /* '//' RelativeLocationPath */
Michal Vasko03ff5a72019-09-11 13:49:33 +02008408 /* evaluate '//' - deferred so as not to waste memory by remembering all the nodes */
8409 all_desc = 1;
aPiecek8b0cc152021-05-31 16:40:31 +02008410 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008411 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008412 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008413
Michal Vaskob0099a92020-08-31 14:55:23 +02008414 LY_CHECK_RET(eval_relative_location_path(exp, tok_idx, all_desc, set, options));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008415 }
8416
8417 return LY_SUCCESS;
8418}
8419
8420/**
8421 * @brief Evaluate FunctionCall. Logs directly on error.
8422 *
Michal Vaskod3678892020-05-21 10:06:58 +02008423 * [11] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
Michal Vasko03ff5a72019-09-11 13:49:33 +02008424 *
8425 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008426 * @param[in] tok_idx Position in the expression @p exp.
aPiecek8b0cc152021-05-31 16:40:31 +02008427 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008428 * @param[in] options XPath options.
8429 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8430 */
8431static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008432eval_function_call(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008433{
8434 LY_ERR rc;
Michal Vasko69730152020-10-09 16:30:07 +02008435
Michal Vaskodd528af2022-08-08 14:35:07 +02008436 LY_ERR (*xpath_func)(struct lyxp_set **, uint32_t, struct lyxp_set *, uint32_t) = NULL;
8437 uint32_t arg_count = 0, i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008438 struct lyxp_set **args = NULL, **args_aux;
8439
aPiecek8b0cc152021-05-31 16:40:31 +02008440 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008441 /* FunctionName */
Michal Vasko004d3152020-06-11 19:59:22 +02008442 switch (exp->tok_len[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008443 case 3:
Michal Vasko004d3152020-06-11 19:59:22 +02008444 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008445 xpath_func = &xpath_not;
Michal Vasko004d3152020-06-11 19:59:22 +02008446 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008447 xpath_func = &xpath_sum;
8448 }
8449 break;
8450 case 4:
Michal Vasko004d3152020-06-11 19:59:22 +02008451 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008452 xpath_func = &xpath_lang;
Michal Vasko004d3152020-06-11 19:59:22 +02008453 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008454 xpath_func = &xpath_last;
Michal Vasko004d3152020-06-11 19:59:22 +02008455 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008456 xpath_func = &xpath_name;
Michal Vasko004d3152020-06-11 19:59:22 +02008457 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008458 xpath_func = &xpath_true;
8459 }
8460 break;
8461 case 5:
Michal Vasko004d3152020-06-11 19:59:22 +02008462 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008463 xpath_func = &xpath_count;
Michal Vasko004d3152020-06-11 19:59:22 +02008464 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008465 xpath_func = &xpath_false;
Michal Vasko004d3152020-06-11 19:59:22 +02008466 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008467 xpath_func = &xpath_floor;
Michal Vasko004d3152020-06-11 19:59:22 +02008468 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008469 xpath_func = &xpath_round;
Michal Vasko004d3152020-06-11 19:59:22 +02008470 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008471 xpath_func = &xpath_deref;
8472 }
8473 break;
8474 case 6:
Michal Vasko004d3152020-06-11 19:59:22 +02008475 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008476 xpath_func = &xpath_concat;
Michal Vasko004d3152020-06-11 19:59:22 +02008477 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008478 xpath_func = &xpath_number;
Michal Vasko004d3152020-06-11 19:59:22 +02008479 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008480 xpath_func = &xpath_string;
8481 }
8482 break;
8483 case 7:
Michal Vasko004d3152020-06-11 19:59:22 +02008484 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008485 xpath_func = &xpath_boolean;
Michal Vasko004d3152020-06-11 19:59:22 +02008486 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008487 xpath_func = &xpath_ceiling;
Michal Vasko004d3152020-06-11 19:59:22 +02008488 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008489 xpath_func = &xpath_current;
8490 }
8491 break;
8492 case 8:
Michal Vasko004d3152020-06-11 19:59:22 +02008493 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008494 xpath_func = &xpath_contains;
Michal Vasko004d3152020-06-11 19:59:22 +02008495 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008496 xpath_func = &xpath_position;
Michal Vasko004d3152020-06-11 19:59:22 +02008497 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008498 xpath_func = &xpath_re_match;
8499 }
8500 break;
8501 case 9:
Michal Vasko004d3152020-06-11 19:59:22 +02008502 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008503 xpath_func = &xpath_substring;
Michal Vasko004d3152020-06-11 19:59:22 +02008504 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008505 xpath_func = &xpath_translate;
8506 }
8507 break;
8508 case 10:
Michal Vasko004d3152020-06-11 19:59:22 +02008509 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008510 xpath_func = &xpath_local_name;
Michal Vasko004d3152020-06-11 19:59:22 +02008511 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008512 xpath_func = &xpath_enum_value;
Michal Vasko004d3152020-06-11 19:59:22 +02008513 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008514 xpath_func = &xpath_bit_is_set;
8515 }
8516 break;
8517 case 11:
Michal Vasko004d3152020-06-11 19:59:22 +02008518 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008519 xpath_func = &xpath_starts_with;
8520 }
8521 break;
8522 case 12:
Michal Vasko004d3152020-06-11 19:59:22 +02008523 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008524 xpath_func = &xpath_derived_from;
8525 }
8526 break;
8527 case 13:
Michal Vasko004d3152020-06-11 19:59:22 +02008528 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008529 xpath_func = &xpath_namespace_uri;
Michal Vasko004d3152020-06-11 19:59:22 +02008530 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008531 xpath_func = &xpath_string_length;
8532 }
8533 break;
8534 case 15:
Michal Vasko004d3152020-06-11 19:59:22 +02008535 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008536 xpath_func = &xpath_normalize_space;
Michal Vasko004d3152020-06-11 19:59:22 +02008537 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008538 xpath_func = &xpath_substring_after;
8539 }
8540 break;
8541 case 16:
Michal Vasko004d3152020-06-11 19:59:22 +02008542 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008543 xpath_func = &xpath_substring_before;
8544 }
8545 break;
8546 case 20:
Michal Vasko004d3152020-06-11 19:59:22 +02008547 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008548 xpath_func = &xpath_derived_from_or_self;
8549 }
8550 break;
8551 }
8552
8553 if (!xpath_func) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01008554 LOGVAL(set->ctx, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008555 return LY_EVALID;
8556 }
8557 }
8558
aPiecek8b0cc152021-05-31 16:40:31 +02008559 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008560 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008561 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008562
8563 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02008564 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
aPiecek8b0cc152021-05-31 16:40:31 +02008565 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008566 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008567 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008568
8569 /* ( Expr ( ',' Expr )* )? */
Michal Vasko004d3152020-06-11 19:59:22 +02008570 if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
aPiecek8b0cc152021-05-31 16:40:31 +02008571 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008572 args = malloc(sizeof *args);
8573 LY_CHECK_ERR_GOTO(!args, LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
8574 arg_count = 1;
8575 args[0] = set_copy(set);
8576 if (!args[0]) {
8577 rc = LY_EMEM;
8578 goto cleanup;
8579 }
8580
Michal Vasko004d3152020-06-11 19:59:22 +02008581 rc = eval_expr_select(exp, tok_idx, 0, args[0], options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008582 LY_CHECK_GOTO(rc, cleanup);
8583 } else {
aPiecek8b0cc152021-05-31 16:40:31 +02008584 rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008585 LY_CHECK_GOTO(rc, cleanup);
8586 }
8587 }
Michal Vasko004d3152020-06-11 19:59:22 +02008588 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
aPiecek8b0cc152021-05-31 16:40:31 +02008589 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008590 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008591 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008592
aPiecek8b0cc152021-05-31 16:40:31 +02008593 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008594 ++arg_count;
8595 args_aux = realloc(args, arg_count * sizeof *args);
8596 LY_CHECK_ERR_GOTO(!args_aux, arg_count--; LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
8597 args = args_aux;
8598 args[arg_count - 1] = set_copy(set);
8599 if (!args[arg_count - 1]) {
8600 rc = LY_EMEM;
8601 goto cleanup;
8602 }
8603
Michal Vasko004d3152020-06-11 19:59:22 +02008604 rc = eval_expr_select(exp, tok_idx, 0, args[arg_count - 1], options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008605 LY_CHECK_GOTO(rc, cleanup);
8606 } else {
aPiecek8b0cc152021-05-31 16:40:31 +02008607 rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008608 LY_CHECK_GOTO(rc, cleanup);
8609 }
8610 }
8611
8612 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02008613 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
aPiecek8b0cc152021-05-31 16:40:31 +02008614 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008615 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008616 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008617
aPiecek8b0cc152021-05-31 16:40:31 +02008618 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008619 /* evaluate function */
8620 rc = xpath_func(args, arg_count, set, options);
8621
8622 if (options & LYXP_SCNODE_ALL) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008623 /* merge all nodes from arg evaluations */
8624 for (i = 0; i < arg_count; ++i) {
Michal Vasko1a09b212021-05-06 13:00:10 +02008625 set_scnode_clear_ctx(args[i], LYXP_SET_SCNODE_ATOM_NODE);
Michal Vaskoecd62de2019-11-13 12:35:11 +01008626 lyxp_set_scnode_merge(set, args[i]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008627 }
8628 }
8629 } else {
8630 rc = LY_SUCCESS;
8631 }
8632
8633cleanup:
8634 for (i = 0; i < arg_count; ++i) {
8635 lyxp_set_free(args[i]);
8636 }
8637 free(args);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008638 return rc;
8639}
8640
8641/**
8642 * @brief Evaluate Number. Logs directly on error.
8643 *
8644 * @param[in] ctx Context for errors.
8645 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008646 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008647 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
8648 * @return LY_ERR
8649 */
8650static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008651eval_number(struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008652{
8653 long double num;
8654 char *endptr;
8655
8656 if (set) {
8657 errno = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02008658 num = strtold(&exp->expr[exp->tok_pos[*tok_idx]], &endptr);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008659 if (errno) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01008660 LOGVAL(ctx, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
8661 LOGVAL(ctx, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double (%s).",
Michal Vasko69730152020-10-09 16:30:07 +02008662 exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]], strerror(errno));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008663 return LY_EVALID;
Michal Vasko004d3152020-06-11 19:59:22 +02008664 } else if (endptr - &exp->expr[exp->tok_pos[*tok_idx]] != exp->tok_len[*tok_idx]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01008665 LOGVAL(ctx, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
8666 LOGVAL(ctx, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double.",
Michal Vasko69730152020-10-09 16:30:07 +02008667 exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008668 return LY_EVALID;
8669 }
8670
8671 set_fill_number(set, num);
8672 }
8673
8674 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008675 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008676 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008677 return LY_SUCCESS;
8678}
8679
aPiecekdf23eee2021-10-07 12:21:50 +02008680LY_ERR
8681lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var)
8682{
8683 LY_ERR ret = LY_ENOTFOUND;
8684 LY_ARRAY_COUNT_TYPE u;
8685
8686 assert(vars && name);
8687
8688 name_len = name_len ? name_len : strlen(name);
8689
8690 LY_ARRAY_FOR(vars, u) {
8691 if (!strncmp(vars[u].name, name, name_len)) {
8692 ret = LY_SUCCESS;
8693 break;
8694 }
8695 }
8696
8697 if (var && !ret) {
8698 *var = &vars[u];
8699 }
8700
8701 return ret;
8702}
8703
Michal Vasko03ff5a72019-09-11 13:49:33 +02008704/**
aPiecekfba75362021-10-07 12:39:48 +02008705 * @brief Evaluate VariableReference.
8706 *
8707 * @param[in] exp Parsed XPath expression.
8708 * @param[in] tok_idx Position in the expression @p exp.
8709 * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
8710 * @param[in,out] set Context and result set.
8711 * @param[in] options XPath options.
8712 * @return LY_ERR value.
8713 */
8714static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008715eval_variable_reference(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
aPiecekfba75362021-10-07 12:39:48 +02008716{
8717 LY_ERR ret;
8718 const char *name;
8719 struct lyxp_var *var;
8720 const struct lyxp_var *vars;
8721 struct lyxp_expr *tokens = NULL;
Michal Vaskodd528af2022-08-08 14:35:07 +02008722 uint32_t token_index, name_len;
aPiecekfba75362021-10-07 12:39:48 +02008723
8724 vars = set->vars;
8725
Michal Vasko49fec8e2022-05-24 10:28:33 +02008726 /* find out the name and value of the variable */
aPiecekfba75362021-10-07 12:39:48 +02008727 name = &exp->expr[exp->tok_pos[*tok_idx]];
Michal Vaskoe68b5402022-07-29 14:58:15 +02008728 name_len = exp->tok_len[*tok_idx];
8729 ret = lyxp_vars_find((struct lyxp_var *)vars, name, name_len, &var);
8730 LY_CHECK_ERR_RET(ret, LOGERR(set->ctx, ret, "XPath variable \"%.*s\" not defined.", (int)name_len, name), ret);
aPiecekfba75362021-10-07 12:39:48 +02008731
Michal Vasko49fec8e2022-05-24 10:28:33 +02008732 /* parse value */
aPiecekfba75362021-10-07 12:39:48 +02008733 ret = lyxp_expr_parse(set->ctx, var->value, 0, 1, &tokens);
8734 LY_CHECK_GOTO(ret, cleanup);
8735
Michal Vasko49fec8e2022-05-24 10:28:33 +02008736 /* evaluate value */
aPiecekfba75362021-10-07 12:39:48 +02008737 token_index = 0;
8738 ret = eval_expr_select(tokens, &token_index, 0, set, options);
8739 LY_CHECK_GOTO(ret, cleanup);
8740
8741cleanup:
8742 lyxp_expr_free(set->ctx, tokens);
8743
8744 return ret;
8745}
8746
8747/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02008748 * @brief Evaluate PathExpr. Logs directly on error.
8749 *
Michal Vaskod3678892020-05-21 10:06:58 +02008750 * [12] PathExpr ::= LocationPath | PrimaryExpr Predicate*
Michal Vasko03ff5a72019-09-11 13:49:33 +02008751 * | PrimaryExpr Predicate* '/' RelativeLocationPath
8752 * | PrimaryExpr Predicate* '//' RelativeLocationPath
8753 * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
aPiecekfba75362021-10-07 12:39:48 +02008754 * [10] PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
Michal Vasko03ff5a72019-09-11 13:49:33 +02008755 *
8756 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008757 * @param[in] tok_idx Position in the expression @p exp.
aPiecek8b0cc152021-05-31 16:40:31 +02008758 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008759 * @param[in] options XPath options.
8760 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8761 */
8762static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008763eval_path_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008764{
Michal Vasko49fec8e2022-05-24 10:28:33 +02008765 ly_bool all_desc;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008766 LY_ERR rc;
8767
Michal Vasko004d3152020-06-11 19:59:22 +02008768 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008769 case LYXP_TOKEN_PAR1:
8770 /* '(' Expr ')' */
8771
8772 /* '(' */
aPiecek8b0cc152021-05-31 16:40:31 +02008773 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008774 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008775 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008776
8777 /* Expr */
Michal Vasko004d3152020-06-11 19:59:22 +02008778 rc = eval_expr_select(exp, tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008779 LY_CHECK_RET(rc);
8780
8781 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02008782 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
aPiecek8b0cc152021-05-31 16:40:31 +02008783 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008784 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008785 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008786
Michal Vasko03ff5a72019-09-11 13:49:33 +02008787 goto predicate;
8788
8789 case LYXP_TOKEN_DOT:
8790 case LYXP_TOKEN_DDOT:
Michal Vasko49fec8e2022-05-24 10:28:33 +02008791 case LYXP_TOKEN_AXISNAME:
Michal Vasko03ff5a72019-09-11 13:49:33 +02008792 case LYXP_TOKEN_AT:
8793 case LYXP_TOKEN_NAMETEST:
8794 case LYXP_TOKEN_NODETYPE:
8795 /* RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02008796 rc = eval_relative_location_path(exp, tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008797 LY_CHECK_RET(rc);
8798 break;
8799
aPiecekfba75362021-10-07 12:39:48 +02008800 case LYXP_TOKEN_VARREF:
8801 /* VariableReference */
8802 rc = eval_variable_reference(exp, tok_idx, set, options);
8803 LY_CHECK_RET(rc);
8804 ++(*tok_idx);
8805
aPiecekfba75362021-10-07 12:39:48 +02008806 goto predicate;
8807
Michal Vasko03ff5a72019-09-11 13:49:33 +02008808 case LYXP_TOKEN_FUNCNAME:
8809 /* FunctionCall */
aPiecek8b0cc152021-05-31 16:40:31 +02008810 rc = eval_function_call(exp, tok_idx, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008811 LY_CHECK_RET(rc);
8812
Michal Vasko03ff5a72019-09-11 13:49:33 +02008813 goto predicate;
8814
Michal Vasko3e48bf32020-06-01 08:39:07 +02008815 case LYXP_TOKEN_OPER_PATH:
8816 case LYXP_TOKEN_OPER_RPATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +02008817 /* AbsoluteLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02008818 rc = eval_absolute_location_path(exp, tok_idx, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008819 LY_CHECK_RET(rc);
8820 break;
8821
8822 case LYXP_TOKEN_LITERAL:
8823 /* Literal */
aPiecek8b0cc152021-05-31 16:40:31 +02008824 if ((options & LYXP_SKIP_EXPR) || (options & LYXP_SCNODE_ALL)) {
8825 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko1a09b212021-05-06 13:00:10 +02008826 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008827 }
Michal Vasko004d3152020-06-11 19:59:22 +02008828 eval_literal(exp, tok_idx, NULL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008829 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02008830 eval_literal(exp, tok_idx, set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008831 }
8832
Michal Vasko03ff5a72019-09-11 13:49:33 +02008833 goto predicate;
8834
8835 case LYXP_TOKEN_NUMBER:
8836 /* Number */
aPiecek8b0cc152021-05-31 16:40:31 +02008837 if ((options & LYXP_SKIP_EXPR) || (options & LYXP_SCNODE_ALL)) {
8838 if (!(options & LYXP_SKIP_EXPR)) {
Michal Vasko1a09b212021-05-06 13:00:10 +02008839 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008840 }
Michal Vasko004d3152020-06-11 19:59:22 +02008841 rc = eval_number(NULL, exp, tok_idx, NULL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008842 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02008843 rc = eval_number(set->ctx, exp, tok_idx, set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008844 }
8845 LY_CHECK_RET(rc);
8846
Michal Vasko03ff5a72019-09-11 13:49:33 +02008847 goto predicate;
8848
8849 default:
Michal Vasko49fec8e2022-05-24 10:28:33 +02008850 LOGVAL(set->ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008851 return LY_EVALID;
8852 }
8853
8854 return LY_SUCCESS;
8855
8856predicate:
8857 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02008858 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
Michal Vasko49fec8e2022-05-24 10:28:33 +02008859 rc = eval_predicate(exp, tok_idx, set, options, LYXP_AXIS_CHILD);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008860 LY_CHECK_RET(rc);
8861 }
8862
8863 /* ('/' or '//') RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02008864 if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008865
8866 /* evaluate '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02008867 if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008868 all_desc = 0;
8869 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008870 all_desc = 1;
8871 }
8872
aPiecek8b0cc152021-05-31 16:40:31 +02008873 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008874 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008875 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008876
Michal Vasko004d3152020-06-11 19:59:22 +02008877 rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008878 LY_CHECK_RET(rc);
8879 }
8880
8881 return LY_SUCCESS;
8882}
8883
8884/**
8885 * @brief Evaluate UnionExpr. Logs directly on error.
8886 *
Michal Vaskod3678892020-05-21 10:06:58 +02008887 * [20] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02008888 *
8889 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008890 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008891 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02008892 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008893 * @param[in] options XPath options.
8894 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8895 */
8896static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008897eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008898{
8899 LY_ERR rc = LY_SUCCESS;
8900 struct lyxp_set orig_set, set2;
Michal Vaskodd528af2022-08-08 14:35:07 +02008901 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008902
8903 assert(repeat);
8904
8905 set_init(&orig_set, set);
8906 set_init(&set2, set);
8907
8908 set_fill_set(&orig_set, set);
8909
Michal Vasko004d3152020-06-11 19:59:22 +02008910 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008911 LY_CHECK_GOTO(rc, cleanup);
8912
8913 /* ('|' PathExpr)* */
8914 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02008915 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_UNI);
aPiecek8b0cc152021-05-31 16:40:31 +02008916 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008917 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008918 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008919
aPiecek8b0cc152021-05-31 16:40:31 +02008920 if (options & LYXP_SKIP_EXPR) {
8921 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008922 LY_CHECK_GOTO(rc, cleanup);
8923 continue;
8924 }
8925
8926 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02008927 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008928 LY_CHECK_GOTO(rc, cleanup);
8929
8930 /* eval */
8931 if (options & LYXP_SCNODE_ALL) {
Michal Vaskoecd62de2019-11-13 12:35:11 +01008932 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008933 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008934 rc = moveto_union(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008935 LY_CHECK_GOTO(rc, cleanup);
8936 }
8937 }
8938
8939cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02008940 lyxp_set_free_content(&orig_set);
8941 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008942 return rc;
8943}
8944
8945/**
8946 * @brief Evaluate UnaryExpr. Logs directly on error.
8947 *
Michal Vaskod3678892020-05-21 10:06:58 +02008948 * [19] UnaryExpr ::= UnionExpr | '-' UnaryExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02008949 *
8950 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008951 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008952 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02008953 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008954 * @param[in] options XPath options.
8955 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8956 */
8957static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02008958eval_unary_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008959{
8960 LY_ERR rc;
Michal Vaskodd528af2022-08-08 14:35:07 +02008961 uint32_t this_op, i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008962
8963 assert(repeat);
8964
8965 /* ('-')+ */
Michal Vasko004d3152020-06-11 19:59:22 +02008966 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008967 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02008968 assert(!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) && (exp->expr[exp->tok_pos[*tok_idx]] == '-'));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008969
aPiecek8b0cc152021-05-31 16:40:31 +02008970 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02008971 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02008972 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008973 }
8974
Michal Vasko004d3152020-06-11 19:59:22 +02008975 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNARY, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008976 LY_CHECK_RET(rc);
8977
aPiecek8b0cc152021-05-31 16:40:31 +02008978 if (!(options & LYXP_SKIP_EXPR) && (repeat % 2)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008979 if (options & LYXP_SCNODE_ALL) {
8980 warn_operands(set->ctx, set, NULL, 1, exp->expr, exp->tok_pos[this_op]);
8981 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008982 rc = moveto_op_math(set, NULL, &exp->expr[exp->tok_pos[this_op]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008983 LY_CHECK_RET(rc);
8984 }
8985 }
8986
8987 return LY_SUCCESS;
8988}
8989
8990/**
8991 * @brief Evaluate MultiplicativeExpr. Logs directly on error.
8992 *
Michal Vaskod3678892020-05-21 10:06:58 +02008993 * [18] MultiplicativeExpr ::= UnaryExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02008994 * | MultiplicativeExpr '*' UnaryExpr
8995 * | MultiplicativeExpr 'div' UnaryExpr
8996 * | MultiplicativeExpr 'mod' UnaryExpr
8997 *
8998 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008999 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009000 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02009001 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009002 * @param[in] options XPath options.
9003 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
9004 */
9005static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02009006eval_multiplicative_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set,
Michal Vasko40308e72020-10-20 16:38:40 +02009007 uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009008{
9009 LY_ERR rc;
Michal Vaskodd528af2022-08-08 14:35:07 +02009010 uint32_t i, this_op;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009011 struct lyxp_set orig_set, set2;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009012
9013 assert(repeat);
9014
9015 set_init(&orig_set, set);
9016 set_init(&set2, set);
9017
9018 set_fill_set(&orig_set, set);
9019
Michal Vasko004d3152020-06-11 19:59:22 +02009020 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009021 LY_CHECK_GOTO(rc, cleanup);
9022
9023 /* ('*' / 'div' / 'mod' UnaryExpr)* */
9024 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02009025 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009026
Michal Vasko004d3152020-06-11 19:59:22 +02009027 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
aPiecek8b0cc152021-05-31 16:40:31 +02009028 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02009029 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02009030 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009031
aPiecek8b0cc152021-05-31 16:40:31 +02009032 if (options & LYXP_SKIP_EXPR) {
9033 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009034 LY_CHECK_GOTO(rc, cleanup);
9035 continue;
9036 }
9037
9038 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02009039 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009040 LY_CHECK_GOTO(rc, cleanup);
9041
9042 /* eval */
9043 if (options & LYXP_SCNODE_ALL) {
9044 warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vaskoecd62de2019-11-13 12:35:11 +01009045 lyxp_set_scnode_merge(set, &set2);
Michal Vasko1a09b212021-05-06 13:00:10 +02009046 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009047 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009048 rc = moveto_op_math(set, &set2, &exp->expr[exp->tok_pos[this_op]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009049 LY_CHECK_GOTO(rc, cleanup);
9050 }
9051 }
9052
9053cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02009054 lyxp_set_free_content(&orig_set);
9055 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009056 return rc;
9057}
9058
9059/**
9060 * @brief Evaluate AdditiveExpr. Logs directly on error.
9061 *
Michal Vaskod3678892020-05-21 10:06:58 +02009062 * [17] AdditiveExpr ::= MultiplicativeExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02009063 * | AdditiveExpr '+' MultiplicativeExpr
9064 * | AdditiveExpr '-' MultiplicativeExpr
9065 *
9066 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02009067 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009068 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02009069 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009070 * @param[in] options XPath options.
9071 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
9072 */
9073static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02009074eval_additive_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009075{
9076 LY_ERR rc;
Michal Vaskodd528af2022-08-08 14:35:07 +02009077 uint32_t i, this_op;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009078 struct lyxp_set orig_set, set2;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009079
9080 assert(repeat);
9081
9082 set_init(&orig_set, set);
9083 set_init(&set2, set);
9084
9085 set_fill_set(&orig_set, set);
9086
Michal Vasko004d3152020-06-11 19:59:22 +02009087 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009088 LY_CHECK_GOTO(rc, cleanup);
9089
9090 /* ('+' / '-' MultiplicativeExpr)* */
9091 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02009092 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009093
Michal Vasko004d3152020-06-11 19:59:22 +02009094 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009095 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02009096 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02009097 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009098
aPiecek8b0cc152021-05-31 16:40:31 +02009099 if (options & LYXP_SKIP_EXPR) {
9100 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009101 LY_CHECK_GOTO(rc, cleanup);
9102 continue;
9103 }
9104
9105 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02009106 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009107 LY_CHECK_GOTO(rc, cleanup);
9108
9109 /* eval */
9110 if (options & LYXP_SCNODE_ALL) {
9111 warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vaskoecd62de2019-11-13 12:35:11 +01009112 lyxp_set_scnode_merge(set, &set2);
Michal Vasko1a09b212021-05-06 13:00:10 +02009113 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009114 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009115 rc = moveto_op_math(set, &set2, &exp->expr[exp->tok_pos[this_op]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009116 LY_CHECK_GOTO(rc, cleanup);
9117 }
9118 }
9119
9120cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02009121 lyxp_set_free_content(&orig_set);
9122 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009123 return rc;
9124}
9125
9126/**
9127 * @brief Evaluate RelationalExpr. Logs directly on error.
9128 *
Michal Vaskod3678892020-05-21 10:06:58 +02009129 * [16] RelationalExpr ::= AdditiveExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02009130 * | RelationalExpr '<' AdditiveExpr
9131 * | RelationalExpr '>' AdditiveExpr
9132 * | RelationalExpr '<=' AdditiveExpr
9133 * | RelationalExpr '>=' AdditiveExpr
9134 *
9135 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02009136 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009137 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02009138 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009139 * @param[in] options XPath options.
9140 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
9141 */
9142static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02009143eval_relational_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009144{
9145 LY_ERR rc;
Michal Vaskodd528af2022-08-08 14:35:07 +02009146 uint32_t i, this_op;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009147 struct lyxp_set orig_set, set2;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009148
9149 assert(repeat);
9150
9151 set_init(&orig_set, set);
9152 set_init(&set2, set);
9153
9154 set_fill_set(&orig_set, set);
9155
Michal Vasko004d3152020-06-11 19:59:22 +02009156 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009157 LY_CHECK_GOTO(rc, cleanup);
9158
9159 /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
9160 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02009161 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009162
Michal Vasko004d3152020-06-11 19:59:22 +02009163 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_COMP);
aPiecek8b0cc152021-05-31 16:40:31 +02009164 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02009165 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02009166 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009167
aPiecek8b0cc152021-05-31 16:40:31 +02009168 if (options & LYXP_SKIP_EXPR) {
9169 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009170 LY_CHECK_GOTO(rc, cleanup);
9171 continue;
9172 }
9173
9174 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02009175 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009176 LY_CHECK_GOTO(rc, cleanup);
9177
9178 /* eval */
9179 if (options & LYXP_SCNODE_ALL) {
9180 warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vaskoecd62de2019-11-13 12:35:11 +01009181 lyxp_set_scnode_merge(set, &set2);
Michal Vasko1a09b212021-05-06 13:00:10 +02009182 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009183 } else {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02009184 ly_bool result;
9185
9186 rc = moveto_op_comp(set, &set2, &exp->expr[exp->tok_pos[this_op]], &result);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009187 LY_CHECK_GOTO(rc, cleanup);
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02009188 set_fill_boolean(set, result);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009189 }
9190 }
9191
9192cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02009193 lyxp_set_free_content(&orig_set);
9194 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009195 return rc;
9196}
9197
9198/**
9199 * @brief Evaluate EqualityExpr. Logs directly on error.
9200 *
Michal Vaskod3678892020-05-21 10:06:58 +02009201 * [15] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02009202 * | EqualityExpr '!=' RelationalExpr
9203 *
9204 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02009205 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009206 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02009207 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009208 * @param[in] options XPath options.
9209 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
9210 */
9211static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02009212eval_equality_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009213{
9214 LY_ERR rc;
Michal Vaskodd528af2022-08-08 14:35:07 +02009215 uint32_t i, this_op;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009216 struct lyxp_set orig_set, set2;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009217
9218 assert(repeat);
9219
9220 set_init(&orig_set, set);
9221 set_init(&set2, set);
9222
9223 set_fill_set(&orig_set, set);
9224
Michal Vasko004d3152020-06-11 19:59:22 +02009225 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009226 LY_CHECK_GOTO(rc, cleanup);
9227
9228 /* ('=' / '!=' RelationalExpr)* */
9229 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02009230 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009231
Michal Vasko004d3152020-06-11 19:59:22 +02009232 assert((exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL) || (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_NEQUAL));
aPiecek8b0cc152021-05-31 16:40:31 +02009233 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02009234 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02009235 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009236
aPiecek8b0cc152021-05-31 16:40:31 +02009237 if (options & LYXP_SKIP_EXPR) {
9238 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009239 LY_CHECK_GOTO(rc, cleanup);
9240 continue;
9241 }
9242
9243 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02009244 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009245 LY_CHECK_GOTO(rc, cleanup);
9246
9247 /* eval */
9248 if (options & LYXP_SCNODE_ALL) {
9249 warn_operands(set->ctx, set, &set2, 0, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vasko004d3152020-06-11 19:59:22 +02009250 warn_equality_value(exp, set, *tok_idx - 1, this_op - 1, *tok_idx - 1);
9251 warn_equality_value(exp, &set2, this_op - 1, this_op - 1, *tok_idx - 1);
Michal Vaskoecd62de2019-11-13 12:35:11 +01009252 lyxp_set_scnode_merge(set, &set2);
Michal Vasko1a09b212021-05-06 13:00:10 +02009253 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009254 } else {
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02009255 ly_bool result;
9256
9257 rc = moveto_op_comp(set, &set2, &exp->expr[exp->tok_pos[this_op]], &result);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009258 LY_CHECK_GOTO(rc, cleanup);
Haithem Ben Ghorbal1ac219d2022-09-07 12:30:06 +02009259 set_fill_boolean(set, result);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009260 }
9261 }
9262
9263cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02009264 lyxp_set_free_content(&orig_set);
9265 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009266 return rc;
9267}
9268
9269/**
9270 * @brief Evaluate AndExpr. Logs directly on error.
9271 *
Michal Vaskod3678892020-05-21 10:06:58 +02009272 * [14] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02009273 *
9274 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02009275 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009276 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02009277 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009278 * @param[in] options XPath options.
9279 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
9280 */
9281static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02009282eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009283{
9284 LY_ERR rc;
9285 struct lyxp_set orig_set, set2;
Michal Vaskodd528af2022-08-08 14:35:07 +02009286 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009287
9288 assert(repeat);
9289
9290 set_init(&orig_set, set);
9291 set_init(&set2, set);
9292
9293 set_fill_set(&orig_set, set);
9294
Michal Vasko004d3152020-06-11 19:59:22 +02009295 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009296 LY_CHECK_GOTO(rc, cleanup);
9297
9298 /* cast to boolean, we know that will be the final result */
aPiecek8b0cc152021-05-31 16:40:31 +02009299 if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
Michal Vasko1a09b212021-05-06 13:00:10 +02009300 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009301 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009302 lyxp_set_cast(set, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009303 }
9304
9305 /* ('and' EqualityExpr)* */
9306 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02009307 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
aPiecek8b0cc152021-05-31 16:40:31 +02009308 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, ((options & LYXP_SKIP_EXPR) || !set->val.bln ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02009309 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02009310 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009311
9312 /* lazy evaluation */
aPiecek8b0cc152021-05-31 16:40:31 +02009313 if ((options & LYXP_SKIP_EXPR) || ((set->type == LYXP_SET_BOOLEAN) && !set->val.bln)) {
9314 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options | LYXP_SKIP_EXPR);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009315 LY_CHECK_GOTO(rc, cleanup);
9316 continue;
9317 }
9318
9319 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02009320 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009321 LY_CHECK_GOTO(rc, cleanup);
9322
9323 /* eval - just get boolean value actually */
9324 if (set->type == LYXP_SET_SCNODE_SET) {
Michal Vasko1a09b212021-05-06 13:00:10 +02009325 set_scnode_clear_ctx(&set2, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vaskoecd62de2019-11-13 12:35:11 +01009326 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009327 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009328 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009329 set_fill_set(set, &set2);
9330 }
9331 }
9332
9333cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02009334 lyxp_set_free_content(&orig_set);
9335 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009336 return rc;
9337}
9338
9339/**
9340 * @brief Evaluate OrExpr. Logs directly on error.
9341 *
Michal Vaskod3678892020-05-21 10:06:58 +02009342 * [13] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02009343 *
9344 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02009345 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009346 * @param[in] repeat How many times this expression is repeated.
aPiecek8b0cc152021-05-31 16:40:31 +02009347 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009348 * @param[in] options XPath options.
9349 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
9350 */
9351static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02009352eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009353{
9354 LY_ERR rc;
9355 struct lyxp_set orig_set, set2;
Michal Vaskodd528af2022-08-08 14:35:07 +02009356 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009357
9358 assert(repeat);
9359
9360 set_init(&orig_set, set);
9361 set_init(&set2, set);
9362
9363 set_fill_set(&orig_set, set);
9364
Michal Vasko004d3152020-06-11 19:59:22 +02009365 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009366 LY_CHECK_GOTO(rc, cleanup);
9367
9368 /* cast to boolean, we know that will be the final result */
aPiecek8b0cc152021-05-31 16:40:31 +02009369 if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
Michal Vasko1a09b212021-05-06 13:00:10 +02009370 set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009371 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009372 lyxp_set_cast(set, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009373 }
9374
9375 /* ('or' AndExpr)* */
9376 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02009377 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
aPiecek8b0cc152021-05-31 16:40:31 +02009378 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, ((options & LYXP_SKIP_EXPR) || set->val.bln ? "skipped" : "parsed"),
Michal Vasko49fec8e2022-05-24 10:28:33 +02009379 lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
Michal Vasko004d3152020-06-11 19:59:22 +02009380 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009381
9382 /* lazy evaluation */
aPiecek8b0cc152021-05-31 16:40:31 +02009383 if ((options & LYXP_SKIP_EXPR) || ((set->type == LYXP_SET_BOOLEAN) && set->val.bln)) {
9384 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options | LYXP_SKIP_EXPR);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009385 LY_CHECK_GOTO(rc, cleanup);
9386 continue;
9387 }
9388
9389 set_fill_set(&set2, &orig_set);
9390 /* expr_type cound have been LYXP_EXPR_NONE in all these later calls (except for the first one),
9391 * but it does not matter */
Michal Vasko004d3152020-06-11 19:59:22 +02009392 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009393 LY_CHECK_GOTO(rc, cleanup);
9394
9395 /* eval - just get boolean value actually */
9396 if (set->type == LYXP_SET_SCNODE_SET) {
Michal Vasko1a09b212021-05-06 13:00:10 +02009397 set_scnode_clear_ctx(&set2, LYXP_SET_SCNODE_ATOM_NODE);
Michal Vaskoecd62de2019-11-13 12:35:11 +01009398 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009399 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009400 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009401 set_fill_set(set, &set2);
9402 }
9403 }
9404
9405cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02009406 lyxp_set_free_content(&orig_set);
9407 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009408 return rc;
9409}
9410
9411/**
Michal Vasko004d3152020-06-11 19:59:22 +02009412 * @brief Decide what expression is at the pointer @p tok_idx and evaluate it accordingly.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009413 *
9414 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02009415 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009416 * @param[in] etype Expression type to evaluate.
aPiecek8b0cc152021-05-31 16:40:31 +02009417 * @param[in,out] set Context and result set.
Michal Vasko03ff5a72019-09-11 13:49:33 +02009418 * @param[in] options XPath options.
9419 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
9420 */
9421static LY_ERR
Michal Vaskodd528af2022-08-08 14:35:07 +02009422eval_expr_select(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_expr_type etype, struct lyxp_set *set,
Michal Vasko40308e72020-10-20 16:38:40 +02009423 uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009424{
Michal Vaskodd528af2022-08-08 14:35:07 +02009425 uint32_t i, count;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009426 enum lyxp_expr_type next_etype;
9427 LY_ERR rc;
9428
9429 /* process operator repeats */
Michal Vasko004d3152020-06-11 19:59:22 +02009430 if (!exp->repeat[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02009431 next_etype = LYXP_EXPR_NONE;
9432 } else {
9433 /* find etype repeat */
Radek Krejci1e008d22020-08-17 11:37:37 +02009434 for (i = 0; exp->repeat[*tok_idx][i] > etype; ++i) {}
Michal Vasko03ff5a72019-09-11 13:49:33 +02009435
9436 /* select one-priority lower because etype expression called us */
9437 if (i) {
Michal Vasko004d3152020-06-11 19:59:22 +02009438 next_etype = exp->repeat[*tok_idx][i - 1];
Michal Vasko03ff5a72019-09-11 13:49:33 +02009439 /* count repeats for that expression */
Radek Krejci1e008d22020-08-17 11:37:37 +02009440 for (count = 0; i && exp->repeat[*tok_idx][i - 1] == next_etype; ++count, --i) {}
Michal Vasko03ff5a72019-09-11 13:49:33 +02009441 } else {
9442 next_etype = LYXP_EXPR_NONE;
9443 }
9444 }
9445
9446 /* decide what expression are we parsing based on the repeat */
9447 switch (next_etype) {
9448 case LYXP_EXPR_OR:
Michal Vasko004d3152020-06-11 19:59:22 +02009449 rc = eval_or_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009450 break;
9451 case LYXP_EXPR_AND:
Michal Vasko004d3152020-06-11 19:59:22 +02009452 rc = eval_and_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009453 break;
9454 case LYXP_EXPR_EQUALITY:
Michal Vasko004d3152020-06-11 19:59:22 +02009455 rc = eval_equality_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009456 break;
9457 case LYXP_EXPR_RELATIONAL:
Michal Vasko004d3152020-06-11 19:59:22 +02009458 rc = eval_relational_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009459 break;
9460 case LYXP_EXPR_ADDITIVE:
Michal Vasko004d3152020-06-11 19:59:22 +02009461 rc = eval_additive_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009462 break;
9463 case LYXP_EXPR_MULTIPLICATIVE:
Michal Vasko004d3152020-06-11 19:59:22 +02009464 rc = eval_multiplicative_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009465 break;
9466 case LYXP_EXPR_UNARY:
Michal Vasko004d3152020-06-11 19:59:22 +02009467 rc = eval_unary_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009468 break;
9469 case LYXP_EXPR_UNION:
Michal Vasko004d3152020-06-11 19:59:22 +02009470 rc = eval_union_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009471 break;
9472 case LYXP_EXPR_NONE:
Michal Vasko004d3152020-06-11 19:59:22 +02009473 rc = eval_path_expr(exp, tok_idx, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009474 break;
9475 default:
9476 LOGINT_RET(set->ctx);
9477 }
9478
9479 return rc;
9480}
9481
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009482/**
9483 * @brief Get root type.
9484 *
9485 * @param[in] ctx_node Context node.
9486 * @param[in] ctx_scnode Schema context node.
9487 * @param[in] options XPath options.
9488 * @return Root type.
9489 */
9490static enum lyxp_node_type
Radek Krejci1deb5be2020-08-26 16:43:36 +02009491lyxp_get_root_type(const struct lyd_node *ctx_node, const struct lysc_node *ctx_scnode, uint32_t options)
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009492{
Michal Vasko6b26e742020-07-17 15:02:10 +02009493 const struct lysc_node *op;
9494
Michal Vaskoa27245c2022-05-02 09:01:35 +02009495 /* explicit */
9496 if (options & LYXP_ACCESS_TREE_ALL) {
9497 return LYXP_NODE_ROOT;
9498 } else if (options & LYXP_ACCESS_TREE_CONFIG) {
9499 return LYXP_NODE_ROOT_CONFIG;
9500 }
9501
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009502 if (options & LYXP_SCNODE_ALL) {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009503 /* schema */
Radek Krejci1e008d22020-08-17 11:37:37 +02009504 for (op = ctx_scnode; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {}
Michal Vasko6b26e742020-07-17 15:02:10 +02009505
9506 if (op || (options & LYXP_SCNODE)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009507 /* general root that can access everything */
9508 return LYXP_NODE_ROOT;
9509 } else if (!ctx_scnode || (ctx_scnode->flags & LYS_CONFIG_W)) {
9510 /* root context node can access only config data (because we said so, it is unspecified) */
9511 return LYXP_NODE_ROOT_CONFIG;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009512 }
Michal Vasko6b26e742020-07-17 15:02:10 +02009513 return LYXP_NODE_ROOT;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009514 }
9515
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009516 /* data */
Michal Vasko6b26e742020-07-17 15:02:10 +02009517 op = ctx_node ? ctx_node->schema : NULL;
Michal Vaskod989ba02020-08-24 10:59:24 +02009518 for ( ; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {}
Michal Vasko6b26e742020-07-17 15:02:10 +02009519
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009520 if (op || !(options & LYXP_SCHEMA)) {
9521 /* general root that can access everything */
9522 return LYXP_NODE_ROOT;
Christian Hoppsb6ecaea2021-02-06 09:45:38 -05009523 } else if (!ctx_node || !ctx_node->schema || (ctx_node->schema->flags & LYS_CONFIG_W)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009524 /* root context node can access only config data (because we said so, it is unspecified) */
9525 return LYXP_NODE_ROOT_CONFIG;
9526 }
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009527 return LYXP_NODE_ROOT;
9528}
9529
Michal Vasko03ff5a72019-09-11 13:49:33 +02009530LY_ERR
Michal Vasko400e9672021-01-11 13:39:17 +01009531lyxp_eval(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct lys_module *cur_mod,
Michal Vaskoa3e92bc2022-07-29 14:56:23 +02009532 LY_VALUE_FORMAT format, void *prefix_data, const struct lyd_node *cur_node, const struct lyd_node *ctx_node,
9533 const struct lyd_node *tree, const struct lyxp_var *vars, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009534{
Michal Vaskodd528af2022-08-08 14:35:07 +02009535 uint32_t tok_idx = 0;
Michal Vaskoddd76592022-01-17 13:34:48 +01009536 const struct lysc_node *snode;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009537 LY_ERR rc;
9538
Michal Vasko400e9672021-01-11 13:39:17 +01009539 LY_CHECK_ARG_RET(ctx, ctx, exp, set, LY_EINVAL);
Radek Krejci8df109d2021-04-23 12:19:08 +02009540 if (!cur_mod && ((format == LY_VALUE_SCHEMA) || (format == LY_VALUE_SCHEMA_RESOLVED))) {
Michal Vaskoddd76592022-01-17 13:34:48 +01009541 LOGERR(ctx, LY_EINVAL, "Current module must be set if schema format is used.");
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009542 return LY_EINVAL;
Michal Vasko004d3152020-06-11 19:59:22 +02009543 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02009544
Michal Vaskod3bb12f2020-12-04 14:33:09 +01009545 if (tree) {
9546 /* adjust the pointer to be the first top-level sibling */
9547 while (tree->parent) {
9548 tree = lyd_parent(tree);
9549 }
9550 tree = lyd_first_sibling(tree);
Michal Vaskoddd76592022-01-17 13:34:48 +01009551
9552 for (snode = tree->schema->parent; snode && (snode->nodetype & (LYS_CASE | LYS_CHOICE)); snode = snode->parent) {}
9553 if (snode) {
9554 /* unable to evaluate absolute paths */
9555 LOGERR(ctx, LY_EINVAL, "Data node \"%s\" has no parent but is not instance of a top-level schema node.",
9556 LYD_NAME(tree));
9557 return LY_EINVAL;
9558 }
Michal Vaskod3bb12f2020-12-04 14:33:09 +01009559 }
9560
Michal Vasko03ff5a72019-09-11 13:49:33 +02009561 /* prepare set for evaluation */
Michal Vasko03ff5a72019-09-11 13:49:33 +02009562 memset(set, 0, sizeof *set);
Michal Vaskod3678892020-05-21 10:06:58 +02009563 set->type = LYXP_SET_NODE_SET;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009564 set->root_type = lyxp_get_root_type(ctx_node, NULL, options);
9565 set_insert_node(set, (struct lyd_node *)ctx_node, 0, ctx_node ? LYXP_NODE_ELEM : set->root_type, 0);
9566
Michal Vasko400e9672021-01-11 13:39:17 +01009567 set->ctx = (struct ly_ctx *)ctx;
Michal Vaskoa3e92bc2022-07-29 14:56:23 +02009568 set->cur_node = cur_node;
9569 for (set->context_op = cur_node ? cur_node->schema : NULL;
Radek Krejci0f969882020-08-21 16:56:47 +02009570 set->context_op && !(set->context_op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF));
9571 set->context_op = set->context_op->parent) {}
Michal Vaskof03ed032020-03-04 13:31:44 +01009572 set->tree = tree;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009573 set->cur_mod = cur_mod;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009574 set->format = format;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009575 set->prefix_data = prefix_data;
aPiecekfba75362021-10-07 12:39:48 +02009576 set->vars = vars;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009577
Radek Krejciddace2c2021-01-08 11:30:56 +01009578 LOG_LOCSET(NULL, set->cur_node, NULL, NULL);
Radek Krejci2efc45b2020-12-22 16:25:44 +01009579
Michal Vasko03ff5a72019-09-11 13:49:33 +02009580 /* evaluate */
Michal Vasko004d3152020-06-11 19:59:22 +02009581 rc = eval_expr_select(exp, &tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009582 if (rc != LY_SUCCESS) {
Michal Vaskod3678892020-05-21 10:06:58 +02009583 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009584 }
9585
Michal Vasko4a7d4d62021-12-13 17:05:06 +01009586 if (set->cur_node) {
9587 LOG_LOCBACK(0, 1, 0, 0);
9588 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02009589 return rc;
9590}
9591
9592#if 0
9593
9594/* full xml printing of set elements, not used currently */
9595
9596void
9597lyxp_set_print_xml(FILE *f, struct lyxp_set *set)
9598{
9599 uint32_t i;
9600 char *str_num;
9601 struct lyout out;
9602
9603 memset(&out, 0, sizeof out);
9604
9605 out.type = LYOUT_STREAM;
9606 out.method.f = f;
9607
9608 switch (set->type) {
9609 case LYXP_SET_EMPTY:
Michal Vasko5233e962020-08-14 14:26:20 +02009610 ly_print_(&out, "Empty XPath set\n\n");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009611 break;
9612 case LYXP_SET_BOOLEAN:
Michal Vasko5233e962020-08-14 14:26:20 +02009613 ly_print_(&out, "Boolean XPath set:\n");
9614 ly_print_(&out, "%s\n\n", set->value.bool ? "true" : "false");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009615 break;
9616 case LYXP_SET_STRING:
Michal Vasko5233e962020-08-14 14:26:20 +02009617 ly_print_(&out, "String XPath set:\n");
9618 ly_print_(&out, "\"%s\"\n\n", set->value.str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009619 break;
9620 case LYXP_SET_NUMBER:
Michal Vasko5233e962020-08-14 14:26:20 +02009621 ly_print_(&out, "Number XPath set:\n");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009622
9623 if (isnan(set->value.num)) {
9624 str_num = strdup("NaN");
9625 } else if ((set->value.num == 0) || (set->value.num == -0.0f)) {
9626 str_num = strdup("0");
9627 } else if (isinf(set->value.num) && !signbit(set->value.num)) {
9628 str_num = strdup("Infinity");
9629 } else if (isinf(set->value.num) && signbit(set->value.num)) {
9630 str_num = strdup("-Infinity");
9631 } else if ((long long)set->value.num == set->value.num) {
9632 if (asprintf(&str_num, "%lld", (long long)set->value.num) == -1) {
9633 str_num = NULL;
9634 }
9635 } else {
9636 if (asprintf(&str_num, "%03.1Lf", set->value.num) == -1) {
9637 str_num = NULL;
9638 }
9639 }
9640 if (!str_num) {
9641 LOGMEM;
9642 return;
9643 }
Michal Vasko5233e962020-08-14 14:26:20 +02009644 ly_print_(&out, "%s\n\n", str_num);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009645 free(str_num);
9646 break;
9647 case LYXP_SET_NODE_SET:
Michal Vasko5233e962020-08-14 14:26:20 +02009648 ly_print_(&out, "Node XPath set:\n");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009649
9650 for (i = 0; i < set->used; ++i) {
Michal Vasko5233e962020-08-14 14:26:20 +02009651 ly_print_(&out, "%d. ", i + 1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009652 switch (set->node_type[i]) {
9653 case LYXP_NODE_ROOT_ALL:
Michal Vasko5233e962020-08-14 14:26:20 +02009654 ly_print_(&out, "ROOT all\n\n");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009655 break;
9656 case LYXP_NODE_ROOT_CONFIG:
Michal Vasko5233e962020-08-14 14:26:20 +02009657 ly_print_(&out, "ROOT config\n\n");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009658 break;
9659 case LYXP_NODE_ROOT_STATE:
Michal Vasko5233e962020-08-14 14:26:20 +02009660 ly_print_(&out, "ROOT state\n\n");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009661 break;
9662 case LYXP_NODE_ROOT_NOTIF:
Michal Vasko5233e962020-08-14 14:26:20 +02009663 ly_print_(&out, "ROOT notification \"%s\"\n\n", set->value.nodes[i]->schema->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009664 break;
9665 case LYXP_NODE_ROOT_RPC:
Michal Vasko5233e962020-08-14 14:26:20 +02009666 ly_print_(&out, "ROOT rpc \"%s\"\n\n", set->value.nodes[i]->schema->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009667 break;
9668 case LYXP_NODE_ROOT_OUTPUT:
Michal Vasko5233e962020-08-14 14:26:20 +02009669 ly_print_(&out, "ROOT output \"%s\"\n\n", set->value.nodes[i]->schema->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009670 break;
9671 case LYXP_NODE_ELEM:
Michal Vasko5233e962020-08-14 14:26:20 +02009672 ly_print_(&out, "ELEM \"%s\"\n", set->value.nodes[i]->schema->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009673 xml_print_node(&out, 1, set->value.nodes[i], 1, LYP_FORMAT);
Michal Vasko5233e962020-08-14 14:26:20 +02009674 ly_print_(&out, "\n");
Michal Vasko03ff5a72019-09-11 13:49:33 +02009675 break;
9676 case LYXP_NODE_TEXT:
Michal Vasko5233e962020-08-14 14:26:20 +02009677 ly_print_(&out, "TEXT \"%s\"\n\n", ((struct lyd_node_leaf_list *)set->value.nodes[i])->value_str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009678 break;
9679 case LYXP_NODE_ATTR:
Michal Vasko5233e962020-08-14 14:26:20 +02009680 ly_print_(&out, "ATTR \"%s\" = \"%s\"\n\n", set->value.attrs[i]->name, set->value.attrs[i]->value);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009681 break;
9682 }
9683 }
9684 break;
9685 }
9686}
9687
9688#endif
9689
9690LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009691lyxp_set_cast(struct lyxp_set *set, enum lyxp_set_type target)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009692{
9693 long double num;
9694 char *str;
9695 LY_ERR rc;
9696
9697 if (!set || (set->type == target)) {
9698 return LY_SUCCESS;
9699 }
9700
9701 /* it's not possible to convert anything into a node set */
Michal Vaskod3678892020-05-21 10:06:58 +02009702 assert(target != LYXP_SET_NODE_SET);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009703
9704 if (set->type == LYXP_SET_SCNODE_SET) {
Michal Vaskod3678892020-05-21 10:06:58 +02009705 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009706 return LY_EINVAL;
9707 }
9708
9709 /* to STRING */
Michal Vaskod3678892020-05-21 10:06:58 +02009710 if ((target == LYXP_SET_STRING) || ((target == LYXP_SET_NUMBER) && (set->type == LYXP_SET_NODE_SET))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02009711 switch (set->type) {
9712 case LYXP_SET_NUMBER:
9713 if (isnan(set->val.num)) {
9714 set->val.str = strdup("NaN");
9715 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
9716 } else if ((set->val.num == 0) || (set->val.num == -0.0f)) {
9717 set->val.str = strdup("0");
9718 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
9719 } else if (isinf(set->val.num) && !signbit(set->val.num)) {
9720 set->val.str = strdup("Infinity");
9721 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
9722 } else if (isinf(set->val.num) && signbit(set->val.num)) {
9723 set->val.str = strdup("-Infinity");
9724 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
9725 } else if ((long long)set->val.num == set->val.num) {
9726 if (asprintf(&str, "%lld", (long long)set->val.num) == -1) {
9727 LOGMEM_RET(set->ctx);
9728 }
9729 set->val.str = str;
9730 } else {
9731 if (asprintf(&str, "%03.1Lf", set->val.num) == -1) {
9732 LOGMEM_RET(set->ctx);
9733 }
9734 set->val.str = str;
9735 }
9736 break;
9737 case LYXP_SET_BOOLEAN:
Michal Vasko004d3152020-06-11 19:59:22 +02009738 if (set->val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02009739 set->val.str = strdup("true");
9740 } else {
9741 set->val.str = strdup("false");
9742 }
9743 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), LY_EMEM);
9744 break;
9745 case LYXP_SET_NODE_SET:
Michal Vasko03ff5a72019-09-11 13:49:33 +02009746 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009747 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02009748
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01009749 rc = cast_node_set_to_string(set, &str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009750 LY_CHECK_RET(rc);
Michal Vaskod3678892020-05-21 10:06:58 +02009751 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009752 set->val.str = str;
9753 break;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009754 default:
9755 LOGINT_RET(set->ctx);
9756 }
9757 set->type = LYXP_SET_STRING;
9758 }
9759
9760 /* to NUMBER */
9761 if (target == LYXP_SET_NUMBER) {
9762 switch (set->type) {
9763 case LYXP_SET_STRING:
9764 num = cast_string_to_number(set->val.str);
Michal Vaskod3678892020-05-21 10:06:58 +02009765 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02009766 set->val.num = num;
9767 break;
9768 case LYXP_SET_BOOLEAN:
Michal Vasko004d3152020-06-11 19:59:22 +02009769 if (set->val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02009770 set->val.num = 1;
9771 } else {
9772 set->val.num = 0;
9773 }
9774 break;
9775 default:
9776 LOGINT_RET(set->ctx);
9777 }
9778 set->type = LYXP_SET_NUMBER;
9779 }
9780
9781 /* to BOOLEAN */
9782 if (target == LYXP_SET_BOOLEAN) {
9783 switch (set->type) {
9784 case LYXP_SET_NUMBER:
9785 if ((set->val.num == 0) || (set->val.num == -0.0f) || isnan(set->val.num)) {
Michal Vasko004d3152020-06-11 19:59:22 +02009786 set->val.bln = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009787 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02009788 set->val.bln = 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009789 }
9790 break;
9791 case LYXP_SET_STRING:
9792 if (set->val.str[0]) {
Michal Vaskod3678892020-05-21 10:06:58 +02009793 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02009794 set->val.bln = 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009795 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02009796 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02009797 set->val.bln = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009798 }
9799 break;
9800 case LYXP_SET_NODE_SET:
Michal Vaskod3678892020-05-21 10:06:58 +02009801 if (set->used) {
9802 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02009803 set->val.bln = 1;
Michal Vaskod3678892020-05-21 10:06:58 +02009804 } else {
9805 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02009806 set->val.bln = 0;
Michal Vaskod3678892020-05-21 10:06:58 +02009807 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02009808 break;
9809 default:
9810 LOGINT_RET(set->ctx);
9811 }
9812 set->type = LYXP_SET_BOOLEAN;
9813 }
9814
Michal Vasko03ff5a72019-09-11 13:49:33 +02009815 return LY_SUCCESS;
9816}
9817
9818LY_ERR
Michal Vasko400e9672021-01-11 13:39:17 +01009819lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct lys_module *cur_mod,
Michal Vaskoa3e92bc2022-07-29 14:56:23 +02009820 LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *cur_scnode,
9821 const struct lysc_node *ctx_scnode, struct lyxp_set *set, uint32_t options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02009822{
Radek Krejci2efc45b2020-12-22 16:25:44 +01009823 LY_ERR ret;
Michal Vaskodd528af2022-08-08 14:35:07 +02009824 uint32_t tok_idx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009825
Michal Vasko400e9672021-01-11 13:39:17 +01009826 LY_CHECK_ARG_RET(ctx, ctx, exp, set, LY_EINVAL);
Radek Krejci8df109d2021-04-23 12:19:08 +02009827 if (!cur_mod && ((format == LY_VALUE_SCHEMA) || (format == LY_VALUE_SCHEMA_RESOLVED))) {
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009828 LOGARG(NULL, "Current module must be set if schema format is used.");
9829 return LY_EINVAL;
Michal Vasko004d3152020-06-11 19:59:22 +02009830 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02009831
9832 /* prepare set for evaluation */
Michal Vasko03ff5a72019-09-11 13:49:33 +02009833 memset(set, 0, sizeof *set);
9834 set->type = LYXP_SET_SCNODE_SET;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009835 set->root_type = lyxp_get_root_type(NULL, ctx_scnode, options);
Michal Vasko7333cb32022-07-29 16:30:29 +02009836 LY_CHECK_RET(set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
Radek Krejcif13b87b2020-12-01 22:02:17 +01009837 set->val.scnodes[0].in_ctx = LYXP_SET_SCNODE_START;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009838
Michal Vasko400e9672021-01-11 13:39:17 +01009839 set->ctx = (struct ly_ctx *)ctx;
Michal Vaskoa3e92bc2022-07-29 14:56:23 +02009840 set->cur_scnode = cur_scnode;
9841 for (set->context_op = cur_scnode;
Radek Krejci0f969882020-08-21 16:56:47 +02009842 set->context_op && !(set->context_op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF));
9843 set->context_op = set->context_op->parent) {}
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009844 set->cur_mod = cur_mod;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009845 set->format = format;
Michal Vasko5d24f6c2020-10-13 13:49:06 +02009846 set->prefix_data = prefix_data;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009847
Radek Krejciddace2c2021-01-08 11:30:56 +01009848 LOG_LOCSET(set->cur_scnode, NULL, NULL, NULL);
Radek Krejci2efc45b2020-12-22 16:25:44 +01009849
Michal Vasko03ff5a72019-09-11 13:49:33 +02009850 /* evaluate */
Radek Krejci2efc45b2020-12-22 16:25:44 +01009851 ret = eval_expr_select(exp, &tok_idx, 0, set, options);
9852
Radek Krejciddace2c2021-01-08 11:30:56 +01009853 LOG_LOCBACK(1, 0, 0, 0);
Radek Krejci2efc45b2020-12-22 16:25:44 +01009854 return ret;
Michal Vasko03ff5a72019-09-11 13:49:33 +02009855}
Michal Vaskod43d71a2020-08-07 14:54:58 +02009856
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01009857LIBYANG_API_DEF const char *
Michal Vaskod43d71a2020-08-07 14:54:58 +02009858lyxp_get_expr(const struct lyxp_expr *path)
9859{
9860 if (!path) {
9861 return NULL;
9862 }
9863
9864 return path->expr;
9865}