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