blob: 6f8cfe9891356916219d6d6d150ff2370c1dd2db [file] [log] [blame]
Michal Vasko1324b6c2018-09-07 11:16:23 +02001/**
2 * @file common.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief common internal definitions for libyang
5 *
6 * Copyright (c) 2018 CESNET, z.s.p.o.
7 *
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 */
Radek Krejcib7db73a2018-10-24 14:18:40 +020014
15#include "common.h"
Michal Vasko1324b6c2018-09-07 11:16:23 +020016
Radek Krejci86d106e2018-10-18 09:53:19 +020017#include <assert.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020018#include <ctype.h>
Radek Krejci4546aa62019-07-15 16:53:32 +020019#include <errno.h>
20#include <stdarg.h>
21#include <stdlib.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020022#include <string.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020023#include <sys/mman.h>
24#include <sys/stat.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020025#include <unistd.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020026
Radek Krejci0935f412019-08-20 16:15:18 +020027#include "plugins_exts.h"
Michal Vasko841d1a92018-09-07 15:40:31 +020028#include "tree_schema.h"
Radek Krejcib4a4a272019-06-10 12:44:52 +020029#include "tree_schema_internal.h"
Michal Vasko1324b6c2018-09-07 11:16:23 +020030
Radek Krejcic59bc972018-09-17 16:13:06 +020031const char *const ly_stmt_list[] = {
32 [YANG_ACTION] = "action",
33 [YANG_ANYDATA] = "anydata",
34 [YANG_ANYXML] = "anyxml",
35 [YANG_ARGUMENT] = "argument",
36 [YANG_AUGMENT] = "augment",
37 [YANG_BASE] = "base",
38 [YANG_BELONGS_TO] = "belongs-to",
39 [YANG_BIT] = "bit",
40 [YANG_CASE] = "case",
41 [YANG_CHOICE] = "choice",
42 [YANG_CONFIG] = "config",
43 [YANG_CONTACT] = "contact",
44 [YANG_CONTAINER] = "container",
45 [YANG_CUSTOM] = "<extension-instance>",
46 [YANG_DEFAULT] = "default",
47 [YANG_DESCRIPTION] = "description",
48 [YANG_DEVIATE] = "deviate",
49 [YANG_DEVIATION] = "deviation",
50 [YANG_ENUM] = "enum",
51 [YANG_ERROR_APP_TAG] = "error-app-tag",
52 [YANG_ERROR_MESSAGE] = "error-message",
53 [YANG_EXTENSION] = "extension",
54 [YANG_FEATURE] = "feature",
55 [YANG_FRACTION_DIGITS] = "fraction-digits",
56 [YANG_GROUPING] = "grouping",
57 [YANG_IDENTITY] = "identitiy",
58 [YANG_IF_FEATURE] = "if-feature",
59 [YANG_IMPORT] = "import",
60 [YANG_INCLUDE] = "include",
61 [YANG_INPUT] = "input",
62 [YANG_KEY] = "key",
63 [YANG_LEAF] = "leaf",
64 [YANG_LEAF_LIST] = "leaf-list",
65 [YANG_LENGTH] = "length",
66 [YANG_LIST] = "list",
67 [YANG_MANDATORY] = "mandatory",
68 [YANG_MAX_ELEMENTS] = "max-elements",
69 [YANG_MIN_ELEMENTS] = "min-elements",
70 [YANG_MODIFIER] = "modifier",
71 [YANG_MODULE] = "module",
72 [YANG_MUST] = "must",
73 [YANG_NAMESPACE] = "namespace",
74 [YANG_NOTIFICATION] = "notification",
75 [YANG_ORDERED_BY] = "ordered-by",
76 [YANG_ORGANIZATION] = "organization",
77 [YANG_OUTPUT] = "output",
78 [YANG_PATH] = "path",
79 [YANG_PATTERN] = "pattern",
80 [YANG_POSITION] = "position",
81 [YANG_PREFIX] = "prefix",
82 [YANG_PRESENCE] = "presence",
83 [YANG_RANGE] = "range",
84 [YANG_REFERENCE] = "reference",
85 [YANG_REFINE] = "refine",
86 [YANG_REQUIRE_INSTANCE] = "require-instance",
87 [YANG_REVISION] = "revision",
88 [YANG_REVISION_DATE] = "revision-date",
89 [YANG_RPC] = "rpc",
90 [YANG_STATUS] = "status",
91 [YANG_SUBMODULE] = "submodule",
92 [YANG_TYPE] = "type",
93 [YANG_TYPEDEF] = "typedef",
94 [YANG_UNIQUE] = "unique",
95 [YANG_UNITS] = "units",
96 [YANG_USES] = "uses",
97 [YANG_VALUE] = "value",
98 [YANG_WHEN] = "when",
99 [YANG_YANG_VERSION] = "yang-version",
100 [YANG_YIN_ELEMENT] = "yin-element",
101 [YANG_SEMICOLON] = ";",
102 [YANG_LEFT_BRACE] = "{",
103 [YANG_RIGHT_BRACE] = "}",
104};
105
106const char *const lyext_substmt_list[] = {
107 [LYEXT_SUBSTMT_ARGUMENT] = "argument",
108 [LYEXT_SUBSTMT_BASE] = "base",
109 [LYEXT_SUBSTMT_BELONGSTO] = "belongs-to",
110 [LYEXT_SUBSTMT_CONTACT] = "contact",
111 [LYEXT_SUBSTMT_DEFAULT] = "default",
112 [LYEXT_SUBSTMT_DESCRIPTION] = "description",
113 [LYEXT_SUBSTMT_ERRTAG] = "error-app-tag",
114 [LYEXT_SUBSTMT_ERRMSG] = "error-message",
115 [LYEXT_SUBSTMT_KEY] = "key",
116 [LYEXT_SUBSTMT_NAMESPACE] = "namespace",
117 [LYEXT_SUBSTMT_ORGANIZATION] = "organization",
118 [LYEXT_SUBSTMT_PATH] = "path",
119 [LYEXT_SUBSTMT_PREFIX] = "prefix",
120 [LYEXT_SUBSTMT_PRESENCE] = "presence",
121 [LYEXT_SUBSTMT_REFERENCE] = "reference",
122 [LYEXT_SUBSTMT_REVISIONDATE] = "revision-date",
123 [LYEXT_SUBSTMT_UNITS] = "units",
124 [LYEXT_SUBSTMT_VALUE] = "value",
125 [LYEXT_SUBSTMT_VERSION] = "yang-version",
126 [LYEXT_SUBSTMT_MODIFIER] = "modifier",
127 [LYEXT_SUBSTMT_REQINSTANCE] = "require-instance",
128 [LYEXT_SUBSTMT_YINELEM] = "yin-element",
129 [LYEXT_SUBSTMT_CONFIG] = "config",
130 [LYEXT_SUBSTMT_MANDATORY] = "mandatory",
131 [LYEXT_SUBSTMT_ORDEREDBY] = "ordered-by",
132 [LYEXT_SUBSTMT_STATUS] = "status",
133 [LYEXT_SUBSTMT_FRACDIGITS] = "fraction-digits",
134 [LYEXT_SUBSTMT_MAX] = "max-elements",
135 [LYEXT_SUBSTMT_MIN] = "min-elements",
136 [LYEXT_SUBSTMT_POSITION] = "position",
137 [LYEXT_SUBSTMT_UNIQUE] = "unique",
138 [LYEXT_SUBSTMT_IFFEATURE] = "if-feature",
139};
140
141const char *const ly_devmod_list[] = {
142 [LYS_DEV_NOT_SUPPORTED] = "not-supported",
143 [LYS_DEV_ADD] = "add",
144 [LYS_DEV_DELETE] = "delete",
145 [LYS_DEV_REPLACE] = "replace",
146};
147
Michal Vasko1324b6c2018-09-07 11:16:23 +0200148void *
149ly_realloc(void *ptr, size_t size)
150{
151 void *new_mem;
152
153 new_mem = realloc(ptr, size);
154 if (!new_mem) {
155 free(ptr);
156 }
157
158 return new_mem;
159}
Michal Vasko841d1a92018-09-07 15:40:31 +0200160
Radek Krejcib416be62018-10-01 14:51:45 +0200161LY_ERR
162ly_getutf8(const char **input, unsigned int *utf8_char, size_t *bytes_read)
163{
164 unsigned int c, len;
165 int aux;
166 int i;
167
Radek Krejcicc6a45c2019-05-13 10:16:14 +0200168 if (bytes_read) {
169 (*bytes_read) = 0;
170 }
171
Radek Krejcib416be62018-10-01 14:51:45 +0200172 c = (*input)[0];
173 LY_CHECK_RET(!c, LY_EINVAL);
174
175 if (!(c & 0x80)) {
176 /* one byte character */
177 len = 1;
178
179 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
180 return LY_EINVAL;
181 }
182 } else if ((c & 0xe0) == 0xc0) {
183 /* two bytes character */
184 len = 2;
185
186 aux = (*input)[1];
187 if ((aux & 0xc0) != 0x80) {
188 return LY_EINVAL;
189 }
190 c = ((c & 0x1f) << 6) | (aux & 0x3f);
191
192 if (c < 0x80) {
193 return LY_EINVAL;
194 }
195 } else if ((c & 0xf0) == 0xe0) {
196 /* three bytes character */
197 len = 3;
198
199 c &= 0x0f;
200 for (i = 1; i <= 2; i++) {
201 aux = (*input)[i];
202 if ((aux & 0xc0) != 0x80) {
203 return LY_EINVAL;
204 }
205
206 c = (c << 6) | (aux & 0x3f);
207 }
208
209 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
210 return LY_EINVAL;
211 }
212 } else if ((c & 0xf8) == 0xf0) {
213 /* four bytes character */
214 len = 4;
215
216 c &= 0x07;
217 for (i = 1; i <= 3; i++) {
218 aux = (*input)[i];
219 if ((aux & 0xc0) != 0x80) {
220 return LY_EINVAL;
221 }
222
223 c = (c << 6) | (aux & 0x3f);
224 }
225
226 if (c < 0x1000 || c > 0x10ffff) {
227 return LY_EINVAL;
228 }
229 } else {
230 return LY_EINVAL;
231 }
232
233 (*utf8_char) = c;
234 (*input) += len;
235 if (bytes_read) {
236 (*bytes_read) = len;
237 }
238 return LY_SUCCESS;
239}
240
Radek Krejci76c98012019-08-14 11:23:24 +0200241/**
242 * @brief Static table of the UTF8 characters lengths according to their first byte.
243 */
244static const unsigned char
245utf8_char_length_table[] = {
246 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
247 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
248 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
249 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
250 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
251 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
252 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
253 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
254 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
255 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
256 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
257 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
258 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
259 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
260 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
261 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
262};
263
264size_t
265ly_utf8len(const char *str, size_t bytes)
266{
267 size_t len;
268 const char *ptr;
269
270 for (len = 0, ptr = str; *ptr && (size_t)(ptr - str) < bytes; ++len, ptr += utf8_char_length_table[((unsigned char)(*ptr))]);
271 return len;
272}
273
Radek Krejcid972c252018-09-25 13:23:39 +0200274size_t
275LY_VCODE_INSTREXP_len(const char *str)
276{
277 size_t len = 0;
278 if (!str) {
279 return len;
280 } else if (!str[0]) {
281 return 1;
282 }
283 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len);
284 return len;
285}
286
Radek Krejcif345c012018-09-19 11:12:59 +0200287LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200288ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200289{
Radek Krejci86d106e2018-10-18 09:53:19 +0200290 struct stat sb;
291 long pagesize;
292 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200293
Radek Krejci86d106e2018-10-18 09:53:19 +0200294 assert(length);
295 assert(addr);
296 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200297
Radek Krejci86d106e2018-10-18 09:53:19 +0200298 if (fstat(fd, &sb) == -1) {
299 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
300 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200301 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200302 if (!S_ISREG(sb.st_mode)) {
303 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
304 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200305 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200306 if (!sb.st_size) {
307 *addr = NULL;
308 return LY_SUCCESS;
309 }
310 pagesize = sysconf(_SC_PAGESIZE);
311
312 m = sb.st_size % pagesize;
313 if (m && pagesize - m >= 1) {
314 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
315 *length = sb.st_size + 1;
316 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
317 } else {
318 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
319 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
320 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
321 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
322 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
323 * where the anonymous mapping starts. */
324 *length = sb.st_size + pagesize;
325 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
326 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
327 }
328 if (*addr == MAP_FAILED) {
329 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
330 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200331 }
332
Radek Krejcif345c012018-09-19 11:12:59 +0200333 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200334}
Michal Vasko841d1a92018-09-07 15:40:31 +0200335
Radek Krejci86d106e2018-10-18 09:53:19 +0200336LY_ERR
337ly_munmap(void *addr, size_t length)
338{
339 if (munmap(addr, length)) {
340 return LY_ESYS;
341 }
342 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200343}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100344
345LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200346ly_strcat(char **dest, const char *format, ...)
347{
348 va_list fp;
349 char *addition = NULL;
350 size_t len;
351
352 va_start(fp, format);
353 len = vasprintf(&addition, format, fp);
354 len += (*dest ? strlen(*dest) : 0) + 1;
355
356 if (*dest) {
357 *dest = ly_realloc(*dest, len);
358 if (!*dest) {
359 return LY_EMEM;
360 }
361 *dest = strcat(*dest, addition);
362 free(addition);
363 } else {
364 *dest = addition;
365 }
366
367 va_end(fp);
368 return LY_SUCCESS;
369}
370
371LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200372ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t max, int base, int64_t *ret)
Radek Krejci4f28eda2018-11-12 11:46:16 +0100373{
374 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200375 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100376
Radek Krejci249973a2019-06-10 10:50:54 +0200377 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100378
379 /* convert to 64-bit integer, all the redundant characters are handled */
380 errno = 0;
381 strptr = NULL;
382
383 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200384 i = strtoll(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200385 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100386 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200387 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100388 return LY_EDENIED;
389 } else if (strptr && *strptr) {
390 while (isspace(*strptr)) {
391 ++strptr;
392 }
Radek Krejci249973a2019-06-10 10:50:54 +0200393 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100394 return LY_EVALID;
395 }
396 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200397
398 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100399 return LY_SUCCESS;
400}
401
402LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200403ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base, uint64_t *ret)
Radek Krejci4f28eda2018-11-12 11:46:16 +0100404{
405 char *strptr;
406 uint64_t u;
407
408 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
409
410 errno = 0;
411 strptr = NULL;
412 u = strtoull(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200413 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100414 return LY_EVALID;
415 } else if ((u > max) || (u && val_str[0] == '-')) {
416 return LY_EDENIED;
417 } else if (strptr && *strptr) {
418 while (isspace(*strptr)) {
419 ++strptr;
420 }
Radek Krejci249973a2019-06-10 10:50:54 +0200421 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100422 return LY_EVALID;
423 }
424 }
425
426 *ret = u;
427 return LY_SUCCESS;
428}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200429
430/**
431 * @brief Parse an identifier.
432 *
433 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
434 * identifier = (ALPHA / "_")
435 * *(ALPHA / DIGIT / "_" / "-" / ".")
436 *
437 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
438 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
439 */
440static LY_ERR
441lys_parse_id(const char **id)
442{
443 assert(id && *id);
444
445 if (!is_yangidentstartchar(**id)) {
446 return LY_EINVAL;
447 }
448 ++(*id);
449
450 while (is_yangidentchar(**id)) {
451 ++(*id);
452 }
453 return LY_SUCCESS;
454}
455
456LY_ERR
457ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
458{
459 assert(id && *id);
460 assert(prefix && prefix_len);
461 assert(name && name_len);
462
463 *prefix = *id;
464 *prefix_len = 0;
465 *name = NULL;
466 *name_len = 0;
467
468 LY_CHECK_RET(lys_parse_id(id));
469 if (**id == ':') {
470 /* there is prefix */
471 *prefix_len = *id - *prefix;
472 ++(*id);
473 *name = *id;
474
475 LY_CHECK_RET(lys_parse_id(id));
476 *name_len = *id - *name;
477 } else {
478 /* there is no prefix, so what we have as prefix now is actually the name */
479 *name = *prefix;
480 *name_len = *id - *name;
481 *prefix = NULL;
482 }
483
484 return LY_SUCCESS;
485}
486
487LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200488ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
489 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
490 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200491{
492 LY_ERR ret = LY_EVALID;
493 const char *in = *pred;
494 size_t offset = 1;
495 int expr = 0;
496 char quot;
497
498 assert(in[0] == '\[');
499
500 *prefix = *id = *value = NULL;
501 *prefix_len = *id_len = *value_len = 0;
502
503 /* leading *WSP */
504 for (; isspace(in[offset]); offset++);
505
506 if (isdigit(in[offset])) {
507 /* pos: "[" *WSP positive-integer-value *WSP "]" */
508 if (in[offset] == '0') {
509 /* zero */
510 *errmsg = "The position predicate cannot be zero.";
511 goto error;
512 }
513
514 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200515 *value = &in[offset++];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200516 for (; isdigit(in[offset]); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200517 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200518
519 } else if (in[offset] == '.') {
520 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
521 *id = &in[offset];
522 *id_len = 1;
523 offset++;
524 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200525 } else if (in[offset] == '-') {
526 /* typically negative value */
527 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
528 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200529 } else {
530 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
531 in = &in[offset];
532 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
533 *errmsg = "Invalid node-identifier.";
534 goto error;
535 }
Radek Krejci084289f2019-07-09 17:35:30 +0200536 if (format == LYD_XML && !(*prefix)) {
537 /* all node names MUST be qualified with explicit namespace prefix */
538 *errmsg = "Missing prefix of a node name.";
539 goto error;
540 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200541 offset = in - *pred;
542 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200543 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200544 }
545
546 if (expr) {
547 /* *WSP "=" *WSP quoted-string *WSP "]" */
548 for (; isspace(in[offset]); offset++);
549
550 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200551 if (expr == 1) {
552 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
553 } else { /* 2 */
554 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
555 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200556 goto error;
557 }
558 offset++;
559 for (; isspace(in[offset]); offset++);
560
561 /* quoted-string */
562 quot = in[offset++];
563 if (quot != '\'' && quot != '\"') {
564 *errmsg = "String value is not quoted.";
565 goto error;
566 }
567 *value = &in[offset];
Radek Krejci084289f2019-07-09 17:35:30 +0200568 for (;offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200569 if (in[offset] == quot) {
570 *value_len = &in[offset] - *value;
571 offset++;
572 } else {
573 *errmsg = "Value is not terminated quoted-string.";
574 goto error;
575 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200576 }
577
578 /* *WSP "]" */
579 for(; isspace(in[offset]); offset++);
580 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200581 if (expr == 0) {
582 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
583 } else if (expr == 1) {
584 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
585 } else { /* 2 */
586 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
587 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200588 goto error;
589 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200590 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200591
Radek Krejci10bfdf82019-06-10 14:08:13 +0200592 if (offset <= limit) {
593 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200594 return LY_SUCCESS;
595 }
596
597 /* we read after the limit */
598 *errmsg = "Predicate is incomplete.";
599 *prefix = *id = *value = NULL;
600 *prefix_len = *id_len = *value_len = 0;
601 offset = limit;
602 ret = LY_EINVAL;
603
604error:
605 *pred = &in[offset];
606 return ret;
607}