blob: 4b6fa67c56cfa09a3194b0a370262104e00f3c0a [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 Vasko61ac2f62020-05-25 12:39:51 +02006 * Copyright (c) 2015 - 2020 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 */
Michal Vasko03ff5a72019-09-11 13:49:33 +020014#define _GNU_SOURCE
15
16/* needed by libmath functions isfinite(), isinf(), isnan(), signbit(), ... */
17#define _ISOC99_SOURCE
Radek Krejcib1646a92018-11-02 16:08:26 +010018
Radek Krejci535ea9f2020-05-29 16:01:05 +020019#include "xpath.h"
Radek Krejcib1646a92018-11-02 16:08:26 +010020
Radek Krejci535ea9f2020-05-29 16:01:05 +020021#include <assert.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010022#include <ctype.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020023#include <errno.h>
24#include <limits.h>
25#include <math.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include <stdint.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010027#include <stdio.h>
28#include <stdlib.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010029#include <string.h>
Radek Krejcib1646a92018-11-02 16:08:26 +010030
Radek Krejci535ea9f2020-05-29 16:01:05 +020031#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020032#include "compat.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020033#include "context.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020034#include "dict.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020035#include "hash_table.h"
Radek Krejci7931b192020-06-25 17:05:03 +020036#include "parser_data.h"
Michal Vasko004d3152020-06-11 19:59:22 +020037#include "path.h"
Michal Vasko03ff5a72019-09-11 13:49:33 +020038#include "plugins_types.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020039#include "printer.h"
40#include "printer_data.h"
41#include "tree.h"
42#include "tree_data_internal.h"
43#include "tree_schema_internal.h"
44#include "xml.h"
Michal Vasko03ff5a72019-09-11 13:49:33 +020045
Michal Vasko004d3152020-06-11 19:59:22 +020046static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx);
47static LY_ERR eval_expr_select(struct lyxp_expr *exp, uint16_t *tok_idx, enum lyxp_expr_type etype, struct lyxp_set *set, int options);
Michal Vasko03ff5a72019-09-11 13:49:33 +020048
49/**
50 * @brief Print the type of an XPath \p set.
51 *
52 * @param[in] set Set to use.
53 * @return Set type string.
54 */
55static const char *
56print_set_type(struct lyxp_set *set)
57{
58 switch (set->type) {
Michal Vasko03ff5a72019-09-11 13:49:33 +020059 case LYXP_SET_NODE_SET:
60 return "node set";
61 case LYXP_SET_SCNODE_SET:
62 return "schema node set";
63 case LYXP_SET_BOOLEAN:
64 return "boolean";
65 case LYXP_SET_NUMBER:
66 return "number";
67 case LYXP_SET_STRING:
68 return "string";
69 }
70
71 return NULL;
72}
73
Michal Vasko24cddf82020-06-01 08:17:01 +020074const char *
75lyxp_print_token(enum lyxp_token tok)
Michal Vasko03ff5a72019-09-11 13:49:33 +020076{
77 switch (tok) {
78 case LYXP_TOKEN_PAR1:
79 return "(";
80 case LYXP_TOKEN_PAR2:
81 return ")";
82 case LYXP_TOKEN_BRACK1:
83 return "[";
84 case LYXP_TOKEN_BRACK2:
85 return "]";
86 case LYXP_TOKEN_DOT:
87 return ".";
88 case LYXP_TOKEN_DDOT:
89 return "..";
90 case LYXP_TOKEN_AT:
91 return "@";
92 case LYXP_TOKEN_COMMA:
93 return ",";
94 case LYXP_TOKEN_NAMETEST:
95 return "NameTest";
96 case LYXP_TOKEN_NODETYPE:
97 return "NodeType";
98 case LYXP_TOKEN_FUNCNAME:
99 return "FunctionName";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200100 case LYXP_TOKEN_OPER_LOG:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200101 return "Operator(Logic)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200102 case LYXP_TOKEN_OPER_EQUAL:
103 return "Operator(Equal)";
104 case LYXP_TOKEN_OPER_NEQUAL:
105 return "Operator(Non-equal)";
106 case LYXP_TOKEN_OPER_COMP:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200107 return "Operator(Comparison)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200108 case LYXP_TOKEN_OPER_MATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200109 return "Operator(Math)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200110 case LYXP_TOKEN_OPER_UNI:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200111 return "Operator(Union)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200112 case LYXP_TOKEN_OPER_PATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200113 return "Operator(Path)";
Michal Vasko3e48bf32020-06-01 08:39:07 +0200114 case LYXP_TOKEN_OPER_RPATH:
Michal Vasko14676352020-05-29 11:35:55 +0200115 return "Operator(Recursive Path)";
Michal Vasko03ff5a72019-09-11 13:49:33 +0200116 case LYXP_TOKEN_LITERAL:
117 return "Literal";
118 case LYXP_TOKEN_NUMBER:
119 return "Number";
120 default:
121 LOGINT(NULL);
122 return "";
123 }
124}
125
126/**
127 * @brief Print the whole expression \p exp to debug output.
128 *
129 * @param[in] exp Expression to use.
130 */
131static void
132print_expr_struct_debug(struct lyxp_expr *exp)
133{
134 uint16_t i, j;
135 char tmp[128];
136
137 if (!exp || (ly_log_level < LY_LLDBG)) {
138 return;
139 }
140
141 LOGDBG(LY_LDGXPATH, "expression \"%s\":", exp->expr);
142 for (i = 0; i < exp->used; ++i) {
Michal Vasko24cddf82020-06-01 08:17:01 +0200143 sprintf(tmp, "\ttoken %s, in expression \"%.*s\"", lyxp_print_token(exp->tokens[i]), exp->tok_len[i],
Michal Vasko03ff5a72019-09-11 13:49:33 +0200144 &exp->expr[exp->tok_pos[i]]);
145 if (exp->repeat[i]) {
146 sprintf(tmp + strlen(tmp), " (repeat %d", exp->repeat[i][0]);
147 for (j = 1; exp->repeat[i][j]; ++j) {
148 sprintf(tmp + strlen(tmp), ", %d", exp->repeat[i][j]);
149 }
150 strcat(tmp, ")");
151 }
152 LOGDBG(LY_LDGXPATH, tmp);
153 }
154}
155
156#ifndef NDEBUG
157
158/**
159 * @brief Print XPath set content to debug output.
160 *
161 * @param[in] set Set to print.
162 */
163static void
164print_set_debug(struct lyxp_set *set)
165{
166 uint32_t i;
167 char *str;
168 int dynamic;
169 struct lyxp_set_node *item;
170 struct lyxp_set_scnode *sitem;
171
172 if (ly_log_level < LY_LLDBG) {
173 return;
174 }
175
176 switch (set->type) {
177 case LYXP_SET_NODE_SET:
178 LOGDBG(LY_LDGXPATH, "set NODE SET:");
179 for (i = 0; i < set->used; ++i) {
180 item = &set->val.nodes[i];
181
182 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +0100183 case LYXP_NODE_NONE:
184 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): NONE", i + 1, item->pos);
185 break;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200186 case LYXP_NODE_ROOT:
187 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT", i + 1, item->pos);
188 break;
189 case LYXP_NODE_ROOT_CONFIG:
190 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT CONFIG", i + 1, item->pos);
191 break;
192 case LYXP_NODE_ELEM:
193 if ((item->node->schema->nodetype == LYS_LIST)
194 && (((struct lyd_node_inner *)item->node)->child->schema->nodetype == LYS_LEAF)) {
195 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (1st child val: %s)", i + 1, item->pos,
196 item->node->schema->name,
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200197 (str = (char *)lyd_value2str((struct lyd_node_term *)lyd_node_children(item->node, 0), &dynamic)));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200198 if (dynamic) {
199 free(str);
200 }
201 } else if (((struct lyd_node_inner *)item->node)->schema->nodetype == LYS_LEAFLIST) {
202 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (val: %s)", i + 1, item->pos,
203 item->node->schema->name,
204 (str = (char *)lyd_value2str((struct lyd_node_term *)item->node, &dynamic)));
205 if (dynamic) {
206 free(str);
207 }
208 } else {
209 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s", i + 1, item->pos, item->node->schema->name);
210 }
211 break;
212 case LYXP_NODE_TEXT:
213 if (item->node->schema->nodetype & LYS_ANYDATA) {
214 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT <%s>", i + 1, item->pos,
215 item->node->schema->nodetype == LYS_ANYXML ? "anyxml" : "anydata");
216 } else {
217 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT %s", i + 1, item->pos,
218 (str = (char *)lyd_value2str((struct lyd_node_term *)item->node, &dynamic)));
219 if (dynamic) {
220 free(str);
221 }
222 }
223 break;
Michal Vasko9f96a052020-03-10 09:41:45 +0100224 case LYXP_NODE_META:
225 LOGDBG(LY_LDGXPATH, "\t%d (pos %u): META %s = %s", i + 1, item->pos, set->val.meta[i].meta->name,
226 set->val.meta[i].meta->value);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200227 break;
228 }
229 }
230 break;
231
232 case LYXP_SET_SCNODE_SET:
233 LOGDBG(LY_LDGXPATH, "set SCNODE SET:");
234 for (i = 0; i < set->used; ++i) {
235 sitem = &set->val.scnodes[i];
236
237 switch (sitem->type) {
238 case LYXP_NODE_ROOT:
239 LOGDBG(LY_LDGXPATH, "\t%d (%u): ROOT", i + 1, sitem->in_ctx);
240 break;
241 case LYXP_NODE_ROOT_CONFIG:
242 LOGDBG(LY_LDGXPATH, "\t%d (%u): ROOT CONFIG", i + 1, sitem->in_ctx);
243 break;
244 case LYXP_NODE_ELEM:
245 LOGDBG(LY_LDGXPATH, "\t%d (%u): ELEM %s", i + 1, sitem->in_ctx, sitem->scnode->name);
246 break;
247 default:
248 LOGINT(NULL);
249 break;
250 }
251 }
252 break;
253
Michal Vasko03ff5a72019-09-11 13:49:33 +0200254 case LYXP_SET_BOOLEAN:
255 LOGDBG(LY_LDGXPATH, "set BOOLEAN");
Michal Vasko004d3152020-06-11 19:59:22 +0200256 LOGDBG(LY_LDGXPATH, "\t%s", (set->val.bln ? "true" : "false"));
Michal Vasko03ff5a72019-09-11 13:49:33 +0200257 break;
258
259 case LYXP_SET_STRING:
260 LOGDBG(LY_LDGXPATH, "set STRING");
261 LOGDBG(LY_LDGXPATH, "\t%s", set->val.str);
262 break;
263
264 case LYXP_SET_NUMBER:
265 LOGDBG(LY_LDGXPATH, "set NUMBER");
266
267 if (isnan(set->val.num)) {
268 str = strdup("NaN");
269 } else if ((set->val.num == 0) || (set->val.num == -0.0f)) {
270 str = strdup("0");
271 } else if (isinf(set->val.num) && !signbit(set->val.num)) {
272 str = strdup("Infinity");
273 } else if (isinf(set->val.num) && signbit(set->val.num)) {
274 str = strdup("-Infinity");
275 } else if ((long long)set->val.num == set->val.num) {
276 if (asprintf(&str, "%lld", (long long)set->val.num) == -1) {
277 str = NULL;
278 }
279 } else {
280 if (asprintf(&str, "%03.1Lf", set->val.num) == -1) {
281 str = NULL;
282 }
283 }
284 LY_CHECK_ERR_RET(!str, LOGMEM(NULL), );
285
286 LOGDBG(LY_LDGXPATH, "\t%s", str);
287 free(str);
288 }
289}
290
291#endif
292
293/**
294 * @brief Realloc the string \p str.
295 *
296 * @param[in] ctx libyang context for logging.
297 * @param[in] needed How much free space is required.
298 * @param[in,out] str Pointer to the string to use.
299 * @param[in,out] used Used bytes in \p str.
300 * @param[in,out] size Allocated bytes in \p str.
301 * @return LY_ERR
302 */
303static LY_ERR
Michal Vasko52927e22020-03-16 17:26:14 +0100304cast_string_realloc(const struct ly_ctx *ctx, uint16_t needed, char **str, uint16_t *used, uint16_t *size)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200305{
306 if (*size - *used < needed) {
307 do {
308 if ((UINT16_MAX - *size) < LYXP_STRING_CAST_SIZE_STEP) {
309 LOGERR(ctx, LY_EINVAL, "XPath string length limit (%u) reached.", UINT16_MAX);
310 return LY_EINVAL;
311 }
312 *size += LYXP_STRING_CAST_SIZE_STEP;
313 } while (*size - *used < needed);
314 *str = ly_realloc(*str, *size * sizeof(char));
315 LY_CHECK_ERR_RET(!(*str), LOGMEM(ctx), LY_EMEM);
316 }
317
318 return LY_SUCCESS;
319}
320
321/**
322 * @brief Cast nodes recursively to one string @p str.
323 *
324 * @param[in] node Node to cast.
325 * @param[in] fake_cont Whether to put the data into a "fake" container.
326 * @param[in] root_type Type of the XPath root.
327 * @param[in] indent Current indent.
328 * @param[in,out] str Resulting string.
329 * @param[in,out] used Used bytes in @p str.
330 * @param[in,out] size Allocated bytes in @p str.
331 * @return LY_ERR
332 */
333static LY_ERR
334cast_string_recursive(const struct lyd_node *node, int fake_cont, enum lyxp_node_type root_type, uint16_t indent, char **str,
335 uint16_t *used, uint16_t *size)
336{
Radek Krejci7f769d72020-07-11 23:13:56 +0200337 char *buf, *line, *ptr = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200338 const char *value_str;
339 int dynamic;
340 const struct lyd_node *child;
Michal Vasko60ea6352020-06-29 13:39:39 +0200341 struct lyd_node *tree;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200342 struct lyd_node_any *any;
343 LY_ERR rc;
344
345 if ((root_type == LYXP_NODE_ROOT_CONFIG) && (node->schema->flags & LYS_CONFIG_R)) {
346 return LY_SUCCESS;
347 }
348
349 if (fake_cont) {
350 rc = cast_string_realloc(LYD_NODE_CTX(node), 1, str, used, size);
351 LY_CHECK_RET(rc);
352 strcpy(*str + (*used - 1), "\n");
353 ++(*used);
354
355 ++indent;
356 }
357
358 switch (node->schema->nodetype) {
359 case LYS_CONTAINER:
360 case LYS_LIST:
361 case LYS_RPC:
362 case LYS_NOTIF:
363 rc = cast_string_realloc(LYD_NODE_CTX(node), 1, str, used, size);
364 LY_CHECK_RET(rc);
365 strcpy(*str + (*used - 1), "\n");
366 ++(*used);
367
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200368 for (child = lyd_node_children(node, 0); child; child = child->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200369 rc = cast_string_recursive(child, 0, root_type, indent + 1, str, used, size);
370 LY_CHECK_RET(rc);
371 }
372
373 break;
374
375 case LYS_LEAF:
376 case LYS_LEAFLIST:
377 value_str = lyd_value2str(((struct lyd_node_term *)node), &dynamic);
378
379 /* print indent */
380 rc = cast_string_realloc(LYD_NODE_CTX(node), indent * 2 + strlen(value_str) + 1, str, used, size);
381 if (rc != LY_SUCCESS) {
382 if (dynamic) {
383 free((char *)value_str);
384 }
385 return rc;
386 }
387 memset(*str + (*used - 1), ' ', indent * 2);
388 *used += indent * 2;
389
390 /* print value */
391 if (*used == 1) {
392 sprintf(*str + (*used - 1), "%s", value_str);
393 *used += strlen(value_str);
394 } else {
395 sprintf(*str + (*used - 1), "%s\n", value_str);
396 *used += strlen(value_str) + 1;
397 }
398 if (dynamic) {
399 free((char *)value_str);
400 }
401
402 break;
403
404 case LYS_ANYXML:
405 case LYS_ANYDATA:
406 any = (struct lyd_node_any *)node;
407 if (!(void *)any->value.tree) {
408 /* no content */
409 buf = strdup("");
410 LY_CHECK_ERR_RET(!buf, LOGMEM(LYD_NODE_CTX(node)), LY_EMEM);
411 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200412 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100413
Michal Vasko60ea6352020-06-29 13:39:39 +0200414 if (any->value_type == LYD_ANYDATA_LYB) {
415 /* try to parse it into a data tree */
Radek Krejci7931b192020-06-25 17:05:03 +0200416 if (lyd_parse_data_mem((struct ly_ctx *)LYD_NODE_CTX(node), any->value.mem, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree) == LY_SUCCESS) {
Michal Vasko60ea6352020-06-29 13:39:39 +0200417 /* successfully parsed */
418 free(any->value.mem);
419 any->value.tree = tree;
420 any->value_type = LYD_ANYDATA_DATATREE;
421 }
Radek Krejci7931b192020-06-25 17:05:03 +0200422 /* error is covered by the following switch where LYD_ANYDATA_LYB causes failure */
Michal Vasko60ea6352020-06-29 13:39:39 +0200423 }
424
Michal Vasko03ff5a72019-09-11 13:49:33 +0200425 switch (any->value_type) {
426 case LYD_ANYDATA_STRING:
427 case LYD_ANYDATA_XML:
428 case LYD_ANYDATA_JSON:
429 buf = strdup(any->value.json);
430 LY_CHECK_ERR_RET(!buf, LOGMEM(LYD_NODE_CTX(node)), LY_EMEM);
431 break;
432 case LYD_ANYDATA_DATATREE:
Radek Krejci84ce7b12020-06-11 17:28:25 +0200433 LY_CHECK_RET(ly_out_new_memory(&buf, 0, &out));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200434 rc = lyd_print_all(out, any->value.tree, LYD_XML, 0);
Radek Krejci241f6b52020-05-21 18:13:49 +0200435 ly_out_free(out, NULL, 0);
Radek Krejcia5bba312020-01-09 15:41:20 +0100436 LY_CHECK_RET(rc < 0, -rc);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200437 break;
Michal Vasko60ea6352020-06-29 13:39:39 +0200438 case LYD_ANYDATA_LYB:
Michal Vasko03ff5a72019-09-11 13:49:33 +0200439 LOGERR(LYD_NODE_CTX(node), LY_EINVAL, "Cannot convert LYB anydata into string.");
Michal Vasko60ea6352020-06-29 13:39:39 +0200440 return LY_EINVAL;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200441 }
442 }
443
444 line = strtok_r(buf, "\n", &ptr);
445 do {
446 rc = cast_string_realloc(LYD_NODE_CTX(node), indent * 2 + strlen(line) + 1, str, used, size);
447 if (rc != LY_SUCCESS) {
448 free(buf);
449 return rc;
450 }
451 memset(*str + (*used - 1), ' ', indent * 2);
452 *used += indent * 2;
453
454 strcpy(*str + (*used - 1), line);
455 *used += strlen(line);
456
457 strcpy(*str + (*used - 1), "\n");
458 *used += 1;
459 } while ((line = strtok_r(NULL, "\n", &ptr)));
460
461 free(buf);
462 break;
463
464 default:
465 LOGINT_RET(LYD_NODE_CTX(node));
466 }
467
468 if (fake_cont) {
469 rc = cast_string_realloc(LYD_NODE_CTX(node), 1, str, used, size);
470 LY_CHECK_RET(rc);
471 strcpy(*str + (*used - 1), "\n");
472 ++(*used);
473
474 --indent;
475 }
476
477 return LY_SUCCESS;
478}
479
480/**
481 * @brief Cast an element into a string.
482 *
483 * @param[in] node Node to cast.
484 * @param[in] fake_cont Whether to put the data into a "fake" container.
485 * @param[in] root_type Type of the XPath root.
486 * @param[out] str Element cast to dynamically-allocated string.
487 * @return LY_ERR
488 */
489static LY_ERR
490cast_string_elem(struct lyd_node *node, int fake_cont, enum lyxp_node_type root_type, char **str)
491{
492 uint16_t used, size;
493 LY_ERR rc;
494
495 *str = malloc(LYXP_STRING_CAST_SIZE_START * sizeof(char));
496 LY_CHECK_ERR_RET(!*str, LOGMEM(LYD_NODE_CTX(node)), LY_EMEM);
497 (*str)[0] = '\0';
498 used = 1;
499 size = LYXP_STRING_CAST_SIZE_START;
500
501 rc = cast_string_recursive(node, fake_cont, root_type, 0, str, &used, &size);
502 if (rc != LY_SUCCESS) {
503 free(*str);
504 return rc;
505 }
506
507 if (size > used) {
508 *str = ly_realloc(*str, used * sizeof(char));
509 LY_CHECK_ERR_RET(!*str, LOGMEM(LYD_NODE_CTX(node)), LY_EMEM);
510 }
511 return LY_SUCCESS;
512}
513
514/**
515 * @brief Cast a LYXP_SET_NODE_SET set into a string.
516 * Context position aware.
517 *
518 * @param[in] set Set to cast.
Michal Vasko03ff5a72019-09-11 13:49:33 +0200519 * @param[out] str Cast dynamically-allocated string.
520 * @return LY_ERR
521 */
522static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +0100523cast_node_set_to_string(struct lyxp_set *set, char **str)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200524{
Michal Vasko03ff5a72019-09-11 13:49:33 +0200525 int dynamic;
526
Michal Vasko03ff5a72019-09-11 13:49:33 +0200527 switch (set->val.nodes[0].type) {
Michal Vasko2caefc12019-11-14 16:07:56 +0100528 case LYXP_NODE_NONE:
529 /* invalid */
530 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200531 case LYXP_NODE_ROOT:
532 case LYXP_NODE_ROOT_CONFIG:
Michal Vasko5e0e6eb2019-11-06 15:47:50 +0100533 return cast_string_elem(set->val.nodes[0].node, 1, set->root_type, str);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200534 case LYXP_NODE_ELEM:
535 case LYXP_NODE_TEXT:
Michal Vasko5e0e6eb2019-11-06 15:47:50 +0100536 return cast_string_elem(set->val.nodes[0].node, 0, set->root_type, str);
Michal Vasko9f96a052020-03-10 09:41:45 +0100537 case LYXP_NODE_META:
538 *str = (char *)lyd_meta2str(set->val.meta[0].meta, &dynamic);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200539 if (!dynamic) {
540 *str = strdup(*str);
541 if (!*str) {
542 LOGMEM_RET(set->ctx);
543 }
544 }
545 return LY_SUCCESS;
546 }
547
548 LOGINT_RET(set->ctx);
549}
550
551/**
552 * @brief Cast a string into an XPath number.
553 *
554 * @param[in] str String to use.
555 * @return Cast number.
556 */
557static long double
558cast_string_to_number(const char *str)
559{
560 long double num;
561 char *ptr;
562
563 errno = 0;
564 num = strtold(str, &ptr);
565 if (errno || *ptr) {
566 num = NAN;
567 }
568 return num;
569}
570
571/**
572 * @brief Callback for checking value equality.
573 *
574 * @param[in] val1_p First value.
575 * @param[in] val2_p Second value.
576 * @param[in] mod Whether hash table is being modified.
577 * @param[in] cb_data Callback data.
578 * @return 0 if not equal, non-zero if equal.
579 */
580static int
581set_values_equal_cb(void *val1_p, void *val2_p, int UNUSED(mod), void *UNUSED(cb_data))
582{
583 struct lyxp_set_hash_node *val1, *val2;
584
585 val1 = (struct lyxp_set_hash_node *)val1_p;
586 val2 = (struct lyxp_set_hash_node *)val2_p;
587
588 if ((val1->node == val2->node) && (val1->type == val2->type)) {
589 return 1;
590 }
591
592 return 0;
593}
594
595/**
596 * @brief Insert node and its hash into set.
597 *
598 * @param[in] set et to insert to.
599 * @param[in] node Node with hash.
600 * @param[in] type Node type.
601 */
602static void
603set_insert_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type)
604{
605 int r;
606 uint32_t i, hash;
607 struct lyxp_set_hash_node hnode;
608
609 if (!set->ht && (set->used >= LYD_HT_MIN_ITEMS)) {
610 /* create hash table and add all the nodes */
611 set->ht = lyht_new(1, sizeof(struct lyxp_set_hash_node), set_values_equal_cb, NULL, 1);
612 for (i = 0; i < set->used; ++i) {
613 hnode.node = set->val.nodes[i].node;
614 hnode.type = set->val.nodes[i].type;
615
616 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
617 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
618 hash = dict_hash_multi(hash, NULL, 0);
619
620 r = lyht_insert(set->ht, &hnode, hash, NULL);
621 assert(!r);
622 (void)r;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200623
Michal Vasko9d6befd2019-12-11 14:56:56 +0100624 if (hnode.node == node) {
625 /* it was just added, do not add it twice */
626 node = NULL;
627 }
628 }
629 }
630
631 if (set->ht && node) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200632 /* add the new node into hash table */
633 hnode.node = node;
634 hnode.type = type;
635
636 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
637 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
638 hash = dict_hash_multi(hash, NULL, 0);
639
640 r = lyht_insert(set->ht, &hnode, hash, NULL);
641 assert(!r);
642 (void)r;
643 }
644}
645
646/**
647 * @brief Remove node and its hash from set.
648 *
649 * @param[in] set Set to remove from.
650 * @param[in] node Node to remove.
651 * @param[in] type Node type.
652 */
653static void
654set_remove_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type)
655{
656 int r;
657 struct lyxp_set_hash_node hnode;
658 uint32_t hash;
659
660 if (set->ht) {
661 hnode.node = node;
662 hnode.type = type;
663
664 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
665 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
666 hash = dict_hash_multi(hash, NULL, 0);
667
668 r = lyht_remove(set->ht, &hnode, hash);
669 assert(!r);
670 (void)r;
671
672 if (!set->ht->used) {
673 lyht_free(set->ht);
674 set->ht = NULL;
675 }
676 }
677}
678
679/**
680 * @brief Check whether node is in set based on its hash.
681 *
682 * @param[in] set Set to search in.
683 * @param[in] node Node to search for.
684 * @param[in] type Node type.
685 * @param[in] skip_idx Index in @p set to skip.
686 * @return LY_ERR
687 */
688static LY_ERR
689set_dup_node_hash_check(const struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type, int skip_idx)
690{
691 struct lyxp_set_hash_node hnode, *match_p;
692 uint32_t hash;
693
694 hnode.node = node;
695 hnode.type = type;
696
697 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
698 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
699 hash = dict_hash_multi(hash, NULL, 0);
700
701 if (!lyht_find(set->ht, &hnode, hash, (void **)&match_p)) {
702 if ((skip_idx > -1) && (set->val.nodes[skip_idx].node == match_p->node) && (set->val.nodes[skip_idx].type == match_p->type)) {
703 /* we found it on the index that should be skipped, find another */
704 hnode = *match_p;
705 if (lyht_find_next(set->ht, &hnode, hash, (void **)&match_p)) {
706 /* none other found */
707 return LY_SUCCESS;
708 }
709 }
710
711 return LY_EEXIST;
712 }
713
714 /* not found */
715 return LY_SUCCESS;
716}
717
Michal Vaskod3678892020-05-21 10:06:58 +0200718void
719lyxp_set_free_content(struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200720{
721 if (!set) {
722 return;
723 }
724
725 if (set->type == LYXP_SET_NODE_SET) {
726 free(set->val.nodes);
727 lyht_free(set->ht);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200728 } else if (set->type == LYXP_SET_SCNODE_SET) {
729 free(set->val.scnodes);
Michal Vaskod3678892020-05-21 10:06:58 +0200730 lyht_free(set->ht);
731 } else {
732 if (set->type == LYXP_SET_STRING) {
733 free(set->val.str);
734 }
735 set->type = LYXP_SET_NODE_SET;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200736 }
Michal Vaskod3678892020-05-21 10:06:58 +0200737
738 set->val.nodes = NULL;
739 set->used = 0;
740 set->size = 0;
741 set->ht = NULL;
742 set->ctx_pos = 0;
743 set->ctx_pos = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200744}
745
Michal Vasko5e0e6eb2019-11-06 15:47:50 +0100746/**
747 * @brief Free dynamically-allocated set.
748 *
749 * @param[in] set Set to free.
750 */
751static void
Michal Vasko03ff5a72019-09-11 13:49:33 +0200752lyxp_set_free(struct lyxp_set *set)
753{
754 if (!set) {
755 return;
756 }
757
Michal Vaskod3678892020-05-21 10:06:58 +0200758 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200759 free(set);
760}
761
762/**
763 * @brief Initialize set context.
764 *
765 * @param[in] new Set to initialize.
766 * @param[in] set Arbitrary initialized set.
767 */
768static void
Michal Vasko4c7763f2020-07-27 17:40:37 +0200769set_init(struct lyxp_set *new, const struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200770{
771 memset(new, 0, sizeof *new);
Michal Vasko02a77382019-09-12 11:47:35 +0200772 if (set) {
773 new->ctx = set->ctx;
774 new->ctx_node = set->ctx_node;
Michal Vasko588112f2019-12-10 14:51:53 +0100775 new->root_type = set->root_type;
Michal Vasko6b26e742020-07-17 15:02:10 +0200776 new->context_op = set->context_op;
Michal Vasko02a77382019-09-12 11:47:35 +0200777 new->local_mod = set->local_mod;
Michal Vaskof03ed032020-03-04 13:31:44 +0100778 new->tree = set->tree;
Michal Vasko02a77382019-09-12 11:47:35 +0200779 new->format = set->format;
780 }
Michal Vasko03ff5a72019-09-11 13:49:33 +0200781}
782
783/**
784 * @brief Create a deep copy of a set.
785 *
786 * @param[in] set Set to copy.
787 * @return Copy of @p set.
788 */
789static struct lyxp_set *
790set_copy(struct lyxp_set *set)
791{
792 struct lyxp_set *ret;
793 uint16_t i;
Michal Vaskoba716542019-12-16 10:01:58 +0100794 int idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200795
796 if (!set) {
797 return NULL;
798 }
799
800 ret = malloc(sizeof *ret);
801 LY_CHECK_ERR_RET(!ret, LOGMEM(set->ctx), NULL);
802 set_init(ret, set);
803
804 if (set->type == LYXP_SET_SCNODE_SET) {
805 ret->type = set->type;
806
807 for (i = 0; i < set->used; ++i) {
Michal Vaskoba716542019-12-16 10:01:58 +0100808 if ((set->val.scnodes[i].in_ctx == 1) || (set->val.scnodes[i].in_ctx == -2)) {
809 idx = lyxp_set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type);
Michal Vasko3f27c522020-01-06 08:37:49 +0100810 /* coverity seems to think scnodes can be NULL */
811 if ((idx == -1) || !ret->val.scnodes) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200812 lyxp_set_free(ret);
813 return NULL;
814 }
Michal Vaskoba716542019-12-16 10:01:58 +0100815 ret->val.scnodes[idx].in_ctx = set->val.scnodes[i].in_ctx;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200816 }
817 }
818 } else if (set->type == LYXP_SET_NODE_SET) {
819 ret->type = set->type;
820 ret->val.nodes = malloc(set->used * sizeof *ret->val.nodes);
821 LY_CHECK_ERR_RET(!ret->val.nodes, LOGMEM(set->ctx); free(ret), NULL);
822 memcpy(ret->val.nodes, set->val.nodes, set->used * sizeof *ret->val.nodes);
823
824 ret->used = ret->size = set->used;
825 ret->ctx_pos = set->ctx_pos;
826 ret->ctx_size = set->ctx_size;
827 ret->ht = lyht_dup(set->ht);
828 } else {
829 memcpy(ret, set, sizeof *ret);
830 if (set->type == LYXP_SET_STRING) {
831 ret->val.str = strdup(set->val.str);
832 LY_CHECK_ERR_RET(!ret->val.str, LOGMEM(set->ctx); free(ret), NULL);
833 }
834 }
835
836 return ret;
837}
838
839/**
840 * @brief Fill XPath set with a string. Any current data are disposed of.
841 *
842 * @param[in] set Set to fill.
843 * @param[in] string String to fill into \p set.
844 * @param[in] str_len Length of \p string. 0 is a valid value!
845 */
846static void
847set_fill_string(struct lyxp_set *set, const char *string, uint16_t str_len)
848{
Michal Vaskod3678892020-05-21 10:06:58 +0200849 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200850
851 set->type = LYXP_SET_STRING;
852 if ((str_len == 0) && (string[0] != '\0')) {
853 string = "";
854 }
855 set->val.str = strndup(string, str_len);
856}
857
858/**
859 * @brief Fill XPath set with a number. Any current data are disposed of.
860 *
861 * @param[in] set Set to fill.
862 * @param[in] number Number to fill into \p set.
863 */
864static void
865set_fill_number(struct lyxp_set *set, long double number)
866{
Michal Vaskod3678892020-05-21 10:06:58 +0200867 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200868
869 set->type = LYXP_SET_NUMBER;
870 set->val.num = number;
871}
872
873/**
874 * @brief Fill XPath set with a boolean. Any current data are disposed of.
875 *
876 * @param[in] set Set to fill.
877 * @param[in] boolean Boolean to fill into \p set.
878 */
879static void
880set_fill_boolean(struct lyxp_set *set, int boolean)
881{
Michal Vaskod3678892020-05-21 10:06:58 +0200882 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200883
884 set->type = LYXP_SET_BOOLEAN;
Michal Vasko004d3152020-06-11 19:59:22 +0200885 set->val.bln = boolean;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200886}
887
888/**
889 * @brief Fill XPath set with the value from another set (deep assign).
890 * Any current data are disposed of.
891 *
892 * @param[in] trg Set to fill.
893 * @param[in] src Source set to copy into \p trg.
894 */
895static void
Michal Vasko4c7763f2020-07-27 17:40:37 +0200896set_fill_set(struct lyxp_set *trg, const struct lyxp_set *src)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200897{
898 if (!trg || !src) {
899 return;
900 }
901
902 if (trg->type == LYXP_SET_NODE_SET) {
903 free(trg->val.nodes);
904 } else if (trg->type == LYXP_SET_STRING) {
905 free(trg->val.str);
906 }
907 set_init(trg, src);
908
909 if (src->type == LYXP_SET_SCNODE_SET) {
910 trg->type = LYXP_SET_SCNODE_SET;
911 trg->used = src->used;
912 trg->size = src->used;
913
914 trg->val.scnodes = ly_realloc(trg->val.scnodes, trg->size * sizeof *trg->val.scnodes);
915 LY_CHECK_ERR_RET(!trg->val.scnodes, LOGMEM(src->ctx); memset(trg, 0, sizeof *trg), );
916 memcpy(trg->val.scnodes, src->val.scnodes, src->used * sizeof *src->val.scnodes);
917 } else if (src->type == LYXP_SET_BOOLEAN) {
Michal Vasko004d3152020-06-11 19:59:22 +0200918 set_fill_boolean(trg, src->val.bln);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200919 } else if (src->type == LYXP_SET_NUMBER) {
920 set_fill_number(trg, src->val.num);
921 } else if (src->type == LYXP_SET_STRING) {
922 set_fill_string(trg, src->val.str, strlen(src->val.str));
923 } else {
924 if (trg->type == LYXP_SET_NODE_SET) {
925 free(trg->val.nodes);
926 } else if (trg->type == LYXP_SET_STRING) {
927 free(trg->val.str);
928 }
929
Michal Vaskod3678892020-05-21 10:06:58 +0200930 assert(src->type == LYXP_SET_NODE_SET);
931
932 trg->type = LYXP_SET_NODE_SET;
933 trg->used = src->used;
934 trg->size = src->used;
935 trg->ctx_pos = src->ctx_pos;
936 trg->ctx_size = src->ctx_size;
937
938 trg->val.nodes = malloc(trg->used * sizeof *trg->val.nodes);
939 LY_CHECK_ERR_RET(!trg->val.nodes, LOGMEM(src->ctx); memset(trg, 0, sizeof *trg), );
940 memcpy(trg->val.nodes, src->val.nodes, src->used * sizeof *src->val.nodes);
941 if (src->ht) {
942 trg->ht = lyht_dup(src->ht);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200943 } else {
Michal Vaskod3678892020-05-21 10:06:58 +0200944 trg->ht = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200945 }
946 }
947}
948
949/**
950 * @brief Clear context of all schema nodes.
951 *
952 * @param[in] set Set to clear.
953 */
954static void
955set_scnode_clear_ctx(struct lyxp_set *set)
956{
957 uint32_t i;
958
959 for (i = 0; i < set->used; ++i) {
960 if (set->val.scnodes[i].in_ctx == 1) {
961 set->val.scnodes[i].in_ctx = 0;
Michal Vasko5c4e5892019-11-14 12:31:38 +0100962 } else if (set->val.scnodes[i].in_ctx == -2) {
963 set->val.scnodes[i].in_ctx = -1;
Michal Vasko03ff5a72019-09-11 13:49:33 +0200964 }
965 }
966}
967
968/**
969 * @brief Remove a node from a set. Removing last node changes
970 * set into LYXP_SET_EMPTY. Context position aware.
971 *
972 * @param[in] set Set to use.
973 * @param[in] idx Index from @p set of the node to be removed.
974 */
975static void
976set_remove_node(struct lyxp_set *set, uint32_t idx)
977{
978 assert(set && (set->type == LYXP_SET_NODE_SET));
979 assert(idx < set->used);
980
981 set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
982
983 --set->used;
984 if (set->used) {
985 memmove(&set->val.nodes[idx], &set->val.nodes[idx + 1],
986 (set->used - idx) * sizeof *set->val.nodes);
987 } else {
Michal Vaskod3678892020-05-21 10:06:58 +0200988 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +0200989 }
990}
991
992/**
Michal Vasko2caefc12019-11-14 16:07:56 +0100993 * @brief Remove a node from a set by setting its type to LYXP_NODE_NONE.
Michal Vasko57eab132019-09-24 11:46:26 +0200994 *
995 * @param[in] set Set to use.
996 * @param[in] idx Index from @p set of the node to be removed.
997 */
998static void
Michal Vasko2caefc12019-11-14 16:07:56 +0100999set_remove_node_none(struct lyxp_set *set, uint32_t idx)
Michal Vasko57eab132019-09-24 11:46:26 +02001000{
1001 assert(set && (set->type == LYXP_SET_NODE_SET));
1002 assert(idx < set->used);
1003
Michal Vasko2caefc12019-11-14 16:07:56 +01001004 if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
1005 set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
1006 }
1007 set->val.nodes[idx].type = LYXP_NODE_NONE;
Michal Vasko57eab132019-09-24 11:46:26 +02001008}
1009
1010/**
Michal Vasko2caefc12019-11-14 16:07:56 +01001011 * @brief Remove all LYXP_NODE_NONE nodes from a set. Removing last node changes
Michal Vasko57eab132019-09-24 11:46:26 +02001012 * set into LYXP_SET_EMPTY. Context position aware.
1013 *
1014 * @param[in] set Set to consolidate.
1015 */
1016static void
Michal Vasko2caefc12019-11-14 16:07:56 +01001017set_remove_nodes_none(struct lyxp_set *set)
Michal Vasko57eab132019-09-24 11:46:26 +02001018{
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02001019 uint16_t i, orig_used, end = 0;
Michal Vasko57eab132019-09-24 11:46:26 +02001020 int32_t start;
1021
Michal Vaskod3678892020-05-21 10:06:58 +02001022 assert(set);
Michal Vasko57eab132019-09-24 11:46:26 +02001023
1024 orig_used = set->used;
1025 set->used = 0;
1026 for (i = 0; i < orig_used;) {
1027 start = -1;
1028 do {
Michal Vasko2caefc12019-11-14 16:07:56 +01001029 if ((set->val.nodes[i].type != LYXP_NODE_NONE) && (start == -1)) {
Michal Vasko57eab132019-09-24 11:46:26 +02001030 start = i;
Michal Vasko2caefc12019-11-14 16:07:56 +01001031 } else if ((start > -1) && (set->val.nodes[i].type == LYXP_NODE_NONE)) {
Michal Vasko57eab132019-09-24 11:46:26 +02001032 end = i;
1033 ++i;
1034 break;
1035 }
1036
1037 ++i;
1038 if (i == orig_used) {
1039 end = i;
1040 }
1041 } while (i < orig_used);
1042
1043 if (start > -1) {
1044 /* move the whole chunk of valid nodes together */
1045 if (set->used != (unsigned)start) {
1046 memmove(&set->val.nodes[set->used], &set->val.nodes[start], (end - start) * sizeof *set->val.nodes);
1047 }
1048 set->used += end - start;
1049 }
1050 }
Michal Vasko57eab132019-09-24 11:46:26 +02001051}
1052
1053/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02001054 * @brief Check for duplicates in a node set.
1055 *
1056 * @param[in] set Set to check.
1057 * @param[in] node Node to look for in @p set.
1058 * @param[in] node_type Type of @p node.
1059 * @param[in] skip_idx Index from @p set to skip.
1060 * @return LY_ERR
1061 */
1062static LY_ERR
1063set_dup_node_check(const struct lyxp_set *set, const struct lyd_node *node, enum lyxp_node_type node_type, int skip_idx)
1064{
1065 uint32_t i;
1066
Michal Vasko2caefc12019-11-14 16:07:56 +01001067 if (set->ht && node) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001068 return set_dup_node_hash_check(set, (struct lyd_node *)node, node_type, skip_idx);
1069 }
1070
1071 for (i = 0; i < set->used; ++i) {
1072 if ((skip_idx > -1) && (i == (unsigned)skip_idx)) {
1073 continue;
1074 }
1075
1076 if ((set->val.nodes[i].node == node) && (set->val.nodes[i].type == node_type)) {
1077 return LY_EEXIST;
1078 }
1079 }
1080
1081 return LY_SUCCESS;
1082}
1083
Michal Vaskoecd62de2019-11-13 12:35:11 +01001084int
1085lyxp_set_scnode_dup_node_check(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type, int skip_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001086{
1087 uint32_t i;
1088
1089 for (i = 0; i < set->used; ++i) {
1090 if ((skip_idx > -1) && (i == (unsigned)skip_idx)) {
1091 continue;
1092 }
1093
1094 if ((set->val.scnodes[i].scnode == node) && (set->val.scnodes[i].type == node_type)) {
1095 return i;
1096 }
1097 }
1098
1099 return -1;
1100}
1101
Michal Vaskoecd62de2019-11-13 12:35:11 +01001102void
1103lyxp_set_scnode_merge(struct lyxp_set *set1, struct lyxp_set *set2)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001104{
1105 uint32_t orig_used, i, j;
1106
Michal Vaskod3678892020-05-21 10:06:58 +02001107 assert((set1->type == LYXP_SET_SCNODE_SET) && (set2->type == LYXP_SET_SCNODE_SET));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001108
Michal Vaskod3678892020-05-21 10:06:58 +02001109 if (!set2->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001110 return;
1111 }
1112
Michal Vaskod3678892020-05-21 10:06:58 +02001113 if (!set1->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001114 memcpy(set1, set2, sizeof *set1);
1115 return;
1116 }
1117
1118 if (set1->used + set2->used > set1->size) {
1119 set1->size = set1->used + set2->used;
1120 set1->val.scnodes = ly_realloc(set1->val.scnodes, set1->size * sizeof *set1->val.scnodes);
1121 LY_CHECK_ERR_RET(!set1->val.scnodes, LOGMEM(set1->ctx), );
1122 }
1123
1124 orig_used = set1->used;
1125
1126 for (i = 0; i < set2->used; ++i) {
1127 for (j = 0; j < orig_used; ++j) {
1128 /* detect duplicities */
1129 if (set1->val.scnodes[j].scnode == set2->val.scnodes[i].scnode) {
1130 break;
1131 }
1132 }
1133
1134 if (j == orig_used) {
1135 memcpy(&set1->val.scnodes[set1->used], &set2->val.scnodes[i], sizeof *set2->val.scnodes);
1136 ++set1->used;
1137 }
1138 }
1139
Michal Vaskod3678892020-05-21 10:06:58 +02001140 lyxp_set_free_content(set2);
1141 set2->type = LYXP_SET_SCNODE_SET;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001142}
1143
1144/**
1145 * @brief Insert a node into a set. Context position aware.
1146 *
1147 * @param[in] set Set to use.
1148 * @param[in] node Node to insert to @p set.
1149 * @param[in] pos Sort position of @p node. If left 0, it is filled just before sorting.
1150 * @param[in] node_type Node type of @p node.
1151 * @param[in] idx Index in @p set to insert into.
1152 */
1153static void
1154set_insert_node(struct lyxp_set *set, const struct lyd_node *node, uint32_t pos, enum lyxp_node_type node_type, uint32_t idx)
1155{
Michal Vaskod3678892020-05-21 10:06:58 +02001156 assert(set && (set->type == LYXP_SET_NODE_SET));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001157
Michal Vaskod3678892020-05-21 10:06:58 +02001158 if (!set->size) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001159 /* first item */
1160 if (idx) {
1161 /* no real harm done, but it is a bug */
1162 LOGINT(set->ctx);
1163 idx = 0;
1164 }
1165 set->val.nodes = malloc(LYXP_SET_SIZE_START * sizeof *set->val.nodes);
1166 LY_CHECK_ERR_RET(!set->val.nodes, LOGMEM(set->ctx), );
1167 set->type = LYXP_SET_NODE_SET;
1168 set->used = 0;
1169 set->size = LYXP_SET_SIZE_START;
1170 set->ctx_pos = 1;
1171 set->ctx_size = 1;
1172 set->ht = NULL;
1173 } else {
1174 /* not an empty set */
1175 if (set->used == set->size) {
1176
1177 /* set is full */
1178 set->val.nodes = ly_realloc(set->val.nodes, (set->size + LYXP_SET_SIZE_STEP) * sizeof *set->val.nodes);
1179 LY_CHECK_ERR_RET(!set->val.nodes, LOGMEM(set->ctx), );
1180 set->size += LYXP_SET_SIZE_STEP;
1181 }
1182
1183 if (idx > set->used) {
1184 LOGINT(set->ctx);
1185 idx = set->used;
1186 }
1187
1188 /* make space for the new node */
1189 if (idx < set->used) {
1190 memmove(&set->val.nodes[idx + 1], &set->val.nodes[idx], (set->used - idx) * sizeof *set->val.nodes);
1191 }
1192 }
1193
1194 /* finally assign the value */
1195 set->val.nodes[idx].node = (struct lyd_node *)node;
1196 set->val.nodes[idx].type = node_type;
1197 set->val.nodes[idx].pos = pos;
1198 ++set->used;
1199
Michal Vasko2caefc12019-11-14 16:07:56 +01001200 if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
1201 set_insert_node_hash(set, (struct lyd_node *)node, node_type);
1202 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02001203}
1204
Michal Vaskoecd62de2019-11-13 12:35:11 +01001205int
1206lyxp_set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001207{
1208 int ret;
1209
1210 assert(set->type == LYXP_SET_SCNODE_SET);
1211
Michal Vaskoecd62de2019-11-13 12:35:11 +01001212 ret = lyxp_set_scnode_dup_node_check(set, node, node_type, -1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001213 if (ret > -1) {
1214 set->val.scnodes[ret].in_ctx = 1;
1215 } else {
1216 if (set->used == set->size) {
1217 set->val.scnodes = ly_realloc(set->val.scnodes, (set->size + LYXP_SET_SIZE_STEP) * sizeof *set->val.scnodes);
1218 LY_CHECK_ERR_RET(!set->val.scnodes, LOGMEM(set->ctx), -1);
1219 set->size += LYXP_SET_SIZE_STEP;
1220 }
1221
1222 ret = set->used;
1223 set->val.scnodes[ret].scnode = (struct lysc_node *)node;
1224 set->val.scnodes[ret].type = node_type;
1225 set->val.scnodes[ret].in_ctx = 1;
1226 ++set->used;
1227 }
1228
1229 return ret;
1230}
1231
1232/**
1233 * @brief Replace a node in a set with another. Context position aware.
1234 *
1235 * @param[in] set Set to use.
1236 * @param[in] node Node to insert to @p set.
1237 * @param[in] pos Sort position of @p node. If left 0, it is filled just before sorting.
1238 * @param[in] node_type Node type of @p node.
1239 * @param[in] idx Index in @p set of the node to replace.
1240 */
1241static void
1242set_replace_node(struct lyxp_set *set, const struct lyd_node *node, uint32_t pos, enum lyxp_node_type node_type, uint32_t idx)
1243{
1244 assert(set && (idx < set->used));
1245
Michal Vasko2caefc12019-11-14 16:07:56 +01001246 if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
1247 set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
1248 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02001249 set->val.nodes[idx].node = (struct lyd_node *)node;
1250 set->val.nodes[idx].type = node_type;
1251 set->val.nodes[idx].pos = pos;
Michal Vasko2caefc12019-11-14 16:07:56 +01001252 if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
1253 set_insert_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
1254 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02001255}
1256
1257/**
1258 * @brief Set all nodes with ctx 1 to a new unique context value.
1259 *
1260 * @param[in] set Set to modify.
1261 * @return New context value.
1262 */
Michal Vasko5c4e5892019-11-14 12:31:38 +01001263static int32_t
Michal Vasko03ff5a72019-09-11 13:49:33 +02001264set_scnode_new_in_ctx(struct lyxp_set *set)
1265{
Michal Vasko5c4e5892019-11-14 12:31:38 +01001266 uint32_t i;
1267 int32_t ret_ctx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001268
1269 assert(set->type == LYXP_SET_SCNODE_SET);
1270
1271 ret_ctx = 3;
1272retry:
1273 for (i = 0; i < set->used; ++i) {
1274 if (set->val.scnodes[i].in_ctx >= ret_ctx) {
1275 ret_ctx = set->val.scnodes[i].in_ctx + 1;
1276 goto retry;
1277 }
1278 }
1279 for (i = 0; i < set->used; ++i) {
1280 if (set->val.scnodes[i].in_ctx == 1) {
1281 set->val.scnodes[i].in_ctx = ret_ctx;
1282 }
1283 }
1284
1285 return ret_ctx;
1286}
1287
1288/**
1289 * @brief Get unique @p node position in the data.
1290 *
1291 * @param[in] node Node to find.
1292 * @param[in] node_type Node type of @p node.
1293 * @param[in] root Root node.
1294 * @param[in] root_type Type of the XPath @p root node.
1295 * @param[in] prev Node that we think is before @p node in DFS from @p root. Can optionally
1296 * be used to increase efficiency and start the DFS from this node.
1297 * @param[in] prev_pos Node @p prev position. Optional, but must be set if @p prev is set.
1298 * @return Node position.
1299 */
1300static uint32_t
1301get_node_pos(const struct lyd_node *node, enum lyxp_node_type node_type, const struct lyd_node *root,
1302 enum lyxp_node_type root_type, const struct lyd_node **prev, uint32_t *prev_pos)
1303{
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02001304 const struct lyd_node *next, *elem = NULL, *top_sibling;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001305 uint32_t pos = 1;
1306
1307 assert(prev && prev_pos && !root->prev->next);
1308
1309 if ((node_type == LYXP_NODE_ROOT) || (node_type == LYXP_NODE_ROOT_CONFIG)) {
1310 return 0;
1311 }
1312
1313 if (*prev) {
1314 /* start from the previous element instead from the root */
1315 elem = next = *prev;
1316 pos = *prev_pos;
1317 for (top_sibling = elem; top_sibling->parent; top_sibling = (struct lyd_node *)top_sibling->parent);
1318 goto dfs_search;
1319 }
1320
1321 for (top_sibling = root; top_sibling; top_sibling = top_sibling->next) {
1322 /* TREE DFS */
1323 LYD_TREE_DFS_BEGIN(top_sibling, next, elem) {
1324dfs_search:
1325 if ((root_type == LYXP_NODE_ROOT_CONFIG) && (elem->schema->flags & LYS_CONFIG_R)) {
1326 goto skip_children;
1327 }
1328
1329 if (elem == node) {
1330 break;
1331 }
1332 ++pos;
1333
1334 /* TREE DFS END */
1335 /* select element for the next run - children first,
1336 * child exception for lyd_node_leaf and lyd_node_leaflist, but not the root */
1337 if (elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
1338 next = NULL;
1339 } else {
Michal Vasko5bfd4be2020-06-23 13:26:19 +02001340 next = lyd_node_children(elem, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001341 }
1342 if (!next) {
1343skip_children:
1344 /* no children */
1345 if (elem == top_sibling) {
1346 /* we are done, root has no children */
1347 elem = NULL;
1348 break;
1349 }
1350 /* try siblings */
1351 next = elem->next;
1352 }
1353 while (!next) {
1354 /* no siblings, go back through parents */
1355 if (elem->parent == top_sibling->parent) {
1356 /* we are done, no next element to process */
1357 elem = NULL;
1358 break;
1359 }
1360 /* parent is already processed, go to its sibling */
1361 elem = (struct lyd_node *)elem->parent;
1362 next = elem->next;
1363 }
1364 }
1365
1366 /* node found */
1367 if (elem) {
1368 break;
1369 }
1370 }
1371
1372 if (!elem) {
1373 if (!(*prev)) {
1374 /* we went from root and failed to find it, cannot be */
1375 LOGINT(node->schema->module->ctx);
1376 return 0;
1377 } else {
1378 *prev = NULL;
1379 *prev_pos = 0;
1380
1381 elem = next = top_sibling = root;
1382 pos = 1;
1383 goto dfs_search;
1384 }
1385 }
1386
1387 /* remember the last found node for next time */
1388 *prev = node;
1389 *prev_pos = pos;
1390
1391 return pos;
1392}
1393
1394/**
1395 * @brief Assign (fill) missing node positions.
1396 *
1397 * @param[in] set Set to fill positions in.
1398 * @param[in] root Context root node.
1399 * @param[in] root_type Context root type.
1400 * @return LY_ERR
1401 */
1402static LY_ERR
1403set_assign_pos(struct lyxp_set *set, const struct lyd_node *root, enum lyxp_node_type root_type)
1404{
1405 const struct lyd_node *prev = NULL, *tmp_node;
1406 uint32_t i, tmp_pos = 0;
1407
1408 for (i = 0; i < set->used; ++i) {
1409 if (!set->val.nodes[i].pos) {
1410 tmp_node = NULL;
1411 switch (set->val.nodes[i].type) {
Michal Vasko9f96a052020-03-10 09:41:45 +01001412 case LYXP_NODE_META:
1413 tmp_node = set->val.meta[i].meta->parent;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001414 if (!tmp_node) {
1415 LOGINT_RET(root->schema->module->ctx);
1416 }
1417 /* fallthrough */
1418 case LYXP_NODE_ELEM:
1419 case LYXP_NODE_TEXT:
1420 if (!tmp_node) {
1421 tmp_node = set->val.nodes[i].node;
1422 }
1423 set->val.nodes[i].pos = get_node_pos(tmp_node, set->val.nodes[i].type, root, root_type, &prev, &tmp_pos);
1424 break;
1425 default:
1426 /* all roots have position 0 */
1427 break;
1428 }
1429 }
1430 }
1431
1432 return LY_SUCCESS;
1433}
1434
1435/**
Michal Vasko9f96a052020-03-10 09:41:45 +01001436 * @brief Get unique @p meta position in the parent metadata.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001437 *
Michal Vasko9f96a052020-03-10 09:41:45 +01001438 * @param[in] meta Metadata to use.
1439 * @return Metadata position.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001440 */
1441static uint16_t
Michal Vasko9f96a052020-03-10 09:41:45 +01001442get_meta_pos(struct lyd_meta *meta)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001443{
1444 uint16_t pos = 0;
Michal Vasko9f96a052020-03-10 09:41:45 +01001445 struct lyd_meta *meta2;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001446
Michal Vasko9f96a052020-03-10 09:41:45 +01001447 for (meta2 = meta->parent->meta; meta2 && (meta2 != meta); meta2 = meta2->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001448 ++pos;
1449 }
1450
Michal Vasko9f96a052020-03-10 09:41:45 +01001451 assert(meta2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001452 return pos;
1453}
1454
1455/**
1456 * @brief Compare 2 nodes in respect to XPath document order.
1457 *
1458 * @param[in] item1 1st node.
1459 * @param[in] item2 2nd node.
1460 * @return If 1st > 2nd returns 1, 1st == 2nd returns 0, and 1st < 2nd returns -1.
1461 */
1462static int
1463set_sort_compare(struct lyxp_set_node *item1, struct lyxp_set_node *item2)
1464{
Michal Vasko9f96a052020-03-10 09:41:45 +01001465 uint32_t meta_pos1 = 0, meta_pos2 = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001466
1467 if (item1->pos < item2->pos) {
1468 return -1;
1469 }
1470
1471 if (item1->pos > item2->pos) {
1472 return 1;
1473 }
1474
1475 /* node positions are equal, the fun case */
1476
1477 /* 1st ELEM - == - 2nd TEXT, 1st TEXT - == - 2nd ELEM */
1478 /* special case since text nodes are actually saved as their parents */
1479 if ((item1->node == item2->node) && (item1->type != item2->type)) {
1480 if (item1->type == LYXP_NODE_ELEM) {
1481 assert(item2->type == LYXP_NODE_TEXT);
1482 return -1;
1483 } else {
1484 assert((item1->type == LYXP_NODE_TEXT) && (item2->type == LYXP_NODE_ELEM));
1485 return 1;
1486 }
1487 }
1488
Michal Vasko9f96a052020-03-10 09:41:45 +01001489 /* we need meta positions now */
1490 if (item1->type == LYXP_NODE_META) {
1491 meta_pos1 = get_meta_pos((struct lyd_meta *)item1->node);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001492 }
Michal Vasko9f96a052020-03-10 09:41:45 +01001493 if (item2->type == LYXP_NODE_META) {
1494 meta_pos2 = get_meta_pos((struct lyd_meta *)item2->node);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001495 }
1496
Michal Vasko9f96a052020-03-10 09:41:45 +01001497 /* 1st ROOT - 2nd ROOT, 1st ELEM - 2nd ELEM, 1st TEXT - 2nd TEXT, 1st META - =pos= - 2nd META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001498 /* check for duplicates */
1499 if (item1->node == item2->node) {
Michal Vasko9f96a052020-03-10 09:41:45 +01001500 assert((item1->type == item2->type) && ((item1->type != LYXP_NODE_META) || (meta_pos1 == meta_pos2)));
Michal Vasko03ff5a72019-09-11 13:49:33 +02001501 return 0;
1502 }
1503
Michal Vasko9f96a052020-03-10 09:41:45 +01001504 /* 1st ELEM - 2nd TEXT, 1st ELEM - any pos - 2nd META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001505 /* elem is always first, 2nd node is after it */
1506 if (item1->type == LYXP_NODE_ELEM) {
1507 assert(item2->type != LYXP_NODE_ELEM);
1508 return -1;
1509 }
1510
Michal Vasko9f96a052020-03-10 09:41:45 +01001511 /* 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 +02001512 /* 2nd is before 1st */
1513 if (((item1->type == LYXP_NODE_TEXT)
Michal Vasko9f96a052020-03-10 09:41:45 +01001514 && ((item2->type == LYXP_NODE_ELEM) || (item2->type == LYXP_NODE_META)))
1515 || ((item1->type == LYXP_NODE_META) && (item2->type == LYXP_NODE_ELEM))
1516 || (((item1->type == LYXP_NODE_META) && (item2->type == LYXP_NODE_META))
1517 && (meta_pos1 > meta_pos2))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001518 return 1;
1519 }
1520
Michal Vasko9f96a052020-03-10 09:41:45 +01001521 /* 1st META - any pos - 2nd TEXT, 1st META <pos< - 2nd META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02001522 /* 2nd is after 1st */
1523 return -1;
1524}
1525
1526/**
1527 * @brief Set cast for comparisons.
1528 *
1529 * @param[in] trg Target set to cast source into.
1530 * @param[in] src Source set.
1531 * @param[in] type Target set type.
1532 * @param[in] src_idx Source set node index.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001533 * @return LY_ERR
1534 */
1535static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001536set_comp_cast(struct lyxp_set *trg, struct lyxp_set *src, enum lyxp_set_type type, uint32_t src_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001537{
1538 assert(src->type == LYXP_SET_NODE_SET);
1539
1540 set_init(trg, src);
1541
1542 /* insert node into target set */
1543 set_insert_node(trg, src->val.nodes[src_idx].node, src->val.nodes[src_idx].pos, src->val.nodes[src_idx].type, 0);
1544
1545 /* cast target set appropriately */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001546 return lyxp_set_cast(trg, type);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001547}
1548
Michal Vasko4c7763f2020-07-27 17:40:37 +02001549/**
1550 * @brief Set content canonization for comparisons.
1551 *
1552 * @param[in] trg Target set to put the canononized source into.
1553 * @param[in] src Source set.
1554 * @param[in] xp_node Source XPath node/meta to use for canonization.
1555 * @return LY_ERR
1556 */
1557static LY_ERR
1558set_comp_canonize(struct lyxp_set *trg, const struct lyxp_set *src, const struct lyxp_set_node *xp_node)
1559{
1560 struct lysc_type *type = NULL;
1561 struct lyd_value val;
1562 struct ly_err_item *err = NULL;
1563 char *str, *ptr;
1564 int dynamic;
1565 LY_ERR rc;
1566
1567 /* is there anything to canonize even? */
1568 if ((src->type == LYXP_SET_NUMBER) || (src->type == LYXP_SET_STRING)) {
1569 /* do we have a type to use for canonization? */
1570 if ((xp_node->type == LYXP_NODE_ELEM) && (xp_node->node->schema->nodetype & LYD_NODE_TERM)) {
1571 type = ((struct lyd_node_term *)xp_node->node)->value.realtype;
1572 } else if (xp_node->type == LYXP_NODE_META) {
1573 type = ((struct lyd_meta *)xp_node->node)->value.realtype;
1574 }
1575 }
1576 if (!type) {
1577 goto fill;
1578 }
1579
1580 if (src->type == LYXP_SET_NUMBER) {
1581 /* canonize number */
1582 if (asprintf(&str, "%Lf", src->val.num) == -1) {
1583 LOGMEM(src->ctx);
1584 return LY_EMEM;
1585 }
1586 } else {
1587 /* canonize string */
1588 str = strdup(src->val.str);
1589 }
1590
1591 /* ignore errors, the value may not satisfy schema constraints */
1592 rc = type->plugin->store(src->ctx, type, str, strlen(str),
1593 LY_TYPE_OPTS_STORE | LY_TYPE_OPTS_INCOMPLETE_DATA | LY_TYPE_OPTS_DYNAMIC, NULL,
1594 NULL, LYD_JSON, NULL, NULL, &val, NULL, &err);
1595 ly_err_free(err);
1596 if (rc) {
1597 /* invalid value */
1598 free(str);
1599 goto fill;
1600 }
1601
1602 /* storing successful, now print the canonical value */
1603 str = (char *)type->plugin->print(&val, LYD_JSON, json_print_get_prefix, NULL, &dynamic);
1604
1605 /* use the canonized value */
1606 set_init(trg, src);
1607 trg->type = src->type;
1608 if (src->type == LYXP_SET_NUMBER) {
1609 trg->val.num = strtold(str, &ptr);
1610 if (dynamic) {
1611 free(str);
1612 }
1613 LY_CHECK_ERR_RET(ptr[0], LOGINT(src->ctx), LY_EINT);
1614 } else {
1615 trg->val.str = (dynamic ? str : strdup(str));
1616 }
1617 type->plugin->free(src->ctx, &val);
1618 return LY_SUCCESS;
1619
1620fill:
1621 /* no canonization needed/possible */
1622 set_fill_set(trg, src);
1623 return LY_SUCCESS;
1624}
1625
Michal Vasko03ff5a72019-09-11 13:49:33 +02001626#ifndef NDEBUG
1627
1628/**
1629 * @brief Bubble sort @p set into XPath document order.
1630 * Context position aware. Unused in the 'Release' build target.
1631 *
1632 * @param[in] set Set to sort.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001633 * @return How many times the whole set was traversed - 1 (if set was sorted, returns 0).
1634 */
1635static int
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001636set_sort(struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001637{
1638 uint32_t i, j;
1639 int ret = 0, cmp, inverted, change;
1640 const struct lyd_node *root;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001641 struct lyxp_set_node item;
1642 struct lyxp_set_hash_node hnode;
1643 uint64_t hash;
1644
Michal Vasko3cf8fbf2020-05-27 15:21:21 +02001645 if ((set->type != LYXP_SET_NODE_SET) || (set->used < 2)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001646 return 0;
1647 }
1648
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001649 /* find first top-level node to be used as anchor for positions */
1650 for (root = set->ctx_node; root->parent; root = (const struct lyd_node *)root->parent);
1651 for (; root->prev->next; root = root->prev);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001652
1653 /* fill positions */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001654 if (set_assign_pos(set, root, set->root_type)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001655 return -1;
1656 }
1657
1658 LOGDBG(LY_LDGXPATH, "SORT BEGIN");
1659 print_set_debug(set);
1660
1661 for (i = 0; i < set->used; ++i) {
1662 inverted = 0;
1663 change = 0;
1664
1665 for (j = 1; j < set->used - i; ++j) {
1666 /* compare node positions */
1667 if (inverted) {
1668 cmp = set_sort_compare(&set->val.nodes[j], &set->val.nodes[j - 1]);
1669 } else {
1670 cmp = set_sort_compare(&set->val.nodes[j - 1], &set->val.nodes[j]);
1671 }
1672
1673 /* swap if needed */
1674 if ((inverted && (cmp < 0)) || (!inverted && (cmp > 0))) {
1675 change = 1;
1676
1677 item = set->val.nodes[j - 1];
1678 set->val.nodes[j - 1] = set->val.nodes[j];
1679 set->val.nodes[j] = item;
1680 } else {
1681 /* whether node_pos1 should be smaller than node_pos2 or the other way around */
1682 inverted = !inverted;
1683 }
1684 }
1685
1686 ++ret;
1687
1688 if (!change) {
1689 break;
1690 }
1691 }
1692
1693 LOGDBG(LY_LDGXPATH, "SORT END %d", ret);
1694 print_set_debug(set);
1695
1696 /* check node hashes */
1697 if (set->used >= LYD_HT_MIN_ITEMS) {
1698 assert(set->ht);
1699 for (i = 0; i < set->used; ++i) {
1700 hnode.node = set->val.nodes[i].node;
1701 hnode.type = set->val.nodes[i].type;
1702
1703 hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
1704 hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
1705 hash = dict_hash_multi(hash, NULL, 0);
1706
1707 assert(!lyht_find(set->ht, &hnode, hash, NULL));
1708 }
1709 }
1710
1711 return ret - 1;
1712}
1713
1714/**
1715 * @brief Remove duplicate entries in a sorted node set.
1716 *
1717 * @param[in] set Sorted set to check.
1718 * @return LY_ERR (LY_EEXIST if some duplicates are found)
1719 */
1720static LY_ERR
1721set_sorted_dup_node_clean(struct lyxp_set *set)
1722{
1723 uint32_t i = 0;
1724 LY_ERR ret = LY_SUCCESS;
1725
1726 if (set->used > 1) {
1727 while (i < set->used - 1) {
1728 if ((set->val.nodes[i].node == set->val.nodes[i + 1].node)
1729 && (set->val.nodes[i].type == set->val.nodes[i + 1].type)) {
Michal Vasko2caefc12019-11-14 16:07:56 +01001730 set_remove_node_none(set, i + 1);
Michal Vasko57eab132019-09-24 11:46:26 +02001731 ret = LY_EEXIST;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001732 }
Michal Vasko57eab132019-09-24 11:46:26 +02001733 ++i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001734 }
1735 }
1736
Michal Vasko2caefc12019-11-14 16:07:56 +01001737 set_remove_nodes_none(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001738 return ret;
1739}
1740
1741#endif
1742
1743/**
1744 * @brief Merge 2 sorted sets into one.
1745 *
1746 * @param[in,out] trg Set to merge into. Duplicates are removed.
1747 * @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 +02001748 * @return LY_ERR
1749 */
1750static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001751set_sorted_merge(struct lyxp_set *trg, struct lyxp_set *src)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001752{
1753 uint32_t i, j, k, count, dup_count;
1754 int cmp;
1755 const struct lyd_node *root;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001756
Michal Vaskod3678892020-05-21 10:06:58 +02001757 if ((trg->type != LYXP_SET_NODE_SET) || (src->type != LYXP_SET_NODE_SET)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001758 return LY_EINVAL;
1759 }
1760
Michal Vaskod3678892020-05-21 10:06:58 +02001761 if (!src->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001762 return LY_SUCCESS;
Michal Vaskod3678892020-05-21 10:06:58 +02001763 } else if (!trg->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001764 set_fill_set(trg, src);
Michal Vaskod3678892020-05-21 10:06:58 +02001765 lyxp_set_free_content(src);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001766 return LY_SUCCESS;
1767 }
1768
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001769 /* find first top-level node to be used as anchor for positions */
1770 for (root = trg->ctx_node; root->parent; root = (const struct lyd_node *)root->parent);
1771 for (; root->prev->next; root = root->prev);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001772
1773 /* fill positions */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01001774 if (set_assign_pos(trg, root, trg->root_type) || set_assign_pos(src, root, src->root_type)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001775 return LY_EINT;
1776 }
1777
1778#ifndef NDEBUG
1779 LOGDBG(LY_LDGXPATH, "MERGE target");
1780 print_set_debug(trg);
1781 LOGDBG(LY_LDGXPATH, "MERGE source");
1782 print_set_debug(src);
1783#endif
1784
1785 /* make memory for the merge (duplicates are not detected yet, so space
1786 * will likely be wasted on them, too bad) */
1787 if (trg->size - trg->used < src->used) {
1788 trg->size = trg->used + src->used;
1789
1790 trg->val.nodes = ly_realloc(trg->val.nodes, trg->size * sizeof *trg->val.nodes);
1791 LY_CHECK_ERR_RET(!trg->val.nodes, LOGMEM(src->ctx), LY_EMEM);
1792 }
1793
1794 i = 0;
1795 j = 0;
1796 count = 0;
1797 dup_count = 0;
1798 do {
1799 cmp = set_sort_compare(&src->val.nodes[i], &trg->val.nodes[j]);
1800 if (!cmp) {
1801 if (!count) {
1802 /* duplicate, just skip it */
1803 ++i;
1804 ++j;
1805 } else {
1806 /* we are copying something already, so let's copy the duplicate too,
1807 * we are hoping that afterwards there are some more nodes to
1808 * copy and this way we can copy them all together */
1809 ++count;
1810 ++dup_count;
1811 ++i;
1812 ++j;
1813 }
1814 } else if (cmp < 0) {
1815 /* inserting src node into trg, just remember it for now */
1816 ++count;
1817 ++i;
1818
1819 /* insert the hash now */
1820 set_insert_node_hash(trg, src->val.nodes[i - 1].node, src->val.nodes[i - 1].type);
1821 } else if (count) {
1822copy_nodes:
1823 /* time to actually copy the nodes, we have found the largest block of nodes */
1824 memmove(&trg->val.nodes[j + (count - dup_count)],
1825 &trg->val.nodes[j],
1826 (trg->used - j) * sizeof *trg->val.nodes);
1827 memcpy(&trg->val.nodes[j - dup_count], &src->val.nodes[i - count], count * sizeof *src->val.nodes);
1828
1829 trg->used += count - dup_count;
1830 /* do not change i, except the copying above, we are basically doing exactly what is in the else branch below */
1831 j += count - dup_count;
1832
1833 count = 0;
1834 dup_count = 0;
1835 } else {
1836 ++j;
1837 }
1838 } while ((i < src->used) && (j < trg->used));
1839
1840 if ((i < src->used) || count) {
1841 /* insert all the hashes first */
1842 for (k = i; k < src->used; ++k) {
1843 set_insert_node_hash(trg, src->val.nodes[k].node, src->val.nodes[k].type);
1844 }
1845
1846 /* loop ended, but we need to copy something at trg end */
1847 count += src->used - i;
1848 i = src->used;
1849 goto copy_nodes;
1850 }
1851
1852 /* we are inserting hashes before the actual node insert, which causes
1853 * situations when there were initially not enough items for a hash table,
1854 * but even after some were inserted, hash table was not created (during
1855 * insertion the number of items is not updated yet) */
1856 if (!trg->ht && (trg->used >= LYD_HT_MIN_ITEMS)) {
1857 set_insert_node_hash(trg, NULL, 0);
1858 }
1859
1860#ifndef NDEBUG
1861 LOGDBG(LY_LDGXPATH, "MERGE result");
1862 print_set_debug(trg);
1863#endif
1864
Michal Vaskod3678892020-05-21 10:06:58 +02001865 lyxp_set_free_content(src);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001866 return LY_SUCCESS;
1867}
1868
1869/*
1870 * (re)parse functions
1871 *
1872 * Parse functions parse the expression into
1873 * tokens (syntactic analysis).
1874 *
1875 * Reparse functions perform semantic analysis
1876 * (do not save the result, just a check) of
1877 * the expression and fill repeat indices.
1878 */
1879
Michal Vasko14676352020-05-29 11:35:55 +02001880LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02001881lyxp_check_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint16_t tok_idx, enum lyxp_token want_tok)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001882{
Michal Vasko004d3152020-06-11 19:59:22 +02001883 if (exp->used == tok_idx) {
Michal Vasko14676352020-05-29 11:35:55 +02001884 if (ctx) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001885 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOF);
1886 }
Michal Vasko14676352020-05-29 11:35:55 +02001887 return LY_EINCOMPLETE;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001888 }
1889
Michal Vasko004d3152020-06-11 19:59:22 +02001890 if (want_tok && (exp->tokens[tok_idx] != want_tok)) {
Michal Vasko14676352020-05-29 11:35:55 +02001891 if (ctx) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02001892 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
Michal Vasko004d3152020-06-11 19:59:22 +02001893 lyxp_print_token(exp->tokens[tok_idx]), &exp->expr[exp->tok_pos[tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001894 }
Michal Vasko14676352020-05-29 11:35:55 +02001895 return LY_ENOT;
1896 }
1897
1898 return LY_SUCCESS;
1899}
1900
Michal Vasko004d3152020-06-11 19:59:22 +02001901LY_ERR
1902lyxp_next_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint16_t *tok_idx, enum lyxp_token want_tok)
1903{
1904 LY_CHECK_RET(lyxp_check_token(ctx, exp, *tok_idx, want_tok));
1905
1906 /* skip the token */
1907 ++(*tok_idx);
1908
1909 return LY_SUCCESS;
1910}
1911
Michal Vasko14676352020-05-29 11:35:55 +02001912/* just like lyxp_check_token() but tests for 2 tokens */
1913static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02001914exp_check_token2(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t tok_idx, enum lyxp_token want_tok1,
Michal Vasko14676352020-05-29 11:35:55 +02001915 enum lyxp_token want_tok2)
1916{
Michal Vasko004d3152020-06-11 19:59:22 +02001917 if (exp->used == tok_idx) {
Michal Vasko14676352020-05-29 11:35:55 +02001918 if (ctx) {
1919 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOF);
1920 }
1921 return LY_EINCOMPLETE;
1922 }
1923
Michal Vasko004d3152020-06-11 19:59:22 +02001924 if ((exp->tokens[tok_idx] != want_tok1) && (exp->tokens[tok_idx] != want_tok2)) {
Michal Vasko14676352020-05-29 11:35:55 +02001925 if (ctx) {
1926 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
Michal Vasko004d3152020-06-11 19:59:22 +02001927 lyxp_print_token(exp->tokens[tok_idx]), &exp->expr[exp->tok_pos[tok_idx]]);
Michal Vasko14676352020-05-29 11:35:55 +02001928 }
1929 return LY_ENOT;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001930 }
1931
1932 return LY_SUCCESS;
1933}
1934
1935/**
1936 * @brief Stack operation push on the repeat array.
1937 *
1938 * @param[in] exp Expression to use.
Michal Vasko004d3152020-06-11 19:59:22 +02001939 * @param[in] tok_idx Position in the expresion \p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001940 * @param[in] repeat_op_idx Index from \p exp of the operator token. This value is pushed.
1941 */
1942static void
Michal Vasko004d3152020-06-11 19:59:22 +02001943exp_repeat_push(struct lyxp_expr *exp, uint16_t tok_idx, uint16_t repeat_op_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001944{
1945 uint16_t i;
1946
Michal Vasko004d3152020-06-11 19:59:22 +02001947 if (exp->repeat[tok_idx]) {
1948 for (i = 0; exp->repeat[tok_idx][i]; ++i);
1949 exp->repeat[tok_idx] = realloc(exp->repeat[tok_idx], (i + 2) * sizeof *exp->repeat[tok_idx]);
1950 LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
1951 exp->repeat[tok_idx][i] = repeat_op_idx;
1952 exp->repeat[tok_idx][i + 1] = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001953 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02001954 exp->repeat[tok_idx] = calloc(2, sizeof *exp->repeat[tok_idx]);
1955 LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
1956 exp->repeat[tok_idx][0] = repeat_op_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02001957 }
1958}
1959
1960/**
1961 * @brief Reparse Predicate. Logs directly on error.
1962 *
1963 * [7] Predicate ::= '[' Expr ']'
1964 *
1965 * @param[in] ctx Context for logging.
1966 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02001967 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001968 * @return LY_ERR
1969 */
1970static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02001971reparse_predicate(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02001972{
1973 LY_ERR rc;
1974
Michal Vasko004d3152020-06-11 19:59:22 +02001975 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001976 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02001977 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001978
Michal Vasko004d3152020-06-11 19:59:22 +02001979 rc = reparse_or_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001980 LY_CHECK_RET(rc);
1981
Michal Vasko004d3152020-06-11 19:59:22 +02001982 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001983 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02001984 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02001985
1986 return LY_SUCCESS;
1987}
1988
1989/**
1990 * @brief Reparse RelativeLocationPath. Logs directly on error.
1991 *
1992 * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
1993 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
1994 * [6] NodeTest ::= NameTest | NodeType '(' ')'
1995 *
1996 * @param[in] ctx Context for logging.
1997 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02001998 * @param[in] tok_idx Position in the expression \p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02001999 * @return LY_ERR (LY_EINCOMPLETE on forward reference)
2000 */
2001static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002002reparse_relative_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002003{
2004 LY_ERR rc;
2005
Michal Vasko004d3152020-06-11 19:59:22 +02002006 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002007 LY_CHECK_RET(rc);
2008
2009 goto step;
2010 do {
2011 /* '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02002012 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002013
Michal Vasko004d3152020-06-11 19:59:22 +02002014 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002015 LY_CHECK_RET(rc);
2016step:
2017 /* Step */
Michal Vasko004d3152020-06-11 19:59:22 +02002018 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002019 case LYXP_TOKEN_DOT:
Michal Vasko004d3152020-06-11 19:59:22 +02002020 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002021 break;
2022
2023 case LYXP_TOKEN_DDOT:
Michal Vasko004d3152020-06-11 19:59:22 +02002024 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002025 break;
2026
2027 case LYXP_TOKEN_AT:
Michal Vasko004d3152020-06-11 19:59:22 +02002028 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002029
Michal Vasko004d3152020-06-11 19:59:22 +02002030 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002031 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002032 if ((exp->tokens[*tok_idx] != LYXP_TOKEN_NAMETEST) && (exp->tokens[*tok_idx] != LYXP_TOKEN_NODETYPE)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002033 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
Michal Vasko004d3152020-06-11 19:59:22 +02002034 lyxp_print_token(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002035 return LY_EVALID;
2036 }
2037 /* fall through */
2038 case LYXP_TOKEN_NAMETEST:
Michal Vasko004d3152020-06-11 19:59:22 +02002039 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002040 goto reparse_predicate;
2041 break;
2042
2043 case LYXP_TOKEN_NODETYPE:
Michal Vasko004d3152020-06-11 19:59:22 +02002044 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002045
2046 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02002047 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002048 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002049 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002050
2051 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02002052 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002053 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002054 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002055
2056reparse_predicate:
2057 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02002058 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
2059 rc = reparse_predicate(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002060 LY_CHECK_RET(rc);
2061 }
2062 break;
2063 default:
2064 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
Michal Vasko004d3152020-06-11 19:59:22 +02002065 lyxp_print_token(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002066 return LY_EVALID;
2067 }
Michal Vasko004d3152020-06-11 19:59:22 +02002068 } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
Michal Vasko03ff5a72019-09-11 13:49:33 +02002069
2070 return LY_SUCCESS;
2071}
2072
2073/**
2074 * @brief Reparse AbsoluteLocationPath. Logs directly on error.
2075 *
2076 * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
2077 *
2078 * @param[in] ctx Context for logging.
2079 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002080 * @param[in] tok_idx Position in the expression \p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002081 * @return LY_ERR
2082 */
2083static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002084reparse_absolute_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002085{
2086 LY_ERR rc;
2087
Michal Vasko004d3152020-06-11 19:59:22 +02002088 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 +02002089
2090 /* '/' RelativeLocationPath? */
Michal Vasko004d3152020-06-11 19:59:22 +02002091 if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002092 /* '/' */
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 if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002096 return LY_SUCCESS;
2097 }
Michal Vasko004d3152020-06-11 19:59:22 +02002098 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002099 case LYXP_TOKEN_DOT:
2100 case LYXP_TOKEN_DDOT:
2101 case LYXP_TOKEN_AT:
2102 case LYXP_TOKEN_NAMETEST:
2103 case LYXP_TOKEN_NODETYPE:
Michal Vasko004d3152020-06-11 19:59:22 +02002104 rc = reparse_relative_location_path(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002105 LY_CHECK_RET(rc);
2106 /* fall through */
2107 default:
2108 break;
2109 }
2110
2111 /* '//' RelativeLocationPath */
2112 } else {
2113 /* '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02002114 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002115
Michal Vasko004d3152020-06-11 19:59:22 +02002116 rc = reparse_relative_location_path(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002117 LY_CHECK_RET(rc);
2118 }
2119
2120 return LY_SUCCESS;
2121}
2122
2123/**
2124 * @brief Reparse FunctionCall. Logs directly on error.
2125 *
2126 * [9] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
2127 *
2128 * @param[in] ctx Context for logging.
2129 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002130 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002131 * @return LY_ERR
2132 */
2133static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002134reparse_function_call(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002135{
2136 int min_arg_count = -1, max_arg_count, arg_count;
Michal Vasko004d3152020-06-11 19:59:22 +02002137 uint16_t func_tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002138 LY_ERR rc;
2139
Michal Vasko004d3152020-06-11 19:59:22 +02002140 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002141 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002142 func_tok_idx = *tok_idx;
2143 switch (exp->tok_len[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002144 case 3:
Michal Vasko004d3152020-06-11 19:59:22 +02002145 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002146 min_arg_count = 1;
2147 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002148 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002149 min_arg_count = 1;
2150 max_arg_count = 1;
2151 }
2152 break;
2153 case 4:
Michal Vasko004d3152020-06-11 19:59:22 +02002154 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002155 min_arg_count = 1;
2156 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002157 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002158 min_arg_count = 0;
2159 max_arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002160 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002161 min_arg_count = 0;
2162 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002163 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002164 min_arg_count = 0;
2165 max_arg_count = 0;
2166 }
2167 break;
2168 case 5:
Michal Vasko004d3152020-06-11 19:59:22 +02002169 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002170 min_arg_count = 1;
2171 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002172 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002173 min_arg_count = 0;
2174 max_arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002175 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002176 min_arg_count = 1;
2177 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002178 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002179 min_arg_count = 1;
2180 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002181 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002182 min_arg_count = 1;
2183 max_arg_count = 1;
2184 }
2185 break;
2186 case 6:
Michal Vasko004d3152020-06-11 19:59:22 +02002187 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002188 min_arg_count = 2;
Michal Vaskobe2e3562019-10-15 15:35:35 +02002189 max_arg_count = INT_MAX;
Michal Vasko004d3152020-06-11 19:59:22 +02002190 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002191 min_arg_count = 0;
2192 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002193 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002194 min_arg_count = 0;
2195 max_arg_count = 1;
2196 }
2197 break;
2198 case 7:
Michal Vasko004d3152020-06-11 19:59:22 +02002199 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002200 min_arg_count = 1;
2201 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002202 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002203 min_arg_count = 1;
2204 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002205 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002206 min_arg_count = 0;
2207 max_arg_count = 0;
2208 }
2209 break;
2210 case 8:
Michal Vasko004d3152020-06-11 19:59:22 +02002211 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002212 min_arg_count = 2;
2213 max_arg_count = 2;
Michal Vasko004d3152020-06-11 19:59:22 +02002214 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002215 min_arg_count = 0;
2216 max_arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002217 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002218 min_arg_count = 2;
2219 max_arg_count = 2;
2220 }
2221 break;
2222 case 9:
Michal Vasko004d3152020-06-11 19:59:22 +02002223 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002224 min_arg_count = 2;
2225 max_arg_count = 3;
Michal Vasko004d3152020-06-11 19:59:22 +02002226 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002227 min_arg_count = 3;
2228 max_arg_count = 3;
2229 }
2230 break;
2231 case 10:
Michal Vasko004d3152020-06-11 19:59:22 +02002232 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002233 min_arg_count = 0;
2234 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002235 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002236 min_arg_count = 1;
2237 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002238 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002239 min_arg_count = 2;
2240 max_arg_count = 2;
2241 }
2242 break;
2243 case 11:
Michal Vasko004d3152020-06-11 19:59:22 +02002244 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002245 min_arg_count = 2;
2246 max_arg_count = 2;
2247 }
2248 break;
2249 case 12:
Michal Vasko004d3152020-06-11 19:59:22 +02002250 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002251 min_arg_count = 2;
2252 max_arg_count = 2;
2253 }
2254 break;
2255 case 13:
Michal Vasko004d3152020-06-11 19:59:22 +02002256 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002257 min_arg_count = 0;
2258 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002259 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002260 min_arg_count = 0;
2261 max_arg_count = 1;
2262 }
2263 break;
2264 case 15:
Michal Vasko004d3152020-06-11 19:59:22 +02002265 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002266 min_arg_count = 0;
2267 max_arg_count = 1;
Michal Vasko004d3152020-06-11 19:59:22 +02002268 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002269 min_arg_count = 2;
2270 max_arg_count = 2;
2271 }
2272 break;
2273 case 16:
Michal Vasko004d3152020-06-11 19:59:22 +02002274 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002275 min_arg_count = 2;
2276 max_arg_count = 2;
2277 }
2278 break;
2279 case 20:
Michal Vasko004d3152020-06-11 19:59:22 +02002280 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002281 min_arg_count = 2;
2282 max_arg_count = 2;
2283 }
2284 break;
2285 }
2286 if (min_arg_count == -1) {
Michal Vasko004d3152020-06-11 19:59:22 +02002287 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002288 return LY_EINVAL;
2289 }
Michal Vasko004d3152020-06-11 19:59:22 +02002290 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002291
2292 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02002293 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002294 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002295 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002296
2297 /* ( Expr ( ',' Expr )* )? */
2298 arg_count = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002299 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002300 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002301 if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002302 ++arg_count;
Michal Vasko004d3152020-06-11 19:59:22 +02002303 rc = reparse_or_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002304 LY_CHECK_RET(rc);
2305 }
Michal Vasko004d3152020-06-11 19:59:22 +02002306 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
2307 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002308
2309 ++arg_count;
Michal Vasko004d3152020-06-11 19:59:22 +02002310 rc = reparse_or_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002311 LY_CHECK_RET(rc);
2312 }
2313
2314 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02002315 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002316 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002317 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002318
2319 if ((arg_count < min_arg_count) || (arg_count > max_arg_count)) {
Michal Vasko004d3152020-06-11 19:59:22 +02002320 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INARGCOUNT, arg_count, exp->tok_len[func_tok_idx],
2321 &exp->expr[exp->tok_pos[func_tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002322 return LY_EVALID;
2323 }
2324
2325 return LY_SUCCESS;
2326}
2327
2328/**
2329 * @brief Reparse PathExpr. Logs directly on error.
2330 *
2331 * [10] PathExpr ::= LocationPath | PrimaryExpr Predicate*
2332 * | PrimaryExpr Predicate* '/' RelativeLocationPath
2333 * | PrimaryExpr Predicate* '//' RelativeLocationPath
2334 * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
2335 * [8] PrimaryExpr ::= '(' Expr ')' | Literal | Number | FunctionCall
2336 *
2337 * @param[in] ctx Context for logging.
2338 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002339 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002340 * @return LY_ERR
2341 */
2342static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002343reparse_path_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002344{
2345 LY_ERR rc;
2346
Michal Vasko004d3152020-06-11 19:59:22 +02002347 if (lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE)) {
Michal Vasko14676352020-05-29 11:35:55 +02002348 return LY_EVALID;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002349 }
2350
Michal Vasko004d3152020-06-11 19:59:22 +02002351 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002352 case LYXP_TOKEN_PAR1:
2353 /* '(' Expr ')' Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02002354 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002355
Michal Vasko004d3152020-06-11 19:59:22 +02002356 rc = reparse_or_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002357 LY_CHECK_RET(rc);
2358
Michal Vasko004d3152020-06-11 19:59:22 +02002359 rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002360 LY_CHECK_RET(rc);
Michal Vasko004d3152020-06-11 19:59:22 +02002361 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002362 goto predicate;
2363 break;
2364 case LYXP_TOKEN_DOT:
2365 case LYXP_TOKEN_DDOT:
2366 case LYXP_TOKEN_AT:
2367 case LYXP_TOKEN_NAMETEST:
2368 case LYXP_TOKEN_NODETYPE:
2369 /* RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02002370 rc = reparse_relative_location_path(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002371 LY_CHECK_RET(rc);
2372 break;
2373 case LYXP_TOKEN_FUNCNAME:
2374 /* FunctionCall */
Michal Vasko004d3152020-06-11 19:59:22 +02002375 rc = reparse_function_call(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002376 LY_CHECK_RET(rc);
2377 goto predicate;
2378 break;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002379 case LYXP_TOKEN_OPER_PATH:
2380 case LYXP_TOKEN_OPER_RPATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +02002381 /* AbsoluteLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02002382 rc = reparse_absolute_location_path(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002383 LY_CHECK_RET(rc);
2384 break;
2385 case LYXP_TOKEN_LITERAL:
2386 /* Literal */
Michal Vasko004d3152020-06-11 19:59:22 +02002387 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002388 goto predicate;
2389 break;
2390 case LYXP_TOKEN_NUMBER:
2391 /* Number */
Michal Vasko004d3152020-06-11 19:59:22 +02002392 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002393 goto predicate;
2394 break;
2395 default:
2396 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
Michal Vasko004d3152020-06-11 19:59:22 +02002397 lyxp_print_token(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002398 return LY_EVALID;
2399 }
2400
2401 return LY_SUCCESS;
2402
2403predicate:
2404 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02002405 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
2406 rc = reparse_predicate(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002407 LY_CHECK_RET(rc);
2408 }
2409
2410 /* ('/' or '//') RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02002411 if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002412
2413 /* '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02002414 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002415
Michal Vasko004d3152020-06-11 19:59:22 +02002416 rc = reparse_relative_location_path(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002417 LY_CHECK_RET(rc);
2418 }
2419
2420 return LY_SUCCESS;
2421}
2422
2423/**
2424 * @brief Reparse UnaryExpr. Logs directly on error.
2425 *
2426 * [17] UnaryExpr ::= UnionExpr | '-' UnaryExpr
2427 * [18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
2428 *
2429 * @param[in] ctx Context for logging.
2430 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002431 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002432 * @return LY_ERR
2433 */
2434static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002435reparse_unary_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002436{
2437 uint16_t prev_exp;
2438 LY_ERR rc;
2439
2440 /* ('-')* */
Michal Vasko004d3152020-06-11 19:59:22 +02002441 prev_exp = *tok_idx;
2442 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH)
2443 && (exp->expr[exp->tok_pos[*tok_idx]] == '-')) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002444 exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNARY);
Michal Vasko004d3152020-06-11 19:59:22 +02002445 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002446 }
2447
2448 /* PathExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002449 prev_exp = *tok_idx;
2450 rc = reparse_path_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002451 LY_CHECK_RET(rc);
2452
2453 /* ('|' PathExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002454 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_UNI)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002455 exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNION);
Michal Vasko004d3152020-06-11 19:59:22 +02002456 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002457
Michal Vasko004d3152020-06-11 19:59:22 +02002458 rc = reparse_path_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002459 LY_CHECK_RET(rc);
2460 }
2461
2462 return LY_SUCCESS;
2463}
2464
2465/**
2466 * @brief Reparse AdditiveExpr. Logs directly on error.
2467 *
2468 * [15] AdditiveExpr ::= MultiplicativeExpr
2469 * | AdditiveExpr '+' MultiplicativeExpr
2470 * | AdditiveExpr '-' MultiplicativeExpr
2471 * [16] MultiplicativeExpr ::= UnaryExpr
2472 * | MultiplicativeExpr '*' UnaryExpr
2473 * | MultiplicativeExpr 'div' UnaryExpr
2474 * | MultiplicativeExpr 'mod' UnaryExpr
2475 *
2476 * @param[in] ctx Context for logging.
2477 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002478 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002479 * @return LY_ERR
2480 */
2481static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002482reparse_additive_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002483{
2484 uint16_t prev_add_exp, prev_mul_exp;
2485 LY_ERR rc;
2486
Michal Vasko004d3152020-06-11 19:59:22 +02002487 prev_add_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002488 goto reparse_multiplicative_expr;
2489
2490 /* ('+' / '-' MultiplicativeExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002491 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH)
2492 && ((exp->expr[exp->tok_pos[*tok_idx]] == '+') || (exp->expr[exp->tok_pos[*tok_idx]] == '-'))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002493 exp_repeat_push(exp, prev_add_exp, LYXP_EXPR_ADDITIVE);
Michal Vasko004d3152020-06-11 19:59:22 +02002494 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002495
2496reparse_multiplicative_expr:
2497 /* UnaryExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002498 prev_mul_exp = *tok_idx;
2499 rc = reparse_unary_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002500 LY_CHECK_RET(rc);
2501
2502 /* ('*' / 'div' / 'mod' UnaryExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002503 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH)
2504 && ((exp->expr[exp->tok_pos[*tok_idx]] == '*') || (exp->tok_len[*tok_idx] == 3))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002505 exp_repeat_push(exp, prev_mul_exp, LYXP_EXPR_MULTIPLICATIVE);
Michal Vasko004d3152020-06-11 19:59:22 +02002506 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002507
Michal Vasko004d3152020-06-11 19:59:22 +02002508 rc = reparse_unary_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002509 LY_CHECK_RET(rc);
2510 }
2511 }
2512
2513 return LY_SUCCESS;
2514}
2515
2516/**
2517 * @brief Reparse EqualityExpr. Logs directly on error.
2518 *
2519 * [13] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
2520 * | EqualityExpr '!=' RelationalExpr
2521 * [14] RelationalExpr ::= AdditiveExpr
2522 * | RelationalExpr '<' AdditiveExpr
2523 * | RelationalExpr '>' AdditiveExpr
2524 * | RelationalExpr '<=' AdditiveExpr
2525 * | RelationalExpr '>=' AdditiveExpr
2526 *
2527 * @param[in] ctx Context for logging.
2528 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002529 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002530 * @return LY_ERR
2531 */
2532static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002533reparse_equality_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002534{
2535 uint16_t prev_eq_exp, prev_rel_exp;
2536 LY_ERR rc;
2537
Michal Vasko004d3152020-06-11 19:59:22 +02002538 prev_eq_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002539 goto reparse_additive_expr;
2540
2541 /* ('=' / '!=' RelationalExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002542 while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_EQUAL, LYXP_TOKEN_OPER_NEQUAL)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002543 exp_repeat_push(exp, prev_eq_exp, LYXP_EXPR_EQUALITY);
Michal Vasko004d3152020-06-11 19:59:22 +02002544 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002545
2546reparse_additive_expr:
2547 /* AdditiveExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002548 prev_rel_exp = *tok_idx;
2549 rc = reparse_additive_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002550 LY_CHECK_RET(rc);
2551
2552 /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002553 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_COMP)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02002554 exp_repeat_push(exp, prev_rel_exp, LYXP_EXPR_RELATIONAL);
Michal Vasko004d3152020-06-11 19:59:22 +02002555 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002556
Michal Vasko004d3152020-06-11 19:59:22 +02002557 rc = reparse_additive_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002558 LY_CHECK_RET(rc);
2559 }
2560 }
2561
2562 return LY_SUCCESS;
2563}
2564
2565/**
2566 * @brief Reparse OrExpr. Logs directly on error.
2567 *
2568 * [11] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
2569 * [12] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
2570 *
2571 * @param[in] ctx Context for logging.
2572 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02002573 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002574 * @return LY_ERR
2575 */
2576static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02002577reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
Michal Vasko03ff5a72019-09-11 13:49:33 +02002578{
2579 uint16_t prev_or_exp, prev_and_exp;
2580 LY_ERR rc;
2581
Michal Vasko004d3152020-06-11 19:59:22 +02002582 prev_or_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002583 goto reparse_equality_expr;
2584
2585 /* ('or' AndExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002586 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 +02002587 exp_repeat_push(exp, prev_or_exp, LYXP_EXPR_OR);
Michal Vasko004d3152020-06-11 19:59:22 +02002588 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002589
2590reparse_equality_expr:
2591 /* EqualityExpr */
Michal Vasko004d3152020-06-11 19:59:22 +02002592 prev_and_exp = *tok_idx;
2593 rc = reparse_equality_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002594 LY_CHECK_RET(rc);
2595
2596 /* ('and' EqualityExpr)* */
Michal Vasko004d3152020-06-11 19:59:22 +02002597 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 +02002598 exp_repeat_push(exp, prev_and_exp, LYXP_EXPR_AND);
Michal Vasko004d3152020-06-11 19:59:22 +02002599 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002600
Michal Vasko004d3152020-06-11 19:59:22 +02002601 rc = reparse_equality_expr(ctx, exp, tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02002602 LY_CHECK_RET(rc);
2603 }
2604 }
2605
2606 return LY_SUCCESS;
2607}
Radek Krejcib1646a92018-11-02 16:08:26 +01002608
2609/**
2610 * @brief Parse NCName.
2611 *
2612 * @param[in] ncname Name to parse.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002613 * @return Length of @p ncname valid bytes.
Radek Krejcib1646a92018-11-02 16:08:26 +01002614 */
Radek Krejcid4270262019-01-07 15:07:25 +01002615static long int
Radek Krejcib1646a92018-11-02 16:08:26 +01002616parse_ncname(const char *ncname)
2617{
2618 unsigned int uc;
Radek Krejcid4270262019-01-07 15:07:25 +01002619 size_t size;
2620 long int len = 0;
Radek Krejcib1646a92018-11-02 16:08:26 +01002621
2622 LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), 0);
2623 if (!is_xmlqnamestartchar(uc) || (uc == ':')) {
2624 return len;
2625 }
2626
2627 do {
2628 len += size;
Radek Krejci9a564c92019-01-07 14:53:57 +01002629 if (!*ncname) {
2630 break;
2631 }
Radek Krejcid4270262019-01-07 15:07:25 +01002632 LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), -len);
Radek Krejcib1646a92018-11-02 16:08:26 +01002633 } while (is_xmlqnamechar(uc) && (uc != ':'));
2634
2635 return len;
2636}
2637
2638/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02002639 * @brief Add @p token into the expression @p exp.
Radek Krejcib1646a92018-11-02 16:08:26 +01002640 *
Michal Vasko03ff5a72019-09-11 13:49:33 +02002641 * @param[in] ctx Context for logging.
Radek Krejcib1646a92018-11-02 16:08:26 +01002642 * @param[in] exp Expression to use.
2643 * @param[in] token Token to add.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002644 * @param[in] tok_pos Token position in the XPath expression.
Radek Krejcib1646a92018-11-02 16:08:26 +01002645 * @param[in] tok_len Token length in the XPath expression.
Michal Vasko03ff5a72019-09-11 13:49:33 +02002646 * @return LY_ERR
Radek Krejcib1646a92018-11-02 16:08:26 +01002647 */
2648static LY_ERR
Michal Vasko14676352020-05-29 11:35:55 +02002649exp_add_token(const struct ly_ctx *ctx, struct lyxp_expr *exp, enum lyxp_token token, uint16_t tok_pos, uint16_t tok_len)
Radek Krejcib1646a92018-11-02 16:08:26 +01002650{
2651 uint32_t prev;
2652
2653 if (exp->used == exp->size) {
2654 prev = exp->size;
2655 exp->size += LYXP_EXPR_SIZE_STEP;
2656 if (prev > exp->size) {
2657 LOGINT(ctx);
2658 return LY_EINT;
2659 }
2660
2661 exp->tokens = ly_realloc(exp->tokens, exp->size * sizeof *exp->tokens);
2662 LY_CHECK_ERR_RET(!exp->tokens, LOGMEM(ctx), LY_EMEM);
2663 exp->tok_pos = ly_realloc(exp->tok_pos, exp->size * sizeof *exp->tok_pos);
2664 LY_CHECK_ERR_RET(!exp->tok_pos, LOGMEM(ctx), LY_EMEM);
2665 exp->tok_len = ly_realloc(exp->tok_len, exp->size * sizeof *exp->tok_len);
2666 LY_CHECK_ERR_RET(!exp->tok_len, LOGMEM(ctx), LY_EMEM);
2667 }
2668
2669 exp->tokens[exp->used] = token;
Michal Vasko03ff5a72019-09-11 13:49:33 +02002670 exp->tok_pos[exp->used] = tok_pos;
Radek Krejcib1646a92018-11-02 16:08:26 +01002671 exp->tok_len[exp->used] = tok_len;
2672 ++exp->used;
2673 return LY_SUCCESS;
2674}
2675
2676void
Michal Vasko14676352020-05-29 11:35:55 +02002677lyxp_expr_free(const struct ly_ctx *ctx, struct lyxp_expr *expr)
Radek Krejcib1646a92018-11-02 16:08:26 +01002678{
2679 uint16_t i;
2680
2681 if (!expr) {
2682 return;
2683 }
2684
2685 lydict_remove(ctx, expr->expr);
2686 free(expr->tokens);
2687 free(expr->tok_pos);
2688 free(expr->tok_len);
2689 if (expr->repeat) {
2690 for (i = 0; i < expr->used; ++i) {
2691 free(expr->repeat[i]);
2692 }
2693 }
2694 free(expr->repeat);
2695 free(expr);
2696}
2697
2698struct lyxp_expr *
Michal Vasko004d3152020-06-11 19:59:22 +02002699lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr, size_t expr_len, int reparse)
Radek Krejcib1646a92018-11-02 16:08:26 +01002700{
2701 struct lyxp_expr *ret;
Radek Krejcid4270262019-01-07 15:07:25 +01002702 size_t parsed = 0, tok_len;
2703 long int ncname_len;
Radek Krejcib1646a92018-11-02 16:08:26 +01002704 enum lyxp_token tok_type;
2705 int prev_function_check = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02002706 uint16_t tok_idx = 0;
Radek Krejcib1646a92018-11-02 16:08:26 +01002707
Michal Vasko004d3152020-06-11 19:59:22 +02002708 if (!expr[0]) {
2709 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOF);
2710 return NULL;
2711 }
2712
2713 if (!expr_len) {
2714 expr_len = strlen(expr);
2715 }
2716 if (expr_len > UINT16_MAX) {
Radek Krejcib1646a92018-11-02 16:08:26 +01002717 LOGERR(ctx, LY_EINVAL, "XPath expression cannot be longer than %ud characters.", UINT16_MAX);
2718 return NULL;
2719 }
2720
2721 /* init lyxp_expr structure */
2722 ret = calloc(1, sizeof *ret);
2723 LY_CHECK_ERR_GOTO(!ret, LOGMEM(ctx), error);
Michal Vasko004d3152020-06-11 19:59:22 +02002724 ret->expr = lydict_insert(ctx, expr, expr_len);
Radek Krejcib1646a92018-11-02 16:08:26 +01002725 LY_CHECK_ERR_GOTO(!ret->expr, LOGMEM(ctx), error);
2726 ret->used = 0;
2727 ret->size = LYXP_EXPR_SIZE_START;
2728 ret->tokens = malloc(ret->size * sizeof *ret->tokens);
2729 LY_CHECK_ERR_GOTO(!ret->tokens, LOGMEM(ctx), error);
2730
2731 ret->tok_pos = malloc(ret->size * sizeof *ret->tok_pos);
2732 LY_CHECK_ERR_GOTO(!ret->tok_pos, LOGMEM(ctx), error);
2733
2734 ret->tok_len = malloc(ret->size * sizeof *ret->tok_len);
2735 LY_CHECK_ERR_GOTO(!ret->tok_len, LOGMEM(ctx), error);
2736
Michal Vasko004d3152020-06-11 19:59:22 +02002737 /* make expr 0-terminated */
2738 expr = ret->expr;
2739
Radek Krejcib1646a92018-11-02 16:08:26 +01002740 while (is_xmlws(expr[parsed])) {
2741 ++parsed;
2742 }
2743
2744 do {
2745 if (expr[parsed] == '(') {
2746
2747 /* '(' */
2748 tok_len = 1;
2749 tok_type = LYXP_TOKEN_PAR1;
2750
2751 if (prev_function_check && ret->used && (ret->tokens[ret->used - 1] == LYXP_TOKEN_NAMETEST)) {
2752 /* it is a NodeType/FunctionName after all */
2753 if (((ret->tok_len[ret->used - 1] == 4)
2754 && (!strncmp(&expr[ret->tok_pos[ret->used - 1]], "node", 4)
2755 || !strncmp(&expr[ret->tok_pos[ret->used - 1]], "text", 4))) ||
2756 ((ret->tok_len[ret->used - 1] == 7)
2757 && !strncmp(&expr[ret->tok_pos[ret->used - 1]], "comment", 7))) {
2758 ret->tokens[ret->used - 1] = LYXP_TOKEN_NODETYPE;
2759 } else {
2760 ret->tokens[ret->used - 1] = LYXP_TOKEN_FUNCNAME;
2761 }
2762 prev_function_check = 0;
2763 }
2764
2765 } else if (expr[parsed] == ')') {
2766
2767 /* ')' */
2768 tok_len = 1;
2769 tok_type = LYXP_TOKEN_PAR2;
2770
2771 } else if (expr[parsed] == '[') {
2772
2773 /* '[' */
2774 tok_len = 1;
2775 tok_type = LYXP_TOKEN_BRACK1;
2776
2777 } else if (expr[parsed] == ']') {
2778
2779 /* ']' */
2780 tok_len = 1;
2781 tok_type = LYXP_TOKEN_BRACK2;
2782
2783 } else if (!strncmp(&expr[parsed], "..", 2)) {
2784
2785 /* '..' */
2786 tok_len = 2;
2787 tok_type = LYXP_TOKEN_DDOT;
2788
2789 } else if ((expr[parsed] == '.') && (!isdigit(expr[parsed + 1]))) {
2790
2791 /* '.' */
2792 tok_len = 1;
2793 tok_type = LYXP_TOKEN_DOT;
2794
2795 } else if (expr[parsed] == '@') {
2796
2797 /* '@' */
2798 tok_len = 1;
2799 tok_type = LYXP_TOKEN_AT;
2800
2801 } else if (expr[parsed] == ',') {
2802
2803 /* ',' */
2804 tok_len = 1;
2805 tok_type = LYXP_TOKEN_COMMA;
2806
2807 } else if (expr[parsed] == '\'') {
2808
2809 /* Literal with ' */
2810 for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\''); ++tok_len);
2811 LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
2812 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
2813 ++tok_len;
2814 tok_type = LYXP_TOKEN_LITERAL;
2815
2816 } else if (expr[parsed] == '\"') {
2817
2818 /* Literal with " */
2819 for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\"'); ++tok_len);
2820 LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
2821 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
2822 ++tok_len;
2823 tok_type = LYXP_TOKEN_LITERAL;
2824
2825 } else if ((expr[parsed] == '.') || (isdigit(expr[parsed]))) {
2826
2827 /* Number */
2828 for (tok_len = 0; isdigit(expr[parsed + tok_len]); ++tok_len);
2829 if (expr[parsed + tok_len] == '.') {
2830 ++tok_len;
2831 for (; isdigit(expr[parsed + tok_len]); ++tok_len);
2832 }
2833 tok_type = LYXP_TOKEN_NUMBER;
2834
2835 } else if (expr[parsed] == '/') {
2836
2837 /* Operator '/', '//' */
2838 if (!strncmp(&expr[parsed], "//", 2)) {
2839 tok_len = 2;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002840 tok_type = LYXP_TOKEN_OPER_RPATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01002841 } else {
2842 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002843 tok_type = LYXP_TOKEN_OPER_PATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01002844 }
Radek Krejcib1646a92018-11-02 16:08:26 +01002845
Michal Vasko3e48bf32020-06-01 08:39:07 +02002846 } else if (!strncmp(&expr[parsed], "!=", 2)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01002847
Michal Vasko3e48bf32020-06-01 08:39:07 +02002848 /* Operator '!=' */
Radek Krejcib1646a92018-11-02 16:08:26 +01002849 tok_len = 2;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002850 tok_type = LYXP_TOKEN_OPER_NEQUAL;
2851
2852 } else if (!strncmp(&expr[parsed], "<=", 2) || !strncmp(&expr[parsed], ">=", 2)) {
2853
2854 /* Operator '<=', '>=' */
2855 tok_len = 2;
2856 tok_type = LYXP_TOKEN_OPER_COMP;
Radek Krejcib1646a92018-11-02 16:08:26 +01002857
2858 } else if (expr[parsed] == '|') {
2859
2860 /* Operator '|' */
2861 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002862 tok_type = LYXP_TOKEN_OPER_UNI;
Radek Krejcib1646a92018-11-02 16:08:26 +01002863
2864 } else if ((expr[parsed] == '+') || (expr[parsed] == '-')) {
2865
2866 /* Operator '+', '-' */
2867 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002868 tok_type = LYXP_TOKEN_OPER_MATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01002869
Michal Vasko3e48bf32020-06-01 08:39:07 +02002870 } else if (expr[parsed] == '=') {
Radek Krejcib1646a92018-11-02 16:08:26 +01002871
Michal Vasko3e48bf32020-06-01 08:39:07 +02002872 /* Operator '=' */
Radek Krejcib1646a92018-11-02 16:08:26 +01002873 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002874 tok_type = LYXP_TOKEN_OPER_EQUAL;
2875
2876 } else if ((expr[parsed] == '<') || (expr[parsed] == '>')) {
2877
2878 /* Operator '<', '>' */
2879 tok_len = 1;
2880 tok_type = LYXP_TOKEN_OPER_COMP;
Radek Krejcib1646a92018-11-02 16:08:26 +01002881
2882 } else if (ret->used && (ret->tokens[ret->used - 1] != LYXP_TOKEN_AT)
2883 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_PAR1)
2884 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_BRACK1)
2885 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_COMMA)
Michal Vasko3e48bf32020-06-01 08:39:07 +02002886 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_LOG)
2887 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_EQUAL)
2888 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_NEQUAL)
2889 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_COMP)
2890 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_MATH)
2891 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_UNI)
2892 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_PATH)
2893 && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_RPATH)) {
Radek Krejcib1646a92018-11-02 16:08:26 +01002894
2895 /* Operator '*', 'or', 'and', 'mod', or 'div' */
2896 if (expr[parsed] == '*') {
2897 tok_len = 1;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002898 tok_type = LYXP_TOKEN_OPER_MATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01002899
2900 } else if (!strncmp(&expr[parsed], "or", 2)) {
2901 tok_len = 2;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002902 tok_type = LYXP_TOKEN_OPER_LOG;
Radek Krejcib1646a92018-11-02 16:08:26 +01002903
2904 } else if (!strncmp(&expr[parsed], "and", 3)) {
2905 tok_len = 3;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002906 tok_type = LYXP_TOKEN_OPER_LOG;
Radek Krejcib1646a92018-11-02 16:08:26 +01002907
2908 } else if (!strncmp(&expr[parsed], "mod", 3) || !strncmp(&expr[parsed], "div", 3)) {
2909 tok_len = 3;
Michal Vasko3e48bf32020-06-01 08:39:07 +02002910 tok_type = LYXP_TOKEN_OPER_MATH;
Radek Krejcib1646a92018-11-02 16:08:26 +01002911
2912 } else if (prev_function_check) {
Michal Vasko53078572019-05-24 08:50:15 +02002913 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid character 0x%x ('%c'), perhaps \"%.*s\" is supposed to be a function call.",
2914 expr[parsed], expr[parsed], ret->tok_len[ret->used - 1], &ret->expr[ret->tok_pos[ret->used - 1]]);
Radek Krejcib1646a92018-11-02 16:08:26 +01002915 goto error;
2916 } else {
Radek Krejcid4270262019-01-07 15:07:25 +01002917 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed + 1, expr);
Radek Krejcib1646a92018-11-02 16:08:26 +01002918 goto error;
2919 }
2920 } else if (expr[parsed] == '*') {
2921
2922 /* NameTest '*' */
2923 tok_len = 1;
2924 tok_type = LYXP_TOKEN_NAMETEST;
2925
2926 } else {
2927
2928 /* NameTest (NCName ':' '*' | QName) or NodeType/FunctionName */
2929 ncname_len = parse_ncname(&expr[parsed]);
Radek Krejcid4270262019-01-07 15:07:25 +01002930 LY_CHECK_ERR_GOTO(ncname_len < 0, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed - ncname_len + 1, expr), error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002931 tok_len = ncname_len;
2932
2933 if (expr[parsed + tok_len] == ':') {
2934 ++tok_len;
2935 if (expr[parsed + tok_len] == '*') {
2936 ++tok_len;
2937 } else {
2938 ncname_len = parse_ncname(&expr[parsed + tok_len]);
Radek Krejcid4270262019-01-07 15:07:25 +01002939 LY_CHECK_ERR_GOTO(ncname_len < 0, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed - ncname_len + 1, expr), error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002940 tok_len += ncname_len;
2941 }
2942 /* remove old flag to prevent ambiguities */
2943 prev_function_check = 0;
2944 tok_type = LYXP_TOKEN_NAMETEST;
2945 } else {
2946 /* there is no prefix so it can still be NodeType/FunctionName, we can't finally decide now */
2947 prev_function_check = 1;
2948 tok_type = LYXP_TOKEN_NAMETEST;
2949 }
2950 }
2951
2952 /* store the token, move on to the next one */
2953 LY_CHECK_GOTO(exp_add_token(ctx, ret, tok_type, parsed, tok_len), error);
2954 parsed += tok_len;
2955 while (is_xmlws(expr[parsed])) {
2956 ++parsed;
2957 }
2958
2959 } while (expr[parsed]);
2960
Michal Vasko004d3152020-06-11 19:59:22 +02002961 if (reparse) {
2962 /* prealloc repeat */
2963 ret->repeat = calloc(ret->size, sizeof *ret->repeat);
2964 LY_CHECK_ERR_GOTO(!ret->repeat, LOGMEM(ctx), error);
Radek Krejcib1646a92018-11-02 16:08:26 +01002965
Michal Vasko004d3152020-06-11 19:59:22 +02002966 /* fill repeat */
2967 LY_CHECK_GOTO(reparse_or_expr(ctx, ret, &tok_idx), error);
2968 if (ret->used > tok_idx) {
2969 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of an XPath expression.",
2970 &ret->expr[ret->tok_pos[tok_idx]]);
2971 goto error;
2972 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02002973 }
2974
2975 print_expr_struct_debug(ret);
2976
Radek Krejcib1646a92018-11-02 16:08:26 +01002977 return ret;
2978
2979error:
2980 lyxp_expr_free(ctx, ret);
2981 return NULL;
2982}
2983
Michal Vasko004d3152020-06-11 19:59:22 +02002984struct lyxp_expr *
2985lyxp_expr_dup(const struct ly_ctx *ctx, const struct lyxp_expr *exp)
2986{
2987 struct lyxp_expr *dup;
2988 uint32_t i, j;
2989
2990 dup = calloc(1, sizeof *dup);
2991 LY_CHECK_ERR_GOTO(!dup, LOGMEM(ctx), error);
2992
2993 dup->tokens = malloc(exp->used * sizeof *dup->tokens);
2994 LY_CHECK_ERR_GOTO(!dup->tokens, LOGMEM(ctx), error);
2995 memcpy(dup->tokens, exp->tokens, exp->used * sizeof *dup->tokens);
2996
2997 dup->tok_pos = malloc(exp->used * sizeof *dup->tok_pos);
2998 LY_CHECK_ERR_GOTO(!dup->tok_pos, LOGMEM(ctx), error);
2999 memcpy(dup->tok_pos, exp->tok_pos, exp->used * sizeof *dup->tok_pos);
3000
3001 dup->tok_len = malloc(exp->used * sizeof *dup->tok_len);
3002 LY_CHECK_ERR_GOTO(!dup->tok_len, LOGMEM(ctx), error);
3003 memcpy(dup->tok_len, exp->tok_len, exp->used * sizeof *dup->tok_len);
3004
3005 dup->repeat = malloc(exp->used * sizeof *dup->repeat);
3006 LY_CHECK_ERR_GOTO(!dup->repeat, LOGMEM(ctx), error);
3007 for (i = 0; i < exp->used; ++i) {
3008 if (!exp->repeat[i]) {
3009 dup->repeat[i] = NULL;
3010 } else {
3011 for (j = 0; exp->repeat[i][j]; ++j);
3012 /* the ending 0 as well */
3013 ++j;
3014
Michal Vasko99c71642020-07-03 13:33:36 +02003015 dup->repeat[i] = malloc(j * sizeof **dup->repeat);
Michal Vasko004d3152020-06-11 19:59:22 +02003016 LY_CHECK_ERR_GOTO(!dup->repeat[i], LOGMEM(ctx), error);
3017 memcpy(dup->repeat[i], exp->repeat[i], j * sizeof **dup->repeat);
3018 dup->repeat[i][j - 1] = 0;
3019 }
3020 }
3021
3022 dup->used = exp->used;
3023 dup->size = exp->used;
3024 dup->expr = lydict_insert(ctx, exp->expr, 0);
3025
3026 return dup;
3027
3028error:
3029 lyxp_expr_free(ctx, dup);
3030 return NULL;
3031}
3032
Michal Vasko03ff5a72019-09-11 13:49:33 +02003033/*
3034 * warn functions
3035 *
3036 * Warn functions check specific reasonable conditions for schema XPath
3037 * and print a warning if they are not satisfied.
3038 */
3039
3040/**
3041 * @brief Get the last-added schema node that is currently in the context.
3042 *
3043 * @param[in] set Set to search in.
3044 * @return Last-added schema context node, NULL if no node is in context.
3045 */
3046static struct lysc_node *
3047warn_get_scnode_in_ctx(struct lyxp_set *set)
3048{
3049 uint32_t i;
3050
3051 if (!set || (set->type != LYXP_SET_SCNODE_SET)) {
3052 return NULL;
3053 }
3054
3055 i = set->used;
3056 do {
3057 --i;
3058 if (set->val.scnodes[i].in_ctx == 1) {
3059 /* if there are more, simply return the first found (last added) */
3060 return set->val.scnodes[i].scnode;
3061 }
3062 } while (i);
3063
3064 return NULL;
3065}
3066
3067/**
3068 * @brief Test whether a type is numeric - integer type or decimal64.
3069 *
3070 * @param[in] type Type to test.
3071 * @return 1 if numeric, 0 otherwise.
3072 */
3073static int
3074warn_is_numeric_type(struct lysc_type *type)
3075{
3076 struct lysc_type_union *uni;
3077 int ret;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003078 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003079
3080 switch (type->basetype) {
3081 case LY_TYPE_DEC64:
3082 case LY_TYPE_INT8:
3083 case LY_TYPE_UINT8:
3084 case LY_TYPE_INT16:
3085 case LY_TYPE_UINT16:
3086 case LY_TYPE_INT32:
3087 case LY_TYPE_UINT32:
3088 case LY_TYPE_INT64:
3089 case LY_TYPE_UINT64:
3090 return 1;
3091 case LY_TYPE_UNION:
3092 uni = (struct lysc_type_union *)type;
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003093 LY_ARRAY_FOR(uni->types, u) {
3094 ret = warn_is_numeric_type(uni->types[u]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003095 if (ret) {
3096 /* found a suitable type */
3097 return 1;
3098 }
3099 }
3100 /* did not find any suitable type */
3101 return 0;
3102 case LY_TYPE_LEAFREF:
3103 return warn_is_numeric_type(((struct lysc_type_leafref *)type)->realtype);
3104 default:
3105 return 0;
3106 }
3107}
3108
3109/**
3110 * @brief Test whether a type is string-like - no integers, decimal64 or binary.
3111 *
3112 * @param[in] type Type to test.
3113 * @return 1 if string, 0 otherwise.
3114 */
3115static int
3116warn_is_string_type(struct lysc_type *type)
3117{
3118 struct lysc_type_union *uni;
3119 int ret;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003120 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003121
3122 switch (type->basetype) {
3123 case LY_TYPE_BITS:
3124 case LY_TYPE_ENUM:
3125 case LY_TYPE_IDENT:
3126 case LY_TYPE_INST:
3127 case LY_TYPE_STRING:
3128 return 1;
3129 case LY_TYPE_UNION:
3130 uni = (struct lysc_type_union *)type;
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003131 LY_ARRAY_FOR(uni->types, u) {
3132 ret = warn_is_string_type(uni->types[u]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003133 if (ret) {
3134 /* found a suitable type */
3135 return 1;
3136 }
3137 }
3138 /* did not find any suitable type */
3139 return 0;
3140 case LY_TYPE_LEAFREF:
3141 return warn_is_string_type(((struct lysc_type_leafref *)type)->realtype);
3142 default:
3143 return 0;
3144 }
3145}
3146
3147/**
3148 * @brief Test whether a type is one specific type.
3149 *
3150 * @param[in] type Type to test.
3151 * @param[in] base Expected type.
3152 * @return 1 if it is, 0 otherwise.
3153 */
3154static int
3155warn_is_specific_type(struct lysc_type *type, LY_DATA_TYPE base)
3156{
3157 struct lysc_type_union *uni;
3158 int ret;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003159 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003160
3161 if (type->basetype == base) {
3162 return 1;
3163 } else if (type->basetype == LY_TYPE_UNION) {
3164 uni = (struct lysc_type_union *)type;
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003165 LY_ARRAY_FOR(uni->types, u) {
3166 ret = warn_is_specific_type(uni->types[u], base);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003167 if (ret) {
3168 /* found a suitable type */
3169 return 1;
3170 }
3171 }
3172 /* did not find any suitable type */
3173 return 0;
3174 } else if (type->basetype == LY_TYPE_LEAFREF) {
3175 return warn_is_specific_type(((struct lysc_type_leafref *)type)->realtype, base);
3176 }
3177
3178 return 0;
3179}
3180
3181/**
3182 * @brief Get next type of a (union) type.
3183 *
3184 * @param[in] type Base type.
3185 * @param[in] prev_type Previously returned type.
3186 * @return Next type or NULL.
3187 */
3188static struct lysc_type *
3189warn_is_equal_type_next_type(struct lysc_type *type, struct lysc_type *prev_type)
3190{
3191 struct lysc_type_union *uni;
3192 int found = 0;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003193 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003194
3195 switch (type->basetype) {
3196 case LY_TYPE_UNION:
3197 uni = (struct lysc_type_union *)type;
3198 if (!prev_type) {
3199 return uni->types[0];
3200 }
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003201 LY_ARRAY_FOR(uni->types, u) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003202 if (found) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003203 return uni->types[u];
Michal Vasko03ff5a72019-09-11 13:49:33 +02003204 }
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003205 if (prev_type == uni->types[u]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003206 found = 1;
3207 }
3208 }
3209 return NULL;
3210 default:
3211 if (prev_type) {
3212 assert(type == prev_type);
3213 return NULL;
3214 } else {
3215 return type;
3216 }
3217 }
3218}
3219
3220/**
3221 * @brief Test whether 2 types have a common type.
3222 *
3223 * @param[in] type1 First type.
3224 * @param[in] type2 Second type.
3225 * @return 1 if they do, 0 otherwise.
3226 */
3227static int
3228warn_is_equal_type(struct lysc_type *type1, struct lysc_type *type2)
3229{
3230 struct lysc_type *t1, *rt1, *t2, *rt2;
3231
3232 t1 = NULL;
3233 while ((t1 = warn_is_equal_type_next_type(type1, t1))) {
3234 if (t1->basetype == LY_TYPE_LEAFREF) {
3235 rt1 = ((struct lysc_type_leafref *)t1)->realtype;
3236 } else {
3237 rt1 = t1;
3238 }
3239
3240 t2 = NULL;
3241 while ((t2 = warn_is_equal_type_next_type(type2, t2))) {
3242 if (t2->basetype == LY_TYPE_LEAFREF) {
3243 rt2 = ((struct lysc_type_leafref *)t2)->realtype;
3244 } else {
3245 rt2 = t2;
3246 }
3247
3248 if (rt2->basetype == rt1->basetype) {
3249 /* match found */
3250 return 1;
3251 }
3252 }
3253 }
3254
3255 return 0;
3256}
3257
3258/**
3259 * @brief Check both operands of comparison operators.
3260 *
3261 * @param[in] ctx Context for errors.
3262 * @param[in] set1 First operand set.
3263 * @param[in] set2 Second operand set.
3264 * @param[in] numbers_only Whether accept only numbers or other types are fine too (for '=' and '!=').
3265 * @param[in] expr Start of the expression to print with the warning.
3266 * @param[in] tok_pos Token position.
3267 */
3268static void
3269warn_operands(struct ly_ctx *ctx, struct lyxp_set *set1, struct lyxp_set *set2, int numbers_only, const char *expr, uint16_t tok_pos)
3270{
3271 struct lysc_node_leaf *node1, *node2;
3272 int leaves = 1, warning = 0;
3273
3274 node1 = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set1);
3275 node2 = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set2);
3276
3277 if (!node1 && !node2) {
3278 /* no node-sets involved, nothing to do */
3279 return;
3280 }
3281
3282 if (node1) {
3283 if (!(node1->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3284 LOGWRN(ctx, "Node type %s \"%s\" used as operand.", lys_nodetype2str(node1->nodetype), node1->name);
3285 warning = 1;
3286 leaves = 0;
3287 } else if (numbers_only && !warn_is_numeric_type(node1->type)) {
3288 LOGWRN(ctx, "Node \"%s\" is not of a numeric type, but used where it was expected.", node1->name);
3289 warning = 1;
3290 }
3291 }
3292
3293 if (node2) {
3294 if (!(node2->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3295 LOGWRN(ctx, "Node type %s \"%s\" used as operand.", lys_nodetype2str(node2->nodetype), node2->name);
3296 warning = 1;
3297 leaves = 0;
3298 } else if (numbers_only && !warn_is_numeric_type(node2->type)) {
3299 LOGWRN(ctx, "Node \"%s\" is not of a numeric type, but used where it was expected.", node2->name);
3300 warning = 1;
3301 }
3302 }
3303
3304 if (node1 && node2 && leaves && !numbers_only) {
3305 if ((warn_is_numeric_type(node1->type) && !warn_is_numeric_type(node2->type))
3306 || (!warn_is_numeric_type(node1->type) && warn_is_numeric_type(node2->type))
3307 || (!warn_is_numeric_type(node1->type) && !warn_is_numeric_type(node2->type)
3308 && !warn_is_equal_type(node1->type, node2->type))) {
3309 LOGWRN(ctx, "Incompatible types of operands \"%s\" and \"%s\" for comparison.", node1->name, node2->name);
3310 warning = 1;
3311 }
3312 }
3313
3314 if (warning) {
3315 LOGWRN(ctx, "Previous warning generated by XPath subexpression[%u] \"%.20s\".", tok_pos, expr + tok_pos);
3316 }
3317}
3318
3319/**
3320 * @brief Check that a value is valid for a leaf. If not applicable, does nothing.
3321 *
3322 * @param[in] exp Parsed XPath expression.
3323 * @param[in] set Set with the leaf/leaf-list.
3324 * @param[in] val_exp Index of the value (literal/number) in @p exp.
3325 * @param[in] equal_exp Index of the start of the equality expression in @p exp.
3326 * @param[in] last_equal_exp Index of the end of the equality expression in @p exp.
3327 */
3328static void
3329warn_equality_value(struct lyxp_expr *exp, struct lyxp_set *set, uint16_t val_exp, uint16_t equal_exp, uint16_t last_equal_exp)
3330{
3331 struct lysc_node *scnode;
3332 struct lysc_type *type;
3333 char *value;
3334 LY_ERR rc;
3335 struct ly_err_item *err = NULL;
3336
3337 if ((scnode = warn_get_scnode_in_ctx(set)) && (scnode->nodetype & (LYS_LEAF | LYS_LEAFLIST))
3338 && ((exp->tokens[val_exp] == LYXP_TOKEN_LITERAL) || (exp->tokens[val_exp] == LYXP_TOKEN_NUMBER))) {
3339 /* check that the node can have the specified value */
3340 if (exp->tokens[val_exp] == LYXP_TOKEN_LITERAL) {
3341 value = strndup(exp->expr + exp->tok_pos[val_exp] + 1, exp->tok_len[val_exp] - 2);
3342 } else {
3343 value = strndup(exp->expr + exp->tok_pos[val_exp], exp->tok_len[val_exp]);
3344 }
3345 if (!value) {
3346 LOGMEM(set->ctx);
3347 return;
3348 }
3349
3350 if ((((struct lysc_node_leaf *)scnode)->type->basetype == LY_TYPE_IDENT) && !strchr(value, ':')) {
3351 LOGWRN(set->ctx, "Identityref \"%s\" comparison with identity \"%s\" without prefix, consider adding"
3352 " a prefix or best using \"derived-from(-or-self)()\" functions.", scnode->name, value);
3353 LOGWRN(set->ctx, "Previous warning generated by XPath subexpression[%u] \"%.*s\".", exp->tok_pos[equal_exp],
3354 (exp->tok_pos[last_equal_exp] - exp->tok_pos[equal_exp]) + exp->tok_len[last_equal_exp],
3355 exp->expr + exp->tok_pos[equal_exp]);
3356 }
3357
3358 type = ((struct lysc_node_leaf *)scnode)->type;
3359 if (type->basetype != LY_TYPE_IDENT) {
Michal Vasko93b18da2020-08-05 13:27:55 +02003360 rc = type->plugin->store(set->ctx, type, value, strlen(value), LY_TYPE_OPTS_SCHEMA, lys_resolve_prefix,
3361 (void *)set->local_mod, LYD_XML, NULL, NULL, NULL, NULL, &err);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003362
3363 if (err) {
3364 LOGWRN(set->ctx, "Invalid value \"%s\" which does not fit the type (%s).", value, err->msg);
3365 ly_err_free(err);
3366 } else if (rc != LY_SUCCESS) {
3367 LOGWRN(set->ctx, "Invalid value \"%s\" which does not fit the type.", value);
3368 }
3369 if (rc != LY_SUCCESS) {
3370 LOGWRN(set->ctx, "Previous warning generated by XPath subexpression[%u] \"%.*s\".", exp->tok_pos[equal_exp],
3371 (exp->tok_pos[last_equal_exp] - exp->tok_pos[equal_exp]) + exp->tok_len[last_equal_exp],
3372 exp->expr + exp->tok_pos[equal_exp]);
3373 }
3374 }
3375 free(value);
3376 }
3377}
3378
3379/*
3380 * XPath functions
3381 */
3382
3383/**
3384 * @brief Execute the YANG 1.1 bit-is-set(node-set, string) function. Returns LYXP_SET_BOOLEAN
3385 * depending on whether the first node bit value from the second argument is set.
3386 *
3387 * @param[in] args Array of arguments.
3388 * @param[in] arg_count Count of elements in @p args.
3389 * @param[in,out] set Context and result set at the same time.
3390 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003391 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003392 */
3393static LY_ERR
3394xpath_bit_is_set(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3395{
3396 struct lyd_node_term *leaf;
3397 struct lysc_node_leaf *sleaf;
3398 struct lysc_type_bits *bits;
3399 LY_ERR rc = LY_SUCCESS;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003400 LY_ARRAY_COUNT_TYPE u;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003401
3402 if (options & LYXP_SCNODE_ALL) {
3403 if ((args[0]->type != LYXP_SET_SCNODE_SET) || !(sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3404 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003405 } else if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3406 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 +02003407 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_BITS)) {
3408 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"bits\".", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003409 }
3410
3411 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
3412 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3413 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 +02003414 } else if (!warn_is_string_type(sleaf->type)) {
3415 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003416 }
3417 }
3418 set_scnode_clear_ctx(set);
3419 return rc;
3420 }
3421
Michal Vaskod3678892020-05-21 10:06:58 +02003422 if (args[0]->type != LYXP_SET_NODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003423 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "bit-is-set(node-set, string)");
3424 return LY_EVALID;
3425 }
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003426 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003427 LY_CHECK_RET(rc);
3428
3429 set_fill_boolean(set, 0);
Michal Vaskod3678892020-05-21 10:06:58 +02003430 if (args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003431 leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
3432 if ((leaf->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))
3433 && (((struct lysc_node_leaf *)leaf->schema)->type->basetype == LY_TYPE_BITS)) {
3434 bits = (struct lysc_type_bits *)((struct lysc_node_leaf *)leaf->schema)->type;
Radek Krejci7eb54ba2020-05-18 16:30:04 +02003435 LY_ARRAY_FOR(bits->bits, u) {
3436 if (!strcmp(bits->bits[u].name, args[1]->val.str)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003437 set_fill_boolean(set, 1);
3438 break;
3439 }
3440 }
3441 }
3442 }
3443
3444 return LY_SUCCESS;
3445}
3446
3447/**
3448 * @brief Execute the XPath boolean(object) function. Returns LYXP_SET_BOOLEAN
3449 * with the argument converted to boolean.
3450 *
3451 * @param[in] args Array of arguments.
3452 * @param[in] arg_count Count of elements in @p args.
3453 * @param[in,out] set Context and result set at the same time.
3454 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003455 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003456 */
3457static LY_ERR
3458xpath_boolean(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3459{
3460 LY_ERR rc;
3461
3462 if (options & LYXP_SCNODE_ALL) {
3463 set_scnode_clear_ctx(set);
3464 return LY_SUCCESS;
3465 }
3466
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003467 rc = lyxp_set_cast(args[0], LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003468 LY_CHECK_RET(rc);
3469 set_fill_set(set, args[0]);
3470
3471 return LY_SUCCESS;
3472}
3473
3474/**
3475 * @brief Execute the XPath ceiling(number) function. Returns LYXP_SET_NUMBER
3476 * with the first argument rounded up to the nearest integer.
3477 *
3478 * @param[in] args Array of arguments.
3479 * @param[in] arg_count Count of elements in @p args.
3480 * @param[in,out] set Context and result set at the same time.
3481 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003482 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003483 */
3484static LY_ERR
3485xpath_ceiling(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3486{
3487 struct lysc_node_leaf *sleaf;
3488 LY_ERR rc = LY_SUCCESS;
3489
3490 if (options & LYXP_SCNODE_ALL) {
3491 if ((args[0]->type != LYXP_SET_SCNODE_SET) || !(sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3492 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003493 } else if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3494 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 +02003495 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
3496 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003497 }
3498 set_scnode_clear_ctx(set);
3499 return rc;
3500 }
3501
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003502 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003503 LY_CHECK_RET(rc);
3504 if ((long long)args[0]->val.num != args[0]->val.num) {
3505 set_fill_number(set, ((long long)args[0]->val.num) + 1);
3506 } else {
3507 set_fill_number(set, args[0]->val.num);
3508 }
3509
3510 return LY_SUCCESS;
3511}
3512
3513/**
3514 * @brief Execute the XPath concat(string, string, string*) function.
3515 * Returns LYXP_SET_STRING with the concatenation of all the arguments.
3516 *
3517 * @param[in] args Array of arguments.
3518 * @param[in] arg_count Count of elements in @p args.
3519 * @param[in,out] set Context and result set at the same time.
3520 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003521 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003522 */
3523static LY_ERR
3524xpath_concat(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
3525{
3526 uint16_t i;
3527 char *str = NULL;
3528 size_t used = 1;
3529 LY_ERR rc = LY_SUCCESS;
3530 struct lysc_node_leaf *sleaf;
3531
3532 if (options & LYXP_SCNODE_ALL) {
3533 for (i = 0; i < arg_count; ++i) {
3534 if ((args[i]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[i]))) {
3535 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3536 LOGWRN(set->ctx, "Argument #%u of %s is a %s node \"%s\".",
3537 i + 1, __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003538 } else if (!warn_is_string_type(sleaf->type)) {
3539 LOGWRN(set->ctx, "Argument #%u of %s is node \"%s\", not of string-type.", __func__, i + 1, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003540 }
3541 }
3542 }
3543 set_scnode_clear_ctx(set);
3544 return rc;
3545 }
3546
3547 for (i = 0; i < arg_count; ++i) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003548 rc = lyxp_set_cast(args[i], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003549 if (rc != LY_SUCCESS) {
3550 free(str);
3551 return rc;
3552 }
3553
3554 str = ly_realloc(str, (used + strlen(args[i]->val.str)) * sizeof(char));
3555 LY_CHECK_ERR_RET(!str, LOGMEM(set->ctx), LY_EMEM);
3556 strcpy(str + used - 1, args[i]->val.str);
3557 used += strlen(args[i]->val.str);
3558 }
3559
3560 /* free, kind of */
Michal Vaskod3678892020-05-21 10:06:58 +02003561 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003562 set->type = LYXP_SET_STRING;
3563 set->val.str = str;
3564
3565 return LY_SUCCESS;
3566}
3567
3568/**
3569 * @brief Execute the XPath contains(string, string) function.
3570 * Returns LYXP_SET_BOOLEAN whether the second argument can
3571 * be found in the first or not.
3572 *
3573 * @param[in] args Array of arguments.
3574 * @param[in] arg_count Count of elements in @p args.
3575 * @param[in,out] set Context and result set at the same time.
3576 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003577 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003578 */
3579static LY_ERR
3580xpath_contains(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3581{
3582 struct lysc_node_leaf *sleaf;
3583 LY_ERR rc = LY_SUCCESS;
3584
3585 if (options & LYXP_SCNODE_ALL) {
3586 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3587 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3588 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 +02003589 } else if (!warn_is_string_type(sleaf->type)) {
3590 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003591 }
3592 }
3593
3594 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
3595 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3596 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 +02003597 } else if (!warn_is_string_type(sleaf->type)) {
3598 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003599 }
3600 }
3601 set_scnode_clear_ctx(set);
3602 return rc;
3603 }
3604
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003605 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003606 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01003607 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003608 LY_CHECK_RET(rc);
3609
3610 if (strstr(args[0]->val.str, args[1]->val.str)) {
3611 set_fill_boolean(set, 1);
3612 } else {
3613 set_fill_boolean(set, 0);
3614 }
3615
3616 return LY_SUCCESS;
3617}
3618
3619/**
3620 * @brief Execute the XPath count(node-set) function. Returns LYXP_SET_NUMBER
3621 * with the size of the node-set from the argument.
3622 *
3623 * @param[in] args Array of arguments.
3624 * @param[in] arg_count Count of elements in @p args.
3625 * @param[in,out] set Context and result set at the same time.
3626 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003627 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003628 */
3629static LY_ERR
3630xpath_count(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3631{
3632 struct lysc_node *scnode = NULL;
3633 LY_ERR rc = LY_SUCCESS;
3634
3635 if (options & LYXP_SCNODE_ALL) {
3636 if ((args[0]->type != LYXP_SET_SCNODE_SET) || !(scnode = warn_get_scnode_in_ctx(args[0]))) {
3637 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003638 }
3639 set_scnode_clear_ctx(set);
3640 return rc;
3641 }
3642
Michal Vasko03ff5a72019-09-11 13:49:33 +02003643 if (args[0]->type != LYXP_SET_NODE_SET) {
3644 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "count(node-set)");
3645 return LY_EVALID;
3646 }
3647
3648 set_fill_number(set, args[0]->used);
3649 return LY_SUCCESS;
3650}
3651
3652/**
3653 * @brief Execute the XPath current() function. Returns LYXP_SET_NODE_SET
3654 * with the context with the intial node.
3655 *
3656 * @param[in] args Array of arguments.
3657 * @param[in] arg_count Count of elements in @p args.
3658 * @param[in,out] set Context and result set at the same time.
3659 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003660 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003661 */
3662static LY_ERR
3663xpath_current(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
3664{
3665 if (arg_count || args) {
3666 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGCOUNT, arg_count, "current()");
3667 return LY_EVALID;
3668 }
3669
3670 if (options & LYXP_SCNODE_ALL) {
3671 set_scnode_clear_ctx(set);
3672
Michal Vaskoecd62de2019-11-13 12:35:11 +01003673 lyxp_set_scnode_insert_node(set, set->ctx_scnode, LYXP_NODE_ELEM);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003674 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02003675 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003676
3677 /* position is filled later */
3678 set_insert_node(set, set->ctx_node, 0, LYXP_NODE_ELEM, 0);
3679 }
3680
3681 return LY_SUCCESS;
3682}
3683
3684/**
3685 * @brief Execute the YANG 1.1 deref(node-set) function. Returns LYXP_SET_NODE_SET with either
3686 * leafref or instance-identifier target node(s).
3687 *
3688 * @param[in] args Array of arguments.
3689 * @param[in] arg_count Count of elements in @p args.
3690 * @param[in,out] set Context and result set at the same time.
3691 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003692 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003693 */
3694static LY_ERR
3695xpath_deref(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3696{
3697 struct lyd_node_term *leaf;
Michal Vasko42e497c2020-01-06 08:38:25 +01003698 struct lysc_node_leaf *sleaf = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +02003699 struct lysc_type_leafref *lref;
Michal Vaskoae9e4cb2019-09-25 08:43:05 +02003700 const struct lysc_node *target;
Michal Vasko004d3152020-06-11 19:59:22 +02003701 struct ly_path *p;
3702 struct lyd_node *node;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003703 char *errmsg = NULL;
3704 const char *val;
3705 int dynamic;
Michal Vasko00cbf532020-06-15 13:58:47 +02003706 uint8_t oper;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003707 LY_ERR rc = LY_SUCCESS;
3708
3709 if (options & LYXP_SCNODE_ALL) {
3710 if ((args[0]->type != LYXP_SET_SCNODE_SET) || !(sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3711 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003712 } else if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3713 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 +02003714 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_LEAFREF) && !warn_is_specific_type(sleaf->type, LY_TYPE_INST)) {
3715 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"leafref\" nor \"instance-identifier\".",
3716 __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003717 }
3718 set_scnode_clear_ctx(set);
Michal Vasko42e497c2020-01-06 08:38:25 +01003719 if (sleaf && (sleaf->type->basetype == LY_TYPE_LEAFREF)) {
Michal Vasko004d3152020-06-11 19:59:22 +02003720 lref = (struct lysc_type_leafref *)sleaf->type;
Michal Vasko00cbf532020-06-15 13:58:47 +02003721 oper = lysc_is_output((struct lysc_node *)sleaf) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
Michal Vasko004d3152020-06-11 19:59:22 +02003722
3723 /* it was already evaluated on schema, it must succeed */
3724 if (set->format == LYD_JSON) {
Michal Vasko00cbf532020-06-15 13:58:47 +02003725 rc = ly_path_compile(set->ctx, sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
3726 oper, LY_PATH_TARGET_MANY, lydjson_resolve_prefix, NULL, LYD_JSON, &p);
Michal Vasko004d3152020-06-11 19:59:22 +02003727 } else {
3728 assert(set->format == LYD_SCHEMA);
Michal Vasko00cbf532020-06-15 13:58:47 +02003729 rc = ly_path_compile(set->ctx, sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
3730 oper, LY_PATH_TARGET_MANY, lys_resolve_prefix, lref->path_context, LYD_SCHEMA, &p);
Michal Vaskoae9e4cb2019-09-25 08:43:05 +02003731 }
Michal Vasko004d3152020-06-11 19:59:22 +02003732 assert(!rc);
3733
3734 /* get the target node */
Michal Vaskofd69e1d2020-07-03 11:57:17 +02003735 target = p[LY_ARRAY_COUNT(p) - 1].node;
Michal Vasko004d3152020-06-11 19:59:22 +02003736 ly_path_free(set->ctx, p);
3737
3738 lyxp_set_scnode_insert_node(set, target, LYXP_NODE_ELEM);
Michal Vaskoae9e4cb2019-09-25 08:43:05 +02003739 }
3740
Michal Vasko03ff5a72019-09-11 13:49:33 +02003741 return rc;
3742 }
3743
Michal Vaskod3678892020-05-21 10:06:58 +02003744 if (args[0]->type != LYXP_SET_NODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003745 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "deref(node-set)");
3746 return LY_EVALID;
3747 }
3748
Michal Vaskod3678892020-05-21 10:06:58 +02003749 lyxp_set_free_content(set);
3750 if (args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003751 leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
3752 sleaf = (struct lysc_node_leaf *)leaf->schema;
3753 if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
3754 if (sleaf->type->basetype == LY_TYPE_LEAFREF) {
3755 /* find leafref target */
Michal Vasko004d3152020-06-11 19:59:22 +02003756 if (ly_type_find_leafref((struct lysc_type_leafref *)sleaf->type, (struct lyd_node *)leaf,
3757 &leaf->value, set->tree, &node, &errmsg)) {
3758 LOGERR(set->ctx, LY_EVALID, errmsg);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003759 free(errmsg);
Michal Vasko004d3152020-06-11 19:59:22 +02003760 return LY_EVALID;
Michal Vasko03ff5a72019-09-11 13:49:33 +02003761 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02003762 } else {
3763 assert(sleaf->type->basetype == LY_TYPE_INST);
Michal Vasko004d3152020-06-11 19:59:22 +02003764 if (ly_path_eval(leaf->value.target, set->tree, &node)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003765 val = lyd_value2str(leaf, &dynamic);
3766 LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.", val);
3767 if (dynamic) {
3768 free((char *)val);
3769 }
3770 return LY_EVALID;
3771 }
3772 }
Michal Vasko004d3152020-06-11 19:59:22 +02003773
3774 /* insert it */
3775 set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003776 }
3777 }
3778
3779 return LY_SUCCESS;
3780}
3781
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02003782static LY_ERR
3783xpath_derived_(struct lyxp_set **args, struct lyxp_set *set, int options, int self_match, const char *func)
3784{
3785 uint16_t i;
3786 LY_ARRAY_COUNT_TYPE u;
3787 struct lyd_node_term *leaf;
3788 struct lysc_node_leaf *sleaf;
3789 struct lyd_meta *meta;
3790 struct lyd_value data = {0}, *val;
3791 struct ly_err_item *err = NULL;
3792 LY_ERR rc = LY_SUCCESS;
3793 int found;
3794
3795 if (options & LYXP_SCNODE_ALL) {
3796 if ((args[0]->type != LYXP_SET_SCNODE_SET) || !(sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3797 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", func);
3798 } else if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3799 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", func, lys_nodetype2str(sleaf->nodetype),
3800 sleaf->name);
3801 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_IDENT)) {
3802 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"identityref\".", func, sleaf->name);
3803 }
3804
3805 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
3806 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3807 LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", func, lys_nodetype2str(sleaf->nodetype),
3808 sleaf->name);
3809 } else if (!warn_is_string_type(sleaf->type)) {
3810 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", func, sleaf->name);
3811 }
3812 }
3813 set_scnode_clear_ctx(set);
3814 return rc;
3815 }
3816
3817 if (args[0]->type != LYXP_SET_NODE_SET) {
3818 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]),
3819 "derived-from(-or-self)(node-set, string)");
3820 return LY_EVALID;
3821 }
3822 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
3823 LY_CHECK_RET(rc);
3824
3825 set_fill_boolean(set, 0);
3826 found = 0;
3827 for (i = 0; i < args[0]->used; ++i) {
3828 if ((args[0]->val.nodes[i].type != LYXP_NODE_ELEM) && (args[0]->val.nodes[i].type != LYXP_NODE_META)) {
3829 continue;
3830 }
3831
3832 if (args[0]->val.nodes[i].type == LYXP_NODE_ELEM) {
3833 leaf = (struct lyd_node_term *)args[0]->val.nodes[i].node;
3834 sleaf = (struct lysc_node_leaf *)leaf->schema;
3835 val = &leaf->value;
3836 if (!(sleaf->nodetype & LYD_NODE_TERM) || (leaf->value.realtype->basetype != LY_TYPE_IDENT)) {
3837 /* uninteresting */
3838 continue;
3839 }
3840
3841 /* store args[1] as ident */
3842 rc = val->realtype->plugin->store(set->ctx, val->realtype, args[1]->val.str, strlen(args[1]->val.str),
Michal Vasko93b18da2020-08-05 13:27:55 +02003843 LY_TYPE_OPTS_STORE, lys_resolve_prefix, (void *)set->local_mod,
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02003844 set->format, (struct lyd_node *)leaf, set->tree, &data, NULL, &err);
3845 } else {
3846 meta = args[0]->val.meta[i].meta;
3847 val = &meta->value;
3848 if (val->realtype->basetype != LY_TYPE_IDENT) {
3849 /* uninteresting */
3850 continue;
3851 }
3852
3853 /* store args[1] as ident */
3854 rc = val->realtype->plugin->store(set->ctx, val->realtype, args[1]->val.str, strlen(args[1]->val.str),
3855 LY_TYPE_OPTS_STORE, lys_resolve_prefix, (void *)meta->annotation->module,
3856 set->format, meta->parent, set->tree, &data, NULL, &err);
3857 }
3858
3859 if (err) {
3860 ly_err_print(err);
3861 ly_err_free(err);
3862 }
3863 LY_CHECK_RET(rc);
3864
3865 /* finally check the identity itself */
3866 if (self_match && (data.ident == val->ident)) {
3867 set_fill_boolean(set, 1);
3868 found = 1;
3869 }
3870 if (!found) {
3871 LY_ARRAY_FOR(data.ident->derived, u) {
3872 if (data.ident->derived[u] == val->ident) {
3873 set_fill_boolean(set, 1);
3874 found = 1;
3875 break;
3876 }
3877 }
3878 }
3879
3880 /* free temporary value */
3881 val->realtype->plugin->free(set->ctx, &data);
3882 if (found) {
3883 break;
3884 }
3885 }
3886
3887 return LY_SUCCESS;
3888}
3889
Michal Vasko03ff5a72019-09-11 13:49:33 +02003890/**
3891 * @brief Execute the YANG 1.1 derived-from(node-set, string) function. Returns LYXP_SET_BOOLEAN depending
3892 * on whether the first argument nodes contain a node of an identity derived from the second
3893 * argument identity.
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
3902xpath_derived_from(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3903{
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02003904 return xpath_derived_(args, set, options, 0, __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003905}
3906
3907/**
3908 * @brief Execute the YANG 1.1 derived-from-or-self(node-set, string) function. Returns LYXP_SET_BOOLEAN depending
3909 * on whether the first argument nodes contain a node of an identity that either is or is derived from
3910 * the second argument identity.
3911 *
3912 * @param[in] args Array of arguments.
3913 * @param[in] arg_count Count of elements in @p args.
3914 * @param[in,out] set Context and result set at the same time.
3915 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003916 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003917 */
3918static LY_ERR
3919xpath_derived_from_or_self(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3920{
Michal Vaskoe0dd59d2020-07-17 16:10:23 +02003921 return xpath_derived_(args, set, options, 1, __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003922}
3923
3924/**
3925 * @brief Execute the YANG 1.1 enum-value(node-set) function. Returns LYXP_SET_NUMBER
3926 * with the integer value of the first node's enum value, otherwise NaN.
3927 *
3928 * @param[in] args Array of arguments.
3929 * @param[in] arg_count Count of elements in @p args.
3930 * @param[in,out] set Context and result set at the same time.
3931 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003932 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003933 */
3934static LY_ERR
3935xpath_enum_value(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3936{
3937 struct lyd_node_term *leaf;
3938 struct lysc_node_leaf *sleaf;
3939 LY_ERR rc = LY_SUCCESS;
3940
3941 if (options & LYXP_SCNODE_ALL) {
3942 if ((args[0]->type != LYXP_SET_SCNODE_SET) || !(sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
3943 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003944 } else if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
3945 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 +02003946 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_ENUM)) {
3947 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"enumeration\".", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02003948 }
3949 set_scnode_clear_ctx(set);
3950 return rc;
3951 }
3952
Michal Vaskod3678892020-05-21 10:06:58 +02003953 if (args[0]->type != LYXP_SET_NODE_SET) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003954 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "enum-value(node-set)");
3955 return LY_EVALID;
3956 }
3957
3958 set_fill_number(set, NAN);
Michal Vaskod3678892020-05-21 10:06:58 +02003959 if (args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02003960 leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
3961 sleaf = (struct lysc_node_leaf *)leaf->schema;
3962 if ((sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && (sleaf->type->basetype == LY_TYPE_ENUM)) {
3963 set_fill_number(set, leaf->value.enum_item->value);
3964 }
3965 }
3966
3967 return LY_SUCCESS;
3968}
3969
3970/**
3971 * @brief Execute the XPath false() function. Returns LYXP_SET_BOOLEAN
3972 * with false value.
3973 *
3974 * @param[in] args Array of arguments.
3975 * @param[in] arg_count Count of elements in @p args.
3976 * @param[in,out] set Context and result set at the same time.
3977 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01003978 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02003979 */
3980static LY_ERR
3981xpath_false(struct lyxp_set **UNUSED(args), uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
3982{
3983 if (options & LYXP_SCNODE_ALL) {
3984 set_scnode_clear_ctx(set);
3985 return LY_SUCCESS;
3986 }
3987
3988 set_fill_boolean(set, 0);
3989 return LY_SUCCESS;
3990}
3991
3992/**
3993 * @brief Execute the XPath floor(number) function. Returns LYXP_SET_NUMBER
3994 * with the first argument floored (truncated).
3995 *
3996 * @param[in] args Array of arguments.
3997 * @param[in] arg_count Count of elements in @p args.
3998 * @param[in,out] set Context and result set at the same time.
3999 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004000 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004001 */
4002static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004003xpath_floor(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int UNUSED(options))
Michal Vasko03ff5a72019-09-11 13:49:33 +02004004{
4005 LY_ERR rc;
4006
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004007 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004008 LY_CHECK_RET(rc);
4009 if (isfinite(args[0]->val.num)) {
4010 set_fill_number(set, (long long)args[0]->val.num);
4011 }
4012
4013 return LY_SUCCESS;
4014}
4015
4016/**
4017 * @brief Execute the XPath lang(string) function. Returns LYXP_SET_BOOLEAN
4018 * whether the language of the text matches the one from the argument.
4019 *
4020 * @param[in] args Array of arguments.
4021 * @param[in] arg_count Count of elements in @p args.
4022 * @param[in,out] set Context and result set at the same time.
4023 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004024 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004025 */
4026static LY_ERR
4027xpath_lang(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4028{
4029 const struct lyd_node *node;
4030 struct lysc_node_leaf *sleaf;
Michal Vasko9f96a052020-03-10 09:41:45 +01004031 struct lyd_meta *meta = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004032 const char *val;
4033 int i, dynamic;
4034 LY_ERR rc = LY_SUCCESS;
4035
4036 if (options & LYXP_SCNODE_ALL) {
4037 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4038 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4039 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 +02004040 } else if (!warn_is_string_type(sleaf->type)) {
4041 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004042 }
4043 }
4044 set_scnode_clear_ctx(set);
4045 return rc;
4046 }
4047
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004048 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004049 LY_CHECK_RET(rc);
4050
Michal Vasko03ff5a72019-09-11 13:49:33 +02004051 if (set->type != LYXP_SET_NODE_SET) {
4052 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INCTX, print_set_type(set), "lang(string)");
4053 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004054 } else if (!set->used) {
4055 set_fill_boolean(set, 0);
4056 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004057 }
4058
4059 switch (set->val.nodes[0].type) {
4060 case LYXP_NODE_ELEM:
4061 case LYXP_NODE_TEXT:
4062 node = set->val.nodes[0].node;
4063 break;
Michal Vasko9f96a052020-03-10 09:41:45 +01004064 case LYXP_NODE_META:
4065 node = set->val.meta[0].meta->parent;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004066 break;
4067 default:
4068 /* nothing to do with roots */
4069 set_fill_boolean(set, 0);
4070 return LY_SUCCESS;
4071 }
4072
Michal Vasko9f96a052020-03-10 09:41:45 +01004073 /* find lang metadata */
Michal Vasko03ff5a72019-09-11 13:49:33 +02004074 for (; node; node = (struct lyd_node *)node->parent) {
Michal Vasko9f96a052020-03-10 09:41:45 +01004075 for (meta = node->meta; meta; meta = meta->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004076 /* annotations */
Michal Vasko9f96a052020-03-10 09:41:45 +01004077 if (meta->name && !strcmp(meta->name, "lang") && !strcmp(meta->annotation->module->name, "xml")) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004078 break;
4079 }
4080 }
4081
Michal Vasko9f96a052020-03-10 09:41:45 +01004082 if (meta) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004083 break;
4084 }
4085 }
4086
4087 /* compare languages */
Michal Vasko9f96a052020-03-10 09:41:45 +01004088 if (!meta) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004089 set_fill_boolean(set, 0);
4090 } else {
Michal Vasko9f96a052020-03-10 09:41:45 +01004091 val = lyd_meta2str(meta, &dynamic);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004092 for (i = 0; args[0]->val.str[i]; ++i) {
4093 if (tolower(args[0]->val.str[i]) != tolower(val[i])) {
4094 set_fill_boolean(set, 0);
4095 break;
4096 }
4097 }
4098 if (!args[0]->val.str[i]) {
4099 if (!val[i] || (val[i] == '-')) {
4100 set_fill_boolean(set, 1);
4101 } else {
4102 set_fill_boolean(set, 0);
4103 }
4104 }
4105 if (dynamic) {
4106 free((char *)val);
4107 }
4108 }
4109
4110 return LY_SUCCESS;
4111}
4112
4113/**
4114 * @brief Execute the XPath last() function. Returns LYXP_SET_NUMBER
4115 * with the context size.
4116 *
4117 * @param[in] args Array of arguments.
4118 * @param[in] arg_count Count of elements in @p args.
4119 * @param[in,out] set Context and result set at the same time.
4120 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004121 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004122 */
4123static LY_ERR
4124xpath_last(struct lyxp_set **UNUSED(args), uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4125{
4126 if (options & LYXP_SCNODE_ALL) {
4127 set_scnode_clear_ctx(set);
4128 return LY_SUCCESS;
4129 }
4130
Michal Vasko03ff5a72019-09-11 13:49:33 +02004131 if (set->type != LYXP_SET_NODE_SET) {
4132 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INCTX, print_set_type(set), "last()");
4133 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004134 } else if (!set->used) {
4135 set_fill_number(set, 0);
4136 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004137 }
4138
4139 set_fill_number(set, set->ctx_size);
4140 return LY_SUCCESS;
4141}
4142
4143/**
4144 * @brief Execute the XPath local-name(node-set?) function. Returns LYXP_SET_STRING
4145 * with the node name without namespace from the argument or the context.
4146 *
4147 * @param[in] args Array of arguments.
4148 * @param[in] arg_count Count of elements in @p args.
4149 * @param[in,out] set Context and result set at the same time.
4150 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004151 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004152 */
4153static LY_ERR
4154xpath_local_name(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4155{
4156 struct lyxp_set_node *item;
4157 /* suppress unused variable warning */
4158 (void)options;
4159
4160 if (options & LYXP_SCNODE_ALL) {
4161 set_scnode_clear_ctx(set);
4162 return LY_SUCCESS;
4163 }
4164
4165 if (arg_count) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004166 if (args[0]->type != LYXP_SET_NODE_SET) {
4167 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "local-name(node-set?)");
4168 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004169 } else if (!args[0]->used) {
4170 set_fill_string(set, "", 0);
4171 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004172 }
4173
4174 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004175 assert(!set_sort(args[0]));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004176
4177 item = &args[0]->val.nodes[0];
4178 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004179 if (set->type != LYXP_SET_NODE_SET) {
4180 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INCTX, print_set_type(set), "local-name(node-set?)");
4181 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004182 } else if (!set->used) {
4183 set_fill_string(set, "", 0);
4184 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004185 }
4186
4187 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004188 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004189
4190 item = &set->val.nodes[0];
4191 }
4192
4193 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +01004194 case LYXP_NODE_NONE:
4195 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004196 case LYXP_NODE_ROOT:
4197 case LYXP_NODE_ROOT_CONFIG:
4198 case LYXP_NODE_TEXT:
4199 set_fill_string(set, "", 0);
4200 break;
4201 case LYXP_NODE_ELEM:
4202 set_fill_string(set, item->node->schema->name, strlen(item->node->schema->name));
4203 break;
Michal Vasko9f96a052020-03-10 09:41:45 +01004204 case LYXP_NODE_META:
4205 set_fill_string(set, ((struct lyd_meta *)item->node)->name, strlen(((struct lyd_meta *)item->node)->name));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004206 break;
4207 }
4208
4209 return LY_SUCCESS;
4210}
4211
4212/**
4213 * @brief Execute the XPath name(node-set?) function. Returns LYXP_SET_STRING
4214 * with the node name fully qualified (with namespace) from the argument or the context.
4215 *
4216 * @param[in] args Array of arguments.
4217 * @param[in] arg_count Count of elements in @p args.
4218 * @param[in,out] set Context and result set at the same time.
4219 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004220 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004221 */
4222static LY_ERR
4223xpath_name(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4224{
4225 struct lyxp_set_node *item;
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02004226 struct lys_module *mod = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004227 char *str;
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02004228 const char *name = NULL;
4229 int rc = -1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004230
4231 if (options & LYXP_SCNODE_ALL) {
4232 set_scnode_clear_ctx(set);
4233 return LY_SUCCESS;
4234 }
4235
4236 if (arg_count) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004237 if (args[0]->type != LYXP_SET_NODE_SET) {
4238 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "name(node-set?)");
4239 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004240 } else if (!args[0]->used) {
4241 set_fill_string(set, "", 0);
4242 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004243 }
4244
4245 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004246 assert(!set_sort(args[0]));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004247
4248 item = &args[0]->val.nodes[0];
4249 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004250 if (set->type != LYXP_SET_NODE_SET) {
4251 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INCTX, print_set_type(set), "name(node-set?)");
4252 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004253 } else if (!set->used) {
4254 set_fill_string(set, "", 0);
4255 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004256 }
4257
4258 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004259 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004260
4261 item = &set->val.nodes[0];
4262 }
4263
4264 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +01004265 case LYXP_NODE_NONE:
4266 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004267 case LYXP_NODE_ROOT:
4268 case LYXP_NODE_ROOT_CONFIG:
4269 case LYXP_NODE_TEXT:
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02004270 /* keep NULL */
Michal Vasko03ff5a72019-09-11 13:49:33 +02004271 break;
4272 case LYXP_NODE_ELEM:
4273 mod = item->node->schema->module;
4274 name = item->node->schema->name;
4275 break;
Michal Vasko9f96a052020-03-10 09:41:45 +01004276 case LYXP_NODE_META:
4277 mod = ((struct lyd_meta *)item->node)->annotation->module;
4278 name = ((struct lyd_meta *)item->node)->name;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004279 break;
4280 }
4281
4282 if (mod && name) {
4283 switch (set->format) {
Michal Vasko52927e22020-03-16 17:26:14 +01004284 case LYD_SCHEMA:
Michal Vasko03ff5a72019-09-11 13:49:33 +02004285 rc = asprintf(&str, "%s:%s", lys_prefix_find_module(set->local_mod, mod), name);
4286 break;
4287 case LYD_JSON:
4288 rc = asprintf(&str, "%s:%s", mod->name, name);
4289 break;
Michal Vasko52927e22020-03-16 17:26:14 +01004290 case LYD_XML:
Michal Vasko60ea6352020-06-29 13:39:39 +02004291 case LYD_LYB:
Michal Vasko9409ef62019-09-12 11:47:17 +02004292 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004293 }
4294 LY_CHECK_ERR_RET(rc == -1, LOGMEM(set->ctx), LY_EMEM);
4295 set_fill_string(set, str, strlen(str));
4296 free(str);
4297 } else {
4298 set_fill_string(set, "", 0);
4299 }
4300
4301 return LY_SUCCESS;
4302}
4303
4304/**
4305 * @brief Execute the XPath namespace-uri(node-set?) function. Returns LYXP_SET_STRING
4306 * with the namespace of the node from the argument or the context.
4307 *
4308 * @param[in] args Array of arguments.
4309 * @param[in] arg_count Count of elements in @p args.
4310 * @param[in,out] set Context and result set at the same time.
4311 * @param[in] options XPath options.
4312 * @return LY_ERR (LY_EINVAL for wrong arguments on schema)
4313 */
4314static LY_ERR
4315xpath_namespace_uri(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4316{
4317 struct lyxp_set_node *item;
4318 struct lys_module *mod;
4319 /* suppress unused variable warning */
4320 (void)options;
4321
4322 if (options & LYXP_SCNODE_ALL) {
4323 set_scnode_clear_ctx(set);
4324 return LY_SUCCESS;
4325 }
4326
4327 if (arg_count) {
Michal Vaskod3678892020-05-21 10:06:58 +02004328 if (args[0]->type != LYXP_SET_NODE_SET) {
4329 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]),
4330 "namespace-uri(node-set?)");
4331 return LY_EVALID;
4332 } else if (!args[0]->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004333 set_fill_string(set, "", 0);
4334 return LY_SUCCESS;
4335 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02004336
4337 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004338 assert(!set_sort(args[0]));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004339
4340 item = &args[0]->val.nodes[0];
4341 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004342 if (set->type != LYXP_SET_NODE_SET) {
4343 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INCTX, print_set_type(set), "namespace-uri(node-set?)");
4344 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004345 } else if (!set->used) {
4346 set_fill_string(set, "", 0);
4347 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004348 }
4349
4350 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004351 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02004352
4353 item = &set->val.nodes[0];
4354 }
4355
4356 switch (item->type) {
Michal Vasko2caefc12019-11-14 16:07:56 +01004357 case LYXP_NODE_NONE:
4358 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004359 case LYXP_NODE_ROOT:
4360 case LYXP_NODE_ROOT_CONFIG:
4361 case LYXP_NODE_TEXT:
4362 set_fill_string(set, "", 0);
4363 break;
4364 case LYXP_NODE_ELEM:
Michal Vasko9f96a052020-03-10 09:41:45 +01004365 case LYXP_NODE_META:
Michal Vasko03ff5a72019-09-11 13:49:33 +02004366 if (item->type == LYXP_NODE_ELEM) {
4367 mod = item->node->schema->module;
Michal Vasko9f96a052020-03-10 09:41:45 +01004368 } else { /* LYXP_NODE_META */
Michal Vasko03ff5a72019-09-11 13:49:33 +02004369 /* annotations */
Michal Vasko9f96a052020-03-10 09:41:45 +01004370 mod = ((struct lyd_meta *)item->node)->annotation->module;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004371 }
4372
4373 set_fill_string(set, mod->ns, strlen(mod->ns));
4374 break;
4375 }
4376
4377 return LY_SUCCESS;
4378}
4379
4380/**
4381 * @brief Execute the XPath node() function (node type). Returns LYXP_SET_NODE_SET
4382 * with only nodes from the context. In practice it either leaves the context
4383 * as it is or returns an empty node set.
4384 *
4385 * @param[in] args Array of arguments.
4386 * @param[in] arg_count Count of elements in @p args.
4387 * @param[in,out] set Context and result set at the same time.
4388 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004389 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004390 */
4391static LY_ERR
4392xpath_node(struct lyxp_set **UNUSED(args), uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4393{
4394 if (options & LYXP_SCNODE_ALL) {
4395 set_scnode_clear_ctx(set);
4396 return LY_SUCCESS;
4397 }
4398
4399 if (set->type != LYXP_SET_NODE_SET) {
Michal Vaskod3678892020-05-21 10:06:58 +02004400 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004401 }
4402 return LY_SUCCESS;
4403}
4404
4405/**
4406 * @brief Execute the XPath normalize-space(string?) function. Returns LYXP_SET_STRING
4407 * with normalized value (no leading, trailing, double white spaces) of the node
4408 * from the argument or the context.
4409 *
4410 * @param[in] args Array of arguments.
4411 * @param[in] arg_count Count of elements in @p args.
4412 * @param[in,out] set Context and result set at the same time.
4413 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004414 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004415 */
4416static LY_ERR
4417xpath_normalize_space(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4418{
4419 uint16_t i, new_used;
4420 char *new;
4421 int have_spaces = 0, space_before = 0;
4422 struct lysc_node_leaf *sleaf;
4423 LY_ERR rc = LY_SUCCESS;
4424
4425 if (options & LYXP_SCNODE_ALL) {
4426 if (arg_count && (args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4427 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4428 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 +02004429 } else if (!warn_is_string_type(sleaf->type)) {
4430 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004431 }
4432 }
4433 set_scnode_clear_ctx(set);
4434 return rc;
4435 }
4436
4437 if (arg_count) {
4438 set_fill_set(set, args[0]);
4439 }
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004440 rc = lyxp_set_cast(set, LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004441 LY_CHECK_RET(rc);
4442
4443 /* is there any normalization necessary? */
4444 for (i = 0; set->val.str[i]; ++i) {
4445 if (is_xmlws(set->val.str[i])) {
4446 if ((i == 0) || space_before || (!set->val.str[i + 1])) {
4447 have_spaces = 1;
4448 break;
4449 }
4450 space_before = 1;
4451 } else {
4452 space_before = 0;
4453 }
4454 }
4455
4456 /* yep, there is */
4457 if (have_spaces) {
4458 /* it's enough, at least one character will go, makes space for ending '\0' */
4459 new = malloc(strlen(set->val.str) * sizeof(char));
4460 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
4461 new_used = 0;
4462
4463 space_before = 0;
4464 for (i = 0; set->val.str[i]; ++i) {
4465 if (is_xmlws(set->val.str[i])) {
4466 if ((i == 0) || space_before) {
4467 space_before = 1;
4468 continue;
4469 } else {
4470 space_before = 1;
4471 }
4472 } else {
4473 space_before = 0;
4474 }
4475
4476 new[new_used] = (space_before ? ' ' : set->val.str[i]);
4477 ++new_used;
4478 }
4479
4480 /* at worst there is one trailing space now */
4481 if (new_used && is_xmlws(new[new_used - 1])) {
4482 --new_used;
4483 }
4484
4485 new = ly_realloc(new, (new_used + 1) * sizeof(char));
4486 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
4487 new[new_used] = '\0';
4488
4489 free(set->val.str);
4490 set->val.str = new;
4491 }
4492
4493 return LY_SUCCESS;
4494}
4495
4496/**
4497 * @brief Execute the XPath not(boolean) function. Returns LYXP_SET_BOOLEAN
4498 * with the argument converted to boolean and logically inverted.
4499 *
4500 * @param[in] args Array of arguments.
4501 * @param[in] arg_count Count of elements in @p args.
4502 * @param[in,out] set Context and result set at the same time.
4503 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004504 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004505 */
4506static LY_ERR
4507xpath_not(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4508{
4509 if (options & LYXP_SCNODE_ALL) {
4510 set_scnode_clear_ctx(set);
4511 return LY_SUCCESS;
4512 }
4513
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004514 lyxp_set_cast(args[0], LYXP_SET_BOOLEAN);
Michal Vasko004d3152020-06-11 19:59:22 +02004515 if (args[0]->val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02004516 set_fill_boolean(set, 0);
4517 } else {
4518 set_fill_boolean(set, 1);
4519 }
4520
4521 return LY_SUCCESS;
4522}
4523
4524/**
4525 * @brief Execute the XPath number(object?) function. Returns LYXP_SET_NUMBER
4526 * with the number representation of either the argument or the context.
4527 *
4528 * @param[in] args Array of arguments.
4529 * @param[in] arg_count Count of elements in @p args.
4530 * @param[in,out] set Context and result set at the same time.
4531 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004532 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004533 */
4534static LY_ERR
4535xpath_number(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4536{
4537 LY_ERR rc;
4538
4539 if (options & LYXP_SCNODE_ALL) {
4540 set_scnode_clear_ctx(set);
4541 return LY_SUCCESS;
4542 }
4543
4544 if (arg_count) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004545 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004546 LY_CHECK_RET(rc);
4547 set_fill_set(set, args[0]);
4548 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004549 rc = lyxp_set_cast(set, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004550 LY_CHECK_RET(rc);
4551 }
4552
4553 return LY_SUCCESS;
4554}
4555
4556/**
4557 * @brief Execute the XPath position() function. Returns LYXP_SET_NUMBER
4558 * with the context position.
4559 *
4560 * @param[in] args Array of arguments.
4561 * @param[in] arg_count Count of elements in @p args.
4562 * @param[in,out] set Context and result set at the same time.
4563 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004564 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004565 */
4566static LY_ERR
4567xpath_position(struct lyxp_set **UNUSED(args), uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4568{
4569 if (options & LYXP_SCNODE_ALL) {
4570 set_scnode_clear_ctx(set);
4571 return LY_SUCCESS;
4572 }
4573
Michal Vasko03ff5a72019-09-11 13:49:33 +02004574 if (set->type != LYXP_SET_NODE_SET) {
4575 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INCTX, print_set_type(set), "position()");
4576 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02004577 } else if (!set->used) {
4578 set_fill_number(set, 0);
4579 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02004580 }
4581
4582 set_fill_number(set, set->ctx_pos);
4583
4584 /* UNUSED in 'Release' build type */
4585 (void)options;
4586 return LY_SUCCESS;
4587}
4588
4589/**
4590 * @brief Execute the YANG 1.1 re-match(string, string) function. Returns LYXP_SET_BOOLEAN
4591 * depending on whether the second argument regex matches the first argument string. For details refer to
4592 * YANG 1.1 RFC section 10.2.1.
4593 *
4594 * @param[in] args Array of arguments.
4595 * @param[in] arg_count Count of elements in @p args.
4596 * @param[in,out] set Context and result set at the same time.
4597 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004598 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004599 */
4600static LY_ERR
4601xpath_re_match(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4602{
4603 struct lysc_pattern **patterns = NULL, **pattern;
4604 struct lysc_node_leaf *sleaf;
4605 char *path;
4606 LY_ERR rc = LY_SUCCESS;
4607 struct ly_err_item *err;
4608
4609 if (options & LYXP_SCNODE_ALL) {
4610 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4611 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4612 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 +02004613 } else if (!warn_is_string_type(sleaf->type)) {
4614 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004615 }
4616 }
4617
4618 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
4619 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4620 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 +02004621 } else if (!warn_is_string_type(sleaf->type)) {
4622 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004623 }
4624 }
4625 set_scnode_clear_ctx(set);
4626 return rc;
4627 }
4628
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004629 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004630 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004631 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004632 LY_CHECK_RET(rc);
4633
4634 LY_ARRAY_NEW_RET(set->ctx, patterns, pattern, LY_EMEM);
4635 *pattern = malloc(sizeof **pattern);
4636 path = lyd_path(set->ctx_node, LYD_PATH_LOG, NULL, 0);
4637 rc = lys_compile_type_pattern_check(set->ctx, path, args[1]->val.str, &(*pattern)->code);
4638 free(path);
4639 if (rc != LY_SUCCESS) {
4640 LY_ARRAY_FREE(patterns);
4641 return rc;
4642 }
4643
4644 rc = ly_type_validate_patterns(patterns, args[0]->val.str, strlen(args[0]->val.str), &err);
4645 pcre2_code_free((*pattern)->code);
4646 free(*pattern);
4647 LY_ARRAY_FREE(patterns);
4648 if (rc && (rc != LY_EVALID)) {
4649 ly_err_print(err);
4650 ly_err_free(err);
4651 return rc;
4652 }
4653
4654 if (rc == LY_EVALID) {
4655 ly_err_free(err);
4656 set_fill_boolean(set, 0);
4657 } else {
4658 set_fill_boolean(set, 1);
4659 }
4660
4661 return LY_SUCCESS;
4662}
4663
4664/**
4665 * @brief Execute the XPath round(number) function. Returns LYXP_SET_NUMBER
4666 * with the rounded first argument. For details refer to
4667 * http://www.w3.org/TR/1999/REC-xpath-19991116/#function-round.
4668 *
4669 * @param[in] args Array of arguments.
4670 * @param[in] arg_count Count of elements in @p args.
4671 * @param[in,out] set Context and result set at the same time.
4672 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004673 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004674 */
4675static LY_ERR
4676xpath_round(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4677{
4678 struct lysc_node_leaf *sleaf;
4679 LY_ERR rc = LY_SUCCESS;
4680
4681 if (options & LYXP_SCNODE_ALL) {
4682 if ((args[0]->type != LYXP_SET_SCNODE_SET) || !(sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4683 LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004684 } else if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4685 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 +02004686 } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
4687 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004688 }
4689 set_scnode_clear_ctx(set);
4690 return rc;
4691 }
4692
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004693 rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004694 LY_CHECK_RET(rc);
4695
4696 /* cover only the cases where floor can't be used */
4697 if ((args[0]->val.num == -0.0f) || ((args[0]->val.num < 0) && (args[0]->val.num >= -0.5))) {
4698 set_fill_number(set, -0.0f);
4699 } else {
4700 args[0]->val.num += 0.5;
4701 rc = xpath_floor(args, 1, args[0], options);
4702 LY_CHECK_RET(rc);
4703 set_fill_number(set, args[0]->val.num);
4704 }
4705
4706 return LY_SUCCESS;
4707}
4708
4709/**
4710 * @brief Execute the XPath starts-with(string, string) function.
4711 * Returns LYXP_SET_BOOLEAN whether the second argument is
4712 * the prefix of the first or not.
4713 *
4714 * @param[in] args Array of arguments.
4715 * @param[in] arg_count Count of elements in @p args.
4716 * @param[in,out] set Context and result set at the same time.
4717 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004718 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004719 */
4720static LY_ERR
4721xpath_starts_with(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4722{
4723 struct lysc_node_leaf *sleaf;
4724 LY_ERR rc = LY_SUCCESS;
4725
4726 if (options & LYXP_SCNODE_ALL) {
4727 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4728 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4729 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 +02004730 } else if (!warn_is_string_type(sleaf->type)) {
4731 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004732 }
4733 }
4734
4735 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
4736 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4737 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 +02004738 } else if (!warn_is_string_type(sleaf->type)) {
4739 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004740 }
4741 }
4742 set_scnode_clear_ctx(set);
4743 return rc;
4744 }
4745
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004746 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004747 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004748 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004749 LY_CHECK_RET(rc);
4750
4751 if (strncmp(args[0]->val.str, args[1]->val.str, strlen(args[1]->val.str))) {
4752 set_fill_boolean(set, 0);
4753 } else {
4754 set_fill_boolean(set, 1);
4755 }
4756
4757 return LY_SUCCESS;
4758}
4759
4760/**
4761 * @brief Execute the XPath string(object?) function. Returns LYXP_SET_STRING
4762 * with the string representation of either the argument or the context.
4763 *
4764 * @param[in] args Array of arguments.
4765 * @param[in] arg_count Count of elements in @p args.
4766 * @param[in,out] set Context and result set at the same time.
4767 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004768 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004769 */
4770static LY_ERR
4771xpath_string(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4772{
4773 LY_ERR rc;
4774
4775 if (options & LYXP_SCNODE_ALL) {
4776 set_scnode_clear_ctx(set);
4777 return LY_SUCCESS;
4778 }
4779
4780 if (arg_count) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004781 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004782 LY_CHECK_RET(rc);
4783 set_fill_set(set, args[0]);
4784 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004785 rc = lyxp_set_cast(set, LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004786 LY_CHECK_RET(rc);
4787 }
4788
4789 return LY_SUCCESS;
4790}
4791
4792/**
4793 * @brief Execute the XPath string-length(string?) function. Returns LYXP_SET_NUMBER
4794 * with the length of the string in either the argument or the context.
4795 *
4796 * @param[in] args Array of arguments.
4797 * @param[in] arg_count Count of elements in @p args.
4798 * @param[in,out] set Context and result set at the same time.
4799 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004800 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004801 */
4802static LY_ERR
4803xpath_string_length(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4804{
4805 struct lysc_node_leaf *sleaf;
4806 LY_ERR rc = LY_SUCCESS;
4807
4808 if (options & LYXP_SCNODE_ALL) {
4809 if (arg_count && (args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4810 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4811 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 +02004812 } else if (!warn_is_string_type(sleaf->type)) {
4813 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004814 }
4815 }
4816 if (!arg_count && (set->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set))) {
4817 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4818 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 +02004819 } else if (!warn_is_string_type(sleaf->type)) {
4820 LOGWRN(set->ctx, "Argument #0 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004821 }
4822 }
4823 set_scnode_clear_ctx(set);
4824 return rc;
4825 }
4826
4827 if (arg_count) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004828 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004829 LY_CHECK_RET(rc);
4830 set_fill_number(set, strlen(args[0]->val.str));
4831 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004832 rc = lyxp_set_cast(set, LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004833 LY_CHECK_RET(rc);
4834 set_fill_number(set, strlen(set->val.str));
4835 }
4836
4837 return LY_SUCCESS;
4838}
4839
4840/**
4841 * @brief Execute the XPath substring(string, number, number?) function.
4842 * Returns LYXP_SET_STRING substring of the first argument starting
4843 * on the second argument index ending on the third argument index,
4844 * indexed from 1. For exact definition refer to
4845 * http://www.w3.org/TR/1999/REC-xpath-19991116/#function-substring.
4846 *
4847 * @param[in] args Array of arguments.
4848 * @param[in] arg_count Count of elements in @p args.
4849 * @param[in,out] set Context and result set at the same time.
4850 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004851 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004852 */
4853static LY_ERR
4854xpath_substring(struct lyxp_set **args, uint16_t arg_count, struct lyxp_set *set, int options)
4855{
4856 int start, len;
4857 uint16_t str_start, str_len, pos;
4858 struct lysc_node_leaf *sleaf;
4859 LY_ERR rc = LY_SUCCESS;
4860
4861 if (options & LYXP_SCNODE_ALL) {
4862 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4863 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4864 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 +02004865 } else if (!warn_is_string_type(sleaf->type)) {
4866 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004867 }
4868 }
4869
4870 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
4871 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4872 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 +02004873 } else if (!warn_is_numeric_type(sleaf->type)) {
4874 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004875 }
4876 }
4877
4878 if ((arg_count == 3) && (args[2]->type == LYXP_SET_SCNODE_SET)
4879 && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[2]))) {
4880 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4881 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 +02004882 } else if (!warn_is_numeric_type(sleaf->type)) {
4883 LOGWRN(set->ctx, "Argument #3 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004884 }
4885 }
4886 set_scnode_clear_ctx(set);
4887 return rc;
4888 }
4889
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004890 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004891 LY_CHECK_RET(rc);
4892
4893 /* start */
4894 if (xpath_round(&args[1], 1, args[1], options)) {
4895 return -1;
4896 }
4897 if (isfinite(args[1]->val.num)) {
4898 start = args[1]->val.num - 1;
4899 } else if (isinf(args[1]->val.num) && signbit(args[1]->val.num)) {
4900 start = INT_MIN;
4901 } else {
4902 start = INT_MAX;
4903 }
4904
4905 /* len */
4906 if (arg_count == 3) {
4907 rc = xpath_round(&args[2], 1, args[2], options);
4908 LY_CHECK_RET(rc);
4909 if (isfinite(args[2]->val.num)) {
4910 len = args[2]->val.num;
4911 } else if (isnan(args[2]->val.num) || signbit(args[2]->val.num)) {
4912 len = 0;
4913 } else {
4914 len = INT_MAX;
4915 }
4916 } else {
4917 len = INT_MAX;
4918 }
4919
4920 /* find matching character positions */
4921 str_start = 0;
4922 str_len = 0;
4923 for (pos = 0; args[0]->val.str[pos]; ++pos) {
4924 if (pos < start) {
4925 ++str_start;
4926 } else if (pos < start + len) {
4927 ++str_len;
4928 } else {
4929 break;
4930 }
4931 }
4932
4933 set_fill_string(set, args[0]->val.str + str_start, str_len);
4934 return LY_SUCCESS;
4935}
4936
4937/**
4938 * @brief Execute the XPath substring-after(string, string) function.
4939 * Returns LYXP_SET_STRING with the string succeeding the occurance
4940 * of the second argument in the first or an empty string.
4941 *
4942 * @param[in] args Array of arguments.
4943 * @param[in] arg_count Count of elements in @p args.
4944 * @param[in,out] set Context and result set at the same time.
4945 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004946 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02004947 */
4948static LY_ERR
4949xpath_substring_after(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
4950{
4951 char *ptr;
4952 struct lysc_node_leaf *sleaf;
4953 LY_ERR rc = LY_SUCCESS;
4954
4955 if (options & LYXP_SCNODE_ALL) {
4956 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
4957 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4958 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 +02004959 } else if (!warn_is_string_type(sleaf->type)) {
4960 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004961 }
4962 }
4963
4964 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
4965 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
4966 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 +02004967 } else if (!warn_is_string_type(sleaf->type)) {
4968 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004969 }
4970 }
4971 set_scnode_clear_ctx(set);
4972 return rc;
4973 }
4974
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004975 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004976 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01004977 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02004978 LY_CHECK_RET(rc);
4979
4980 ptr = strstr(args[0]->val.str, args[1]->val.str);
4981 if (ptr) {
4982 set_fill_string(set, ptr + strlen(args[1]->val.str), strlen(ptr + strlen(args[1]->val.str)));
4983 } else {
4984 set_fill_string(set, "", 0);
4985 }
4986
4987 return LY_SUCCESS;
4988}
4989
4990/**
4991 * @brief Execute the XPath substring-before(string, string) function.
4992 * Returns LYXP_SET_STRING with the string preceding the occurance
4993 * of the second argument in the first or an empty string.
4994 *
4995 * @param[in] args Array of arguments.
4996 * @param[in] arg_count Count of elements in @p args.
4997 * @param[in,out] set Context and result set at the same time.
4998 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01004999 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005000 */
5001static LY_ERR
5002xpath_substring_before(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
5003{
5004 char *ptr;
5005 struct lysc_node_leaf *sleaf;
5006 LY_ERR rc = LY_SUCCESS;
5007
5008 if (options & LYXP_SCNODE_ALL) {
5009 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
5010 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5011 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 +02005012 } else if (!warn_is_string_type(sleaf->type)) {
5013 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005014 }
5015 }
5016
5017 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
5018 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5019 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 +02005020 } else if (!warn_is_string_type(sleaf->type)) {
5021 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005022 }
5023 }
5024 set_scnode_clear_ctx(set);
5025 return rc;
5026 }
5027
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005028 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005029 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005030 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005031 LY_CHECK_RET(rc);
5032
5033 ptr = strstr(args[0]->val.str, args[1]->val.str);
5034 if (ptr) {
5035 set_fill_string(set, args[0]->val.str, ptr - args[0]->val.str);
5036 } else {
5037 set_fill_string(set, "", 0);
5038 }
5039
5040 return LY_SUCCESS;
5041}
5042
5043/**
5044 * @brief Execute the XPath sum(node-set) function. Returns LYXP_SET_NUMBER
5045 * with the sum of all the nodes in the context.
5046 *
5047 * @param[in] args Array of arguments.
5048 * @param[in] arg_count Count of elements in @p args.
5049 * @param[in,out] set Context and result set at the same time.
5050 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005051 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005052 */
5053static LY_ERR
5054xpath_sum(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
5055{
5056 long double num;
5057 char *str;
5058 uint16_t i;
5059 struct lyxp_set set_item;
5060 struct lysc_node_leaf *sleaf;
5061 LY_ERR rc = LY_SUCCESS;
5062
5063 if (options & LYXP_SCNODE_ALL) {
5064 if (args[0]->type == LYXP_SET_SCNODE_SET) {
5065 for (i = 0; i < args[0]->used; ++i) {
5066 if (args[0]->val.scnodes[i].in_ctx == 1) {
5067 sleaf = (struct lysc_node_leaf *)args[0]->val.scnodes[i].scnode;
5068 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5069 LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__,
5070 lys_nodetype2str(sleaf->nodetype), sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005071 } else if (!warn_is_numeric_type(sleaf->type)) {
5072 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005073 }
5074 }
5075 }
5076 }
5077 set_scnode_clear_ctx(set);
5078 return rc;
5079 }
5080
5081 set_fill_number(set, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005082
5083 if (args[0]->type != LYXP_SET_NODE_SET) {
5084 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "sum(node-set)");
5085 return LY_EVALID;
Michal Vaskod3678892020-05-21 10:06:58 +02005086 } else if (!args[0]->used) {
5087 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005088 }
5089
Michal Vasko5c4e5892019-11-14 12:31:38 +01005090 set_init(&set_item, set);
5091
Michal Vasko03ff5a72019-09-11 13:49:33 +02005092 set_item.type = LYXP_SET_NODE_SET;
5093 set_item.val.nodes = malloc(sizeof *set_item.val.nodes);
5094 LY_CHECK_ERR_RET(!set_item.val.nodes, LOGMEM(set->ctx), LY_EMEM);
5095
5096 set_item.used = 1;
5097 set_item.size = 1;
5098
5099 for (i = 0; i < args[0]->used; ++i) {
5100 set_item.val.nodes[0] = args[0]->val.nodes[i];
5101
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005102 rc = cast_node_set_to_string(&set_item, &str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005103 LY_CHECK_RET(rc);
5104 num = cast_string_to_number(str);
5105 free(str);
5106 set->val.num += num;
5107 }
5108
5109 free(set_item.val.nodes);
5110
5111 return LY_SUCCESS;
5112}
5113
5114/**
5115 * @brief Execute the XPath text() function (node type). Returns LYXP_SET_NODE_SET
5116 * with the text content of the nodes in the context.
5117 *
5118 * @param[in] args Array of arguments.
5119 * @param[in] arg_count Count of elements in @p args.
5120 * @param[in,out] set Context and result set at the same time.
5121 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005122 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005123 */
5124static LY_ERR
5125xpath_text(struct lyxp_set **UNUSED(args), uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
5126{
5127 uint32_t i;
5128
5129 if (options & LYXP_SCNODE_ALL) {
5130 set_scnode_clear_ctx(set);
5131 return LY_SUCCESS;
5132 }
5133
Michal Vasko03ff5a72019-09-11 13:49:33 +02005134 if (set->type != LYXP_SET_NODE_SET) {
5135 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INCTX, print_set_type(set), "text()");
5136 return LY_EVALID;
5137 }
5138
5139 for (i = 0; i < set->used;) {
5140 switch (set->val.nodes[i].type) {
Michal Vasko2caefc12019-11-14 16:07:56 +01005141 case LYXP_NODE_NONE:
5142 LOGINT_RET(set->ctx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005143 case LYXP_NODE_ELEM:
Michal Vasko03ff5a72019-09-11 13:49:33 +02005144 if (set->val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
5145 set->val.nodes[i].type = LYXP_NODE_TEXT;
5146 ++i;
5147 break;
5148 }
5149 /* fall through */
5150 case LYXP_NODE_ROOT:
5151 case LYXP_NODE_ROOT_CONFIG:
5152 case LYXP_NODE_TEXT:
Michal Vasko9f96a052020-03-10 09:41:45 +01005153 case LYXP_NODE_META:
Michal Vasko03ff5a72019-09-11 13:49:33 +02005154 set_remove_node(set, i);
5155 break;
5156 }
5157 }
5158
5159 return LY_SUCCESS;
5160}
5161
5162/**
5163 * @brief Execute the XPath translate(string, string, string) function.
5164 * Returns LYXP_SET_STRING with the first argument with the characters
5165 * from the second argument replaced by those on the corresponding
5166 * positions in the third argument.
5167 *
5168 * @param[in] args Array of arguments.
5169 * @param[in] arg_count Count of elements in @p args.
5170 * @param[in,out] set Context and result set at the same time.
5171 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005172 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005173 */
5174static LY_ERR
5175xpath_translate(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
5176{
5177 uint16_t i, j, new_used;
5178 char *new;
5179 int found, have_removed;
5180 struct lysc_node_leaf *sleaf;
5181 LY_ERR rc = LY_SUCCESS;
5182
5183 if (options & LYXP_SCNODE_ALL) {
5184 if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
5185 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5186 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 +02005187 } else if (!warn_is_string_type(sleaf->type)) {
5188 LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005189 }
5190 }
5191
5192 if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
5193 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5194 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 +02005195 } else if (!warn_is_string_type(sleaf->type)) {
5196 LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005197 }
5198 }
5199
5200 if ((args[2]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[2]))) {
5201 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
5202 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 +02005203 } else if (!warn_is_string_type(sleaf->type)) {
5204 LOGWRN(set->ctx, "Argument #3 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005205 }
5206 }
5207 set_scnode_clear_ctx(set);
5208 return rc;
5209 }
5210
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005211 rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005212 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005213 rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005214 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005215 rc = lyxp_set_cast(args[2], LYXP_SET_STRING);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005216 LY_CHECK_RET(rc);
5217
5218 new = malloc((strlen(args[0]->val.str) + 1) * sizeof(char));
5219 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
5220 new_used = 0;
5221
5222 have_removed = 0;
5223 for (i = 0; args[0]->val.str[i]; ++i) {
5224 found = 0;
5225
5226 for (j = 0; args[1]->val.str[j]; ++j) {
5227 if (args[0]->val.str[i] == args[1]->val.str[j]) {
5228 /* removing this char */
5229 if (j >= strlen(args[2]->val.str)) {
5230 have_removed = 1;
5231 found = 1;
5232 break;
5233 }
5234 /* replacing this char */
5235 new[new_used] = args[2]->val.str[j];
5236 ++new_used;
5237 found = 1;
5238 break;
5239 }
5240 }
5241
5242 /* copying this char */
5243 if (!found) {
5244 new[new_used] = args[0]->val.str[i];
5245 ++new_used;
5246 }
5247 }
5248
5249 if (have_removed) {
5250 new = ly_realloc(new, (new_used + 1) * sizeof(char));
5251 LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
5252 }
5253 new[new_used] = '\0';
5254
Michal Vaskod3678892020-05-21 10:06:58 +02005255 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005256 set->type = LYXP_SET_STRING;
5257 set->val.str = new;
5258
5259 return LY_SUCCESS;
5260}
5261
5262/**
5263 * @brief Execute the XPath true() function. Returns LYXP_SET_BOOLEAN
5264 * with true value.
5265 *
5266 * @param[in] args Array of arguments.
5267 * @param[in] arg_count Count of elements in @p args.
5268 * @param[in,out] set Context and result set at the same time.
5269 * @param[in] options XPath options.
Michal Vasko0cbf54f2019-12-16 10:01:06 +01005270 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005271 */
5272static LY_ERR
5273xpath_true(struct lyxp_set **UNUSED(args), uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
5274{
5275 if (options & LYXP_SCNODE_ALL) {
5276 set_scnode_clear_ctx(set);
5277 return LY_SUCCESS;
5278 }
5279
5280 set_fill_boolean(set, 1);
5281 return LY_SUCCESS;
5282}
5283
5284/*
5285 * moveto functions
5286 *
5287 * They and only they actually change the context (set).
5288 */
5289
5290/**
Michal Vasko6346ece2019-09-24 13:12:53 +02005291 * @brief Skip prefix and return corresponding model if there is a prefix. Logs directly.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005292 *
Michal Vasko2104e9f2020-03-06 08:23:25 +01005293 * XPath @p set is expected to be a (sc)node set!
5294 *
Michal Vasko6346ece2019-09-24 13:12:53 +02005295 * @param[in,out] qname Qualified node name. If includes prefix, it is skipped.
5296 * @param[in,out] qname_len Length of @p qname, is updated accordingly.
5297 * @param[in] set Set with XPath context.
5298 * @param[out] moveto_mod Expected module of a matching node.
5299 * @return LY_ERR
Michal Vasko03ff5a72019-09-11 13:49:33 +02005300 */
Michal Vasko6346ece2019-09-24 13:12:53 +02005301static LY_ERR
5302moveto_resolve_model(const char **qname, uint16_t *qname_len, struct lyxp_set *set, const struct lys_module **moveto_mod)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005303{
Michal Vaskoed4fcfe2020-07-08 10:38:56 +02005304 const struct lys_module *mod = NULL;
Michal Vasko6346ece2019-09-24 13:12:53 +02005305 const char *ptr;
5306 int pref_len;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005307 char *str;
5308
Michal Vasko2104e9f2020-03-06 08:23:25 +01005309 assert((set->type == LYXP_SET_NODE_SET) || (set->type == LYXP_SET_SCNODE_SET));
5310
Michal Vasko6346ece2019-09-24 13:12:53 +02005311 if ((ptr = ly_strnchr(*qname, ':', *qname_len))) {
5312 /* specific module */
5313 pref_len = ptr - *qname;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005314
Michal Vasko6346ece2019-09-24 13:12:53 +02005315 switch (set->format) {
Michal Vasko52927e22020-03-16 17:26:14 +01005316 case LYD_SCHEMA:
Michal Vasko6346ece2019-09-24 13:12:53 +02005317 /* schema, search all local module imports */
5318 mod = lys_module_find_prefix(set->local_mod, *qname, pref_len);
5319 break;
5320 case LYD_JSON:
5321 /* JSON data, search in context */
5322 str = strndup(*qname, pref_len);
Michal Vasko97e76fd2020-05-27 15:22:01 +02005323 mod = ly_ctx_get_module_implemented(set->ctx, str);
Michal Vasko6346ece2019-09-24 13:12:53 +02005324 free(str);
5325 break;
Michal Vasko52927e22020-03-16 17:26:14 +01005326 case LYD_XML:
Michal Vasko60ea6352020-06-29 13:39:39 +02005327 case LYD_LYB:
Michal Vasko6346ece2019-09-24 13:12:53 +02005328 LOGINT_RET(set->ctx);
5329 }
5330
Michal Vasko004d3152020-06-11 19:59:22 +02005331 /* check for errors and non-implemented modules, as they are not valid */
Juraj Vijtiukd75faa62019-11-26 14:10:10 +01005332 if (!mod || !mod->implemented) {
Michal Vasko2104e9f2020-03-06 08:23:25 +01005333 if (set->type == LYXP_SET_SCNODE_SET) {
5334 LOGVAL(set->ctx, LY_VLOG_LYSC, set->ctx_scnode, LY_VCODE_XP_INMOD, pref_len, *qname);
5335 } else {
5336 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INMOD, pref_len, *qname);
5337 }
Michal Vasko6346ece2019-09-24 13:12:53 +02005338 return LY_EVALID;
5339 }
Juraj Vijtiukd75faa62019-11-26 14:10:10 +01005340
Michal Vasko6346ece2019-09-24 13:12:53 +02005341 *qname += pref_len + 1;
5342 *qname_len -= pref_len + 1;
5343 } else if (((*qname)[0] == '*') && (*qname_len == 1)) {
5344 /* all modules - special case */
Michal Vasko03ff5a72019-09-11 13:49:33 +02005345 mod = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +02005346 } else if (set->type == LYXP_SET_SCNODE_SET) {
5347 /* current node module */
5348 mod = set->ctx_scnode->module;
Michal Vasko6346ece2019-09-24 13:12:53 +02005349 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02005350 /* current node module */
5351 mod = set->ctx_node->schema->module;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005352 }
5353
Michal Vasko6346ece2019-09-24 13:12:53 +02005354 *moveto_mod = mod;
5355 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005356}
5357
5358/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02005359 * @brief Move context @p set to the root. Handles absolute path.
5360 * Result is LYXP_SET_NODE_SET.
5361 *
5362 * @param[in,out] set Set to use.
5363 * @param[in] options Xpath options.
5364 */
5365static void
5366moveto_root(struct lyxp_set *set, int options)
5367{
Michal Vasko03ff5a72019-09-11 13:49:33 +02005368 if (!set) {
5369 return;
5370 }
5371
5372 if (options & LYXP_SCNODE_ALL) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005373 set_scnode_clear_ctx(set);
Michal Vaskoecd62de2019-11-13 12:35:11 +01005374 lyxp_set_scnode_insert_node(set, NULL, set->root_type);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005375 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02005376 set->type = LYXP_SET_NODE_SET;
5377 set->used = 0;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005378 set_insert_node(set, NULL, 0, set->root_type, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005379 }
5380}
5381
5382/**
Michal Vaskoa1424542019-11-14 16:08:52 +01005383 * @brief Check whether a node has some unresolved "when".
5384 *
5385 * @param[in] node Node to check.
5386 * @return LY_ERR value (LY_EINCOMPLETE if there are some unresolved "when")
5387 */
5388static LY_ERR
5389moveto_when_check(const struct lyd_node *node)
5390{
5391 const struct lysc_node *schema;
5392
5393 if (!node) {
5394 return LY_SUCCESS;
5395 }
5396
5397 schema = node->schema;
5398 do {
5399 if (schema->when && !(node->flags & LYD_WHEN_TRUE)) {
5400 return LY_EINCOMPLETE;
5401 }
5402 schema = schema->parent;
5403 } while (schema && (schema->nodetype & (LYS_CASE | LYS_CHOICE)));
5404
5405 return LY_SUCCESS;
5406}
5407
5408/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02005409 * @brief Check @p node as a part of NameTest processing.
5410 *
5411 * @param[in] node Node to check.
5412 * @param[in] root_type XPath root node type.
Michal Vasko6b26e742020-07-17 15:02:10 +02005413 * @param[in] context_op XPath operation parent.
Michal Vaskod3678892020-05-21 10:06:58 +02005414 * @param[in] node_name Node name in the dictionary to move to, NULL for any node.
5415 * @param[in] moveto_mod Expected module of the node, NULL for any.
Michal Vasko6346ece2019-09-24 13:12:53 +02005416 * @return LY_ERR (LY_ENOT if node does not match, LY_EINCOMPLETE on unresolved when,
5417 * LY_EINVAL if netither node nor any children match)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005418 */
5419static LY_ERR
Michal Vasko6b26e742020-07-17 15:02:10 +02005420moveto_node_check(const struct lyd_node *node, enum lyxp_node_type root_type, const struct lysc_node *context_op,
5421 const char *node_name, const struct lys_module *moveto_mod)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005422{
5423 /* module check */
5424 if (moveto_mod && (node->schema->module != moveto_mod)) {
Michal Vasko6346ece2019-09-24 13:12:53 +02005425 return LY_ENOT;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005426 }
5427
Michal Vasko5c4e5892019-11-14 12:31:38 +01005428 /* context check */
5429 if ((root_type == LYXP_NODE_ROOT_CONFIG) && (node->schema->flags & LYS_CONFIG_R)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005430 return LY_EINVAL;
Michal Vasko6b26e742020-07-17 15:02:10 +02005431 } else if (context_op && (node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node->schema != context_op)) {
5432 return LY_EINVAL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005433 }
5434
5435 /* name check */
Michal Vasko61ac2f62020-05-25 12:39:51 +02005436 if (node_name && strcmp(node_name, "*") && (node->schema->name != node_name)) {
Michal Vasko6346ece2019-09-24 13:12:53 +02005437 return LY_ENOT;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005438 }
5439
Michal Vaskoa1424542019-11-14 16:08:52 +01005440 /* when check */
5441 if (moveto_when_check(node)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005442 return LY_EINCOMPLETE;
Michal Vaskoa1424542019-11-14 16:08:52 +01005443 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005444
5445 /* match */
5446 return LY_SUCCESS;
5447}
5448
5449/**
5450 * @brief Check @p node as a part of schema NameTest processing.
5451 *
5452 * @param[in] node Schema node to check.
5453 * @param[in] root_type XPath root node type.
Michal Vasko6b26e742020-07-17 15:02:10 +02005454 * @param[in] context_op XPath operation parent.
Michal Vaskod3678892020-05-21 10:06:58 +02005455 * @param[in] node_name Node name in the dictionary to move to, NULL for any nodes.
5456 * @param[in] moveto_mod Expected module of the node, NULL for any.
Michal Vasko6346ece2019-09-24 13:12:53 +02005457 * @return LY_ERR (LY_ENOT if node does not match, LY_EINVAL if neither node nor any children match)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005458 */
5459static LY_ERR
Michal Vasko6b26e742020-07-17 15:02:10 +02005460moveto_scnode_check(const struct lysc_node *node, enum lyxp_node_type root_type, const struct lysc_node *context_op,
5461 const char *node_name, const struct lys_module *moveto_mod)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005462{
Michal Vasko03ff5a72019-09-11 13:49:33 +02005463 /* module check */
Michal Vaskod3678892020-05-21 10:06:58 +02005464 if (moveto_mod && (node->module != moveto_mod)) {
Michal Vasko6346ece2019-09-24 13:12:53 +02005465 return LY_ENOT;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005466 }
5467
5468 /* context check */
5469 if ((root_type == LYXP_NODE_ROOT_CONFIG) && (node->flags & LYS_CONFIG_R)) {
5470 return LY_EINVAL;
Michal Vasko6b26e742020-07-17 15:02:10 +02005471 } else if (context_op && (node->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node != context_op)) {
5472 return LY_EINVAL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005473 }
5474
5475 /* name check */
Michal Vasko61ac2f62020-05-25 12:39:51 +02005476 if (node_name && strcmp(node_name, "*") && (node->name != node_name)) {
Michal Vasko6346ece2019-09-24 13:12:53 +02005477 return LY_ENOT;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005478 }
5479
5480 /* match */
5481 return LY_SUCCESS;
5482}
5483
5484/**
Michal Vaskod3678892020-05-21 10:06:58 +02005485 * @brief Move context @p set to a node. Result is LYXP_SET_NODE_SET (or LYXP_SET_EMPTY). Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005486 *
5487 * @param[in,out] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02005488 * @param[in] mod Matching node module, NULL for any.
5489 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005490 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
5491 */
5492static LY_ERR
Michal Vaskod3678892020-05-21 10:06:58 +02005493moveto_node(struct lyxp_set *set, const struct lys_module *mod, const char *ncname)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005494{
Michal Vaskof03ed032020-03-04 13:31:44 +01005495 uint32_t i;
Michal Vasko6346ece2019-09-24 13:12:53 +02005496 int replaced;
Michal Vaskod3678892020-05-21 10:06:58 +02005497 const struct lyd_node *siblings, *sub;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005498 LY_ERR rc;
5499
Michal Vaskod3678892020-05-21 10:06:58 +02005500 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005501 return LY_SUCCESS;
5502 }
5503
5504 if (set->type != LYXP_SET_NODE_SET) {
5505 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
5506 return LY_EVALID;
5507 }
5508
Michal Vasko03ff5a72019-09-11 13:49:33 +02005509 for (i = 0; i < set->used; ) {
5510 replaced = 0;
5511
5512 if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005513 assert(!set->val.nodes[i].node);
Michal Vaskod3678892020-05-21 10:06:58 +02005514
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005515 /* search in all the trees */
Michal Vaskod3678892020-05-21 10:06:58 +02005516 siblings = set->tree;
Michal Vasko5bfd4be2020-06-23 13:26:19 +02005517 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02005518 /* search in children */
Michal Vasko5bfd4be2020-06-23 13:26:19 +02005519 siblings = lyd_node_children(set->val.nodes[i].node, 0);
Michal Vaskod3678892020-05-21 10:06:58 +02005520 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005521
Michal Vaskod3678892020-05-21 10:06:58 +02005522 for (sub = siblings; sub; sub = sub->next) {
Michal Vasko6b26e742020-07-17 15:02:10 +02005523 rc = moveto_node_check(sub, set->root_type, set->context_op, ncname, mod);
Michal Vaskod3678892020-05-21 10:06:58 +02005524 if (rc == LY_SUCCESS) {
5525 if (!replaced) {
5526 set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
5527 replaced = 1;
5528 } else {
5529 set_insert_node(set, sub, 0, LYXP_NODE_ELEM, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005530 }
Michal Vaskod3678892020-05-21 10:06:58 +02005531 ++i;
5532 } else if (rc == LY_EINCOMPLETE) {
5533 return rc;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005534 }
5535 }
5536
5537 if (!replaced) {
5538 /* no match */
5539 set_remove_node(set, i);
5540 }
5541 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005542
5543 return LY_SUCCESS;
5544}
5545
5546/**
Michal Vaskod3678892020-05-21 10:06:58 +02005547 * @brief Move context @p set to a node using hashes. Result is LYXP_SET_NODE_SET (or LYXP_SET_EMPTY).
5548 * Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005549 *
5550 * @param[in,out] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02005551 * @param[in] scnode Matching node schema.
Michal Vasko004d3152020-06-11 19:59:22 +02005552 * @param[in] predicates If @p scnode is ::LYS_LIST or ::LYS_LEAFLIST, the predicates specifying a single instance.
Michal Vaskod3678892020-05-21 10:06:58 +02005553 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
5554 */
5555static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02005556moveto_node_hash(struct lyxp_set *set, const struct lysc_node *scnode, const struct ly_path_predicate *predicates)
Michal Vaskod3678892020-05-21 10:06:58 +02005557{
Michal Vasko004d3152020-06-11 19:59:22 +02005558 LY_ERR ret = LY_SUCCESS;
Michal Vaskod3678892020-05-21 10:06:58 +02005559 uint32_t i;
Michal Vaskod3678892020-05-21 10:06:58 +02005560 const struct lyd_node *siblings;
Michal Vasko004d3152020-06-11 19:59:22 +02005561 struct lyd_node *sub, *inst = NULL;
Michal Vaskod3678892020-05-21 10:06:58 +02005562
Michal Vasko004d3152020-06-11 19:59:22 +02005563 assert(scnode && (!(scnode->nodetype & (LYS_LIST | LYS_LEAFLIST)) || predicates));
Michal Vaskod3678892020-05-21 10:06:58 +02005564
5565 if (!set) {
Michal Vasko004d3152020-06-11 19:59:22 +02005566 goto cleanup;
Michal Vaskod3678892020-05-21 10:06:58 +02005567 }
5568
5569 if (set->type != LYXP_SET_NODE_SET) {
5570 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko004d3152020-06-11 19:59:22 +02005571 ret = LY_EVALID;
5572 goto cleanup;
Michal Vaskod3678892020-05-21 10:06:58 +02005573 }
5574
5575 /* context check for all the nodes since we have the schema node */
5576 if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (scnode->flags & LYS_CONFIG_R)) {
5577 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02005578 goto cleanup;
Michal Vasko6b26e742020-07-17 15:02:10 +02005579 } else if (set->context_op && (scnode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))
5580 && (scnode != set->context_op)) {
5581 lyxp_set_free_content(set);
5582 goto cleanup;
Michal Vasko004d3152020-06-11 19:59:22 +02005583 }
5584
5585 /* create specific data instance if needed */
5586 if (scnode->nodetype == LYS_LIST) {
5587 LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, &inst), cleanup);
5588 } else if (scnode->nodetype == LYS_LEAFLIST) {
5589 LY_CHECK_GOTO(ret = lyd_create_term2(scnode, &predicates[0].value, &inst), cleanup);
Michal Vaskod3678892020-05-21 10:06:58 +02005590 }
5591
5592 for (i = 0; i < set->used; ) {
Michal Vaskod3678892020-05-21 10:06:58 +02005593 siblings = NULL;
5594
5595 if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
5596 assert(!set->val.nodes[i].node);
5597
5598 /* search in all the trees */
5599 siblings = set->tree;
5600 } else if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
5601 /* search in children */
Michal Vasko5bfd4be2020-06-23 13:26:19 +02005602 siblings = lyd_node_children(set->val.nodes[i].node, 0);
Michal Vaskod3678892020-05-21 10:06:58 +02005603 }
5604
5605 /* find the node using hashes */
Michal Vasko004d3152020-06-11 19:59:22 +02005606 if (inst) {
5607 lyd_find_sibling_first(siblings, inst, &sub);
Michal Vaskod3678892020-05-21 10:06:58 +02005608 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02005609 lyd_find_sibling_val(siblings, scnode, NULL, 0, &sub);
Michal Vaskod3678892020-05-21 10:06:58 +02005610 }
5611
5612 /* when check */
5613 if (sub && moveto_when_check(sub)) {
Michal Vasko004d3152020-06-11 19:59:22 +02005614 ret = LY_EINCOMPLETE;
5615 goto cleanup;
Michal Vaskod3678892020-05-21 10:06:58 +02005616 }
5617
5618 if (sub) {
5619 /* pos filled later */
Michal Vaskocb1b7c02020-07-03 13:38:12 +02005620 set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
Michal Vaskod3678892020-05-21 10:06:58 +02005621 ++i;
Michal Vaskocb1b7c02020-07-03 13:38:12 +02005622 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02005623 /* no match */
5624 set_remove_node(set, i);
5625 }
5626 }
5627
Michal Vasko004d3152020-06-11 19:59:22 +02005628cleanup:
5629 lyd_free_tree(inst);
5630 return ret;
Michal Vaskod3678892020-05-21 10:06:58 +02005631}
5632
5633/**
5634 * @brief Move context @p set to a schema node. Result is LYXP_SET_SCNODE_SET (or LYXP_SET_EMPTY).
5635 *
5636 * @param[in,out] set Set to use.
5637 * @param[in] mod Matching node module, NULL for any.
5638 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005639 * @param[in] options XPath options.
5640 * @return LY_ERR
5641 */
5642static LY_ERR
Michal Vaskod3678892020-05-21 10:06:58 +02005643moveto_scnode(struct lyxp_set *set, const struct lys_module *mod, const char *ncname, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005644{
Michal Vaskocafad9d2019-11-07 15:20:03 +01005645 int i, orig_used, idx, temp_ctx = 0, getnext_opts;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005646 uint32_t mod_idx;
Michal Vasko519fd602020-05-26 12:17:39 +02005647 const struct lysc_node *iter, *start_parent;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005648
Michal Vaskod3678892020-05-21 10:06:58 +02005649 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005650 return LY_SUCCESS;
5651 }
5652
5653 if (set->type != LYXP_SET_SCNODE_SET) {
Michal Vaskof6e51882019-12-16 09:59:45 +01005654 LOGVAL(set->ctx, LY_VLOG_LYSC, set->ctx_scnode, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02005655 return LY_EVALID;
5656 }
5657
Michal Vaskocafad9d2019-11-07 15:20:03 +01005658 /* getnext opts */
5659 getnext_opts = LYS_GETNEXT_NOSTATECHECK;
5660 if (options & LYXP_SCNODE_OUTPUT) {
5661 getnext_opts |= LYS_GETNEXT_OUTPUT;
5662 }
5663
Michal Vasko03ff5a72019-09-11 13:49:33 +02005664 orig_used = set->used;
5665 for (i = 0; i < orig_used; ++i) {
5666 if (set->val.scnodes[i].in_ctx != 1) {
Michal Vasko5c4e5892019-11-14 12:31:38 +01005667 if (set->val.scnodes[i].in_ctx != -2) {
5668 continue;
5669 }
5670
5671 /* remember context node */
5672 set->val.scnodes[i].in_ctx = -1;
Michal Vaskoec4df482019-12-16 10:02:18 +01005673 } else {
5674 set->val.scnodes[i].in_ctx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005675 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005676
5677 start_parent = set->val.scnodes[i].scnode;
5678
5679 if ((set->val.scnodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.scnodes[i].type == LYXP_NODE_ROOT)) {
Michal Vaskod3678892020-05-21 10:06:58 +02005680 /* it can actually be in any module, it's all <running>, but we know it's mod (if set),
Michal Vasko03ff5a72019-09-11 13:49:33 +02005681 * so use it directly (root node itself is useless in this case) */
5682 mod_idx = 0;
Michal Vaskod3678892020-05-21 10:06:58 +02005683 while (mod || (mod = (struct lys_module *)ly_ctx_get_module_iter(set->ctx, &mod_idx))) {
Michal Vasko519fd602020-05-26 12:17:39 +02005684 iter = NULL;
Michal Vasko509de4d2019-12-10 14:51:30 +01005685 /* module may not be implemented */
Michal Vasko519fd602020-05-26 12:17:39 +02005686 while (mod->implemented && (iter = lys_getnext(iter, NULL, mod->compiled, getnext_opts))) {
Michal Vasko6b26e742020-07-17 15:02:10 +02005687 if (!moveto_scnode_check(iter, set->root_type, set->context_op, ncname, mod)) {
Michal Vasko519fd602020-05-26 12:17:39 +02005688 idx = lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005689 /* we need to prevent these nodes from being considered in this moveto */
5690 if ((idx < orig_used) && (idx > i)) {
5691 set->val.scnodes[idx].in_ctx = 2;
5692 temp_ctx = 1;
5693 }
5694 }
5695 }
5696
5697 if (!mod_idx) {
Michal Vaskod3678892020-05-21 10:06:58 +02005698 /* mod was specified, we are not going through the whole context */
Michal Vasko03ff5a72019-09-11 13:49:33 +02005699 break;
5700 }
5701 /* next iteration */
Michal Vaskod3678892020-05-21 10:06:58 +02005702 mod = NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005703 }
5704
Michal Vasko519fd602020-05-26 12:17:39 +02005705 } else if (set->val.scnodes[i].type == LYXP_NODE_ELEM) {
5706 iter = NULL;
5707 while ((iter = lys_getnext(iter, start_parent, NULL, getnext_opts))) {
Michal Vasko6b26e742020-07-17 15:02:10 +02005708 if (!moveto_scnode_check(iter, set->root_type, set->context_op, ncname, (mod ? mod : set->local_mod))) {
Michal Vasko519fd602020-05-26 12:17:39 +02005709 idx = lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005710 if ((idx < orig_used) && (idx > i)) {
5711 set->val.scnodes[idx].in_ctx = 2;
5712 temp_ctx = 1;
5713 }
5714 }
5715 }
5716 }
5717 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005718
5719 /* correct temporary in_ctx values */
5720 if (temp_ctx) {
5721 for (i = 0; i < orig_used; ++i) {
5722 if (set->val.scnodes[i].in_ctx == 2) {
5723 set->val.scnodes[i].in_ctx = 1;
5724 }
5725 }
5726 }
5727
5728 return LY_SUCCESS;
5729}
5730
5731/**
Michal Vaskod3678892020-05-21 10:06:58 +02005732 * @brief Move context @p set to a node and all its descendants. Result is LYXP_SET_NODE_SET (or LYXP_SET_EMPTY).
Michal Vasko03ff5a72019-09-11 13:49:33 +02005733 * Context position aware.
5734 *
5735 * @param[in] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02005736 * @param[in] mod Matching node module, NULL for any.
5737 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005738 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
5739 */
5740static LY_ERR
Michal Vaskod3678892020-05-21 10:06:58 +02005741moveto_node_alldesc(struct lyxp_set *set, const struct lys_module *mod, const char *ncname)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005742{
5743 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005744 const struct lyd_node *next, *elem, *start;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005745 struct lyxp_set ret_set;
5746 LY_ERR rc;
5747
Michal Vaskod3678892020-05-21 10:06:58 +02005748 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005749 return LY_SUCCESS;
5750 }
5751
5752 if (set->type != LYXP_SET_NODE_SET) {
5753 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
5754 return LY_EVALID;
5755 }
5756
Michal Vasko9f96a052020-03-10 09:41:45 +01005757 /* replace the original nodes (and throws away all text and meta nodes, root is replaced by a child) */
Michal Vaskod3678892020-05-21 10:06:58 +02005758 rc = moveto_node(set, NULL, NULL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005759 LY_CHECK_RET(rc);
5760
Michal Vasko6346ece2019-09-24 13:12:53 +02005761 /* this loop traverses all the nodes in the set and adds/keeps only those that match qname */
Michal Vasko03ff5a72019-09-11 13:49:33 +02005762 set_init(&ret_set, set);
5763 for (i = 0; i < set->used; ++i) {
5764
5765 /* TREE DFS */
5766 start = set->val.nodes[i].node;
5767 for (elem = next = start; elem; elem = next) {
Michal Vasko6b26e742020-07-17 15:02:10 +02005768 rc = moveto_node_check(elem, set->root_type, set->context_op, ncname, mod);
Michal Vasko6346ece2019-09-24 13:12:53 +02005769 if (!rc) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005770 /* add matching node into result set */
5771 set_insert_node(&ret_set, elem, 0, LYXP_NODE_ELEM, ret_set.used);
5772 if (set_dup_node_check(set, elem, LYXP_NODE_ELEM, i)) {
5773 /* the node is a duplicate, we'll process it later in the set */
5774 goto skip_children;
5775 }
Michal Vasko6346ece2019-09-24 13:12:53 +02005776 } else if (rc == LY_EINCOMPLETE) {
Michal Vasko6346ece2019-09-24 13:12:53 +02005777 return rc;
5778 } else if (rc == LY_EINVAL) {
5779 goto skip_children;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005780 }
5781
5782 /* TREE DFS NEXT ELEM */
5783 /* select element for the next run - children first */
Michal Vasko5bfd4be2020-06-23 13:26:19 +02005784 next = lyd_node_children(elem, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005785 if (!next) {
5786skip_children:
5787 /* no children, so try siblings, but only if it's not the start,
5788 * that is considered to be the root and it's siblings are not traversed */
5789 if (elem != start) {
5790 next = elem->next;
5791 } else {
5792 break;
5793 }
5794 }
5795 while (!next) {
5796 /* no siblings, go back through the parents */
5797 if ((struct lyd_node *)elem->parent == start) {
5798 /* we are done, no next element to process */
5799 break;
5800 }
5801 /* parent is already processed, go to its sibling */
5802 elem = (struct lyd_node *)elem->parent;
5803 next = elem->next;
5804 }
5805 }
5806 }
5807
5808 /* make the temporary set the current one */
5809 ret_set.ctx_pos = set->ctx_pos;
5810 ret_set.ctx_size = set->ctx_size;
Michal Vaskod3678892020-05-21 10:06:58 +02005811 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005812 memcpy(set, &ret_set, sizeof *set);
5813
5814 return LY_SUCCESS;
5815}
5816
5817/**
Michal Vasko61ac2f62020-05-25 12:39:51 +02005818 * @brief Move context @p set to a schema node and all its descendants. Result is LYXP_SET_NODE_SET.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005819 *
5820 * @param[in] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02005821 * @param[in] mod Matching node module, NULL for any.
5822 * @param[in] ncname Matching node name in the dictionary, NULL for any.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005823 * @param[in] options XPath options.
5824 * @return LY_ERR
5825 */
5826static LY_ERR
Michal Vaskod3678892020-05-21 10:06:58 +02005827moveto_scnode_alldesc(struct lyxp_set *set, const struct lys_module *mod, const char *ncname, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005828{
Michal Vasko6346ece2019-09-24 13:12:53 +02005829 int i, orig_used, idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005830 const struct lysc_node *next, *elem, *start;
Michal Vasko6346ece2019-09-24 13:12:53 +02005831 LY_ERR rc;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005832
Michal Vaskod3678892020-05-21 10:06:58 +02005833 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005834 return LY_SUCCESS;
5835 }
5836
5837 if (set->type != LYXP_SET_SCNODE_SET) {
Michal Vaskof6e51882019-12-16 09:59:45 +01005838 LOGVAL(set->ctx, LY_VLOG_LYSC, set->ctx_scnode, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02005839 return LY_EVALID;
5840 }
5841
Michal Vasko03ff5a72019-09-11 13:49:33 +02005842 orig_used = set->used;
5843 for (i = 0; i < orig_used; ++i) {
5844 if (set->val.scnodes[i].in_ctx != 1) {
Michal Vasko5c4e5892019-11-14 12:31:38 +01005845 if (set->val.scnodes[i].in_ctx != -2) {
5846 continue;
5847 }
5848
5849 /* remember context node */
5850 set->val.scnodes[i].in_ctx = -1;
Michal Vaskoec4df482019-12-16 10:02:18 +01005851 } else {
5852 set->val.scnodes[i].in_ctx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005853 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02005854
5855 /* TREE DFS */
5856 start = set->val.scnodes[i].scnode;
5857 for (elem = next = start; elem; elem = next) {
Michal Vasko6346ece2019-09-24 13:12:53 +02005858 if ((elem == start) || (elem->nodetype & (LYS_CHOICE | LYS_CASE))) {
5859 /* schema-only nodes, skip root */
Michal Vasko03ff5a72019-09-11 13:49:33 +02005860 goto next_iter;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005861 }
5862
Michal Vasko6b26e742020-07-17 15:02:10 +02005863 rc = moveto_scnode_check(elem, set->root_type, set->context_op, ncname, mod);
Michal Vasko6346ece2019-09-24 13:12:53 +02005864 if (!rc) {
Michal Vaskoecd62de2019-11-13 12:35:11 +01005865 if ((idx = lyxp_set_scnode_dup_node_check(set, elem, LYXP_NODE_ELEM, i)) > -1) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005866 set->val.scnodes[idx].in_ctx = 1;
5867 if (idx > i) {
5868 /* we will process it later in the set */
5869 goto skip_children;
5870 }
5871 } else {
Michal Vaskoecd62de2019-11-13 12:35:11 +01005872 lyxp_set_scnode_insert_node(set, elem, LYXP_NODE_ELEM);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005873 }
Michal Vasko6346ece2019-09-24 13:12:53 +02005874 } else if (rc == LY_EINVAL) {
5875 goto skip_children;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005876 }
5877
5878next_iter:
5879 /* TREE DFS NEXT ELEM */
5880 /* select element for the next run - children first */
5881 next = lysc_node_children(elem, options & LYXP_SCNODE_OUTPUT ? LYS_CONFIG_R : LYS_CONFIG_W);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005882 if (!next) {
5883skip_children:
5884 /* no children, so try siblings, but only if it's not the start,
5885 * that is considered to be the root and it's siblings are not traversed */
5886 if (elem != start) {
5887 next = elem->next;
5888 } else {
5889 break;
5890 }
5891 }
5892 while (!next) {
5893 /* no siblings, go back through the parents */
5894 if (elem->parent == start) {
5895 /* we are done, no next element to process */
5896 break;
5897 }
5898 /* parent is already processed, go to its sibling */
5899 elem = elem->parent;
5900 next = elem->next;
5901 }
5902 }
5903 }
5904
5905 return LY_SUCCESS;
5906}
5907
5908/**
Michal Vasko61ac2f62020-05-25 12:39:51 +02005909 * @brief Move context @p set to an attribute. Result is LYXP_SET_NODE_SET.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005910 * Indirectly context position aware.
5911 *
5912 * @param[in,out] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02005913 * @param[in] mod Matching metadata module, NULL for any.
5914 * @param[in] ncname Matching metadata name in the dictionary, NULL for any.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005915 * @return LY_ERR
5916 */
5917static LY_ERR
Michal Vaskod3678892020-05-21 10:06:58 +02005918moveto_attr(struct lyxp_set *set, const struct lys_module *mod, const char *ncname)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005919{
5920 uint32_t i;
Michal Vaskod3678892020-05-21 10:06:58 +02005921 int replaced;
Michal Vasko9f96a052020-03-10 09:41:45 +01005922 struct lyd_meta *sub;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005923
Michal Vaskod3678892020-05-21 10:06:58 +02005924 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005925 return LY_SUCCESS;
5926 }
5927
5928 if (set->type != LYXP_SET_NODE_SET) {
5929 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
5930 return LY_EVALID;
5931 }
5932
Michal Vasko03ff5a72019-09-11 13:49:33 +02005933 for (i = 0; i < set->used; ) {
5934 replaced = 0;
5935
5936 /* only attributes of an elem (not dummy) can be in the result, skip all the rest;
5937 * our attributes are always qualified */
Michal Vasko5c4e5892019-11-14 12:31:38 +01005938 if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
Michal Vasko9f96a052020-03-10 09:41:45 +01005939 for (sub = set->val.nodes[i].node->meta; sub; sub = sub->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005940
5941 /* check "namespace" */
Michal Vaskod3678892020-05-21 10:06:58 +02005942 if (mod && (sub->annotation->module != mod)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005943 continue;
5944 }
5945
Michal Vaskod3678892020-05-21 10:06:58 +02005946 if (!ncname || (sub->name == ncname)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005947 /* match */
5948 if (!replaced) {
Michal Vasko9f96a052020-03-10 09:41:45 +01005949 set->val.meta[i].meta = sub;
5950 set->val.meta[i].type = LYXP_NODE_META;
Michal Vasko03ff5a72019-09-11 13:49:33 +02005951 /* pos does not change */
5952 replaced = 1;
5953 } else {
Michal Vasko9f96a052020-03-10 09:41:45 +01005954 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 +02005955 }
5956 ++i;
5957 }
5958 }
5959 }
5960
5961 if (!replaced) {
5962 /* no match */
5963 set_remove_node(set, i);
5964 }
5965 }
5966
5967 return LY_SUCCESS;
5968}
5969
5970/**
5971 * @brief Move context @p set1 to union with @p set2. @p set2 is emptied afterwards.
Michal Vasko61ac2f62020-05-25 12:39:51 +02005972 * Result is LYXP_SET_NODE_SET. Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005973 *
5974 * @param[in,out] set1 Set to use for the result.
5975 * @param[in] set2 Set that is copied to @p set1.
Michal Vasko03ff5a72019-09-11 13:49:33 +02005976 * @return LY_ERR
5977 */
5978static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01005979moveto_union(struct lyxp_set *set1, struct lyxp_set *set2)
Michal Vasko03ff5a72019-09-11 13:49:33 +02005980{
5981 LY_ERR rc;
5982
Michal Vaskod3678892020-05-21 10:06:58 +02005983 if ((set1->type != LYXP_SET_NODE_SET) || (set2->type != LYXP_SET_NODE_SET)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005984 LOGVAL(set1->ctx, LY_VLOG_LYD, set1->ctx_node, LY_VCODE_XP_INOP_2, "union", print_set_type(set1), print_set_type(set2));
5985 return LY_EVALID;
5986 }
5987
5988 /* set2 is empty or both set1 and set2 */
Michal Vaskod3678892020-05-21 10:06:58 +02005989 if (!set2->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005990 return LY_SUCCESS;
5991 }
5992
Michal Vaskod3678892020-05-21 10:06:58 +02005993 if (!set1->used) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02005994 memcpy(set1, set2, sizeof *set1);
5995 /* dynamic memory belongs to set1 now, do not free */
Michal Vaskod3678892020-05-21 10:06:58 +02005996 memset(set2, 0, sizeof *set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02005997 return LY_SUCCESS;
5998 }
5999
6000 /* we assume sets are sorted */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006001 assert(!set_sort(set1) && !set_sort(set2));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006002
6003 /* sort, remove duplicates */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006004 rc = set_sorted_merge(set1, set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006005 LY_CHECK_RET(rc);
6006
6007 /* final set must be sorted */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006008 assert(!set_sort(set1));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006009
6010 return LY_SUCCESS;
6011}
6012
6013/**
Michal Vasko61ac2f62020-05-25 12:39:51 +02006014 * @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 +02006015 * Context position aware.
6016 *
6017 * @param[in,out] set Set to use.
Michal Vaskod3678892020-05-21 10:06:58 +02006018 * @param[in] mod Matching metadata module, NULL for any.
6019 * @param[in] ncname Matching metadata name in the dictionary, NULL for any.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006020 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6021 */
6022static int
Michal Vaskod3678892020-05-21 10:06:58 +02006023moveto_attr_alldesc(struct lyxp_set *set, const struct lys_module *mod, const char *ncname)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006024{
6025 uint32_t i;
Michal Vaskod3678892020-05-21 10:06:58 +02006026 int replaced;
Michal Vasko9f96a052020-03-10 09:41:45 +01006027 struct lyd_meta *sub;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006028 struct lyxp_set *set_all_desc = NULL;
6029 LY_ERR rc;
6030
Michal Vaskod3678892020-05-21 10:06:58 +02006031 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006032 return LY_SUCCESS;
6033 }
6034
6035 if (set->type != LYXP_SET_NODE_SET) {
6036 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
6037 return LY_EVALID;
6038 }
6039
Michal Vasko03ff5a72019-09-11 13:49:33 +02006040 /* can be optimized similarly to moveto_node_alldesc() and save considerable amount of memory,
6041 * but it likely won't be used much, so it's a waste of time */
6042 /* copy the context */
6043 set_all_desc = set_copy(set);
6044 /* get all descendant nodes (the original context nodes are removed) */
Michal Vaskod3678892020-05-21 10:06:58 +02006045 rc = moveto_node_alldesc(set_all_desc, NULL, NULL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006046 if (rc != LY_SUCCESS) {
6047 lyxp_set_free(set_all_desc);
6048 return rc;
6049 }
6050 /* prepend the original context nodes */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006051 rc = moveto_union(set, set_all_desc);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006052 if (rc != LY_SUCCESS) {
6053 lyxp_set_free(set_all_desc);
6054 return rc;
6055 }
6056 lyxp_set_free(set_all_desc);
6057
Michal Vasko03ff5a72019-09-11 13:49:33 +02006058 for (i = 0; i < set->used; ) {
6059 replaced = 0;
6060
6061 /* only attributes of an elem can be in the result, skip all the rest,
6062 * we have all attributes qualified in lyd tree */
6063 if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
Michal Vasko9f96a052020-03-10 09:41:45 +01006064 for (sub = set->val.nodes[i].node->meta; sub; sub = sub->next) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006065 /* check "namespace" */
Michal Vaskod3678892020-05-21 10:06:58 +02006066 if (mod && (sub->annotation->module != mod)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006067 continue;
6068 }
6069
Michal Vaskod3678892020-05-21 10:06:58 +02006070 if (!ncname || (sub->name == ncname)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006071 /* match */
6072 if (!replaced) {
Michal Vasko9f96a052020-03-10 09:41:45 +01006073 set->val.meta[i].meta = sub;
6074 set->val.meta[i].type = LYXP_NODE_META;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006075 /* pos does not change */
6076 replaced = 1;
6077 } else {
Michal Vasko9f96a052020-03-10 09:41:45 +01006078 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 +02006079 }
6080 ++i;
6081 }
6082 }
6083 }
6084
6085 if (!replaced) {
6086 /* no match */
6087 set_remove_node(set, i);
6088 }
6089 }
6090
6091 return LY_SUCCESS;
6092}
6093
6094/**
Michal Vasko61ac2f62020-05-25 12:39:51 +02006095 * @brief Move context @p set to self and al chilren, recursively. Handles '/' or '//' and '.'. Result is LYXP_SET_NODE_SET.
6096 * Context position aware.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006097 *
6098 * @param[in] parent Current parent.
6099 * @param[in] parent_pos Position of @p parent.
6100 * @param[in] parent_type Node type of @p parent.
6101 * @param[in,out] to_set Set to use.
6102 * @param[in] dup_check_set Set for checking duplicities.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006103 * @param[in] options XPath options.
6104 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6105 */
6106static LY_ERR
6107moveto_self_add_children_r(const struct lyd_node *parent, uint32_t parent_pos, enum lyxp_node_type parent_type,
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006108 struct lyxp_set *to_set, const struct lyxp_set *dup_check_set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006109{
Michal Vasko61ac2f62020-05-25 12:39:51 +02006110 const struct lyd_node *iter, *first;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006111 LY_ERR rc;
6112
6113 switch (parent_type) {
6114 case LYXP_NODE_ROOT:
6115 case LYXP_NODE_ROOT_CONFIG:
Michal Vasko61ac2f62020-05-25 12:39:51 +02006116 assert(!parent);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006117
Michal Vasko61ac2f62020-05-25 12:39:51 +02006118 /* add all top-level nodes as elements */
6119 first = to_set->tree;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006120 break;
6121 case LYXP_NODE_ELEM:
Michal Vasko61ac2f62020-05-25 12:39:51 +02006122 /* add just the text node of this term element node */
6123 if (parent->schema->nodetype & (LYD_NODE_TERM | LYD_NODE_ANY)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006124 if (!set_dup_node_check(dup_check_set, parent, LYXP_NODE_TEXT, -1)) {
6125 set_insert_node(to_set, parent, parent_pos, LYXP_NODE_TEXT, to_set->used);
6126 }
Michal Vasko61ac2f62020-05-25 12:39:51 +02006127 return LY_SUCCESS;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006128 }
Michal Vasko61ac2f62020-05-25 12:39:51 +02006129
6130 /* add all the children of this node */
Michal Vasko5bfd4be2020-06-23 13:26:19 +02006131 first = lyd_node_children(parent, 0);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006132 break;
6133 default:
6134 LOGINT_RET(parent->schema->module->ctx);
6135 }
6136
Michal Vasko61ac2f62020-05-25 12:39:51 +02006137 /* add all top-level nodes as elements */
6138 LY_LIST_FOR(first, iter) {
6139 /* context check */
6140 if ((parent_type == LYXP_NODE_ROOT_CONFIG) && (iter->schema->flags & LYS_CONFIG_R)) {
6141 continue;
6142 }
6143
6144 /* when check */
6145 if (moveto_when_check(iter)) {
6146 return LY_EINCOMPLETE;
6147 }
6148
6149 if (!set_dup_node_check(dup_check_set, iter, LYXP_NODE_ELEM, -1)) {
6150 set_insert_node(to_set, iter, 0, LYXP_NODE_ELEM, to_set->used);
6151
6152 /* also add all the children of this node, recursively */
6153 rc = moveto_self_add_children_r(iter, 0, LYXP_NODE_ELEM, to_set, dup_check_set, options);
6154 LY_CHECK_RET(rc);
6155 }
6156 }
6157
Michal Vasko03ff5a72019-09-11 13:49:33 +02006158 return LY_SUCCESS;
6159}
6160
6161/**
6162 * @brief Move context @p set to self. Handles '/' or '//' and '.'. Result is LYXP_SET_NODE_SET
6163 * (or LYXP_SET_EMPTY). Context position aware.
6164 *
6165 * @param[in,out] set Set to use.
6166 * @param[in] all_desc Whether to go to all descendants ('//') or not ('/').
6167 * @param[in] options XPath options.
6168 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6169 */
6170static LY_ERR
6171moveto_self(struct lyxp_set *set, int all_desc, int options)
6172{
6173 uint32_t i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006174 struct lyxp_set ret_set;
6175 LY_ERR rc;
6176
Michal Vaskod3678892020-05-21 10:06:58 +02006177 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006178 return LY_SUCCESS;
6179 }
6180
6181 if (set->type != LYXP_SET_NODE_SET) {
6182 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
6183 return LY_EVALID;
6184 }
6185
6186 /* nothing to do */
6187 if (!all_desc) {
6188 return LY_SUCCESS;
6189 }
6190
Michal Vasko03ff5a72019-09-11 13:49:33 +02006191 /* add all the children, they get added recursively */
6192 set_init(&ret_set, set);
6193 for (i = 0; i < set->used; ++i) {
6194 /* copy the current node to tmp */
6195 set_insert_node(&ret_set, set->val.nodes[i].node, set->val.nodes[i].pos, set->val.nodes[i].type, ret_set.used);
6196
6197 /* do not touch attributes and text nodes */
Michal Vasko9f96a052020-03-10 09:41:45 +01006198 if ((set->val.nodes[i].type == LYXP_NODE_TEXT) || (set->val.nodes[i].type == LYXP_NODE_META)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006199 continue;
6200 }
6201
Michal Vasko03ff5a72019-09-11 13:49:33 +02006202 /* add all the children */
6203 rc = moveto_self_add_children_r(set->val.nodes[i].node, set->val.nodes[i].pos, set->val.nodes[i].type, &ret_set,
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006204 set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006205 if (rc != LY_SUCCESS) {
Michal Vaskod3678892020-05-21 10:06:58 +02006206 lyxp_set_free_content(&ret_set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006207 return rc;
6208 }
6209 }
6210
6211 /* use the temporary set as the current one */
6212 ret_set.ctx_pos = set->ctx_pos;
6213 ret_set.ctx_size = set->ctx_size;
Michal Vaskod3678892020-05-21 10:06:58 +02006214 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006215 memcpy(set, &ret_set, sizeof *set);
6216
6217 return LY_SUCCESS;
6218}
6219
6220/**
6221 * @brief Move context schema @p set to self. Handles '/' or '//' and '.'. Result is LYXP_SET_SCNODE_SET
6222 * (or LYXP_SET_EMPTY).
6223 *
6224 * @param[in,out] set Set to use.
6225 * @param[in] all_desc Whether to go to all descendants ('//') or not ('/').
6226 * @param[in] options XPath options.
6227 * @return LY_ERR
6228 */
6229static LY_ERR
6230moveto_scnode_self(struct lyxp_set *set, int all_desc, int options)
6231{
Michal Vasko519fd602020-05-26 12:17:39 +02006232 int getnext_opts;
6233 uint32_t i, mod_idx;
6234 const struct lysc_node *iter, *start_parent;
6235 const struct lys_module *mod;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006236
Michal Vaskod3678892020-05-21 10:06:58 +02006237 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006238 return LY_SUCCESS;
6239 }
6240
6241 if (set->type != LYXP_SET_SCNODE_SET) {
Michal Vaskof6e51882019-12-16 09:59:45 +01006242 LOGVAL(set->ctx, LY_VLOG_LYSC, set->ctx_scnode, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006243 return LY_EVALID;
6244 }
6245
6246 /* nothing to do */
6247 if (!all_desc) {
6248 return LY_SUCCESS;
6249 }
6250
Michal Vasko519fd602020-05-26 12:17:39 +02006251 /* getnext opts */
6252 getnext_opts = LYS_GETNEXT_NOSTATECHECK;
6253 if (options & LYXP_SCNODE_OUTPUT) {
6254 getnext_opts |= LYS_GETNEXT_OUTPUT;
6255 }
6256
6257 /* add all the children, recursively as they are being added into the same set */
Michal Vasko03ff5a72019-09-11 13:49:33 +02006258 for (i = 0; i < set->used; ++i) {
6259 if (set->val.scnodes[i].in_ctx != 1) {
Michal Vasko5c4e5892019-11-14 12:31:38 +01006260 if (set->val.scnodes[i].in_ctx != -2) {
6261 continue;
6262 }
6263
Michal Vasko519fd602020-05-26 12:17:39 +02006264 /* remember context node */
6265 set->val.scnodes[i].in_ctx = -1;
6266 } else {
6267 set->val.scnodes[i].in_ctx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006268 }
6269
Michal Vasko519fd602020-05-26 12:17:39 +02006270 start_parent = set->val.scnodes[i].scnode;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006271
Michal Vasko519fd602020-05-26 12:17:39 +02006272 if ((set->val.scnodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.scnodes[i].type == LYXP_NODE_ROOT)) {
6273 /* it can actually be in any module, it's all <running> */
6274 mod_idx = 0;
6275 while ((mod = (struct lys_module *)ly_ctx_get_module_iter(set->ctx, &mod_idx))) {
6276 iter = NULL;
6277 /* module may not be implemented */
6278 while (mod->implemented && (iter = lys_getnext(iter, NULL, mod->compiled, getnext_opts))) {
6279 /* context check */
6280 if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (iter->flags & LYS_CONFIG_R)) {
6281 continue;
6282 }
6283
6284 lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
6285 /* throw away the insert index, we want to consider that node again, recursively */
6286 }
6287 }
6288
6289 } else if (set->val.scnodes[i].type == LYXP_NODE_ELEM) {
6290 iter = NULL;
6291 while ((iter = lys_getnext(iter, start_parent, NULL, getnext_opts))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006292 /* context check */
Michal Vasko519fd602020-05-26 12:17:39 +02006293 if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (iter->flags & LYS_CONFIG_R)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006294 continue;
6295 }
6296
Michal Vasko519fd602020-05-26 12:17:39 +02006297 lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006298 }
6299 }
6300 }
6301
6302 return LY_SUCCESS;
6303}
6304
6305/**
6306 * @brief Move context @p set to parent. Handles '/' or '//' and '..'. Result is LYXP_SET_NODE_SET
6307 * (or LYXP_SET_EMPTY). Context position aware.
6308 *
6309 * @param[in] set Set to use.
6310 * @param[in] all_desc Whether to go to all descendants ('//') or not ('/').
6311 * @param[in] options XPath options.
6312 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6313 */
6314static LY_ERR
6315moveto_parent(struct lyxp_set *set, int all_desc, int options)
6316{
6317 LY_ERR rc;
6318 uint32_t i;
6319 struct lyd_node *node, *new_node;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006320 enum lyxp_node_type new_type;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006321
Michal Vaskod3678892020-05-21 10:06:58 +02006322 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006323 return LY_SUCCESS;
6324 }
6325
6326 if (set->type != LYXP_SET_NODE_SET) {
6327 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
6328 return LY_EVALID;
6329 }
6330
6331 if (all_desc) {
6332 /* <path>//.. == <path>//./.. */
6333 rc = moveto_self(set, 1, options);
6334 LY_CHECK_RET(rc);
6335 }
6336
Michal Vasko57eab132019-09-24 11:46:26 +02006337 for (i = 0; i < set->used; ++i) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006338 node = set->val.nodes[i].node;
6339
6340 if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
6341 new_node = (struct lyd_node *)node->parent;
6342 } else if (set->val.nodes[i].type == LYXP_NODE_TEXT) {
6343 new_node = node;
Michal Vasko9f96a052020-03-10 09:41:45 +01006344 } else if (set->val.nodes[i].type == LYXP_NODE_META) {
6345 new_node = set->val.meta[i].meta->parent;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006346 if (!new_node) {
6347 LOGINT_RET(set->ctx);
6348 }
6349 } else {
6350 /* root does not have a parent */
Michal Vasko2caefc12019-11-14 16:07:56 +01006351 set_remove_node_none(set, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006352 continue;
6353 }
6354
Michal Vaskoa1424542019-11-14 16:08:52 +01006355 /* when check */
6356 if (moveto_when_check(new_node)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006357 return LY_EINCOMPLETE;
Michal Vaskoa1424542019-11-14 16:08:52 +01006358 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02006359
6360 /* node already there can also be the root */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006361 if (!new_node) {
6362 new_type = set->root_type;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006363
6364 /* node has a standard parent (it can equal the root, it's not the root yet since they are fake) */
6365 } else {
6366 new_type = LYXP_NODE_ELEM;
6367 }
6368
Michal Vasko03ff5a72019-09-11 13:49:33 +02006369 if (set_dup_node_check(set, new_node, new_type, -1)) {
Michal Vasko2caefc12019-11-14 16:07:56 +01006370 set_remove_node_none(set, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006371 } else {
6372 set_replace_node(set, new_node, 0, new_type, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006373 }
6374 }
6375
Michal Vasko2caefc12019-11-14 16:07:56 +01006376 set_remove_nodes_none(set);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006377 assert(!set_sort(set) && !set_sorted_dup_node_clean(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006378
6379 return LY_SUCCESS;
6380}
6381
6382/**
6383 * @brief Move context schema @p set to parent. Handles '/' or '//' and '..'. Result is LYXP_SET_SCNODE_SET
6384 * (or LYXP_SET_EMPTY).
6385 *
6386 * @param[in] set Set to use.
6387 * @param[in] all_desc Whether to go to all descendants ('//') or not ('/').
6388 * @param[in] options XPath options.
6389 * @return LY_ERR
6390 */
6391static LY_ERR
6392moveto_scnode_parent(struct lyxp_set *set, int all_desc, int options)
6393{
6394 int idx, i, orig_used, temp_ctx = 0;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006395 const struct lysc_node *node, *new_node;
6396 enum lyxp_node_type new_type;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006397 LY_ERR rc;
6398
Michal Vaskod3678892020-05-21 10:06:58 +02006399 if (!set) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006400 return LY_SUCCESS;
6401 }
6402
6403 if (set->type != LYXP_SET_SCNODE_SET) {
Michal Vaskof6e51882019-12-16 09:59:45 +01006404 LOGVAL(set->ctx, LY_VLOG_LYSC, set->ctx_scnode, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006405 return LY_EVALID;
6406 }
6407
6408 if (all_desc) {
6409 /* <path>//.. == <path>//./.. */
6410 rc = moveto_scnode_self(set, 1, options);
6411 LY_CHECK_RET(rc);
6412 }
6413
Michal Vasko03ff5a72019-09-11 13:49:33 +02006414 orig_used = set->used;
6415 for (i = 0; i < orig_used; ++i) {
6416 if (set->val.scnodes[i].in_ctx != 1) {
Michal Vasko5c4e5892019-11-14 12:31:38 +01006417 if (set->val.scnodes[i].in_ctx != -2) {
6418 continue;
6419 }
6420
6421 /* remember context node */
6422 set->val.scnodes[i].in_ctx = -1;
Michal Vaskoec4df482019-12-16 10:02:18 +01006423 } else {
6424 set->val.scnodes[i].in_ctx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006425 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02006426
6427 node = set->val.scnodes[i].scnode;
6428
6429 if (set->val.scnodes[i].type == LYXP_NODE_ELEM) {
Michal Vaskod3678892020-05-21 10:06:58 +02006430 new_node = lysc_data_parent(node);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006431 } else {
6432 /* root does not have a parent */
6433 continue;
6434 }
6435
Michal Vasko03ff5a72019-09-11 13:49:33 +02006436 /* node has no parent */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006437 if (!new_node) {
6438 new_type = set->root_type;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006439
6440 /* node has a standard parent (it can equal the root, it's not the root yet since they are fake) */
6441 } else {
6442 new_type = LYXP_NODE_ELEM;
6443 }
6444
Michal Vaskoecd62de2019-11-13 12:35:11 +01006445 idx = lyxp_set_scnode_insert_node(set, new_node, new_type);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006446 if ((idx < orig_used) && (idx > i)) {
6447 set->val.scnodes[idx].in_ctx = 2;
6448 temp_ctx = 1;
6449 }
6450 }
6451
6452 if (temp_ctx) {
6453 for (i = 0; i < orig_used; ++i) {
6454 if (set->val.scnodes[i].in_ctx == 2) {
6455 set->val.scnodes[i].in_ctx = 1;
6456 }
6457 }
6458 }
6459
6460 return LY_SUCCESS;
6461}
6462
6463/**
6464 * @brief Move context @p set to the result of a comparison. Handles '=', '!=', '<=', '<', '>=', or '>'.
6465 * Result is LYXP_SET_BOOLEAN. Indirectly context position aware.
6466 *
6467 * @param[in,out] set1 Set to use for the result.
6468 * @param[in] set2 Set acting as the second operand for @p op.
6469 * @param[in] op Comparison operator to process.
6470 * @param[in] options XPath options.
6471 * @return LY_ERR
6472 */
6473static LY_ERR
6474moveto_op_comp(struct lyxp_set *set1, struct lyxp_set *set2, const char *op, int options)
6475{
6476 /*
6477 * NODE SET + NODE SET = NODE SET + STRING /(1 NODE SET) 2 STRING
6478 * NODE SET + STRING = STRING + STRING /1 STRING (2 STRING)
6479 * NODE SET + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
6480 * NODE SET + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
6481 * STRING + NODE SET = STRING + STRING /(1 STRING) 2 STRING
6482 * NUMBER + NODE SET = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
6483 * BOOLEAN + NODE SET = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
6484 *
6485 * '=' or '!='
6486 * BOOLEAN + BOOLEAN
6487 * BOOLEAN + STRING = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
6488 * BOOLEAN + NUMBER = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
6489 * STRING + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
6490 * NUMBER + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
6491 * NUMBER + NUMBER
6492 * NUMBER + STRING = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
6493 * STRING + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
6494 * STRING + STRING
6495 *
6496 * '<=', '<', '>=', '>'
6497 * NUMBER + NUMBER
6498 * BOOLEAN + BOOLEAN = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
6499 * BOOLEAN + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
6500 * BOOLEAN + STRING = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
6501 * NUMBER + STRING = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
6502 * STRING + STRING = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
6503 * STRING + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
6504 * NUMBER + BOOLEAN = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
6505 * STRING + BOOLEAN = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
6506 */
6507 struct lyxp_set iter1, iter2;
6508 int result;
6509 int64_t i;
6510 LY_ERR rc;
6511
Michal Vasko004d3152020-06-11 19:59:22 +02006512 memset(&iter1, 0, sizeof iter1);
6513 memset(&iter2, 0, sizeof iter2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006514
6515 /* iterative evaluation with node-sets */
6516 if ((set1->type == LYXP_SET_NODE_SET) || (set2->type == LYXP_SET_NODE_SET)) {
6517 if (set1->type == LYXP_SET_NODE_SET) {
6518 for (i = 0; i < set1->used; ++i) {
Michal Vasko4c7763f2020-07-27 17:40:37 +02006519 /* cast set1 */
Michal Vasko03ff5a72019-09-11 13:49:33 +02006520 switch (set2->type) {
6521 case LYXP_SET_NUMBER:
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006522 rc = set_comp_cast(&iter1, set1, LYXP_SET_NUMBER, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006523 break;
6524 case LYXP_SET_BOOLEAN:
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006525 rc = set_comp_cast(&iter1, set1, LYXP_SET_BOOLEAN, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006526 break;
6527 default:
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006528 rc = set_comp_cast(&iter1, set1, LYXP_SET_STRING, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006529 break;
6530 }
6531 LY_CHECK_RET(rc);
6532
Michal Vasko4c7763f2020-07-27 17:40:37 +02006533 /* canonize set2 */
6534 LY_CHECK_ERR_RET(rc = set_comp_canonize(&iter2, set2, &set1->val.nodes[i]), lyxp_set_free_content(&iter1), rc);
6535
6536 /* compare recursively */
6537 rc = moveto_op_comp(&iter1, &iter2, op, options);
6538 lyxp_set_free_content(&iter2);
6539 LY_CHECK_ERR_RET(rc, lyxp_set_free_content(&iter1), rc);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006540
6541 /* lazy evaluation until true */
Michal Vasko004d3152020-06-11 19:59:22 +02006542 if (iter1.val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006543 set_fill_boolean(set1, 1);
6544 return LY_SUCCESS;
6545 }
6546 }
6547 } else {
6548 for (i = 0; i < set2->used; ++i) {
Michal Vasko4c7763f2020-07-27 17:40:37 +02006549 /* set set2 */
Michal Vasko03ff5a72019-09-11 13:49:33 +02006550 switch (set1->type) {
Michal Vasko4c7763f2020-07-27 17:40:37 +02006551 case LYXP_SET_NUMBER:
6552 rc = set_comp_cast(&iter2, set2, LYXP_SET_NUMBER, i);
6553 break;
6554 case LYXP_SET_BOOLEAN:
6555 rc = set_comp_cast(&iter2, set2, LYXP_SET_BOOLEAN, i);
6556 break;
6557 default:
6558 rc = set_comp_cast(&iter2, set2, LYXP_SET_STRING, i);
6559 break;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006560 }
6561 LY_CHECK_RET(rc);
6562
Michal Vasko4c7763f2020-07-27 17:40:37 +02006563 /* canonize set1 */
6564 LY_CHECK_ERR_RET(rc = set_comp_canonize(&iter1, set1, &set2->val.nodes[i]), lyxp_set_free_content(&iter2), rc);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006565
Michal Vasko4c7763f2020-07-27 17:40:37 +02006566 /* compare recursively */
Michal Vasko03ff5a72019-09-11 13:49:33 +02006567 rc = moveto_op_comp(&iter1, &iter2, op, options);
Michal Vaskod3678892020-05-21 10:06:58 +02006568 lyxp_set_free_content(&iter2);
Michal Vasko4c7763f2020-07-27 17:40:37 +02006569 LY_CHECK_ERR_RET(rc, lyxp_set_free_content(&iter1), rc);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006570
6571 /* lazy evaluation until true */
Michal Vasko004d3152020-06-11 19:59:22 +02006572 if (iter1.val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006573 set_fill_boolean(set1, 1);
6574 return LY_SUCCESS;
6575 }
6576 }
6577 }
6578
6579 /* false for all nodes */
6580 set_fill_boolean(set1, 0);
6581 return LY_SUCCESS;
6582 }
6583
6584 /* first convert properly */
6585 if ((op[0] == '=') || (op[0] == '!')) {
6586 if ((set1->type == LYXP_SET_BOOLEAN) || (set2->type == LYXP_SET_BOOLEAN)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006587 lyxp_set_cast(set1, LYXP_SET_BOOLEAN);
6588 lyxp_set_cast(set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006589 } else if ((set1->type == LYXP_SET_NUMBER) || (set2->type == LYXP_SET_NUMBER)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006590 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006591 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006592 rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006593 LY_CHECK_RET(rc);
6594 } /* else we have 2 strings */
6595 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006596 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006597 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006598 rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006599 LY_CHECK_RET(rc);
6600 }
6601
6602 assert(set1->type == set2->type);
6603
6604 /* compute result */
6605 if (op[0] == '=') {
6606 if (set1->type == LYXP_SET_BOOLEAN) {
Michal Vasko004d3152020-06-11 19:59:22 +02006607 result = (set1->val.bln == set2->val.bln);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006608 } else if (set1->type == LYXP_SET_NUMBER) {
6609 result = (set1->val.num == set2->val.num);
6610 } else {
6611 assert(set1->type == LYXP_SET_STRING);
Michal Vaskoac6c72f2019-11-14 16:09:34 +01006612 result = !strcmp(set1->val.str, set2->val.str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006613 }
6614 } else if (op[0] == '!') {
6615 if (set1->type == LYXP_SET_BOOLEAN) {
Michal Vasko004d3152020-06-11 19:59:22 +02006616 result = (set1->val.bln != set2->val.bln);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006617 } else if (set1->type == LYXP_SET_NUMBER) {
6618 result = (set1->val.num != set2->val.num);
6619 } else {
6620 assert(set1->type == LYXP_SET_STRING);
Michal Vaskoac6c72f2019-11-14 16:09:34 +01006621 result = !strcmp(set1->val.str, set2->val.str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006622 }
6623 } else {
6624 assert(set1->type == LYXP_SET_NUMBER);
6625 if (op[0] == '<') {
6626 if (op[1] == '=') {
6627 result = (set1->val.num <= set2->val.num);
6628 } else {
6629 result = (set1->val.num < set2->val.num);
6630 }
6631 } else {
6632 if (op[1] == '=') {
6633 result = (set1->val.num >= set2->val.num);
6634 } else {
6635 result = (set1->val.num > set2->val.num);
6636 }
6637 }
6638 }
6639
6640 /* assign result */
6641 if (result) {
6642 set_fill_boolean(set1, 1);
6643 } else {
6644 set_fill_boolean(set1, 0);
6645 }
6646
6647 return LY_SUCCESS;
6648}
6649
6650/**
6651 * @brief Move context @p set to the result of a basic operation. Handles '+', '-', unary '-', '*', 'div',
6652 * or 'mod'. Result is LYXP_SET_NUMBER. Indirectly context position aware.
6653 *
6654 * @param[in,out] set1 Set to use for the result.
6655 * @param[in] set2 Set acting as the second operand for @p op.
6656 * @param[in] op Operator to process.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006657 * @return LY_ERR
6658 */
6659static LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006660moveto_op_math(struct lyxp_set *set1, struct lyxp_set *set2, const char *op)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006661{
6662 LY_ERR rc;
6663
6664 /* unary '-' */
6665 if (!set2 && (op[0] == '-')) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006666 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006667 LY_CHECK_RET(rc);
6668 set1->val.num *= -1;
6669 lyxp_set_free(set2);
6670 return LY_SUCCESS;
6671 }
6672
6673 assert(set1 && set2);
6674
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006675 rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006676 LY_CHECK_RET(rc);
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006677 rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006678 LY_CHECK_RET(rc);
6679
6680 switch (op[0]) {
6681 /* '+' */
6682 case '+':
6683 set1->val.num += set2->val.num;
6684 break;
6685
6686 /* '-' */
6687 case '-':
6688 set1->val.num -= set2->val.num;
6689 break;
6690
6691 /* '*' */
6692 case '*':
6693 set1->val.num *= set2->val.num;
6694 break;
6695
6696 /* 'div' */
6697 case 'd':
6698 set1->val.num /= set2->val.num;
6699 break;
6700
6701 /* 'mod' */
6702 case 'm':
6703 set1->val.num = ((long long)set1->val.num) % ((long long)set2->val.num);
6704 break;
6705
6706 default:
6707 LOGINT_RET(set1->ctx);
6708 }
6709
6710 return LY_SUCCESS;
6711}
6712
6713/*
6714 * eval functions
6715 *
6716 * They execute a parsed XPath expression on some data subtree.
6717 */
6718
6719/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02006720 * @brief Evaluate Predicate. Logs directly on error.
6721 *
Michal Vaskod3678892020-05-21 10:06:58 +02006722 * [9] Predicate ::= '[' Expr ']'
Michal Vasko03ff5a72019-09-11 13:49:33 +02006723 *
6724 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02006725 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02006726 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
6727 * @param[in] options XPath options.
6728 * @param[in] parent_pos_pred Whether parent predicate was a positional one.
6729 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
6730 */
6731static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02006732eval_predicate(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options, int parent_pos_pred)
Michal Vasko03ff5a72019-09-11 13:49:33 +02006733{
6734 LY_ERR rc;
Michal Vasko57eab132019-09-24 11:46:26 +02006735 uint16_t i, orig_exp;
Michal Vasko5c4e5892019-11-14 12:31:38 +01006736 uint32_t orig_pos, orig_size;
6737 int32_t pred_in_ctx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006738 struct lyxp_set set2;
6739 struct lyd_node *orig_parent;
6740
6741 /* '[' */
6742 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02006743 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
6744 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006745
6746 if (!set) {
6747only_parse:
Michal Vasko004d3152020-06-11 19:59:22 +02006748 rc = eval_expr_select(exp, tok_idx, 0, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006749 LY_CHECK_RET(rc);
6750 } else if (set->type == LYXP_SET_NODE_SET) {
6751 /* 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 +01006752 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02006753
6754 /* empty set, nothing to evaluate */
6755 if (!set->used) {
6756 goto only_parse;
6757 }
6758
Michal Vasko004d3152020-06-11 19:59:22 +02006759 orig_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006760 orig_pos = 0;
6761 orig_size = set->used;
6762 orig_parent = NULL;
Michal Vasko39dbf352020-05-21 10:08:59 +02006763 for (i = 0; i < set->used; ++i) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02006764 set_init(&set2, set);
6765 set_insert_node(&set2, set->val.nodes[i].node, set->val.nodes[i].pos, set->val.nodes[i].type, 0);
6766 /* remember the node context position for position() and context size for last(),
6767 * predicates should always be evaluated with respect to the child axis (since we do
6768 * not support explicit axes) so we assign positions based on their parents */
6769 if (parent_pos_pred && ((struct lyd_node *)set->val.nodes[i].node->parent != orig_parent)) {
6770 orig_parent = (struct lyd_node *)set->val.nodes[i].node->parent;
6771 orig_pos = 1;
6772 } else {
6773 ++orig_pos;
6774 }
6775
6776 set2.ctx_pos = orig_pos;
6777 set2.ctx_size = orig_size;
Michal Vasko004d3152020-06-11 19:59:22 +02006778 *tok_idx = orig_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006779
Michal Vasko004d3152020-06-11 19:59:22 +02006780 rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006781 if (rc != LY_SUCCESS) {
Michal Vaskod3678892020-05-21 10:06:58 +02006782 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006783 return rc;
6784 }
6785
6786 /* number is a position */
6787 if (set2.type == LYXP_SET_NUMBER) {
6788 if ((long long)set2.val.num == orig_pos) {
6789 set2.val.num = 1;
6790 } else {
6791 set2.val.num = 0;
6792 }
6793 }
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006794 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006795
6796 /* predicate satisfied or not? */
Michal Vasko004d3152020-06-11 19:59:22 +02006797 if (!set2.val.bln) {
Michal Vasko2caefc12019-11-14 16:07:56 +01006798 set_remove_node_none(set, i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006799 }
6800 }
Michal Vasko2caefc12019-11-14 16:07:56 +01006801 set_remove_nodes_none(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006802
6803 } else if (set->type == LYXP_SET_SCNODE_SET) {
6804 for (i = 0; i < set->used; ++i) {
6805 if (set->val.scnodes[i].in_ctx == 1) {
6806 /* there is a currently-valid node */
6807 break;
6808 }
6809 }
6810 /* empty set, nothing to evaluate */
6811 if (i == set->used) {
6812 goto only_parse;
6813 }
6814
Michal Vasko004d3152020-06-11 19:59:22 +02006815 orig_exp = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006816
Michal Vasko03ff5a72019-09-11 13:49:33 +02006817 /* set special in_ctx to all the valid snodes */
6818 pred_in_ctx = set_scnode_new_in_ctx(set);
6819
6820 /* use the valid snodes one-by-one */
6821 for (i = 0; i < set->used; ++i) {
6822 if (set->val.scnodes[i].in_ctx != pred_in_ctx) {
6823 continue;
6824 }
6825 set->val.scnodes[i].in_ctx = 1;
6826
Michal Vasko004d3152020-06-11 19:59:22 +02006827 *tok_idx = orig_exp;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006828
Michal Vasko004d3152020-06-11 19:59:22 +02006829 rc = eval_expr_select(exp, tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006830 LY_CHECK_RET(rc);
6831
6832 set->val.scnodes[i].in_ctx = pred_in_ctx;
6833 }
6834
6835 /* restore the state as it was before the predicate */
6836 for (i = 0; i < set->used; ++i) {
6837 if (set->val.scnodes[i].in_ctx == 1) {
6838 set->val.scnodes[i].in_ctx = 0;
6839 } else if (set->val.scnodes[i].in_ctx == pred_in_ctx) {
6840 set->val.scnodes[i].in_ctx = 1;
6841 }
6842 }
6843
6844 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02006845 set2.type = LYXP_SET_NODE_SET;
Michal Vasko03ff5a72019-09-11 13:49:33 +02006846 set_fill_set(&set2, set);
6847
Michal Vasko004d3152020-06-11 19:59:22 +02006848 rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006849 if (rc != LY_SUCCESS) {
Michal Vaskod3678892020-05-21 10:06:58 +02006850 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006851 return rc;
6852 }
6853
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01006854 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko004d3152020-06-11 19:59:22 +02006855 if (!set2.val.bln) {
Michal Vaskod3678892020-05-21 10:06:58 +02006856 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006857 }
Michal Vaskod3678892020-05-21 10:06:58 +02006858 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006859 }
6860
6861 /* ']' */
Michal Vasko004d3152020-06-11 19:59:22 +02006862 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006863 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02006864 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
6865 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02006866
6867 return LY_SUCCESS;
6868}
6869
6870/**
Michal Vaskod3678892020-05-21 10:06:58 +02006871 * @brief Evaluate Literal. Logs directly on error.
6872 *
6873 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02006874 * @param[in] tok_idx Position in the expression @p exp.
Michal Vaskod3678892020-05-21 10:06:58 +02006875 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
6876 */
6877static void
Michal Vasko004d3152020-06-11 19:59:22 +02006878eval_literal(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set)
Michal Vaskod3678892020-05-21 10:06:58 +02006879{
6880 if (set) {
Michal Vasko004d3152020-06-11 19:59:22 +02006881 if (exp->tok_len[*tok_idx] == 2) {
Michal Vaskod3678892020-05-21 10:06:58 +02006882 set_fill_string(set, "", 0);
6883 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02006884 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 +02006885 }
6886 }
6887 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02006888 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
6889 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02006890}
6891
6892/**
Michal Vasko004d3152020-06-11 19:59:22 +02006893 * @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 +02006894 *
Michal Vasko004d3152020-06-11 19:59:22 +02006895 * @param[in] exp Full parsed XPath expression.
6896 * @param[in,out] tok_idx Index in @p exp at the beginning of the predicate, is updated on success.
6897 * @param[in] scnode Found schema node as context for the predicate.
6898 * @param[in] format Format of any prefixes in key names/values.
6899 * @param[out] predicates Parsed predicates.
6900 * @param[out] pred_type Type of @p predicates.
6901 * @return LY_SUCCESS on success,
6902 * @return LY_ERR on any error.
Michal Vaskod3678892020-05-21 10:06:58 +02006903 */
6904static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02006905eval_name_test_try_compile_predicates(struct lyxp_expr *exp, uint16_t *tok_idx, const struct lysc_node *scnode,
6906 LYD_FORMAT format, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
Michal Vaskod3678892020-05-21 10:06:58 +02006907{
Michal Vasko004d3152020-06-11 19:59:22 +02006908 LY_ERR ret = LY_SUCCESS;
6909 uint16_t key_count, e_idx, pred_idx = 0;
Michal Vaskod3678892020-05-21 10:06:58 +02006910 const struct lysc_node *key;
Michal Vasko004d3152020-06-11 19:59:22 +02006911 size_t pred_len;
6912 int prev_lo;
6913 struct lyxp_expr *exp2 = NULL;
Michal Vaskod3678892020-05-21 10:06:58 +02006914
Michal Vasko004d3152020-06-11 19:59:22 +02006915 assert(scnode->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vaskod3678892020-05-21 10:06:58 +02006916
Michal Vasko004d3152020-06-11 19:59:22 +02006917 if (scnode->nodetype == LYS_LIST) {
6918 /* get key count */
6919 if (scnode->flags & LYS_KEYLESS) {
6920 return LY_EINVAL;
6921 }
6922 for (key_count = 0, key = lysc_node_children(scnode, 0); key && (key->flags & LYS_KEY); key = key->next, ++key_count);
6923 assert(key_count);
Michal Vaskod3678892020-05-21 10:06:58 +02006924
Michal Vasko004d3152020-06-11 19:59:22 +02006925 /* learn where the predicates end */
6926 e_idx = *tok_idx;
6927 while (key_count) {
6928 /* '[' */
6929 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
6930 return LY_EINVAL;
6931 }
6932 ++e_idx;
6933
6934 /* ']' */
6935 while (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
6936 ++e_idx;
6937 }
6938 ++e_idx;
6939
6940 /* another presumably key predicate parsed */
6941 --key_count;
6942 }
Michal Vasko004d3152020-06-11 19:59:22 +02006943 } else {
6944 /* learn just where this single predicate ends */
6945 e_idx = *tok_idx;
6946
Michal Vaskod3678892020-05-21 10:06:58 +02006947 /* '[' */
Michal Vasko004d3152020-06-11 19:59:22 +02006948 if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
6949 return LY_EINVAL;
6950 }
6951 ++e_idx;
Michal Vaskod3678892020-05-21 10:06:58 +02006952
6953 /* ']' */
Michal Vasko004d3152020-06-11 19:59:22 +02006954 while (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
6955 ++e_idx;
6956 }
6957 ++e_idx;
Michal Vaskod3678892020-05-21 10:06:58 +02006958 }
6959
Michal Vasko004d3152020-06-11 19:59:22 +02006960 /* get the joined length of all the keys predicates/of the single leaf-list predicate */
6961 pred_len = (exp->tok_pos[e_idx - 1] + exp->tok_len[e_idx - 1]) - exp->tok_pos[*tok_idx];
6962
6963 /* turn logging off */
6964 prev_lo = ly_log_options(0);
6965
6966 /* parse the predicate(s) */
Michal Vasko6b26e742020-07-17 15:02:10 +02006967 LY_CHECK_GOTO(ret = ly_path_parse_predicate(scnode->module->ctx, scnode, exp->expr + exp->tok_pos[*tok_idx], pred_len,
Michal Vasko004d3152020-06-11 19:59:22 +02006968 LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &exp2), cleanup);
6969
6970 /* compile */
6971 switch (format) {
6972 case LYD_SCHEMA:
Michal Vasko6b26e742020-07-17 15:02:10 +02006973 ret = ly_path_compile_predicate(scnode->module->ctx, scnode, scnode->module, scnode, exp2, &pred_idx,
6974 lys_resolve_prefix, scnode->module, LYD_SCHEMA, predicates, pred_type);
Michal Vasko004d3152020-06-11 19:59:22 +02006975 break;
6976 case LYD_JSON:
Michal Vasko6b26e742020-07-17 15:02:10 +02006977 ret = ly_path_compile_predicate(scnode->module->ctx, scnode, scnode->module, scnode, exp2, &pred_idx,
Michal Vasko00cbf532020-06-15 13:58:47 +02006978 lydjson_resolve_prefix, NULL, LYD_JSON, predicates, pred_type);
Michal Vasko004d3152020-06-11 19:59:22 +02006979 break;
6980 case LYD_XML:
Michal Vasko60ea6352020-06-29 13:39:39 +02006981 case LYD_LYB:
Michal Vasko004d3152020-06-11 19:59:22 +02006982 ret = LY_EINT;
6983 goto cleanup;
6984 }
6985 LY_CHECK_GOTO(ret, cleanup);
6986
6987 /* success, the predicate must include all the needed information for hash-based search */
6988 *tok_idx = e_idx;
6989
6990cleanup:
6991 ly_log_options(prev_lo);
6992 lyxp_expr_free(scnode->module->ctx, exp2);
6993 return ret;
Michal Vaskod3678892020-05-21 10:06:58 +02006994}
6995
6996/**
6997 * @brief Evaluate NameTest and any following Predicates. Logs directly on error.
6998 *
6999 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
7000 * [6] NodeTest ::= NameTest | NodeType '(' ')'
7001 * [7] NameTest ::= '*' | NCName ':' '*' | QName
7002 *
7003 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007004 * @param[in] tok_idx Position in the expression @p exp.
Michal Vaskod3678892020-05-21 10:06:58 +02007005 * @param[in] attr_axis Whether to search attributes or standard nodes.
7006 * @param[in] all_desc Whether to search all the descendants or children only.
7007 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7008 * @param[in] options XPath options.
7009 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7010 */
7011static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007012eval_name_test_with_predicate(struct lyxp_expr *exp, uint16_t *tok_idx, int attr_axis, int all_desc, struct lyxp_set *set,
Michal Vaskod3678892020-05-21 10:06:58 +02007013 int options)
7014{
7015 int i;
7016 char *path;
Michal Vasko004d3152020-06-11 19:59:22 +02007017 const char *ncname, *ncname_dict = NULL;
7018 uint16_t ncname_len;
Michal Vaskod3678892020-05-21 10:06:58 +02007019 const struct lys_module *moveto_mod;
7020 const struct lysc_node *scnode = NULL, *tmp;
Michal Vasko004d3152020-06-11 19:59:22 +02007021 struct ly_path_predicate *predicates = NULL;
7022 enum ly_path_pred_type pred_type = 0;
Michal Vaskod3678892020-05-21 10:06:58 +02007023 LY_ERR rc = LY_SUCCESS;
7024
7025 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007026 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7027 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02007028
7029 if (!set) {
7030 goto moveto;
7031 }
7032
Michal Vasko004d3152020-06-11 19:59:22 +02007033 ncname = &exp->expr[exp->tok_pos[*tok_idx - 1]];
7034 ncname_len = exp->tok_len[*tok_idx - 1];
Michal Vaskod3678892020-05-21 10:06:58 +02007035
7036 /* parse (and skip) module name */
7037 rc = moveto_resolve_model(&ncname, &ncname_len, set, &moveto_mod);
7038 LY_CHECK_GOTO(rc, cleanup);
7039
7040 if (moveto_mod && !attr_axis && !all_desc && (set->type == LYXP_SET_NODE_SET)) {
7041 /* find the matching schema node in some parent in the context */
7042 for (i = 0; i < (signed)set->used; ++i) {
7043 if (set->val.nodes[i].type == set->root_type) {
7044 tmp = lys_find_child(NULL, moveto_mod, ncname, ncname_len, 0, 0);
7045 } else if ((set->val.nodes[i].type == LYXP_NODE_ELEM)
7046 && (!scnode || (lysc_data_parent(scnode) != set->val.nodes[i].node->schema))) {
7047 /* do not repeat the same search */
7048 tmp = lys_find_child(set->val.nodes[i].node->schema, moveto_mod, ncname, ncname_len, 0, 0);
Radek Krejcib1247842020-07-02 16:22:38 +02007049 } else {
7050 tmp = NULL;
Michal Vaskod3678892020-05-21 10:06:58 +02007051 }
7052
7053 /* additional context check */
7054 if (tmp && (set->root_type == LYXP_NODE_ROOT_CONFIG) && (tmp->flags & LYS_CONFIG_R)) {
7055 tmp = NULL;
7056 }
7057
7058 if (tmp) {
7059 if (scnode) {
7060 /* we found a schema node with the same name but at different level, give up, too complicated */
7061 scnode = NULL;
7062 break;
7063 } else {
7064 /* remember the found schema node and continue to make sure it can be used */
7065 scnode = tmp;
7066 }
Michal Vaskod3678892020-05-21 10:06:58 +02007067 }
7068 }
7069
Michal Vasko004d3152020-06-11 19:59:22 +02007070 if (scnode && (scnode->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
7071 /* try to create the predicates */
7072 if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set->format, &predicates, &pred_type)) {
7073 /* hashes cannot be used */
Michal Vaskod3678892020-05-21 10:06:58 +02007074 scnode = NULL;
7075 }
7076 }
7077 }
7078
Michal Vasko004d3152020-06-11 19:59:22 +02007079 if (!scnode && moveto_mod) {
7080 /* we are not able to match based on a schema node and not all the modules match,
7081 * use dictionary for efficient comparison */
7082 ncname_dict = lydict_insert(set->ctx, ncname, ncname_len);
Michal Vaskod3678892020-05-21 10:06:58 +02007083 }
7084
7085moveto:
7086 /* move to the attribute(s), data node(s), or schema node(s) */
7087 if (attr_axis) {
7088 if (set && (options & LYXP_SCNODE_ALL)) {
7089 set_scnode_clear_ctx(set);
7090 } else {
7091 if (all_desc) {
Michal Vasko004d3152020-06-11 19:59:22 +02007092 rc = moveto_attr_alldesc(set, moveto_mod, ncname_dict);
Michal Vaskod3678892020-05-21 10:06:58 +02007093 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007094 rc = moveto_attr(set, moveto_mod, ncname_dict);
Michal Vaskod3678892020-05-21 10:06:58 +02007095 }
7096 LY_CHECK_GOTO(rc, cleanup);
7097 }
7098 } else {
7099 if (set && (options & LYXP_SCNODE_ALL)) {
7100 if (all_desc) {
Michal Vasko004d3152020-06-11 19:59:22 +02007101 rc = moveto_scnode_alldesc(set, moveto_mod, ncname_dict, options);
Michal Vaskod3678892020-05-21 10:06:58 +02007102 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007103 rc = moveto_scnode(set, moveto_mod, ncname_dict, options);
Michal Vaskod3678892020-05-21 10:06:58 +02007104 }
7105 LY_CHECK_GOTO(rc, cleanup);
7106
7107 for (i = set->used - 1; i > -1; --i) {
7108 if (set->val.scnodes[i].in_ctx > 0) {
7109 break;
7110 }
7111 }
7112 if (i == -1) {
7113 path = lysc_path(set->ctx_scnode, LYSC_PATH_LOG, NULL, 0);
7114 LOGWRN(set->ctx, "Schema node \"%.*s\" not found (%.*s) with context node \"%s\".",
Michal Vasko6b26e742020-07-17 15:02:10 +02007115 ncname_len, ncname, ncname - exp->expr, exp->expr, path);
Michal Vaskod3678892020-05-21 10:06:58 +02007116 free(path);
7117 }
7118 } else {
7119 if (all_desc) {
Michal Vasko004d3152020-06-11 19:59:22 +02007120 rc = moveto_node_alldesc(set, moveto_mod, ncname_dict);
Michal Vaskod3678892020-05-21 10:06:58 +02007121 } else {
7122 if (scnode) {
7123 /* we can find the nodes using hashes */
Michal Vasko004d3152020-06-11 19:59:22 +02007124 rc = moveto_node_hash(set, scnode, predicates);
Michal Vaskod3678892020-05-21 10:06:58 +02007125 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007126 rc = moveto_node(set, moveto_mod, ncname_dict);
Michal Vaskod3678892020-05-21 10:06:58 +02007127 }
7128 }
7129 LY_CHECK_GOTO(rc, cleanup);
7130 }
7131 }
7132
7133 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02007134 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
7135 rc = eval_predicate(exp, tok_idx, set, options, 1);
Michal Vaskod3678892020-05-21 10:06:58 +02007136 LY_CHECK_RET(rc);
7137 }
7138
7139cleanup:
Michal Vaskodb51a8d2020-05-27 15:22:29 +02007140 if (set) {
Michal Vasko004d3152020-06-11 19:59:22 +02007141 lydict_remove(set->ctx, ncname_dict);
7142 ly_path_predicates_free(set->ctx, pred_type, scnode, predicates);
Michal Vaskodb51a8d2020-05-27 15:22:29 +02007143 }
Michal Vaskod3678892020-05-21 10:06:58 +02007144 return rc;
7145}
7146
7147/**
7148 * @brief Evaluate NodeType and any following Predicates. Logs directly on error.
7149 *
7150 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
7151 * [6] NodeTest ::= NameTest | NodeType '(' ')'
7152 * [8] NodeType ::= 'text' | 'node'
7153 *
7154 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007155 * @param[in] tok_idx Position in the expression @p exp.
Michal Vaskod3678892020-05-21 10:06:58 +02007156 * @param[in] attr_axis Whether to search attributes or standard nodes.
7157 * @param[in] all_desc Whether to search all the descendants or children only.
7158 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7159 * @param[in] options XPath options.
7160 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7161 */
7162static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007163eval_node_type_with_predicate(struct lyxp_expr *exp, uint16_t *tok_idx, int attr_axis, int all_desc,
Michal Vaskod3678892020-05-21 10:06:58 +02007164 struct lyxp_set *set, int options)
7165{
7166 LY_ERR rc;
7167
7168 /* TODO */
7169 (void)attr_axis;
7170 (void)all_desc;
7171
7172 if (set) {
Michal Vasko004d3152020-06-11 19:59:22 +02007173 assert(exp->tok_len[*tok_idx] == 4);
Michal Vaskod3678892020-05-21 10:06:58 +02007174 if (set->type == LYXP_SET_SCNODE_SET) {
7175 set_scnode_clear_ctx(set);
7176 /* just for the debug message below */
7177 set = NULL;
7178 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007179 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "node", 4)) {
Michal Vaskod3678892020-05-21 10:06:58 +02007180 rc = xpath_node(NULL, 0, set, options);
7181 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007182 assert(!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "text", 4));
Michal Vaskod3678892020-05-21 10:06:58 +02007183 rc = xpath_text(NULL, 0, set, options);
7184 }
7185 LY_CHECK_RET(rc);
7186 }
7187 }
7188 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007189 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7190 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02007191
7192 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02007193 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
Michal Vaskod3678892020-05-21 10:06:58 +02007194 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007195 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7196 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02007197
7198 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02007199 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
Michal Vaskod3678892020-05-21 10:06:58 +02007200 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007201 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7202 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02007203
7204 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02007205 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
7206 rc = eval_predicate(exp, tok_idx, set, options, 1);
Michal Vaskod3678892020-05-21 10:06:58 +02007207 LY_CHECK_RET(rc);
7208 }
7209
7210 return LY_SUCCESS;
7211}
7212
7213/**
Michal Vasko03ff5a72019-09-11 13:49:33 +02007214 * @brief Evaluate RelativeLocationPath. Logs directly on error.
7215 *
7216 * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
7217 * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
Michal Vaskod3678892020-05-21 10:06:58 +02007218 * [6] NodeTest ::= NameTest | NodeType '(' ')'
Michal Vasko03ff5a72019-09-11 13:49:33 +02007219 *
7220 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007221 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007222 * @param[in] all_desc Whether to search all the descendants or children only.
7223 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7224 * @param[in] options XPath options.
7225 * @return LY_ERR (YL_EINCOMPLETE on unresolved when)
7226 */
7227static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007228eval_relative_location_path(struct lyxp_expr *exp, uint16_t *tok_idx, int all_desc, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007229{
7230 int attr_axis;
7231 LY_ERR rc;
7232
7233 goto step;
7234 do {
7235 /* evaluate '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02007236 if (exp->tok_len[*tok_idx] == 1) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007237 all_desc = 0;
7238 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007239 assert(exp->tok_len[*tok_idx] == 2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007240 all_desc = 1;
7241 }
7242 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007243 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7244 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007245
7246step:
Michal Vaskod3678892020-05-21 10:06:58 +02007247 /* evaluate abbreviated axis '@'? if any */
Michal Vasko004d3152020-06-11 19:59:22 +02007248 if (exp->tokens[*tok_idx] == LYXP_TOKEN_AT) {
Michal Vaskod3678892020-05-21 10:06:58 +02007249 attr_axis = 1;
7250
7251 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007252 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7253 ++(*tok_idx);
Michal Vaskod3678892020-05-21 10:06:58 +02007254 } else {
7255 attr_axis = 0;
7256 }
7257
Michal Vasko03ff5a72019-09-11 13:49:33 +02007258 /* Step */
Michal Vasko004d3152020-06-11 19:59:22 +02007259 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007260 case LYXP_TOKEN_DOT:
7261 /* evaluate '.' */
7262 if (set && (options & LYXP_SCNODE_ALL)) {
7263 rc = moveto_scnode_self(set, all_desc, options);
7264 } else {
7265 rc = moveto_self(set, all_desc, options);
7266 }
7267 LY_CHECK_RET(rc);
7268 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007269 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7270 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007271 break;
7272
7273 case LYXP_TOKEN_DDOT:
7274 /* evaluate '..' */
7275 if (set && (options & LYXP_SCNODE_ALL)) {
7276 rc = moveto_scnode_parent(set, all_desc, options);
7277 } else {
7278 rc = moveto_parent(set, all_desc, options);
7279 }
7280 LY_CHECK_RET(rc);
7281 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007282 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7283 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007284 break;
7285
Michal Vasko03ff5a72019-09-11 13:49:33 +02007286 case LYXP_TOKEN_NAMETEST:
Michal Vaskod3678892020-05-21 10:06:58 +02007287 /* evaluate NameTest Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02007288 rc = eval_name_test_with_predicate(exp, tok_idx, attr_axis, all_desc, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007289 LY_CHECK_RET(rc);
Michal Vaskod3678892020-05-21 10:06:58 +02007290 break;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007291
Michal Vaskod3678892020-05-21 10:06:58 +02007292 case LYXP_TOKEN_NODETYPE:
7293 /* evaluate NodeType Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02007294 rc = eval_node_type_with_predicate(exp, tok_idx, attr_axis, all_desc, set, options);
Michal Vaskod3678892020-05-21 10:06:58 +02007295 LY_CHECK_RET(rc);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007296 break;
7297
7298 default:
Michal Vasko02a77382019-09-12 11:47:35 +02007299 LOGINT_RET(set ? set->ctx : NULL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007300 }
Michal Vasko004d3152020-06-11 19:59:22 +02007301 } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007302
7303 return LY_SUCCESS;
7304}
7305
7306/**
7307 * @brief Evaluate AbsoluteLocationPath. Logs directly on error.
7308 *
7309 * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
7310 *
7311 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007312 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007313 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7314 * @param[in] options XPath options.
7315 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7316 */
7317static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007318eval_absolute_location_path(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007319{
7320 int all_desc;
7321 LY_ERR rc;
7322
7323 if (set) {
7324 /* no matter what tokens follow, we need to be at the root */
7325 moveto_root(set, options);
7326 }
7327
7328 /* '/' RelativeLocationPath? */
Michal Vasko004d3152020-06-11 19:59:22 +02007329 if (exp->tok_len[*tok_idx] == 1) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007330 /* evaluate '/' - deferred */
7331 all_desc = 0;
7332 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007333 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7334 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007335
Michal Vasko004d3152020-06-11 19:59:22 +02007336 if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007337 return LY_SUCCESS;
7338 }
Michal Vasko004d3152020-06-11 19:59:22 +02007339 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007340 case LYXP_TOKEN_DOT:
7341 case LYXP_TOKEN_DDOT:
7342 case LYXP_TOKEN_AT:
7343 case LYXP_TOKEN_NAMETEST:
7344 case LYXP_TOKEN_NODETYPE:
Michal Vasko004d3152020-06-11 19:59:22 +02007345 rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007346 LY_CHECK_RET(rc);
7347 break;
7348 default:
7349 break;
7350 }
7351
7352 /* '//' RelativeLocationPath */
7353 } else {
7354 /* evaluate '//' - deferred so as not to waste memory by remembering all the nodes */
7355 all_desc = 1;
7356 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007357 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7358 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007359
Michal Vasko004d3152020-06-11 19:59:22 +02007360 rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007361 LY_CHECK_RET(rc);
7362 }
7363
7364 return LY_SUCCESS;
7365}
7366
7367/**
7368 * @brief Evaluate FunctionCall. Logs directly on error.
7369 *
Michal Vaskod3678892020-05-21 10:06:58 +02007370 * [11] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
Michal Vasko03ff5a72019-09-11 13:49:33 +02007371 *
7372 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007373 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007374 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7375 * @param[in] options XPath options.
7376 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7377 */
7378static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007379eval_function_call(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007380{
7381 LY_ERR rc;
7382 LY_ERR (*xpath_func)(struct lyxp_set **, uint16_t, struct lyxp_set *, int) = NULL;
Michal Vasko0cbf54f2019-12-16 10:01:06 +01007383 uint16_t arg_count = 0, i;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007384 struct lyxp_set **args = NULL, **args_aux;
7385
7386 if (set) {
7387 /* FunctionName */
Michal Vasko004d3152020-06-11 19:59:22 +02007388 switch (exp->tok_len[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007389 case 3:
Michal Vasko004d3152020-06-11 19:59:22 +02007390 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007391 xpath_func = &xpath_not;
Michal Vasko004d3152020-06-11 19:59:22 +02007392 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007393 xpath_func = &xpath_sum;
7394 }
7395 break;
7396 case 4:
Michal Vasko004d3152020-06-11 19:59:22 +02007397 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007398 xpath_func = &xpath_lang;
Michal Vasko004d3152020-06-11 19:59:22 +02007399 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007400 xpath_func = &xpath_last;
Michal Vasko004d3152020-06-11 19:59:22 +02007401 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007402 xpath_func = &xpath_name;
Michal Vasko004d3152020-06-11 19:59:22 +02007403 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007404 xpath_func = &xpath_true;
7405 }
7406 break;
7407 case 5:
Michal Vasko004d3152020-06-11 19:59:22 +02007408 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007409 xpath_func = &xpath_count;
Michal Vasko004d3152020-06-11 19:59:22 +02007410 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007411 xpath_func = &xpath_false;
Michal Vasko004d3152020-06-11 19:59:22 +02007412 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007413 xpath_func = &xpath_floor;
Michal Vasko004d3152020-06-11 19:59:22 +02007414 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007415 xpath_func = &xpath_round;
Michal Vasko004d3152020-06-11 19:59:22 +02007416 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007417 xpath_func = &xpath_deref;
7418 }
7419 break;
7420 case 6:
Michal Vasko004d3152020-06-11 19:59:22 +02007421 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007422 xpath_func = &xpath_concat;
Michal Vasko004d3152020-06-11 19:59:22 +02007423 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007424 xpath_func = &xpath_number;
Michal Vasko004d3152020-06-11 19:59:22 +02007425 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007426 xpath_func = &xpath_string;
7427 }
7428 break;
7429 case 7:
Michal Vasko004d3152020-06-11 19:59:22 +02007430 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007431 xpath_func = &xpath_boolean;
Michal Vasko004d3152020-06-11 19:59:22 +02007432 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007433 xpath_func = &xpath_ceiling;
Michal Vasko004d3152020-06-11 19:59:22 +02007434 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007435 xpath_func = &xpath_current;
7436 }
7437 break;
7438 case 8:
Michal Vasko004d3152020-06-11 19:59:22 +02007439 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007440 xpath_func = &xpath_contains;
Michal Vasko004d3152020-06-11 19:59:22 +02007441 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007442 xpath_func = &xpath_position;
Michal Vasko004d3152020-06-11 19:59:22 +02007443 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007444 xpath_func = &xpath_re_match;
7445 }
7446 break;
7447 case 9:
Michal Vasko004d3152020-06-11 19:59:22 +02007448 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007449 xpath_func = &xpath_substring;
Michal Vasko004d3152020-06-11 19:59:22 +02007450 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007451 xpath_func = &xpath_translate;
7452 }
7453 break;
7454 case 10:
Michal Vasko004d3152020-06-11 19:59:22 +02007455 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007456 xpath_func = &xpath_local_name;
Michal Vasko004d3152020-06-11 19:59:22 +02007457 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007458 xpath_func = &xpath_enum_value;
Michal Vasko004d3152020-06-11 19:59:22 +02007459 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007460 xpath_func = &xpath_bit_is_set;
7461 }
7462 break;
7463 case 11:
Michal Vasko004d3152020-06-11 19:59:22 +02007464 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007465 xpath_func = &xpath_starts_with;
7466 }
7467 break;
7468 case 12:
Michal Vasko004d3152020-06-11 19:59:22 +02007469 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007470 xpath_func = &xpath_derived_from;
7471 }
7472 break;
7473 case 13:
Michal Vasko004d3152020-06-11 19:59:22 +02007474 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007475 xpath_func = &xpath_namespace_uri;
Michal Vasko004d3152020-06-11 19:59:22 +02007476 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007477 xpath_func = &xpath_string_length;
7478 }
7479 break;
7480 case 15:
Michal Vasko004d3152020-06-11 19:59:22 +02007481 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007482 xpath_func = &xpath_normalize_space;
Michal Vasko004d3152020-06-11 19:59:22 +02007483 } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007484 xpath_func = &xpath_substring_after;
7485 }
7486 break;
7487 case 16:
Michal Vasko004d3152020-06-11 19:59:22 +02007488 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007489 xpath_func = &xpath_substring_before;
7490 }
7491 break;
7492 case 20:
Michal Vasko004d3152020-06-11 19:59:22 +02007493 if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007494 xpath_func = &xpath_derived_from_or_self;
7495 }
7496 break;
7497 }
7498
7499 if (!xpath_func) {
Michal Vasko004d3152020-06-11 19:59:22 +02007500 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007501 return LY_EVALID;
7502 }
7503 }
7504
7505 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007506 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7507 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007508
7509 /* '(' */
Michal Vasko004d3152020-06-11 19:59:22 +02007510 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007511 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007512 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7513 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007514
7515 /* ( Expr ( ',' Expr )* )? */
Michal Vasko004d3152020-06-11 19:59:22 +02007516 if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007517 if (set) {
7518 args = malloc(sizeof *args);
7519 LY_CHECK_ERR_GOTO(!args, LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
7520 arg_count = 1;
7521 args[0] = set_copy(set);
7522 if (!args[0]) {
7523 rc = LY_EMEM;
7524 goto cleanup;
7525 }
7526
Michal Vasko004d3152020-06-11 19:59:22 +02007527 rc = eval_expr_select(exp, tok_idx, 0, args[0], options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007528 LY_CHECK_GOTO(rc, cleanup);
7529 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007530 rc = eval_expr_select(exp, tok_idx, 0, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007531 LY_CHECK_GOTO(rc, cleanup);
7532 }
7533 }
Michal Vasko004d3152020-06-11 19:59:22 +02007534 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007535 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007536 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7537 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007538
7539 if (set) {
7540 ++arg_count;
7541 args_aux = realloc(args, arg_count * sizeof *args);
7542 LY_CHECK_ERR_GOTO(!args_aux, arg_count--; LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
7543 args = args_aux;
7544 args[arg_count - 1] = set_copy(set);
7545 if (!args[arg_count - 1]) {
7546 rc = LY_EMEM;
7547 goto cleanup;
7548 }
7549
Michal Vasko004d3152020-06-11 19:59:22 +02007550 rc = eval_expr_select(exp, tok_idx, 0, args[arg_count - 1], options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007551 LY_CHECK_GOTO(rc, cleanup);
7552 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007553 rc = eval_expr_select(exp, tok_idx, 0, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007554 LY_CHECK_GOTO(rc, cleanup);
7555 }
7556 }
7557
7558 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02007559 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007560 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007561 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7562 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007563
7564 if (set) {
7565 /* evaluate function */
7566 rc = xpath_func(args, arg_count, set, options);
7567
7568 if (options & LYXP_SCNODE_ALL) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007569 /* merge all nodes from arg evaluations */
7570 for (i = 0; i < arg_count; ++i) {
7571 set_scnode_clear_ctx(args[i]);
Michal Vaskoecd62de2019-11-13 12:35:11 +01007572 lyxp_set_scnode_merge(set, args[i]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007573 }
7574 }
7575 } else {
7576 rc = LY_SUCCESS;
7577 }
7578
7579cleanup:
7580 for (i = 0; i < arg_count; ++i) {
7581 lyxp_set_free(args[i]);
7582 }
7583 free(args);
7584
7585 return rc;
7586}
7587
7588/**
7589 * @brief Evaluate Number. Logs directly on error.
7590 *
7591 * @param[in] ctx Context for errors.
7592 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007593 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007594 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7595 * @return LY_ERR
7596 */
7597static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007598eval_number(struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007599{
7600 long double num;
7601 char *endptr;
7602
7603 if (set) {
7604 errno = 0;
Michal Vasko004d3152020-06-11 19:59:22 +02007605 num = strtold(&exp->expr[exp->tok_pos[*tok_idx]], &endptr);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007606 if (errno) {
Michal Vasko004d3152020-06-11 19:59:22 +02007607 LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007608 LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double (%s).",
Michal Vasko004d3152020-06-11 19:59:22 +02007609 exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]], strerror(errno));
Michal Vasko03ff5a72019-09-11 13:49:33 +02007610 return LY_EVALID;
Michal Vasko004d3152020-06-11 19:59:22 +02007611 } else if (endptr - &exp->expr[exp->tok_pos[*tok_idx]] != exp->tok_len[*tok_idx]) {
7612 LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007613 LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double.",
Michal Vasko004d3152020-06-11 19:59:22 +02007614 exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007615 return LY_EVALID;
7616 }
7617
7618 set_fill_number(set, num);
7619 }
7620
7621 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007622 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7623 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007624 return LY_SUCCESS;
7625}
7626
7627/**
7628 * @brief Evaluate PathExpr. Logs directly on error.
7629 *
Michal Vaskod3678892020-05-21 10:06:58 +02007630 * [12] PathExpr ::= LocationPath | PrimaryExpr Predicate*
Michal Vasko03ff5a72019-09-11 13:49:33 +02007631 * | PrimaryExpr Predicate* '/' RelativeLocationPath
7632 * | PrimaryExpr Predicate* '//' RelativeLocationPath
7633 * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
Michal Vaskod3678892020-05-21 10:06:58 +02007634 * [10] PrimaryExpr ::= '(' Expr ')' | Literal | Number | FunctionCall
Michal Vasko03ff5a72019-09-11 13:49:33 +02007635 *
7636 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007637 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007638 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7639 * @param[in] options XPath options.
7640 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7641 */
7642static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007643eval_path_expr(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007644{
7645 int all_desc, parent_pos_pred;
7646 LY_ERR rc;
7647
Michal Vasko004d3152020-06-11 19:59:22 +02007648 switch (exp->tokens[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007649 case LYXP_TOKEN_PAR1:
7650 /* '(' Expr ')' */
7651
7652 /* '(' */
7653 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007654 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7655 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007656
7657 /* Expr */
Michal Vasko004d3152020-06-11 19:59:22 +02007658 rc = eval_expr_select(exp, tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007659 LY_CHECK_RET(rc);
7660
7661 /* ')' */
Michal Vasko004d3152020-06-11 19:59:22 +02007662 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007663 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007664 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7665 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007666
7667 parent_pos_pred = 0;
7668 goto predicate;
7669
7670 case LYXP_TOKEN_DOT:
7671 case LYXP_TOKEN_DDOT:
7672 case LYXP_TOKEN_AT:
7673 case LYXP_TOKEN_NAMETEST:
7674 case LYXP_TOKEN_NODETYPE:
7675 /* RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02007676 rc = eval_relative_location_path(exp, tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007677 LY_CHECK_RET(rc);
7678 break;
7679
7680 case LYXP_TOKEN_FUNCNAME:
7681 /* FunctionCall */
7682 if (!set) {
Michal Vasko004d3152020-06-11 19:59:22 +02007683 rc = eval_function_call(exp, tok_idx, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007684 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007685 rc = eval_function_call(exp, tok_idx, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007686 }
7687 LY_CHECK_RET(rc);
7688
7689 parent_pos_pred = 1;
7690 goto predicate;
7691
Michal Vasko3e48bf32020-06-01 08:39:07 +02007692 case LYXP_TOKEN_OPER_PATH:
7693 case LYXP_TOKEN_OPER_RPATH:
Michal Vasko03ff5a72019-09-11 13:49:33 +02007694 /* AbsoluteLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02007695 rc = eval_absolute_location_path(exp, tok_idx, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007696 LY_CHECK_RET(rc);
7697 break;
7698
7699 case LYXP_TOKEN_LITERAL:
7700 /* Literal */
7701 if (!set || (options & LYXP_SCNODE_ALL)) {
7702 if (set) {
7703 set_scnode_clear_ctx(set);
7704 }
Michal Vasko004d3152020-06-11 19:59:22 +02007705 eval_literal(exp, tok_idx, NULL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007706 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007707 eval_literal(exp, tok_idx, set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007708 }
7709
7710 parent_pos_pred = 1;
7711 goto predicate;
7712
7713 case LYXP_TOKEN_NUMBER:
7714 /* Number */
7715 if (!set || (options & LYXP_SCNODE_ALL)) {
7716 if (set) {
7717 set_scnode_clear_ctx(set);
7718 }
Michal Vasko004d3152020-06-11 19:59:22 +02007719 rc = eval_number(NULL, exp, tok_idx, NULL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007720 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02007721 rc = eval_number(set->ctx, exp, tok_idx, set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007722 }
7723 LY_CHECK_RET(rc);
7724
7725 parent_pos_pred = 1;
7726 goto predicate;
7727
7728 default:
Michal Vasko004d3152020-06-11 19:59:22 +02007729 LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, lyxp_print_token(exp->tokens[*tok_idx]),
7730 &exp->expr[exp->tok_pos[*tok_idx]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007731 return LY_EVALID;
7732 }
7733
7734 return LY_SUCCESS;
7735
7736predicate:
7737 /* Predicate* */
Michal Vasko004d3152020-06-11 19:59:22 +02007738 while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
7739 rc = eval_predicate(exp, tok_idx, set, options, parent_pos_pred);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007740 LY_CHECK_RET(rc);
7741 }
7742
7743 /* ('/' or '//') RelativeLocationPath */
Michal Vasko004d3152020-06-11 19:59:22 +02007744 if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007745
7746 /* evaluate '/' or '//' */
Michal Vasko004d3152020-06-11 19:59:22 +02007747 if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007748 all_desc = 0;
7749 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02007750 all_desc = 1;
7751 }
7752
7753 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007754 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7755 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007756
Michal Vasko004d3152020-06-11 19:59:22 +02007757 rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007758 LY_CHECK_RET(rc);
7759 }
7760
7761 return LY_SUCCESS;
7762}
7763
7764/**
7765 * @brief Evaluate UnionExpr. Logs directly on error.
7766 *
Michal Vaskod3678892020-05-21 10:06:58 +02007767 * [20] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02007768 *
7769 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007770 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007771 * @param[in] repeat How many times this expression is repeated.
7772 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7773 * @param[in] options XPath options.
7774 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7775 */
7776static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007777eval_union_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007778{
7779 LY_ERR rc = LY_SUCCESS;
7780 struct lyxp_set orig_set, set2;
7781 uint16_t i;
7782
7783 assert(repeat);
7784
7785 set_init(&orig_set, set);
7786 set_init(&set2, set);
7787
7788 set_fill_set(&orig_set, set);
7789
Michal Vasko004d3152020-06-11 19:59:22 +02007790 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007791 LY_CHECK_GOTO(rc, cleanup);
7792
7793 /* ('|' PathExpr)* */
7794 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02007795 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_UNI);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007796 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007797 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7798 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007799
7800 if (!set) {
Michal Vasko004d3152020-06-11 19:59:22 +02007801 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007802 LY_CHECK_GOTO(rc, cleanup);
7803 continue;
7804 }
7805
7806 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02007807 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007808 LY_CHECK_GOTO(rc, cleanup);
7809
7810 /* eval */
7811 if (options & LYXP_SCNODE_ALL) {
Michal Vaskoecd62de2019-11-13 12:35:11 +01007812 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007813 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007814 rc = moveto_union(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007815 LY_CHECK_GOTO(rc, cleanup);
7816 }
7817 }
7818
7819cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02007820 lyxp_set_free_content(&orig_set);
7821 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007822 return rc;
7823}
7824
7825/**
7826 * @brief Evaluate UnaryExpr. Logs directly on error.
7827 *
Michal Vaskod3678892020-05-21 10:06:58 +02007828 * [19] UnaryExpr ::= UnionExpr | '-' UnaryExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02007829 *
7830 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007831 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007832 * @param[in] repeat How many times this expression is repeated.
7833 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7834 * @param[in] options XPath options.
7835 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7836 */
7837static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007838eval_unary_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007839{
7840 LY_ERR rc;
7841 uint16_t this_op, i;
7842
7843 assert(repeat);
7844
7845 /* ('-')+ */
Michal Vasko004d3152020-06-11 19:59:22 +02007846 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007847 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02007848 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 +02007849
7850 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007851 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7852 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007853 }
7854
Michal Vasko004d3152020-06-11 19:59:22 +02007855 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNARY, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007856 LY_CHECK_RET(rc);
7857
7858 if (set && (repeat % 2)) {
7859 if (options & LYXP_SCNODE_ALL) {
7860 warn_operands(set->ctx, set, NULL, 1, exp->expr, exp->tok_pos[this_op]);
7861 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007862 rc = moveto_op_math(set, NULL, &exp->expr[exp->tok_pos[this_op]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007863 LY_CHECK_RET(rc);
7864 }
7865 }
7866
7867 return LY_SUCCESS;
7868}
7869
7870/**
7871 * @brief Evaluate MultiplicativeExpr. Logs directly on error.
7872 *
Michal Vaskod3678892020-05-21 10:06:58 +02007873 * [18] MultiplicativeExpr ::= UnaryExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02007874 * | MultiplicativeExpr '*' UnaryExpr
7875 * | MultiplicativeExpr 'div' UnaryExpr
7876 * | MultiplicativeExpr 'mod' UnaryExpr
7877 *
7878 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007879 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007880 * @param[in] repeat How many times this expression is repeated.
7881 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7882 * @param[in] options XPath options.
7883 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7884 */
7885static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007886eval_multiplicative_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007887{
7888 LY_ERR rc;
7889 uint16_t this_op;
7890 struct lyxp_set orig_set, set2;
7891 uint16_t i;
7892
7893 assert(repeat);
7894
7895 set_init(&orig_set, set);
7896 set_init(&set2, set);
7897
7898 set_fill_set(&orig_set, set);
7899
Michal Vasko004d3152020-06-11 19:59:22 +02007900 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007901 LY_CHECK_GOTO(rc, cleanup);
7902
7903 /* ('*' / 'div' / 'mod' UnaryExpr)* */
7904 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02007905 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007906
Michal Vasko004d3152020-06-11 19:59:22 +02007907 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007908 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007909 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7910 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007911
7912 if (!set) {
Michal Vasko004d3152020-06-11 19:59:22 +02007913 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007914 LY_CHECK_GOTO(rc, cleanup);
7915 continue;
7916 }
7917
7918 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02007919 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007920 LY_CHECK_GOTO(rc, cleanup);
7921
7922 /* eval */
7923 if (options & LYXP_SCNODE_ALL) {
7924 warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vaskoecd62de2019-11-13 12:35:11 +01007925 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007926 set_scnode_clear_ctx(set);
7927 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007928 rc = moveto_op_math(set, &set2, &exp->expr[exp->tok_pos[this_op]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007929 LY_CHECK_GOTO(rc, cleanup);
7930 }
7931 }
7932
7933cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02007934 lyxp_set_free_content(&orig_set);
7935 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007936 return rc;
7937}
7938
7939/**
7940 * @brief Evaluate AdditiveExpr. Logs directly on error.
7941 *
Michal Vaskod3678892020-05-21 10:06:58 +02007942 * [17] AdditiveExpr ::= MultiplicativeExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02007943 * | AdditiveExpr '+' MultiplicativeExpr
7944 * | AdditiveExpr '-' MultiplicativeExpr
7945 *
7946 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02007947 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02007948 * @param[in] repeat How many times this expression is repeated.
7949 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
7950 * @param[in] options XPath options.
7951 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
7952 */
7953static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02007954eval_additive_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02007955{
7956 LY_ERR rc;
7957 uint16_t this_op;
7958 struct lyxp_set orig_set, set2;
7959 uint16_t i;
7960
7961 assert(repeat);
7962
7963 set_init(&orig_set, set);
7964 set_init(&set2, set);
7965
7966 set_fill_set(&orig_set, set);
7967
Michal Vasko004d3152020-06-11 19:59:22 +02007968 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007969 LY_CHECK_GOTO(rc, cleanup);
7970
7971 /* ('+' / '-' MultiplicativeExpr)* */
7972 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02007973 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02007974
Michal Vasko004d3152020-06-11 19:59:22 +02007975 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007976 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02007977 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
7978 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007979
7980 if (!set) {
Michal Vasko004d3152020-06-11 19:59:22 +02007981 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007982 LY_CHECK_GOTO(rc, cleanup);
7983 continue;
7984 }
7985
7986 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02007987 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007988 LY_CHECK_GOTO(rc, cleanup);
7989
7990 /* eval */
7991 if (options & LYXP_SCNODE_ALL) {
7992 warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vaskoecd62de2019-11-13 12:35:11 +01007993 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007994 set_scnode_clear_ctx(set);
7995 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01007996 rc = moveto_op_math(set, &set2, &exp->expr[exp->tok_pos[this_op]]);
Michal Vasko03ff5a72019-09-11 13:49:33 +02007997 LY_CHECK_GOTO(rc, cleanup);
7998 }
7999 }
8000
8001cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02008002 lyxp_set_free_content(&orig_set);
8003 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008004 return rc;
8005}
8006
8007/**
8008 * @brief Evaluate RelationalExpr. Logs directly on error.
8009 *
Michal Vaskod3678892020-05-21 10:06:58 +02008010 * [16] RelationalExpr ::= AdditiveExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02008011 * | RelationalExpr '<' AdditiveExpr
8012 * | RelationalExpr '>' AdditiveExpr
8013 * | RelationalExpr '<=' AdditiveExpr
8014 * | RelationalExpr '>=' AdditiveExpr
8015 *
8016 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008017 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008018 * @param[in] repeat How many times this expression is repeated.
8019 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
8020 * @param[in] options XPath options.
8021 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8022 */
8023static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02008024eval_relational_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008025{
8026 LY_ERR rc;
8027 uint16_t this_op;
8028 struct lyxp_set orig_set, set2;
8029 uint16_t i;
8030
8031 assert(repeat);
8032
8033 set_init(&orig_set, set);
8034 set_init(&set2, set);
8035
8036 set_fill_set(&orig_set, set);
8037
Michal Vasko004d3152020-06-11 19:59:22 +02008038 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008039 LY_CHECK_GOTO(rc, cleanup);
8040
8041 /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
8042 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02008043 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008044
Michal Vasko004d3152020-06-11 19:59:22 +02008045 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_COMP);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008046 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02008047 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
8048 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008049
8050 if (!set) {
Michal Vasko004d3152020-06-11 19:59:22 +02008051 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008052 LY_CHECK_GOTO(rc, cleanup);
8053 continue;
8054 }
8055
8056 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02008057 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008058 LY_CHECK_GOTO(rc, cleanup);
8059
8060 /* eval */
8061 if (options & LYXP_SCNODE_ALL) {
8062 warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vaskoecd62de2019-11-13 12:35:11 +01008063 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008064 set_scnode_clear_ctx(set);
8065 } else {
8066 rc = moveto_op_comp(set, &set2, &exp->expr[exp->tok_pos[this_op]], options);
8067 LY_CHECK_GOTO(rc, cleanup);
8068 }
8069 }
8070
8071cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02008072 lyxp_set_free_content(&orig_set);
8073 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008074 return rc;
8075}
8076
8077/**
8078 * @brief Evaluate EqualityExpr. Logs directly on error.
8079 *
Michal Vaskod3678892020-05-21 10:06:58 +02008080 * [15] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02008081 * | EqualityExpr '!=' RelationalExpr
8082 *
8083 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008084 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008085 * @param[in] repeat How many times this expression is repeated.
8086 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
8087 * @param[in] options XPath options.
8088 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8089 */
8090static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02008091eval_equality_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008092{
8093 LY_ERR rc;
8094 uint16_t this_op;
8095 struct lyxp_set orig_set, set2;
8096 uint16_t i;
8097
8098 assert(repeat);
8099
8100 set_init(&orig_set, set);
8101 set_init(&set2, set);
8102
8103 set_fill_set(&orig_set, set);
8104
Michal Vasko004d3152020-06-11 19:59:22 +02008105 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008106 LY_CHECK_GOTO(rc, cleanup);
8107
8108 /* ('=' / '!=' RelationalExpr)* */
8109 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02008110 this_op = *tok_idx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008111
Michal Vasko004d3152020-06-11 19:59:22 +02008112 assert((exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL) || (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_NEQUAL));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008113 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
Michal Vasko004d3152020-06-11 19:59:22 +02008114 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
8115 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008116
8117 if (!set) {
Michal Vasko004d3152020-06-11 19:59:22 +02008118 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008119 LY_CHECK_GOTO(rc, cleanup);
8120 continue;
8121 }
8122
8123 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02008124 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008125 LY_CHECK_GOTO(rc, cleanup);
8126
8127 /* eval */
8128 if (options & LYXP_SCNODE_ALL) {
8129 warn_operands(set->ctx, set, &set2, 0, exp->expr, exp->tok_pos[this_op - 1]);
Michal Vasko004d3152020-06-11 19:59:22 +02008130 warn_equality_value(exp, set, *tok_idx - 1, this_op - 1, *tok_idx - 1);
8131 warn_equality_value(exp, &set2, this_op - 1, this_op - 1, *tok_idx - 1);
Michal Vaskoecd62de2019-11-13 12:35:11 +01008132 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008133 set_scnode_clear_ctx(set);
8134 } else {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008135 rc = moveto_op_comp(set, &set2, &exp->expr[exp->tok_pos[this_op]], options);
8136 LY_CHECK_GOTO(rc, cleanup);
8137 }
8138 }
8139
8140cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02008141 lyxp_set_free_content(&orig_set);
8142 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008143 return rc;
8144}
8145
8146/**
8147 * @brief Evaluate AndExpr. Logs directly on error.
8148 *
Michal Vaskod3678892020-05-21 10:06:58 +02008149 * [14] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02008150 *
8151 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008152 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008153 * @param[in] repeat How many times this expression is repeated.
8154 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
8155 * @param[in] options XPath options.
8156 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8157 */
8158static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02008159eval_and_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008160{
8161 LY_ERR rc;
8162 struct lyxp_set orig_set, set2;
8163 uint16_t i;
8164
8165 assert(repeat);
8166
8167 set_init(&orig_set, set);
8168 set_init(&set2, set);
8169
8170 set_fill_set(&orig_set, set);
8171
Michal Vasko004d3152020-06-11 19:59:22 +02008172 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008173 LY_CHECK_GOTO(rc, cleanup);
8174
8175 /* cast to boolean, we know that will be the final result */
8176 if (set && (options & LYXP_SCNODE_ALL)) {
8177 set_scnode_clear_ctx(set);
8178 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008179 lyxp_set_cast(set, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008180 }
8181
8182 /* ('and' EqualityExpr)* */
8183 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02008184 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
8185 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (!set || !set->val.bln ? "skipped" : "parsed"),
8186 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
8187 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008188
8189 /* lazy evaluation */
Michal Vasko004d3152020-06-11 19:59:22 +02008190 if (!set || ((set->type == LYXP_SET_BOOLEAN) && !set->val.bln)) {
8191 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008192 LY_CHECK_GOTO(rc, cleanup);
8193 continue;
8194 }
8195
8196 set_fill_set(&set2, &orig_set);
Michal Vasko004d3152020-06-11 19:59:22 +02008197 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008198 LY_CHECK_GOTO(rc, cleanup);
8199
8200 /* eval - just get boolean value actually */
8201 if (set->type == LYXP_SET_SCNODE_SET) {
8202 set_scnode_clear_ctx(&set2);
Michal Vaskoecd62de2019-11-13 12:35:11 +01008203 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008204 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008205 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008206 set_fill_set(set, &set2);
8207 }
8208 }
8209
8210cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02008211 lyxp_set_free_content(&orig_set);
8212 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008213 return rc;
8214}
8215
8216/**
8217 * @brief Evaluate OrExpr. Logs directly on error.
8218 *
Michal Vaskod3678892020-05-21 10:06:58 +02008219 * [13] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
Michal Vasko03ff5a72019-09-11 13:49:33 +02008220 *
8221 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008222 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008223 * @param[in] repeat How many times this expression is repeated.
8224 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
8225 * @param[in] options XPath options.
8226 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8227 */
8228static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02008229eval_or_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008230{
8231 LY_ERR rc;
8232 struct lyxp_set orig_set, set2;
8233 uint16_t i;
8234
8235 assert(repeat);
8236
8237 set_init(&orig_set, set);
8238 set_init(&set2, set);
8239
8240 set_fill_set(&orig_set, set);
8241
Michal Vasko004d3152020-06-11 19:59:22 +02008242 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008243 LY_CHECK_GOTO(rc, cleanup);
8244
8245 /* cast to boolean, we know that will be the final result */
8246 if (set && (options & LYXP_SCNODE_ALL)) {
8247 set_scnode_clear_ctx(set);
8248 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008249 lyxp_set_cast(set, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008250 }
8251
8252 /* ('or' AndExpr)* */
8253 for (i = 0; i < repeat; ++i) {
Michal Vasko004d3152020-06-11 19:59:22 +02008254 assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
8255 LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (!set || set->val.bln ? "skipped" : "parsed"),
8256 lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
8257 ++(*tok_idx);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008258
8259 /* lazy evaluation */
Michal Vasko004d3152020-06-11 19:59:22 +02008260 if (!set || ((set->type == LYXP_SET_BOOLEAN) && set->val.bln)) {
8261 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, NULL, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008262 LY_CHECK_GOTO(rc, cleanup);
8263 continue;
8264 }
8265
8266 set_fill_set(&set2, &orig_set);
8267 /* expr_type cound have been LYXP_EXPR_NONE in all these later calls (except for the first one),
8268 * but it does not matter */
Michal Vasko004d3152020-06-11 19:59:22 +02008269 rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, &set2, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008270 LY_CHECK_GOTO(rc, cleanup);
8271
8272 /* eval - just get boolean value actually */
8273 if (set->type == LYXP_SET_SCNODE_SET) {
8274 set_scnode_clear_ctx(&set2);
Michal Vaskoecd62de2019-11-13 12:35:11 +01008275 lyxp_set_scnode_merge(set, &set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008276 } else {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008277 lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008278 set_fill_set(set, &set2);
8279 }
8280 }
8281
8282cleanup:
Michal Vaskod3678892020-05-21 10:06:58 +02008283 lyxp_set_free_content(&orig_set);
8284 lyxp_set_free_content(&set2);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008285 return rc;
8286}
8287
8288/**
Michal Vasko004d3152020-06-11 19:59:22 +02008289 * @brief Decide what expression is at the pointer @p tok_idx and evaluate it accordingly.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008290 *
8291 * @param[in] exp Parsed XPath expression.
Michal Vasko004d3152020-06-11 19:59:22 +02008292 * @param[in] tok_idx Position in the expression @p exp.
Michal Vasko03ff5a72019-09-11 13:49:33 +02008293 * @param[in] etype Expression type to evaluate.
8294 * @param[in,out] set Context and result set. On NULL the rule is only parsed.
8295 * @param[in] options XPath options.
8296 * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
8297 */
8298static LY_ERR
Michal Vasko004d3152020-06-11 19:59:22 +02008299eval_expr_select(struct lyxp_expr *exp, uint16_t *tok_idx, enum lyxp_expr_type etype, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008300{
8301 uint16_t i, count;
8302 enum lyxp_expr_type next_etype;
8303 LY_ERR rc;
8304
8305 /* process operator repeats */
Michal Vasko004d3152020-06-11 19:59:22 +02008306 if (!exp->repeat[*tok_idx]) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008307 next_etype = LYXP_EXPR_NONE;
8308 } else {
8309 /* find etype repeat */
Michal Vasko004d3152020-06-11 19:59:22 +02008310 for (i = 0; exp->repeat[*tok_idx][i] > etype; ++i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008311
8312 /* select one-priority lower because etype expression called us */
8313 if (i) {
Michal Vasko004d3152020-06-11 19:59:22 +02008314 next_etype = exp->repeat[*tok_idx][i - 1];
Michal Vasko03ff5a72019-09-11 13:49:33 +02008315 /* count repeats for that expression */
Michal Vasko004d3152020-06-11 19:59:22 +02008316 for (count = 0; i && exp->repeat[*tok_idx][i - 1] == next_etype; ++count, --i);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008317 } else {
8318 next_etype = LYXP_EXPR_NONE;
8319 }
8320 }
8321
8322 /* decide what expression are we parsing based on the repeat */
8323 switch (next_etype) {
8324 case LYXP_EXPR_OR:
Michal Vasko004d3152020-06-11 19:59:22 +02008325 rc = eval_or_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008326 break;
8327 case LYXP_EXPR_AND:
Michal Vasko004d3152020-06-11 19:59:22 +02008328 rc = eval_and_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008329 break;
8330 case LYXP_EXPR_EQUALITY:
Michal Vasko004d3152020-06-11 19:59:22 +02008331 rc = eval_equality_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008332 break;
8333 case LYXP_EXPR_RELATIONAL:
Michal Vasko004d3152020-06-11 19:59:22 +02008334 rc = eval_relational_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008335 break;
8336 case LYXP_EXPR_ADDITIVE:
Michal Vasko004d3152020-06-11 19:59:22 +02008337 rc = eval_additive_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008338 break;
8339 case LYXP_EXPR_MULTIPLICATIVE:
Michal Vasko004d3152020-06-11 19:59:22 +02008340 rc = eval_multiplicative_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008341 break;
8342 case LYXP_EXPR_UNARY:
Michal Vasko004d3152020-06-11 19:59:22 +02008343 rc = eval_unary_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008344 break;
8345 case LYXP_EXPR_UNION:
Michal Vasko004d3152020-06-11 19:59:22 +02008346 rc = eval_union_expr(exp, tok_idx, count, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008347 break;
8348 case LYXP_EXPR_NONE:
Michal Vasko004d3152020-06-11 19:59:22 +02008349 rc = eval_path_expr(exp, tok_idx, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008350 break;
8351 default:
8352 LOGINT_RET(set->ctx);
8353 }
8354
8355 return rc;
8356}
8357
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008358/**
8359 * @brief Get root type.
8360 *
8361 * @param[in] ctx_node Context node.
8362 * @param[in] ctx_scnode Schema context node.
8363 * @param[in] options XPath options.
8364 * @return Root type.
8365 */
8366static enum lyxp_node_type
8367lyxp_get_root_type(const struct lyd_node *ctx_node, const struct lysc_node *ctx_scnode, int options)
8368{
Michal Vasko6b26e742020-07-17 15:02:10 +02008369 const struct lysc_node *op;
8370
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008371 if (options & LYXP_SCNODE_ALL) {
Michal Vasko6b26e742020-07-17 15:02:10 +02008372 for (op = ctx_scnode; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent);
8373
8374 if (op || (options & LYXP_SCNODE)) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008375 /* general root that can access everything */
8376 return LYXP_NODE_ROOT;
8377 } else if (!ctx_scnode || (ctx_scnode->flags & LYS_CONFIG_W)) {
8378 /* root context node can access only config data (because we said so, it is unspecified) */
8379 return LYXP_NODE_ROOT_CONFIG;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008380 }
Michal Vasko6b26e742020-07-17 15:02:10 +02008381 return LYXP_NODE_ROOT;
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008382 }
8383
Michal Vasko6b26e742020-07-17 15:02:10 +02008384 op = ctx_node ? ctx_node->schema : NULL;
8385 for (; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent);
8386
8387 if (!ctx_node || (!op && (ctx_node->schema->flags & LYS_CONFIG_W))) {
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008388 /* root context node can access only config data (because we said so, it is unspecified) */
8389 return LYXP_NODE_ROOT_CONFIG;
8390 }
8391
8392 return LYXP_NODE_ROOT;
8393}
8394
Michal Vasko03ff5a72019-09-11 13:49:33 +02008395LY_ERR
Michal Vaskoecd62de2019-11-13 12:35:11 +01008396lyxp_eval(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lyd_node *ctx_node,
Michal Vaskof03ed032020-03-04 13:31:44 +01008397 enum lyxp_node_type ctx_node_type, const struct lyd_node *tree, struct lyxp_set *set, int options)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008398{
Michal Vasko004d3152020-06-11 19:59:22 +02008399 uint16_t tok_idx = 0;
8400 const struct lyd_node *real_ctx_node;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008401 LY_ERR rc;
8402
Michal Vasko004d3152020-06-11 19:59:22 +02008403 LY_CHECK_ARG_RET(NULL, exp, local_mod, ctx_node, set, LY_EINVAL);
8404
8405 if ((ctx_node_type == LYXP_NODE_ROOT) || (ctx_node_type == LYXP_NODE_ROOT_CONFIG)) {
8406 /* we always need some context node because it is used for resolving unqualified names */
8407 real_ctx_node = NULL;
8408 } else {
8409 real_ctx_node = ctx_node;
8410 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02008411
8412 /* prepare set for evaluation */
Michal Vasko004d3152020-06-11 19:59:22 +02008413 tok_idx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008414 memset(set, 0, sizeof *set);
Michal Vaskod3678892020-05-21 10:06:58 +02008415 set->type = LYXP_SET_NODE_SET;
Michal Vasko004d3152020-06-11 19:59:22 +02008416 set_insert_node(set, (struct lyd_node *)real_ctx_node, 0, ctx_node_type, 0);
Michal Vaskoecd62de2019-11-13 12:35:11 +01008417 set->ctx = local_mod->ctx;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008418 set->ctx_node = ctx_node;
Michal Vasko004d3152020-06-11 19:59:22 +02008419 set->root_type = lyxp_get_root_type(real_ctx_node, NULL, options);
Michal Vasko6b26e742020-07-17 15:02:10 +02008420 for (set->context_op = ctx_node->schema;
8421 set->context_op && !(set->context_op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF));
8422 set->context_op = set->context_op->parent);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008423 set->local_mod = local_mod;
Michal Vaskof03ed032020-03-04 13:31:44 +01008424 set->tree = tree;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008425 set->format = format;
8426
8427 /* evaluate */
Michal Vasko004d3152020-06-11 19:59:22 +02008428 rc = eval_expr_select(exp, &tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008429 if (rc != LY_SUCCESS) {
Michal Vaskod3678892020-05-21 10:06:58 +02008430 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008431 }
8432
Michal Vasko03ff5a72019-09-11 13:49:33 +02008433 return rc;
8434}
8435
8436#if 0
8437
8438/* full xml printing of set elements, not used currently */
8439
8440void
8441lyxp_set_print_xml(FILE *f, struct lyxp_set *set)
8442{
8443 uint32_t i;
8444 char *str_num;
8445 struct lyout out;
8446
8447 memset(&out, 0, sizeof out);
8448
8449 out.type = LYOUT_STREAM;
8450 out.method.f = f;
8451
8452 switch (set->type) {
8453 case LYXP_SET_EMPTY:
8454 ly_print(&out, "Empty XPath set\n\n");
8455 break;
8456 case LYXP_SET_BOOLEAN:
8457 ly_print(&out, "Boolean XPath set:\n");
8458 ly_print(&out, "%s\n\n", set->value.bool ? "true" : "false");
8459 break;
8460 case LYXP_SET_STRING:
8461 ly_print(&out, "String XPath set:\n");
8462 ly_print(&out, "\"%s\"\n\n", set->value.str);
8463 break;
8464 case LYXP_SET_NUMBER:
8465 ly_print(&out, "Number XPath set:\n");
8466
8467 if (isnan(set->value.num)) {
8468 str_num = strdup("NaN");
8469 } else if ((set->value.num == 0) || (set->value.num == -0.0f)) {
8470 str_num = strdup("0");
8471 } else if (isinf(set->value.num) && !signbit(set->value.num)) {
8472 str_num = strdup("Infinity");
8473 } else if (isinf(set->value.num) && signbit(set->value.num)) {
8474 str_num = strdup("-Infinity");
8475 } else if ((long long)set->value.num == set->value.num) {
8476 if (asprintf(&str_num, "%lld", (long long)set->value.num) == -1) {
8477 str_num = NULL;
8478 }
8479 } else {
8480 if (asprintf(&str_num, "%03.1Lf", set->value.num) == -1) {
8481 str_num = NULL;
8482 }
8483 }
8484 if (!str_num) {
8485 LOGMEM;
8486 return;
8487 }
8488 ly_print(&out, "%s\n\n", str_num);
8489 free(str_num);
8490 break;
8491 case LYXP_SET_NODE_SET:
8492 ly_print(&out, "Node XPath set:\n");
8493
8494 for (i = 0; i < set->used; ++i) {
8495 ly_print(&out, "%d. ", i + 1);
8496 switch (set->node_type[i]) {
8497 case LYXP_NODE_ROOT_ALL:
8498 ly_print(&out, "ROOT all\n\n");
8499 break;
8500 case LYXP_NODE_ROOT_CONFIG:
8501 ly_print(&out, "ROOT config\n\n");
8502 break;
8503 case LYXP_NODE_ROOT_STATE:
8504 ly_print(&out, "ROOT state\n\n");
8505 break;
8506 case LYXP_NODE_ROOT_NOTIF:
8507 ly_print(&out, "ROOT notification \"%s\"\n\n", set->value.nodes[i]->schema->name);
8508 break;
8509 case LYXP_NODE_ROOT_RPC:
8510 ly_print(&out, "ROOT rpc \"%s\"\n\n", set->value.nodes[i]->schema->name);
8511 break;
8512 case LYXP_NODE_ROOT_OUTPUT:
8513 ly_print(&out, "ROOT output \"%s\"\n\n", set->value.nodes[i]->schema->name);
8514 break;
8515 case LYXP_NODE_ELEM:
8516 ly_print(&out, "ELEM \"%s\"\n", set->value.nodes[i]->schema->name);
8517 xml_print_node(&out, 1, set->value.nodes[i], 1, LYP_FORMAT);
8518 ly_print(&out, "\n");
8519 break;
8520 case LYXP_NODE_TEXT:
8521 ly_print(&out, "TEXT \"%s\"\n\n", ((struct lyd_node_leaf_list *)set->value.nodes[i])->value_str);
8522 break;
8523 case LYXP_NODE_ATTR:
8524 ly_print(&out, "ATTR \"%s\" = \"%s\"\n\n", set->value.attrs[i]->name, set->value.attrs[i]->value);
8525 break;
8526 }
8527 }
8528 break;
8529 }
8530}
8531
8532#endif
8533
8534LY_ERR
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008535lyxp_set_cast(struct lyxp_set *set, enum lyxp_set_type target)
Michal Vasko03ff5a72019-09-11 13:49:33 +02008536{
8537 long double num;
8538 char *str;
8539 LY_ERR rc;
8540
8541 if (!set || (set->type == target)) {
8542 return LY_SUCCESS;
8543 }
8544
8545 /* it's not possible to convert anything into a node set */
Michal Vaskod3678892020-05-21 10:06:58 +02008546 assert(target != LYXP_SET_NODE_SET);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008547
8548 if (set->type == LYXP_SET_SCNODE_SET) {
Michal Vaskod3678892020-05-21 10:06:58 +02008549 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008550 return LY_EINVAL;
8551 }
8552
8553 /* to STRING */
Michal Vaskod3678892020-05-21 10:06:58 +02008554 if ((target == LYXP_SET_STRING) || ((target == LYXP_SET_NUMBER) && (set->type == LYXP_SET_NODE_SET))) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008555 switch (set->type) {
8556 case LYXP_SET_NUMBER:
8557 if (isnan(set->val.num)) {
8558 set->val.str = strdup("NaN");
8559 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
8560 } else if ((set->val.num == 0) || (set->val.num == -0.0f)) {
8561 set->val.str = strdup("0");
8562 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
8563 } else if (isinf(set->val.num) && !signbit(set->val.num)) {
8564 set->val.str = strdup("Infinity");
8565 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
8566 } else if (isinf(set->val.num) && signbit(set->val.num)) {
8567 set->val.str = strdup("-Infinity");
8568 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
8569 } else if ((long long)set->val.num == set->val.num) {
8570 if (asprintf(&str, "%lld", (long long)set->val.num) == -1) {
8571 LOGMEM_RET(set->ctx);
8572 }
8573 set->val.str = str;
8574 } else {
8575 if (asprintf(&str, "%03.1Lf", set->val.num) == -1) {
8576 LOGMEM_RET(set->ctx);
8577 }
8578 set->val.str = str;
8579 }
8580 break;
8581 case LYXP_SET_BOOLEAN:
Michal Vasko004d3152020-06-11 19:59:22 +02008582 if (set->val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008583 set->val.str = strdup("true");
8584 } else {
8585 set->val.str = strdup("false");
8586 }
8587 LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), LY_EMEM);
8588 break;
8589 case LYXP_SET_NODE_SET:
8590 assert(set->used);
8591
8592 /* we need the set sorted, it affects the result */
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008593 assert(!set_sort(set));
Michal Vasko03ff5a72019-09-11 13:49:33 +02008594
Michal Vasko5e0e6eb2019-11-06 15:47:50 +01008595 rc = cast_node_set_to_string(set, &str);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008596 LY_CHECK_RET(rc);
Michal Vaskod3678892020-05-21 10:06:58 +02008597 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008598 set->val.str = str;
8599 break;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008600 default:
8601 LOGINT_RET(set->ctx);
8602 }
8603 set->type = LYXP_SET_STRING;
8604 }
8605
8606 /* to NUMBER */
8607 if (target == LYXP_SET_NUMBER) {
8608 switch (set->type) {
8609 case LYXP_SET_STRING:
8610 num = cast_string_to_number(set->val.str);
Michal Vaskod3678892020-05-21 10:06:58 +02008611 lyxp_set_free_content(set);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008612 set->val.num = num;
8613 break;
8614 case LYXP_SET_BOOLEAN:
Michal Vasko004d3152020-06-11 19:59:22 +02008615 if (set->val.bln) {
Michal Vasko03ff5a72019-09-11 13:49:33 +02008616 set->val.num = 1;
8617 } else {
8618 set->val.num = 0;
8619 }
8620 break;
8621 default:
8622 LOGINT_RET(set->ctx);
8623 }
8624 set->type = LYXP_SET_NUMBER;
8625 }
8626
8627 /* to BOOLEAN */
8628 if (target == LYXP_SET_BOOLEAN) {
8629 switch (set->type) {
8630 case LYXP_SET_NUMBER:
8631 if ((set->val.num == 0) || (set->val.num == -0.0f) || isnan(set->val.num)) {
Michal Vasko004d3152020-06-11 19:59:22 +02008632 set->val.bln = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008633 } else {
Michal Vasko004d3152020-06-11 19:59:22 +02008634 set->val.bln = 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008635 }
8636 break;
8637 case LYXP_SET_STRING:
8638 if (set->val.str[0]) {
Michal Vaskod3678892020-05-21 10:06:58 +02008639 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02008640 set->val.bln = 1;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008641 } else {
Michal Vaskod3678892020-05-21 10:06:58 +02008642 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02008643 set->val.bln = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008644 }
8645 break;
8646 case LYXP_SET_NODE_SET:
Michal Vaskod3678892020-05-21 10:06:58 +02008647 if (set->used) {
8648 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02008649 set->val.bln = 1;
Michal Vaskod3678892020-05-21 10:06:58 +02008650 } else {
8651 lyxp_set_free_content(set);
Michal Vasko004d3152020-06-11 19:59:22 +02008652 set->val.bln = 0;
Michal Vaskod3678892020-05-21 10:06:58 +02008653 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02008654 break;
8655 default:
8656 LOGINT_RET(set->ctx);
8657 }
8658 set->type = LYXP_SET_BOOLEAN;
8659 }
8660
Michal Vasko03ff5a72019-09-11 13:49:33 +02008661 return LY_SUCCESS;
8662}
8663
8664LY_ERR
8665lyxp_atomize(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lysc_node *ctx_scnode,
8666 enum lyxp_node_type ctx_scnode_type, struct lyxp_set *set, int options)
8667{
8668 struct ly_ctx *ctx;
Michal Vasko004d3152020-06-11 19:59:22 +02008669 const struct lysc_node *real_ctx_scnode;
8670 uint16_t tok_idx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008671
Michal Vasko004d3152020-06-11 19:59:22 +02008672 LY_CHECK_ARG_RET(NULL, exp, local_mod, ctx_scnode, set, LY_EINVAL);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008673
8674 ctx = local_mod->ctx;
Michal Vasko004d3152020-06-11 19:59:22 +02008675 if ((ctx_scnode_type == LYXP_NODE_ROOT) || (ctx_scnode_type == LYXP_NODE_ROOT_CONFIG)) {
8676 /* we always need some context node because it is used for resolving unqualified names */
8677 real_ctx_scnode = NULL;
8678 } else {
8679 real_ctx_scnode = ctx_scnode;
8680 }
Michal Vasko03ff5a72019-09-11 13:49:33 +02008681
8682 /* prepare set for evaluation */
Michal Vasko004d3152020-06-11 19:59:22 +02008683 tok_idx = 0;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008684 memset(set, 0, sizeof *set);
8685 set->type = LYXP_SET_SCNODE_SET;
Michal Vasko004d3152020-06-11 19:59:22 +02008686 lyxp_set_scnode_insert_node(set, real_ctx_scnode, ctx_scnode_type);
Michal Vasko5c4e5892019-11-14 12:31:38 +01008687 set->val.scnodes[0].in_ctx = -2;
Michal Vasko03ff5a72019-09-11 13:49:33 +02008688 set->ctx = ctx;
8689 set->ctx_scnode = ctx_scnode;
Michal Vasko004d3152020-06-11 19:59:22 +02008690 set->root_type = lyxp_get_root_type(NULL, real_ctx_scnode, options);
Michal Vasko6b26e742020-07-17 15:02:10 +02008691 for (set->context_op = ctx_scnode;
8692 set->context_op && !(set->context_op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF));
8693 set->context_op = set->context_op->parent);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008694 set->local_mod = local_mod;
8695 set->format = format;
8696
8697 /* evaluate */
Michal Vasko004d3152020-06-11 19:59:22 +02008698 return eval_expr_select(exp, &tok_idx, 0, set, options);
Michal Vasko03ff5a72019-09-11 13:49:33 +02008699}