blob: 2372ceeb57e9362edade287e7fb6c85d9995c6f0 [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 Krejci7f9b6512019-09-18 13:11:09 +0200174int
175ly_strncmp(const char *refstr, const char *str, size_t str_len)
176{
177 int rc = strncmp(refstr, str, str_len);
178 if (!rc && refstr[str_len] == '\0') {
179 return 0;
180 } else {
181 return rc ? rc : 1;
182 }
183}
184
Radek Krejcib416be62018-10-01 14:51:45 +0200185LY_ERR
Michal Vaskob36053d2020-03-26 15:49:30 +0100186ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
Radek Krejcib416be62018-10-01 14:51:45 +0200187{
Michal Vaskob36053d2020-03-26 15:49:30 +0100188 uint32_t c, len;
Radek Krejcib416be62018-10-01 14:51:45 +0200189 int aux;
190 int i;
191
Radek Krejcicc6a45c2019-05-13 10:16:14 +0200192 if (bytes_read) {
193 (*bytes_read) = 0;
194 }
195
Radek Krejcib416be62018-10-01 14:51:45 +0200196 c = (*input)[0];
197 LY_CHECK_RET(!c, LY_EINVAL);
198
199 if (!(c & 0x80)) {
200 /* one byte character */
201 len = 1;
202
203 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
204 return LY_EINVAL;
205 }
206 } else if ((c & 0xe0) == 0xc0) {
207 /* two bytes character */
208 len = 2;
209
210 aux = (*input)[1];
211 if ((aux & 0xc0) != 0x80) {
212 return LY_EINVAL;
213 }
214 c = ((c & 0x1f) << 6) | (aux & 0x3f);
215
216 if (c < 0x80) {
217 return LY_EINVAL;
218 }
219 } else if ((c & 0xf0) == 0xe0) {
220 /* three bytes character */
221 len = 3;
222
223 c &= 0x0f;
224 for (i = 1; i <= 2; i++) {
225 aux = (*input)[i];
226 if ((aux & 0xc0) != 0x80) {
227 return LY_EINVAL;
228 }
229
230 c = (c << 6) | (aux & 0x3f);
231 }
232
233 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
234 return LY_EINVAL;
235 }
236 } else if ((c & 0xf8) == 0xf0) {
237 /* four bytes character */
238 len = 4;
239
240 c &= 0x07;
241 for (i = 1; i <= 3; i++) {
242 aux = (*input)[i];
243 if ((aux & 0xc0) != 0x80) {
244 return LY_EINVAL;
245 }
246
247 c = (c << 6) | (aux & 0x3f);
248 }
249
250 if (c < 0x1000 || c > 0x10ffff) {
251 return LY_EINVAL;
252 }
253 } else {
254 return LY_EINVAL;
255 }
256
257 (*utf8_char) = c;
258 (*input) += len;
259 if (bytes_read) {
260 (*bytes_read) = len;
261 }
262 return LY_SUCCESS;
263}
264
Radek Krejci76c98012019-08-14 11:23:24 +0200265/**
266 * @brief Static table of the UTF8 characters lengths according to their first byte.
267 */
268static const unsigned char
269utf8_char_length_table[] = {
270 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
271 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
272 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
273 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
274 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
275 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
276 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
277 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
278 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
279 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
280 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
281 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
282 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
283 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
284 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
285 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
286};
287
288size_t
289ly_utf8len(const char *str, size_t bytes)
290{
291 size_t len;
292 const char *ptr;
293
294 for (len = 0, ptr = str; *ptr && (size_t)(ptr - str) < bytes; ++len, ptr += utf8_char_length_table[((unsigned char)(*ptr))]);
295 return len;
296}
297
Radek Krejcid972c252018-09-25 13:23:39 +0200298size_t
299LY_VCODE_INSTREXP_len(const char *str)
300{
301 size_t len = 0;
302 if (!str) {
303 return len;
304 } else if (!str[0]) {
305 return 1;
306 }
307 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len);
308 return len;
309}
310
Radek Krejcif345c012018-09-19 11:12:59 +0200311LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200312ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200313{
Radek Krejci86d106e2018-10-18 09:53:19 +0200314 struct stat sb;
315 long pagesize;
316 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200317
Radek Krejci86d106e2018-10-18 09:53:19 +0200318 assert(length);
319 assert(addr);
320 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200321
Radek Krejci86d106e2018-10-18 09:53:19 +0200322 if (fstat(fd, &sb) == -1) {
323 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
324 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200325 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200326 if (!S_ISREG(sb.st_mode)) {
327 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
328 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200329 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200330 if (!sb.st_size) {
331 *addr = NULL;
332 return LY_SUCCESS;
333 }
334 pagesize = sysconf(_SC_PAGESIZE);
335
336 m = sb.st_size % pagesize;
337 if (m && pagesize - m >= 1) {
338 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
339 *length = sb.st_size + 1;
340 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
341 } else {
342 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
343 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
344 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
345 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
346 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
347 * where the anonymous mapping starts. */
348 *length = sb.st_size + pagesize;
349 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
350 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
351 }
352 if (*addr == MAP_FAILED) {
353 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
354 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200355 }
356
Radek Krejcif345c012018-09-19 11:12:59 +0200357 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200358}
Michal Vasko841d1a92018-09-07 15:40:31 +0200359
Radek Krejci86d106e2018-10-18 09:53:19 +0200360LY_ERR
361ly_munmap(void *addr, size_t length)
362{
363 if (munmap(addr, length)) {
364 return LY_ESYS;
365 }
366 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200367}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100368
369LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200370ly_strcat(char **dest, const char *format, ...)
371{
372 va_list fp;
373 char *addition = NULL;
374 size_t len;
375
376 va_start(fp, format);
377 len = vasprintf(&addition, format, fp);
378 len += (*dest ? strlen(*dest) : 0) + 1;
379
380 if (*dest) {
381 *dest = ly_realloc(*dest, len);
382 if (!*dest) {
383 return LY_EMEM;
384 }
385 *dest = strcat(*dest, addition);
386 free(addition);
387 } else {
388 *dest = addition;
389 }
390
391 va_end(fp);
392 return LY_SUCCESS;
393}
394
395LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200396ly_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 +0100397{
398 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200399 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100400
Radek Krejci249973a2019-06-10 10:50:54 +0200401 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100402
403 /* convert to 64-bit integer, all the redundant characters are handled */
404 errno = 0;
405 strptr = NULL;
406
407 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200408 i = strtoll(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200409 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100410 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200411 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100412 return LY_EDENIED;
413 } else if (strptr && *strptr) {
414 while (isspace(*strptr)) {
415 ++strptr;
416 }
Radek Krejci249973a2019-06-10 10:50:54 +0200417 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100418 return LY_EVALID;
419 }
420 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200421
422 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100423 return LY_SUCCESS;
424}
425
426LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200427ly_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 +0100428{
429 char *strptr;
430 uint64_t u;
431
432 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
433
434 errno = 0;
435 strptr = NULL;
436 u = strtoull(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200437 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100438 return LY_EVALID;
439 } else if ((u > max) || (u && val_str[0] == '-')) {
440 return LY_EDENIED;
441 } else if (strptr && *strptr) {
442 while (isspace(*strptr)) {
443 ++strptr;
444 }
Radek Krejci249973a2019-06-10 10:50:54 +0200445 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100446 return LY_EVALID;
447 }
448 }
449
450 *ret = u;
451 return LY_SUCCESS;
452}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200453
454/**
455 * @brief Parse an identifier.
456 *
457 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
458 * identifier = (ALPHA / "_")
459 * *(ALPHA / DIGIT / "_" / "-" / ".")
460 *
461 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
462 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
463 */
464static LY_ERR
465lys_parse_id(const char **id)
466{
467 assert(id && *id);
468
469 if (!is_yangidentstartchar(**id)) {
470 return LY_EINVAL;
471 }
472 ++(*id);
473
474 while (is_yangidentchar(**id)) {
475 ++(*id);
476 }
477 return LY_SUCCESS;
478}
479
480LY_ERR
481ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
482{
483 assert(id && *id);
484 assert(prefix && prefix_len);
485 assert(name && name_len);
486
487 *prefix = *id;
488 *prefix_len = 0;
489 *name = NULL;
490 *name_len = 0;
491
492 LY_CHECK_RET(lys_parse_id(id));
493 if (**id == ':') {
494 /* there is prefix */
495 *prefix_len = *id - *prefix;
496 ++(*id);
497 *name = *id;
498
499 LY_CHECK_RET(lys_parse_id(id));
500 *name_len = *id - *name;
501 } else {
502 /* there is no prefix, so what we have as prefix now is actually the name */
503 *name = *prefix;
504 *name_len = *id - *name;
505 *prefix = NULL;
506 }
507
508 return LY_SUCCESS;
509}
510
511LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200512ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
513 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
514 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200515{
516 LY_ERR ret = LY_EVALID;
517 const char *in = *pred;
518 size_t offset = 1;
519 int expr = 0;
520 char quot;
521
522 assert(in[0] == '\[');
523
524 *prefix = *id = *value = NULL;
525 *prefix_len = *id_len = *value_len = 0;
526
527 /* leading *WSP */
528 for (; isspace(in[offset]); offset++);
529
530 if (isdigit(in[offset])) {
531 /* pos: "[" *WSP positive-integer-value *WSP "]" */
532 if (in[offset] == '0') {
533 /* zero */
534 *errmsg = "The position predicate cannot be zero.";
535 goto error;
536 }
537
538 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200539 *value = &in[offset++];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200540 for (; isdigit(in[offset]); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200541 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200542
543 } else if (in[offset] == '.') {
544 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
545 *id = &in[offset];
546 *id_len = 1;
547 offset++;
548 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200549 } else if (in[offset] == '-') {
550 /* typically negative value */
551 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
552 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200553 } else {
554 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
555 in = &in[offset];
556 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
557 *errmsg = "Invalid node-identifier.";
558 goto error;
559 }
Radek Krejci084289f2019-07-09 17:35:30 +0200560 if (format == LYD_XML && !(*prefix)) {
561 /* all node names MUST be qualified with explicit namespace prefix */
562 *errmsg = "Missing prefix of a node name.";
563 goto error;
564 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200565 offset = in - *pred;
566 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200567 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200568 }
569
570 if (expr) {
571 /* *WSP "=" *WSP quoted-string *WSP "]" */
572 for (; isspace(in[offset]); offset++);
573
574 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200575 if (expr == 1) {
576 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
577 } else { /* 2 */
578 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
579 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200580 goto error;
581 }
582 offset++;
583 for (; isspace(in[offset]); offset++);
584
585 /* quoted-string */
586 quot = in[offset++];
587 if (quot != '\'' && quot != '\"') {
588 *errmsg = "String value is not quoted.";
589 goto error;
590 }
591 *value = &in[offset];
Radek Krejci084289f2019-07-09 17:35:30 +0200592 for (;offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200593 if (in[offset] == quot) {
594 *value_len = &in[offset] - *value;
595 offset++;
596 } else {
597 *errmsg = "Value is not terminated quoted-string.";
598 goto error;
599 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200600 }
601
602 /* *WSP "]" */
603 for(; isspace(in[offset]); offset++);
604 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200605 if (expr == 0) {
606 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
607 } else if (expr == 1) {
608 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
609 } else { /* 2 */
610 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
611 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200612 goto error;
613 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200614 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200615
Radek Krejci10bfdf82019-06-10 14:08:13 +0200616 if (offset <= limit) {
617 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200618 return LY_SUCCESS;
619 }
620
621 /* we read after the limit */
622 *errmsg = "Predicate is incomplete.";
623 *prefix = *id = *value = NULL;
624 *prefix_len = *id_len = *value_len = 0;
625 offset = limit;
626 ret = LY_EINVAL;
627
628error:
629 *pred = &in[offset];
630 return ret;
631}