blob: b1ccd65533d841778567e34c8ab2aa535752a192 [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
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
16
Radek Krejcib7db73a2018-10-24 14:18:40 +020017#include "common.h"
Michal Vasko1324b6c2018-09-07 11:16:23 +020018
Radek Krejci86d106e2018-10-18 09:53:19 +020019#include <assert.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020020#include <ctype.h>
Radek Krejci4546aa62019-07-15 16:53:32 +020021#include <errno.h>
22#include <stdarg.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020023#include <stdio.h>
Radek Krejci4546aa62019-07-15 16:53:32 +020024#include <stdlib.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020025#include <string.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020026#include <sys/mman.h>
27#include <sys/stat.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020028#include <unistd.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020029
Radek Krejciaa45bda2020-07-20 07:43:38 +020030#include "compat.h"
Michal Vasko841d1a92018-09-07 15:40:31 +020031#include "tree_schema.h"
Radek Krejcib4a4a272019-06-10 12:44:52 +020032#include "tree_schema_internal.h"
Michal Vasko1324b6c2018-09-07 11:16:23 +020033
Radek Krejcic59bc972018-09-17 16:13:06 +020034const char *const ly_stmt_list[] = {
Radek Krejcid6b76452019-09-03 17:03:03 +020035 [LY_STMT_ACTION] = "action",
36 [LY_STMT_ANYDATA] = "anydata",
37 [LY_STMT_ANYXML] = "anyxml",
38 [LY_STMT_ARGUMENT] = "argument",
39 [LY_STMT_AUGMENT] = "augment",
40 [LY_STMT_BASE] = "base",
41 [LY_STMT_BELONGS_TO] = "belongs-to",
42 [LY_STMT_BIT] = "bit",
43 [LY_STMT_CASE] = "case",
44 [LY_STMT_CHOICE] = "choice",
45 [LY_STMT_CONFIG] = "config",
46 [LY_STMT_CONTACT] = "contact",
47 [LY_STMT_CONTAINER] = "container",
48 [LY_STMT_EXTENSION_INSTANCE] = "<extension-instance>",
49 [LY_STMT_DEFAULT] = "default",
50 [LY_STMT_DESCRIPTION] = "description",
51 [LY_STMT_DEVIATE] = "deviate",
52 [LY_STMT_DEVIATION] = "deviation",
53 [LY_STMT_ENUM] = "enum",
54 [LY_STMT_ERROR_APP_TAG] = "error-app-tag",
55 [LY_STMT_ERROR_MESSAGE] = "error-message",
56 [LY_STMT_EXTENSION] = "extension",
57 [LY_STMT_FEATURE] = "feature",
58 [LY_STMT_FRACTION_DIGITS] = "fraction-digits",
59 [LY_STMT_GROUPING] = "grouping",
60 [LY_STMT_IDENTITY] = "identity",
61 [LY_STMT_IF_FEATURE] = "if-feature",
62 [LY_STMT_IMPORT] = "import",
63 [LY_STMT_INCLUDE] = "include",
64 [LY_STMT_INPUT] = "input",
65 [LY_STMT_KEY] = "key",
66 [LY_STMT_LEAF] = "leaf",
67 [LY_STMT_LEAF_LIST] = "leaf-list",
68 [LY_STMT_LENGTH] = "length",
69 [LY_STMT_LIST] = "list",
70 [LY_STMT_MANDATORY] = "mandatory",
71 [LY_STMT_MAX_ELEMENTS] = "max-elements",
72 [LY_STMT_MIN_ELEMENTS] = "min-elements",
73 [LY_STMT_MODIFIER] = "modifier",
74 [LY_STMT_MODULE] = "module",
75 [LY_STMT_MUST] = "must",
76 [LY_STMT_NAMESPACE] = "namespace",
77 [LY_STMT_NOTIFICATION] = "notification",
78 [LY_STMT_ORDERED_BY] = "ordered-by",
79 [LY_STMT_ORGANIZATION] = "organization",
80 [LY_STMT_OUTPUT] = "output",
81 [LY_STMT_PATH] = "path",
82 [LY_STMT_PATTERN] = "pattern",
83 [LY_STMT_POSITION] = "position",
84 [LY_STMT_PREFIX] = "prefix",
85 [LY_STMT_PRESENCE] = "presence",
86 [LY_STMT_RANGE] = "range",
87 [LY_STMT_REFERENCE] = "reference",
88 [LY_STMT_REFINE] = "refine",
89 [LY_STMT_REQUIRE_INSTANCE] = "require-instance",
90 [LY_STMT_REVISION] = "revision",
91 [LY_STMT_REVISION_DATE] = "revision-date",
92 [LY_STMT_RPC] = "rpc",
93 [LY_STMT_STATUS] = "status",
94 [LY_STMT_SUBMODULE] = "submodule",
95 [LY_STMT_TYPE] = "type",
96 [LY_STMT_TYPEDEF] = "typedef",
97 [LY_STMT_UNIQUE] = "unique",
98 [LY_STMT_UNITS] = "units",
99 [LY_STMT_USES] = "uses",
100 [LY_STMT_VALUE] = "value",
101 [LY_STMT_WHEN] = "when",
102 [LY_STMT_YANG_VERSION] = "yang-version",
103 [LY_STMT_YIN_ELEMENT] = "yin-element",
104 [LY_STMT_SYNTAX_SEMICOLON] = ";",
105 [LY_STMT_SYNTAX_LEFT_BRACE] = "{",
106 [LY_STMT_SYNTAX_RIGHT_BRACE] = "}",
107 [LY_STMT_ARG_TEXT] = "text",
108 [LY_STMT_ARG_VALUE] = "value",
Radek Krejcic59bc972018-09-17 16:13:06 +0200109};
110
111const char *const lyext_substmt_list[] = {
112 [LYEXT_SUBSTMT_ARGUMENT] = "argument",
113 [LYEXT_SUBSTMT_BASE] = "base",
114 [LYEXT_SUBSTMT_BELONGSTO] = "belongs-to",
115 [LYEXT_SUBSTMT_CONTACT] = "contact",
116 [LYEXT_SUBSTMT_DEFAULT] = "default",
117 [LYEXT_SUBSTMT_DESCRIPTION] = "description",
118 [LYEXT_SUBSTMT_ERRTAG] = "error-app-tag",
119 [LYEXT_SUBSTMT_ERRMSG] = "error-message",
120 [LYEXT_SUBSTMT_KEY] = "key",
121 [LYEXT_SUBSTMT_NAMESPACE] = "namespace",
122 [LYEXT_SUBSTMT_ORGANIZATION] = "organization",
123 [LYEXT_SUBSTMT_PATH] = "path",
124 [LYEXT_SUBSTMT_PREFIX] = "prefix",
125 [LYEXT_SUBSTMT_PRESENCE] = "presence",
126 [LYEXT_SUBSTMT_REFERENCE] = "reference",
127 [LYEXT_SUBSTMT_REVISIONDATE] = "revision-date",
128 [LYEXT_SUBSTMT_UNITS] = "units",
129 [LYEXT_SUBSTMT_VALUE] = "value",
130 [LYEXT_SUBSTMT_VERSION] = "yang-version",
131 [LYEXT_SUBSTMT_MODIFIER] = "modifier",
132 [LYEXT_SUBSTMT_REQINSTANCE] = "require-instance",
133 [LYEXT_SUBSTMT_YINELEM] = "yin-element",
134 [LYEXT_SUBSTMT_CONFIG] = "config",
135 [LYEXT_SUBSTMT_MANDATORY] = "mandatory",
136 [LYEXT_SUBSTMT_ORDEREDBY] = "ordered-by",
137 [LYEXT_SUBSTMT_STATUS] = "status",
138 [LYEXT_SUBSTMT_FRACDIGITS] = "fraction-digits",
139 [LYEXT_SUBSTMT_MAX] = "max-elements",
140 [LYEXT_SUBSTMT_MIN] = "min-elements",
141 [LYEXT_SUBSTMT_POSITION] = "position",
142 [LYEXT_SUBSTMT_UNIQUE] = "unique",
143 [LYEXT_SUBSTMT_IFFEATURE] = "if-feature",
144};
145
146const char *const ly_devmod_list[] = {
147 [LYS_DEV_NOT_SUPPORTED] = "not-supported",
148 [LYS_DEV_ADD] = "add",
149 [LYS_DEV_DELETE] = "delete",
150 [LYS_DEV_REPLACE] = "replace",
151};
152
Michal Vasko1324b6c2018-09-07 11:16:23 +0200153void *
154ly_realloc(void *ptr, size_t size)
155{
156 void *new_mem;
157
158 new_mem = realloc(ptr, size);
159 if (!new_mem) {
160 free(ptr);
161 }
162
163 return new_mem;
164}
Michal Vasko841d1a92018-09-07 15:40:31 +0200165
Michal Vasko03ff5a72019-09-11 13:49:33 +0200166char *
167ly_strnchr(const char *s, int c, unsigned int len)
168{
169 for (; *s != (char)c; ++s, --len) {
170 if ((*s == '\0') || (!len)) {
171 return NULL;
172 }
173 }
174 return (char *)s;
175}
176
Radek Krejci7f9b6512019-09-18 13:11:09 +0200177int
178ly_strncmp(const char *refstr, const char *str, size_t str_len)
179{
180 int rc = strncmp(refstr, str, str_len);
181 if (!rc && refstr[str_len] == '\0') {
182 return 0;
183 } else {
184 return rc ? rc : 1;
185 }
186}
187
Radek Krejcib416be62018-10-01 14:51:45 +0200188LY_ERR
Michal Vaskob36053d2020-03-26 15:49:30 +0100189ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
Radek Krejcib416be62018-10-01 14:51:45 +0200190{
Michal Vaskob36053d2020-03-26 15:49:30 +0100191 uint32_t c, len;
Radek Krejcib416be62018-10-01 14:51:45 +0200192 int aux;
193 int i;
194
Radek Krejcicc6a45c2019-05-13 10:16:14 +0200195 if (bytes_read) {
196 (*bytes_read) = 0;
197 }
198
Radek Krejcib416be62018-10-01 14:51:45 +0200199 c = (*input)[0];
200 LY_CHECK_RET(!c, LY_EINVAL);
201
202 if (!(c & 0x80)) {
203 /* one byte character */
204 len = 1;
205
206 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
207 return LY_EINVAL;
208 }
209 } else if ((c & 0xe0) == 0xc0) {
210 /* two bytes character */
211 len = 2;
212
213 aux = (*input)[1];
214 if ((aux & 0xc0) != 0x80) {
215 return LY_EINVAL;
216 }
217 c = ((c & 0x1f) << 6) | (aux & 0x3f);
218
219 if (c < 0x80) {
220 return LY_EINVAL;
221 }
222 } else if ((c & 0xf0) == 0xe0) {
223 /* three bytes character */
224 len = 3;
225
226 c &= 0x0f;
227 for (i = 1; i <= 2; i++) {
228 aux = (*input)[i];
229 if ((aux & 0xc0) != 0x80) {
230 return LY_EINVAL;
231 }
232
233 c = (c << 6) | (aux & 0x3f);
234 }
235
236 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
237 return LY_EINVAL;
238 }
239 } else if ((c & 0xf8) == 0xf0) {
240 /* four bytes character */
241 len = 4;
242
243 c &= 0x07;
244 for (i = 1; i <= 3; i++) {
245 aux = (*input)[i];
246 if ((aux & 0xc0) != 0x80) {
247 return LY_EINVAL;
248 }
249
250 c = (c << 6) | (aux & 0x3f);
251 }
252
253 if (c < 0x1000 || c > 0x10ffff) {
254 return LY_EINVAL;
255 }
256 } else {
257 return LY_EINVAL;
258 }
259
260 (*utf8_char) = c;
261 (*input) += len;
262 if (bytes_read) {
263 (*bytes_read) = len;
264 }
265 return LY_SUCCESS;
266}
267
Radek Krejci76c98012019-08-14 11:23:24 +0200268/**
269 * @brief Static table of the UTF8 characters lengths according to their first byte.
270 */
271static const unsigned char
272utf8_char_length_table[] = {
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 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
283 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
284 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
285 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
286 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
287 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
288 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
289};
290
291size_t
292ly_utf8len(const char *str, size_t bytes)
293{
294 size_t len;
295 const char *ptr;
296
297 for (len = 0, ptr = str; *ptr && (size_t)(ptr - str) < bytes; ++len, ptr += utf8_char_length_table[((unsigned char)(*ptr))]);
298 return len;
299}
300
Radek Krejcid972c252018-09-25 13:23:39 +0200301size_t
302LY_VCODE_INSTREXP_len(const char *str)
303{
304 size_t len = 0;
305 if (!str) {
306 return len;
307 } else if (!str[0]) {
308 return 1;
309 }
310 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len);
311 return len;
312}
313
Radek Krejcif345c012018-09-19 11:12:59 +0200314LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200315ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200316{
Radek Krejci86d106e2018-10-18 09:53:19 +0200317 struct stat sb;
318 long pagesize;
319 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200320
Radek Krejci86d106e2018-10-18 09:53:19 +0200321 assert(length);
322 assert(addr);
323 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200324
Radek Krejci86d106e2018-10-18 09:53:19 +0200325 if (fstat(fd, &sb) == -1) {
326 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
327 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200328 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200329 if (!S_ISREG(sb.st_mode)) {
330 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
331 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200332 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200333 if (!sb.st_size) {
334 *addr = NULL;
335 return LY_SUCCESS;
336 }
337 pagesize = sysconf(_SC_PAGESIZE);
338
339 m = sb.st_size % pagesize;
340 if (m && pagesize - m >= 1) {
341 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
342 *length = sb.st_size + 1;
343 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
344 } else {
345 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
346 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
347 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
348 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
349 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
350 * where the anonymous mapping starts. */
351 *length = sb.st_size + pagesize;
352 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
353 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
354 }
355 if (*addr == MAP_FAILED) {
356 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
357 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200358 }
359
Radek Krejcif345c012018-09-19 11:12:59 +0200360 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200361}
Michal Vasko841d1a92018-09-07 15:40:31 +0200362
Radek Krejci86d106e2018-10-18 09:53:19 +0200363LY_ERR
364ly_munmap(void *addr, size_t length)
365{
366 if (munmap(addr, length)) {
367 return LY_ESYS;
368 }
369 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200370}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100371
372LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200373ly_strcat(char **dest, const char *format, ...)
374{
375 va_list fp;
376 char *addition = NULL;
377 size_t len;
378
379 va_start(fp, format);
380 len = vasprintf(&addition, format, fp);
381 len += (*dest ? strlen(*dest) : 0) + 1;
382
383 if (*dest) {
384 *dest = ly_realloc(*dest, len);
385 if (!*dest) {
386 return LY_EMEM;
387 }
388 *dest = strcat(*dest, addition);
389 free(addition);
390 } else {
391 *dest = addition;
392 }
393
394 va_end(fp);
395 return LY_SUCCESS;
396}
397
398LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200399ly_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 +0100400{
401 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200402 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100403
Radek Krejci249973a2019-06-10 10:50:54 +0200404 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100405
406 /* convert to 64-bit integer, all the redundant characters are handled */
407 errno = 0;
408 strptr = NULL;
409
410 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200411 i = strtoll(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200412 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100413 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200414 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100415 return LY_EDENIED;
416 } else if (strptr && *strptr) {
417 while (isspace(*strptr)) {
418 ++strptr;
419 }
Radek Krejci249973a2019-06-10 10:50:54 +0200420 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100421 return LY_EVALID;
422 }
423 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200424
425 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100426 return LY_SUCCESS;
427}
428
429LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200430ly_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 +0100431{
432 char *strptr;
433 uint64_t u;
434
435 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
436
437 errno = 0;
438 strptr = NULL;
439 u = strtoull(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200440 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100441 return LY_EVALID;
442 } else if ((u > max) || (u && val_str[0] == '-')) {
443 return LY_EDENIED;
444 } else if (strptr && *strptr) {
445 while (isspace(*strptr)) {
446 ++strptr;
447 }
Radek Krejci249973a2019-06-10 10:50:54 +0200448 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100449 return LY_EVALID;
450 }
451 }
452
453 *ret = u;
454 return LY_SUCCESS;
455}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200456
457/**
458 * @brief Parse an identifier.
459 *
460 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
461 * identifier = (ALPHA / "_")
462 * *(ALPHA / DIGIT / "_" / "-" / ".")
463 *
464 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
465 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
466 */
467static LY_ERR
468lys_parse_id(const char **id)
469{
470 assert(id && *id);
471
472 if (!is_yangidentstartchar(**id)) {
473 return LY_EINVAL;
474 }
475 ++(*id);
476
477 while (is_yangidentchar(**id)) {
478 ++(*id);
479 }
480 return LY_SUCCESS;
481}
482
483LY_ERR
484ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
485{
486 assert(id && *id);
487 assert(prefix && prefix_len);
488 assert(name && name_len);
489
490 *prefix = *id;
491 *prefix_len = 0;
492 *name = NULL;
493 *name_len = 0;
494
495 LY_CHECK_RET(lys_parse_id(id));
496 if (**id == ':') {
497 /* there is prefix */
498 *prefix_len = *id - *prefix;
499 ++(*id);
500 *name = *id;
501
502 LY_CHECK_RET(lys_parse_id(id));
503 *name_len = *id - *name;
504 } else {
505 /* there is no prefix, so what we have as prefix now is actually the name */
506 *name = *prefix;
507 *name_len = *id - *name;
508 *prefix = NULL;
509 }
510
511 return LY_SUCCESS;
512}
513
514LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200515ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
516 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
517 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200518{
519 LY_ERR ret = LY_EVALID;
520 const char *in = *pred;
521 size_t offset = 1;
522 int expr = 0;
523 char quot;
524
525 assert(in[0] == '\[');
526
527 *prefix = *id = *value = NULL;
528 *prefix_len = *id_len = *value_len = 0;
529
530 /* leading *WSP */
531 for (; isspace(in[offset]); offset++);
532
533 if (isdigit(in[offset])) {
534 /* pos: "[" *WSP positive-integer-value *WSP "]" */
535 if (in[offset] == '0') {
536 /* zero */
537 *errmsg = "The position predicate cannot be zero.";
538 goto error;
539 }
540
541 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200542 *value = &in[offset++];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200543 for (; isdigit(in[offset]); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200544 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200545
546 } else if (in[offset] == '.') {
547 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
548 *id = &in[offset];
549 *id_len = 1;
550 offset++;
551 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200552 } else if (in[offset] == '-') {
553 /* typically negative value */
554 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
555 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200556 } else {
557 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
558 in = &in[offset];
559 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
560 *errmsg = "Invalid node-identifier.";
561 goto error;
562 }
Radek Krejci084289f2019-07-09 17:35:30 +0200563 if (format == LYD_XML && !(*prefix)) {
564 /* all node names MUST be qualified with explicit namespace prefix */
565 *errmsg = "Missing prefix of a node name.";
566 goto error;
567 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200568 offset = in - *pred;
569 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200570 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200571 }
572
573 if (expr) {
574 /* *WSP "=" *WSP quoted-string *WSP "]" */
575 for (; isspace(in[offset]); offset++);
576
577 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200578 if (expr == 1) {
579 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
580 } else { /* 2 */
581 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
582 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200583 goto error;
584 }
585 offset++;
586 for (; isspace(in[offset]); offset++);
587
588 /* quoted-string */
589 quot = in[offset++];
590 if (quot != '\'' && quot != '\"') {
591 *errmsg = "String value is not quoted.";
592 goto error;
593 }
594 *value = &in[offset];
Radek Krejci084289f2019-07-09 17:35:30 +0200595 for (;offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200596 if (in[offset] == quot) {
597 *value_len = &in[offset] - *value;
598 offset++;
599 } else {
600 *errmsg = "Value is not terminated quoted-string.";
601 goto error;
602 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200603 }
604
605 /* *WSP "]" */
606 for(; isspace(in[offset]); offset++);
607 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200608 if (expr == 0) {
609 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
610 } else if (expr == 1) {
611 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
612 } else { /* 2 */
613 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
614 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200615 goto error;
616 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200617 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200618
Radek Krejci10bfdf82019-06-10 14:08:13 +0200619 if (offset <= limit) {
620 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200621 return LY_SUCCESS;
622 }
623
624 /* we read after the limit */
625 *errmsg = "Predicate is incomplete.";
626 *prefix = *id = *value = NULL;
627 *prefix_len = *id_len = *value_len = 0;
628 offset = limit;
629 ret = LY_EINVAL;
630
631error:
632 *pred = &in[offset];
633 return ret;
634}