blob: 7a8d83a22db6e499b75c63a5d8ec3f2748faf3f0 [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 Krejci50f0c6b2020-06-18 16:31:48 +0200268LY_ERR
269ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
270{
271 if (value < 0x80) {
272 /* one byte character */
273 if (value < 0x20 &&
274 value != 0x09 &&
275 value != 0x0a &&
276 value != 0x0d) {
277 return LY_EINVAL;
278 }
279
280 dst[0] = value;
281 (*bytes_written) = 1;
282 } else if (value < 0x800) {
283 /* two bytes character */
284 dst[0] = 0xc0 | (value >> 6);
285 dst[1] = 0x80 | (value & 0x3f);
286 (*bytes_written) = 2;
287 } else if (value < 0xfffe) {
288 /* three bytes character */
289 if (((value & 0xf800) == 0xd800) ||
290 (value >= 0xfdd0 && value <= 0xfdef)) {
291 /* exclude surrogate blocks %xD800-DFFF */
292 /* exclude noncharacters %xFDD0-FDEF */
293 return LY_EINVAL;
294 }
295
296 dst[0] = 0xe0 | (value >> 12);
297 dst[1] = 0x80 | ((value >> 6) & 0x3f);
298 dst[2] = 0x80 | (value & 0x3f);
299
300 (*bytes_written) = 3;
301 } else if (value < 0x10fffe) {
302 if ((value & 0xffe) == 0xffe) {
303 /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
304 * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
305 * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
306 return LY_EINVAL;
307 }
308 /* four bytes character */
309 dst[0] = 0xf0 | (value >> 18);
310 dst[1] = 0x80 | ((value >> 12) & 0x3f);
311 dst[2] = 0x80 | ((value >> 6) & 0x3f);
312 dst[3] = 0x80 | (value & 0x3f);
313
314 (*bytes_written) = 4;
315 } else {
316 return LY_EINVAL;
317 }
318 return LY_SUCCESS;
319}
320
Radek Krejci76c98012019-08-14 11:23:24 +0200321/**
322 * @brief Static table of the UTF8 characters lengths according to their first byte.
323 */
324static const unsigned char
325utf8_char_length_table[] = {
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 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
337 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
338 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
339 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
340 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
341 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
342};
343
344size_t
345ly_utf8len(const char *str, size_t bytes)
346{
347 size_t len;
348 const char *ptr;
349
350 for (len = 0, ptr = str; *ptr && (size_t)(ptr - str) < bytes; ++len, ptr += utf8_char_length_table[((unsigned char)(*ptr))]);
351 return len;
352}
353
Radek Krejcid972c252018-09-25 13:23:39 +0200354size_t
355LY_VCODE_INSTREXP_len(const char *str)
356{
357 size_t len = 0;
358 if (!str) {
359 return len;
360 } else if (!str[0]) {
361 return 1;
362 }
363 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len);
364 return len;
365}
366
Radek Krejcif345c012018-09-19 11:12:59 +0200367LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200368ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200369{
Radek Krejci86d106e2018-10-18 09:53:19 +0200370 struct stat sb;
371 long pagesize;
372 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200373
Radek Krejci86d106e2018-10-18 09:53:19 +0200374 assert(length);
375 assert(addr);
376 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200377
Radek Krejci86d106e2018-10-18 09:53:19 +0200378 if (fstat(fd, &sb) == -1) {
379 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
380 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200381 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200382 if (!S_ISREG(sb.st_mode)) {
383 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
384 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200385 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200386 if (!sb.st_size) {
387 *addr = NULL;
388 return LY_SUCCESS;
389 }
390 pagesize = sysconf(_SC_PAGESIZE);
391
392 m = sb.st_size % pagesize;
393 if (m && pagesize - m >= 1) {
394 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
395 *length = sb.st_size + 1;
396 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
397 } else {
398 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
399 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
400 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
401 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
402 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
403 * where the anonymous mapping starts. */
404 *length = sb.st_size + pagesize;
405 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
406 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
407 }
408 if (*addr == MAP_FAILED) {
409 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
410 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200411 }
412
Radek Krejcif345c012018-09-19 11:12:59 +0200413 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200414}
Michal Vasko841d1a92018-09-07 15:40:31 +0200415
Radek Krejci86d106e2018-10-18 09:53:19 +0200416LY_ERR
417ly_munmap(void *addr, size_t length)
418{
419 if (munmap(addr, length)) {
420 return LY_ESYS;
421 }
422 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200423}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100424
425LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200426ly_strcat(char **dest, const char *format, ...)
427{
428 va_list fp;
429 char *addition = NULL;
430 size_t len;
431
432 va_start(fp, format);
433 len = vasprintf(&addition, format, fp);
434 len += (*dest ? strlen(*dest) : 0) + 1;
435
436 if (*dest) {
437 *dest = ly_realloc(*dest, len);
438 if (!*dest) {
439 return LY_EMEM;
440 }
441 *dest = strcat(*dest, addition);
442 free(addition);
443 } else {
444 *dest = addition;
445 }
446
447 va_end(fp);
448 return LY_SUCCESS;
449}
450
451LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200452ly_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 +0100453{
454 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200455 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100456
Radek Krejci249973a2019-06-10 10:50:54 +0200457 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100458
459 /* convert to 64-bit integer, all the redundant characters are handled */
460 errno = 0;
461 strptr = NULL;
462
463 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200464 i = strtoll(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200465 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100466 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200467 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100468 return LY_EDENIED;
469 } else if (strptr && *strptr) {
470 while (isspace(*strptr)) {
471 ++strptr;
472 }
Radek Krejci249973a2019-06-10 10:50:54 +0200473 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100474 return LY_EVALID;
475 }
476 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200477
478 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100479 return LY_SUCCESS;
480}
481
482LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200483ly_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 +0100484{
485 char *strptr;
486 uint64_t u;
487
488 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
489
490 errno = 0;
491 strptr = NULL;
492 u = strtoull(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200493 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100494 return LY_EVALID;
495 } else if ((u > max) || (u && val_str[0] == '-')) {
496 return LY_EDENIED;
497 } else if (strptr && *strptr) {
498 while (isspace(*strptr)) {
499 ++strptr;
500 }
Radek Krejci249973a2019-06-10 10:50:54 +0200501 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100502 return LY_EVALID;
503 }
504 }
505
506 *ret = u;
507 return LY_SUCCESS;
508}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200509
510/**
511 * @brief Parse an identifier.
512 *
513 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
514 * identifier = (ALPHA / "_")
515 * *(ALPHA / DIGIT / "_" / "-" / ".")
516 *
517 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
518 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
519 */
520static LY_ERR
521lys_parse_id(const char **id)
522{
523 assert(id && *id);
524
525 if (!is_yangidentstartchar(**id)) {
526 return LY_EINVAL;
527 }
528 ++(*id);
529
530 while (is_yangidentchar(**id)) {
531 ++(*id);
532 }
533 return LY_SUCCESS;
534}
535
536LY_ERR
537ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
538{
539 assert(id && *id);
540 assert(prefix && prefix_len);
541 assert(name && name_len);
542
543 *prefix = *id;
544 *prefix_len = 0;
545 *name = NULL;
546 *name_len = 0;
547
548 LY_CHECK_RET(lys_parse_id(id));
549 if (**id == ':') {
550 /* there is prefix */
551 *prefix_len = *id - *prefix;
552 ++(*id);
553 *name = *id;
554
555 LY_CHECK_RET(lys_parse_id(id));
556 *name_len = *id - *name;
557 } else {
558 /* there is no prefix, so what we have as prefix now is actually the name */
559 *name = *prefix;
560 *name_len = *id - *name;
561 *prefix = NULL;
562 }
563
564 return LY_SUCCESS;
565}
566
567LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200568ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
569 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
570 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200571{
572 LY_ERR ret = LY_EVALID;
573 const char *in = *pred;
574 size_t offset = 1;
575 int expr = 0;
576 char quot;
577
578 assert(in[0] == '\[');
579
580 *prefix = *id = *value = NULL;
581 *prefix_len = *id_len = *value_len = 0;
582
583 /* leading *WSP */
584 for (; isspace(in[offset]); offset++);
585
586 if (isdigit(in[offset])) {
587 /* pos: "[" *WSP positive-integer-value *WSP "]" */
588 if (in[offset] == '0') {
589 /* zero */
590 *errmsg = "The position predicate cannot be zero.";
591 goto error;
592 }
593
594 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200595 *value = &in[offset++];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200596 for (; isdigit(in[offset]); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200597 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200598
599 } else if (in[offset] == '.') {
600 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
601 *id = &in[offset];
602 *id_len = 1;
603 offset++;
604 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200605 } else if (in[offset] == '-') {
606 /* typically negative value */
607 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
608 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200609 } else {
610 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
611 in = &in[offset];
612 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
613 *errmsg = "Invalid node-identifier.";
614 goto error;
615 }
Radek Krejci084289f2019-07-09 17:35:30 +0200616 if (format == LYD_XML && !(*prefix)) {
617 /* all node names MUST be qualified with explicit namespace prefix */
618 *errmsg = "Missing prefix of a node name.";
619 goto error;
620 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200621 offset = in - *pred;
622 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200623 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200624 }
625
626 if (expr) {
627 /* *WSP "=" *WSP quoted-string *WSP "]" */
628 for (; isspace(in[offset]); offset++);
629
630 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200631 if (expr == 1) {
632 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
633 } else { /* 2 */
634 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
635 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200636 goto error;
637 }
638 offset++;
639 for (; isspace(in[offset]); offset++);
640
641 /* quoted-string */
642 quot = in[offset++];
643 if (quot != '\'' && quot != '\"') {
644 *errmsg = "String value is not quoted.";
645 goto error;
646 }
647 *value = &in[offset];
Radek Krejci084289f2019-07-09 17:35:30 +0200648 for (;offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++);
Radek Krejci10bfdf82019-06-10 14:08:13 +0200649 if (in[offset] == quot) {
650 *value_len = &in[offset] - *value;
651 offset++;
652 } else {
653 *errmsg = "Value is not terminated quoted-string.";
654 goto error;
655 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200656 }
657
658 /* *WSP "]" */
659 for(; isspace(in[offset]); offset++);
660 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200661 if (expr == 0) {
662 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
663 } else if (expr == 1) {
664 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
665 } else { /* 2 */
666 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
667 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200668 goto error;
669 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200670 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200671
Radek Krejci10bfdf82019-06-10 14:08:13 +0200672 if (offset <= limit) {
673 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200674 return LY_SUCCESS;
675 }
676
677 /* we read after the limit */
678 *errmsg = "Predicate is incomplete.";
679 *prefix = *id = *value = NULL;
680 *prefix_len = *id_len = *value_len = 0;
681 offset = limit;
682 ret = LY_EINVAL;
683
684error:
685 *pred = &in[offset];
686 return ret;
687}