blob: 51398260a6c616d35b92dc061a426f5602697949 [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[] = {
Radek Krejcid6b76452019-09-03 17:03:03 +020032 [LY_STMT_ACTION] = "action",
33 [LY_STMT_ANYDATA] = "anydata",
34 [LY_STMT_ANYXML] = "anyxml",
35 [LY_STMT_ARGUMENT] = "argument",
36 [LY_STMT_AUGMENT] = "augment",
37 [LY_STMT_BASE] = "base",
38 [LY_STMT_BELONGS_TO] = "belongs-to",
39 [LY_STMT_BIT] = "bit",
40 [LY_STMT_CASE] = "case",
41 [LY_STMT_CHOICE] = "choice",
42 [LY_STMT_CONFIG] = "config",
43 [LY_STMT_CONTACT] = "contact",
44 [LY_STMT_CONTAINER] = "container",
45 [LY_STMT_EXTENSION_INSTANCE] = "<extension-instance>",
46 [LY_STMT_DEFAULT] = "default",
47 [LY_STMT_DESCRIPTION] = "description",
48 [LY_STMT_DEVIATE] = "deviate",
49 [LY_STMT_DEVIATION] = "deviation",
50 [LY_STMT_ENUM] = "enum",
51 [LY_STMT_ERROR_APP_TAG] = "error-app-tag",
52 [LY_STMT_ERROR_MESSAGE] = "error-message",
53 [LY_STMT_EXTENSION] = "extension",
54 [LY_STMT_FEATURE] = "feature",
55 [LY_STMT_FRACTION_DIGITS] = "fraction-digits",
56 [LY_STMT_GROUPING] = "grouping",
57 [LY_STMT_IDENTITY] = "identity",
58 [LY_STMT_IF_FEATURE] = "if-feature",
59 [LY_STMT_IMPORT] = "import",
60 [LY_STMT_INCLUDE] = "include",
61 [LY_STMT_INPUT] = "input",
62 [LY_STMT_KEY] = "key",
63 [LY_STMT_LEAF] = "leaf",
64 [LY_STMT_LEAF_LIST] = "leaf-list",
65 [LY_STMT_LENGTH] = "length",
66 [LY_STMT_LIST] = "list",
67 [LY_STMT_MANDATORY] = "mandatory",
68 [LY_STMT_MAX_ELEMENTS] = "max-elements",
69 [LY_STMT_MIN_ELEMENTS] = "min-elements",
70 [LY_STMT_MODIFIER] = "modifier",
71 [LY_STMT_MODULE] = "module",
72 [LY_STMT_MUST] = "must",
73 [LY_STMT_NAMESPACE] = "namespace",
74 [LY_STMT_NOTIFICATION] = "notification",
75 [LY_STMT_ORDERED_BY] = "ordered-by",
76 [LY_STMT_ORGANIZATION] = "organization",
77 [LY_STMT_OUTPUT] = "output",
78 [LY_STMT_PATH] = "path",
79 [LY_STMT_PATTERN] = "pattern",
80 [LY_STMT_POSITION] = "position",
81 [LY_STMT_PREFIX] = "prefix",
82 [LY_STMT_PRESENCE] = "presence",
83 [LY_STMT_RANGE] = "range",
84 [LY_STMT_REFERENCE] = "reference",
85 [LY_STMT_REFINE] = "refine",
86 [LY_STMT_REQUIRE_INSTANCE] = "require-instance",
87 [LY_STMT_REVISION] = "revision",
88 [LY_STMT_REVISION_DATE] = "revision-date",
89 [LY_STMT_RPC] = "rpc",
90 [LY_STMT_STATUS] = "status",
91 [LY_STMT_SUBMODULE] = "submodule",
92 [LY_STMT_TYPE] = "type",
93 [LY_STMT_TYPEDEF] = "typedef",
94 [LY_STMT_UNIQUE] = "unique",
95 [LY_STMT_UNITS] = "units",
96 [LY_STMT_USES] = "uses",
97 [LY_STMT_VALUE] = "value",
98 [LY_STMT_WHEN] = "when",
99 [LY_STMT_YANG_VERSION] = "yang-version",
100 [LY_STMT_YIN_ELEMENT] = "yin-element",
101 [LY_STMT_SYNTAX_SEMICOLON] = ";",
102 [LY_STMT_SYNTAX_LEFT_BRACE] = "{",
103 [LY_STMT_SYNTAX_RIGHT_BRACE] = "}",
104 [LY_STMT_ARG_TEXT] = "text",
105 [LY_STMT_ARG_VALUE] = "value",
Radek Krejcic59bc972018-09-17 16:13:06 +0200106};
107
108const char *const lyext_substmt_list[] = {
109 [LYEXT_SUBSTMT_ARGUMENT] = "argument",
110 [LYEXT_SUBSTMT_BASE] = "base",
111 [LYEXT_SUBSTMT_BELONGSTO] = "belongs-to",
112 [LYEXT_SUBSTMT_CONTACT] = "contact",
113 [LYEXT_SUBSTMT_DEFAULT] = "default",
114 [LYEXT_SUBSTMT_DESCRIPTION] = "description",
115 [LYEXT_SUBSTMT_ERRTAG] = "error-app-tag",
116 [LYEXT_SUBSTMT_ERRMSG] = "error-message",
117 [LYEXT_SUBSTMT_KEY] = "key",
118 [LYEXT_SUBSTMT_NAMESPACE] = "namespace",
119 [LYEXT_SUBSTMT_ORGANIZATION] = "organization",
120 [LYEXT_SUBSTMT_PATH] = "path",
121 [LYEXT_SUBSTMT_PREFIX] = "prefix",
122 [LYEXT_SUBSTMT_PRESENCE] = "presence",
123 [LYEXT_SUBSTMT_REFERENCE] = "reference",
124 [LYEXT_SUBSTMT_REVISIONDATE] = "revision-date",
125 [LYEXT_SUBSTMT_UNITS] = "units",
126 [LYEXT_SUBSTMT_VALUE] = "value",
127 [LYEXT_SUBSTMT_VERSION] = "yang-version",
128 [LYEXT_SUBSTMT_MODIFIER] = "modifier",
129 [LYEXT_SUBSTMT_REQINSTANCE] = "require-instance",
130 [LYEXT_SUBSTMT_YINELEM] = "yin-element",
131 [LYEXT_SUBSTMT_CONFIG] = "config",
132 [LYEXT_SUBSTMT_MANDATORY] = "mandatory",
133 [LYEXT_SUBSTMT_ORDEREDBY] = "ordered-by",
134 [LYEXT_SUBSTMT_STATUS] = "status",
135 [LYEXT_SUBSTMT_FRACDIGITS] = "fraction-digits",
136 [LYEXT_SUBSTMT_MAX] = "max-elements",
137 [LYEXT_SUBSTMT_MIN] = "min-elements",
138 [LYEXT_SUBSTMT_POSITION] = "position",
139 [LYEXT_SUBSTMT_UNIQUE] = "unique",
140 [LYEXT_SUBSTMT_IFFEATURE] = "if-feature",
141};
142
143const char *const ly_devmod_list[] = {
144 [LYS_DEV_NOT_SUPPORTED] = "not-supported",
145 [LYS_DEV_ADD] = "add",
146 [LYS_DEV_DELETE] = "delete",
147 [LYS_DEV_REPLACE] = "replace",
148};
149
Michal Vasko1324b6c2018-09-07 11:16:23 +0200150void *
151ly_realloc(void *ptr, size_t size)
152{
153 void *new_mem;
154
155 new_mem = realloc(ptr, size);
156 if (!new_mem) {
157 free(ptr);
158 }
159
160 return new_mem;
161}
Michal Vasko841d1a92018-09-07 15:40:31 +0200162
Michal Vasko03ff5a72019-09-11 13:49:33 +0200163char *
164ly_strnchr(const char *s, int c, unsigned int len)
165{
166 for (; *s != (char)c; ++s, --len) {
167 if ((*s == '\0') || (!len)) {
168 return NULL;
169 }
170 }
171 return (char *)s;
172}
173
Radek Krejcib416be62018-10-01 14:51:45 +0200174LY_ERR
175ly_getutf8(const char **input, unsigned int *utf8_char, size_t *bytes_read)
176{
177 unsigned int c, len;
178 int aux;
179 int i;
180
Radek Krejcicc6a45c2019-05-13 10:16:14 +0200181 if (bytes_read) {
182 (*bytes_read) = 0;
183 }
184
Radek Krejcib416be62018-10-01 14:51:45 +0200185 c = (*input)[0];
186 LY_CHECK_RET(!c, LY_EINVAL);
187
188 if (!(c & 0x80)) {
189 /* one byte character */
190 len = 1;
191
192 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
193 return LY_EINVAL;
194 }
195 } else if ((c & 0xe0) == 0xc0) {
196 /* two bytes character */
197 len = 2;
198
199 aux = (*input)[1];
200 if ((aux & 0xc0) != 0x80) {
201 return LY_EINVAL;
202 }
203 c = ((c & 0x1f) << 6) | (aux & 0x3f);
204
205 if (c < 0x80) {
206 return LY_EINVAL;
207 }
208 } else if ((c & 0xf0) == 0xe0) {
209 /* three bytes character */
210 len = 3;
211
212 c &= 0x0f;
213 for (i = 1; i <= 2; i++) {
214 aux = (*input)[i];
215 if ((aux & 0xc0) != 0x80) {
216 return LY_EINVAL;
217 }
218
219 c = (c << 6) | (aux & 0x3f);
220 }
221
222 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
223 return LY_EINVAL;
224 }
225 } else if ((c & 0xf8) == 0xf0) {
226 /* four bytes character */
227 len = 4;
228
229 c &= 0x07;
230 for (i = 1; i <= 3; i++) {
231 aux = (*input)[i];
232 if ((aux & 0xc0) != 0x80) {
233 return LY_EINVAL;
234 }
235
236 c = (c << 6) | (aux & 0x3f);
237 }
238
239 if (c < 0x1000 || c > 0x10ffff) {
240 return LY_EINVAL;
241 }
242 } else {
243 return LY_EINVAL;
244 }
245
246 (*utf8_char) = c;
247 (*input) += len;
248 if (bytes_read) {
249 (*bytes_read) = len;
250 }
251 return LY_SUCCESS;
252}
253
Radek Krejci76c98012019-08-14 11:23:24 +0200254/**
255 * @brief Static table of the UTF8 characters lengths according to their first byte.
256 */
257static const unsigned char
258utf8_char_length_table[] = {
259 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
260 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
261 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
262 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
263 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
264 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
265 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
266 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
267 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
268 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
269 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
270 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
271 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
272 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
273 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
274 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
275};
276
277size_t
278ly_utf8len(const char *str, size_t bytes)
279{
280 size_t len;
281 const char *ptr;
282
283 for (len = 0, ptr = str; *ptr && (size_t)(ptr - str) < bytes; ++len, ptr += utf8_char_length_table[((unsigned char)(*ptr))]);
284 return len;
285}
286
Radek Krejcid972c252018-09-25 13:23:39 +0200287size_t
288LY_VCODE_INSTREXP_len(const char *str)
289{
290 size_t len = 0;
291 if (!str) {
292 return len;
293 } else if (!str[0]) {
294 return 1;
295 }
296 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len);
297 return len;
298}
299
Radek Krejcif345c012018-09-19 11:12:59 +0200300LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200301ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200302{
Radek Krejci86d106e2018-10-18 09:53:19 +0200303 struct stat sb;
304 long pagesize;
305 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200306
Radek Krejci86d106e2018-10-18 09:53:19 +0200307 assert(length);
308 assert(addr);
309 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200310
Radek Krejci86d106e2018-10-18 09:53:19 +0200311 if (fstat(fd, &sb) == -1) {
312 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
313 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200314 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200315 if (!S_ISREG(sb.st_mode)) {
316 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
317 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200318 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200319 if (!sb.st_size) {
320 *addr = NULL;
321 return LY_SUCCESS;
322 }
323 pagesize = sysconf(_SC_PAGESIZE);
324
325 m = sb.st_size % pagesize;
326 if (m && pagesize - m >= 1) {
327 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
328 *length = sb.st_size + 1;
329 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
330 } else {
331 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
332 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
333 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
334 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
335 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
336 * where the anonymous mapping starts. */
337 *length = sb.st_size + pagesize;
338 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
339 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
340 }
341 if (*addr == MAP_FAILED) {
342 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
343 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200344 }
345
Radek Krejcif345c012018-09-19 11:12:59 +0200346 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200347}
Michal Vasko841d1a92018-09-07 15:40:31 +0200348
Radek Krejci86d106e2018-10-18 09:53:19 +0200349LY_ERR
350ly_munmap(void *addr, size_t length)
351{
352 if (munmap(addr, length)) {
353 return LY_ESYS;
354 }
355 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200356}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100357
358LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200359ly_strcat(char **dest, const char *format, ...)
360{
361 va_list fp;
362 char *addition = NULL;
363 size_t len;
364
365 va_start(fp, format);
366 len = vasprintf(&addition, format, fp);
367 len += (*dest ? strlen(*dest) : 0) + 1;
368
369 if (*dest) {
370 *dest = ly_realloc(*dest, len);
371 if (!*dest) {
372 return LY_EMEM;
373 }
374 *dest = strcat(*dest, addition);
375 free(addition);
376 } else {
377 *dest = addition;
378 }
379
380 va_end(fp);
381 return LY_SUCCESS;
382}
383
384LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200385ly_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 +0100386{
387 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200388 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100389
Radek Krejci249973a2019-06-10 10:50:54 +0200390 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100391
392 /* convert to 64-bit integer, all the redundant characters are handled */
393 errno = 0;
394 strptr = NULL;
395
396 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200397 i = strtoll(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200398 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100399 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200400 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100401 return LY_EDENIED;
402 } else if (strptr && *strptr) {
403 while (isspace(*strptr)) {
404 ++strptr;
405 }
Radek Krejci249973a2019-06-10 10:50:54 +0200406 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100407 return LY_EVALID;
408 }
409 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200410
411 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100412 return LY_SUCCESS;
413}
414
415LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200416ly_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 +0100417{
418 char *strptr;
419 uint64_t u;
420
421 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
422
423 errno = 0;
424 strptr = NULL;
425 u = strtoull(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200426 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100427 return LY_EVALID;
428 } else if ((u > max) || (u && val_str[0] == '-')) {
429 return LY_EDENIED;
430 } else if (strptr && *strptr) {
431 while (isspace(*strptr)) {
432 ++strptr;
433 }
Radek Krejci249973a2019-06-10 10:50:54 +0200434 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100435 return LY_EVALID;
436 }
437 }
438
439 *ret = u;
440 return LY_SUCCESS;
441}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200442
443/**
444 * @brief Parse an identifier.
445 *
446 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
447 * identifier = (ALPHA / "_")
448 * *(ALPHA / DIGIT / "_" / "-" / ".")
449 *
450 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
451 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
452 */
453static LY_ERR
454lys_parse_id(const char **id)
455{
456 assert(id && *id);
457
458 if (!is_yangidentstartchar(**id)) {
459 return LY_EINVAL;
460 }
461 ++(*id);
462
463 while (is_yangidentchar(**id)) {
464 ++(*id);
465 }
466 return LY_SUCCESS;
467}
468
469LY_ERR
470ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
471{
472 assert(id && *id);
473 assert(prefix && prefix_len);
474 assert(name && name_len);
475
476 *prefix = *id;
477 *prefix_len = 0;
478 *name = NULL;
479 *name_len = 0;
480
481 LY_CHECK_RET(lys_parse_id(id));
482 if (**id == ':') {
483 /* there is prefix */
484 *prefix_len = *id - *prefix;
485 ++(*id);
486 *name = *id;
487
488 LY_CHECK_RET(lys_parse_id(id));
489 *name_len = *id - *name;
490 } else {
491 /* there is no prefix, so what we have as prefix now is actually the name */
492 *name = *prefix;
493 *name_len = *id - *name;
494 *prefix = NULL;
495 }
496
497 return LY_SUCCESS;
498}
499
500LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200501ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
502 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
503 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200504{
505 LY_ERR ret = LY_EVALID;
506 const char *in = *pred;
507 size_t offset = 1;
508 int expr = 0;
509 char quot;
510
511 assert(in[0] == '\[');
512
513 *prefix = *id = *value = NULL;
514 *prefix_len = *id_len = *value_len = 0;
515
516 /* leading *WSP */
517 for (; isspace(in[offset]); offset++);
518
519 if (isdigit(in[offset])) {
520 /* pos: "[" *WSP positive-integer-value *WSP "]" */
521 if (in[offset] == '0') {
522 /* zero */
523 *errmsg = "The position predicate cannot be zero.";
524 goto error;
525 }
526
527 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200528 *value = &in[offset++];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200529 for (; isdigit(in[offset]); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200530 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200531
532 } else if (in[offset] == '.') {
533 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
534 *id = &in[offset];
535 *id_len = 1;
536 offset++;
537 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200538 } else if (in[offset] == '-') {
539 /* typically negative value */
540 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
541 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200542 } else {
543 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
544 in = &in[offset];
545 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
546 *errmsg = "Invalid node-identifier.";
547 goto error;
548 }
Radek Krejci084289f2019-07-09 17:35:30 +0200549 if (format == LYD_XML && !(*prefix)) {
550 /* all node names MUST be qualified with explicit namespace prefix */
551 *errmsg = "Missing prefix of a node name.";
552 goto error;
553 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200554 offset = in - *pred;
555 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200556 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200557 }
558
559 if (expr) {
560 /* *WSP "=" *WSP quoted-string *WSP "]" */
561 for (; isspace(in[offset]); offset++);
562
563 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200564 if (expr == 1) {
565 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
566 } else { /* 2 */
567 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
568 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200569 goto error;
570 }
571 offset++;
572 for (; isspace(in[offset]); offset++);
573
574 /* quoted-string */
575 quot = in[offset++];
576 if (quot != '\'' && quot != '\"') {
577 *errmsg = "String value is not quoted.";
578 goto error;
579 }
580 *value = &in[offset];
Radek Krejci084289f2019-07-09 17:35:30 +0200581 for (;offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200582 if (in[offset] == quot) {
583 *value_len = &in[offset] - *value;
584 offset++;
585 } else {
586 *errmsg = "Value is not terminated quoted-string.";
587 goto error;
588 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200589 }
590
591 /* *WSP "]" */
592 for(; isspace(in[offset]); offset++);
593 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200594 if (expr == 0) {
595 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
596 } else if (expr == 1) {
597 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
598 } else { /* 2 */
599 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
600 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200601 goto error;
602 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200603 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200604
Radek Krejci10bfdf82019-06-10 14:08:13 +0200605 if (offset <= limit) {
606 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200607 return LY_SUCCESS;
608 }
609
610 /* we read after the limit */
611 *errmsg = "Predicate is incomplete.";
612 *prefix = *id = *value = NULL;
613 *prefix_len = *id_len = *value_len = 0;
614 offset = limit;
615 ret = LY_EINVAL;
616
617error:
618 *pred = &in[offset];
619 return ret;
620}