blob: 163119b30ebe8e0762aa54afa613a9297f89fa8d [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
Michal Vasko22df3f02020-08-24 13:29:22 +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
Michal Vasko22df3f02020-08-24 13:29:22 +0200111const char * const lyext_substmt_list[] = {
Radek Krejcic59bc972018-09-17 16:13:06 +0200112 [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
Michal Vasko22df3f02020-08-24 13:29:22 +0200146const char * const ly_devmod_list[] = {
Radek Krejcic59bc972018-09-17 16:13:06 +0200147 [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 *
Radek Krejci1deb5be2020-08-26 16:43:36 +0200167ly_strnchr(const char *s, int c, size_t len)
Michal Vasko03ff5a72019-09-11 13:49:33 +0200168{
Michal Vaskod989ba02020-08-24 10:59:24 +0200169 for ( ; *s != (char)c; ++s, --len) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200170 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{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200191 uint32_t c, aux;
192 size_t len;
Radek Krejcib416be62018-10-01 14:51:45 +0200193
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;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200226 for (uint64_t i = 1; i <= 2; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200227 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;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200243 for (uint64_t i = 1; i <= 3; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200244 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 Krejci50f0c6b2020-06-18 16:31:48 +0200267LY_ERR
268ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
269{
270 if (value < 0x80) {
271 /* one byte character */
272 if (value < 0x20 &&
273 value != 0x09 &&
274 value != 0x0a &&
275 value != 0x0d) {
276 return LY_EINVAL;
277 }
278
279 dst[0] = value;
280 (*bytes_written) = 1;
281 } else if (value < 0x800) {
282 /* two bytes character */
283 dst[0] = 0xc0 | (value >> 6);
284 dst[1] = 0x80 | (value & 0x3f);
285 (*bytes_written) = 2;
286 } else if (value < 0xfffe) {
287 /* three bytes character */
288 if (((value & 0xf800) == 0xd800) ||
289 (value >= 0xfdd0 && value <= 0xfdef)) {
290 /* exclude surrogate blocks %xD800-DFFF */
291 /* exclude noncharacters %xFDD0-FDEF */
292 return LY_EINVAL;
293 }
294
295 dst[0] = 0xe0 | (value >> 12);
296 dst[1] = 0x80 | ((value >> 6) & 0x3f);
297 dst[2] = 0x80 | (value & 0x3f);
298
299 (*bytes_written) = 3;
300 } else if (value < 0x10fffe) {
301 if ((value & 0xffe) == 0xffe) {
302 /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
303 * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
304 * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
305 return LY_EINVAL;
306 }
307 /* four bytes character */
308 dst[0] = 0xf0 | (value >> 18);
309 dst[1] = 0x80 | ((value >> 12) & 0x3f);
310 dst[2] = 0x80 | ((value >> 6) & 0x3f);
311 dst[3] = 0x80 | (value & 0x3f);
312
313 (*bytes_written) = 4;
314 } else {
315 return LY_EINVAL;
316 }
317 return LY_SUCCESS;
318}
319
Radek Krejci76c98012019-08-14 11:23:24 +0200320/**
321 * @brief Static table of the UTF8 characters lengths according to their first byte.
322 */
Radek Krejcif6a11002020-08-21 13:29:07 +0200323static const unsigned char utf8_char_length_table[] = {
Radek Krejci76c98012019-08-14 11:23:24 +0200324 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
325 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
326 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
327 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
328 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
329 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
330 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
331 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
332 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
333 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
334 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
335 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
336 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
337 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
338 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
339 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
340};
341
342size_t
343ly_utf8len(const char *str, size_t bytes)
344{
Radek Krejci1e008d22020-08-17 11:37:37 +0200345 size_t len = 0;
346 const char *ptr = str;
Radek Krejci76c98012019-08-14 11:23:24 +0200347
Michal Vaskod989ba02020-08-24 10:59:24 +0200348 while (*ptr && (size_t)(ptr - str) < bytes) {
Radek Krejci1e008d22020-08-17 11:37:37 +0200349 ++len;
350 ptr += utf8_char_length_table[((unsigned char)(*ptr))];
351 }
Radek Krejci76c98012019-08-14 11:23:24 +0200352 return len;
353}
354
Radek Krejcid972c252018-09-25 13:23:39 +0200355size_t
356LY_VCODE_INSTREXP_len(const char *str)
357{
358 size_t len = 0;
359 if (!str) {
360 return len;
361 } else if (!str[0]) {
362 return 1;
363 }
Radek Krejci1e008d22020-08-17 11:37:37 +0200364 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
Radek Krejcid972c252018-09-25 13:23:39 +0200365 return len;
366}
367
Radek Krejcif345c012018-09-19 11:12:59 +0200368LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200369ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200370{
Radek Krejci86d106e2018-10-18 09:53:19 +0200371 struct stat sb;
372 long pagesize;
373 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200374
Radek Krejci86d106e2018-10-18 09:53:19 +0200375 assert(length);
376 assert(addr);
377 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200378
Radek Krejci86d106e2018-10-18 09:53:19 +0200379 if (fstat(fd, &sb) == -1) {
380 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
381 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200382 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200383 if (!S_ISREG(sb.st_mode)) {
384 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
385 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200386 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200387 if (!sb.st_size) {
388 *addr = NULL;
389 return LY_SUCCESS;
390 }
391 pagesize = sysconf(_SC_PAGESIZE);
392
393 m = sb.st_size % pagesize;
394 if (m && pagesize - m >= 1) {
395 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
396 *length = sb.st_size + 1;
397 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
398 } else {
399 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
400 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
401 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
402 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
403 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
404 * where the anonymous mapping starts. */
405 *length = sb.st_size + pagesize;
406 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
407 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
408 }
409 if (*addr == MAP_FAILED) {
410 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
411 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200412 }
413
Radek Krejcif345c012018-09-19 11:12:59 +0200414 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200415}
Michal Vasko841d1a92018-09-07 15:40:31 +0200416
Radek Krejci86d106e2018-10-18 09:53:19 +0200417LY_ERR
418ly_munmap(void *addr, size_t length)
419{
420 if (munmap(addr, length)) {
421 return LY_ESYS;
422 }
423 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200424}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100425
426LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200427ly_strcat(char **dest, const char *format, ...)
428{
429 va_list fp;
430 char *addition = NULL;
431 size_t len;
432
433 va_start(fp, format);
434 len = vasprintf(&addition, format, fp);
435 len += (*dest ? strlen(*dest) : 0) + 1;
436
437 if (*dest) {
438 *dest = ly_realloc(*dest, len);
439 if (!*dest) {
440 return LY_EMEM;
441 }
442 *dest = strcat(*dest, addition);
443 free(addition);
444 } else {
445 *dest = addition;
446 }
447
448 va_end(fp);
449 return LY_SUCCESS;
450}
451
452LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200453ly_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 +0100454{
455 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200456 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100457
Radek Krejci249973a2019-06-10 10:50:54 +0200458 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100459
460 /* convert to 64-bit integer, all the redundant characters are handled */
461 errno = 0;
462 strptr = NULL;
463
464 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200465 i = strtoll(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200466 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100467 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200468 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100469 return LY_EDENIED;
470 } else if (strptr && *strptr) {
471 while (isspace(*strptr)) {
472 ++strptr;
473 }
Radek Krejci249973a2019-06-10 10:50:54 +0200474 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100475 return LY_EVALID;
476 }
477 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200478
479 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100480 return LY_SUCCESS;
481}
482
483LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200484ly_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 +0100485{
486 char *strptr;
487 uint64_t u;
488
489 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
490
491 errno = 0;
492 strptr = NULL;
493 u = strtoull(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200494 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100495 return LY_EVALID;
496 } else if ((u > max) || (u && val_str[0] == '-')) {
497 return LY_EDENIED;
498 } else if (strptr && *strptr) {
499 while (isspace(*strptr)) {
500 ++strptr;
501 }
Radek Krejci249973a2019-06-10 10:50:54 +0200502 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100503 return LY_EVALID;
504 }
505 }
506
507 *ret = u;
508 return LY_SUCCESS;
509}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200510
511/**
512 * @brief Parse an identifier.
513 *
514 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
515 * identifier = (ALPHA / "_")
516 * *(ALPHA / DIGIT / "_" / "-" / ".")
517 *
518 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
519 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
520 */
521static LY_ERR
522lys_parse_id(const char **id)
523{
524 assert(id && *id);
525
526 if (!is_yangidentstartchar(**id)) {
527 return LY_EINVAL;
528 }
529 ++(*id);
530
531 while (is_yangidentchar(**id)) {
532 ++(*id);
533 }
534 return LY_SUCCESS;
535}
536
537LY_ERR
538ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
539{
540 assert(id && *id);
541 assert(prefix && prefix_len);
542 assert(name && name_len);
543
544 *prefix = *id;
545 *prefix_len = 0;
546 *name = NULL;
547 *name_len = 0;
548
549 LY_CHECK_RET(lys_parse_id(id));
550 if (**id == ':') {
551 /* there is prefix */
552 *prefix_len = *id - *prefix;
553 ++(*id);
554 *name = *id;
555
556 LY_CHECK_RET(lys_parse_id(id));
557 *name_len = *id - *name;
558 } else {
559 /* there is no prefix, so what we have as prefix now is actually the name */
560 *name = *prefix;
561 *name_len = *id - *name;
562 *prefix = NULL;
563 }
564
565 return LY_SUCCESS;
566}
567
568LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200569ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
Radek Krejci0f969882020-08-21 16:56:47 +0200570 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
571 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200572{
573 LY_ERR ret = LY_EVALID;
574 const char *in = *pred;
575 size_t offset = 1;
Radek Krejci857189e2020-09-01 13:26:36 +0200576 uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
Radek Krejcib4a4a272019-06-10 12:44:52 +0200577 char quot;
578
579 assert(in[0] == '\[');
580
581 *prefix = *id = *value = NULL;
582 *prefix_len = *id_len = *value_len = 0;
583
584 /* leading *WSP */
Michal Vaskod989ba02020-08-24 10:59:24 +0200585 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200586
587 if (isdigit(in[offset])) {
588 /* pos: "[" *WSP positive-integer-value *WSP "]" */
589 if (in[offset] == '0') {
590 /* zero */
591 *errmsg = "The position predicate cannot be zero.";
592 goto error;
593 }
594
595 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200596 *value = &in[offset++];
Michal Vaskod989ba02020-08-24 10:59:24 +0200597 for ( ; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200598 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200599
600 } else if (in[offset] == '.') {
601 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
602 *id = &in[offset];
603 *id_len = 1;
604 offset++;
605 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200606 } else if (in[offset] == '-') {
607 /* typically negative value */
608 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
609 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200610 } else {
611 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
612 in = &in[offset];
613 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
614 *errmsg = "Invalid node-identifier.";
615 goto error;
616 }
Radek Krejci084289f2019-07-09 17:35:30 +0200617 if (format == LYD_XML && !(*prefix)) {
618 /* all node names MUST be qualified with explicit namespace prefix */
619 *errmsg = "Missing prefix of a node name.";
620 goto error;
621 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200622 offset = in - *pred;
623 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200624 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200625 }
626
627 if (expr) {
628 /* *WSP "=" *WSP quoted-string *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200629 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200630
631 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200632 if (expr == 1) {
633 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
634 } else { /* 2 */
635 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
636 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200637 goto error;
638 }
639 offset++;
Michal Vaskod989ba02020-08-24 10:59:24 +0200640 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200641
642 /* quoted-string */
643 quot = in[offset++];
644 if (quot != '\'' && quot != '\"') {
645 *errmsg = "String value is not quoted.";
646 goto error;
647 }
648 *value = &in[offset];
Michal Vaskod989ba02020-08-24 10:59:24 +0200649 for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200650 if (in[offset] == quot) {
651 *value_len = &in[offset] - *value;
652 offset++;
653 } else {
654 *errmsg = "Value is not terminated quoted-string.";
655 goto error;
656 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200657 }
658
659 /* *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200660 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200661 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200662 if (expr == 0) {
663 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
664 } else if (expr == 1) {
665 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
666 } else { /* 2 */
667 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
668 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200669 goto error;
670 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200671 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200672
Radek Krejci10bfdf82019-06-10 14:08:13 +0200673 if (offset <= limit) {
674 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200675 return LY_SUCCESS;
676 }
677
678 /* we read after the limit */
679 *errmsg = "Predicate is incomplete.";
680 *prefix = *id = *value = NULL;
681 *prefix_len = *id_len = *value_len = 0;
682 offset = limit;
683 ret = LY_EINVAL;
684
685error:
686 *pred = &in[offset];
687 return ret;
688}