blob: fcde3d8fb3ac84c14ec9a3b0d10a97ad22c2053e [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 */
Radek Krejcif6a11002020-08-21 13:29:07 +0200324static const unsigned char utf8_char_length_table[] = {
Radek Krejci76c98012019-08-14 11:23:24 +0200325 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 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
337 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
338 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
339 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
340 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
341};
342
343size_t
344ly_utf8len(const char *str, size_t bytes)
345{
Radek Krejci1e008d22020-08-17 11:37:37 +0200346 size_t len = 0;
347 const char *ptr = str;
Radek Krejci76c98012019-08-14 11:23:24 +0200348
Radek Krejci1e008d22020-08-17 11:37:37 +0200349 while(*ptr && (size_t)(ptr - str) < bytes) {
350 ++len;
351 ptr += utf8_char_length_table[((unsigned char)(*ptr))];
352 }
Radek Krejci76c98012019-08-14 11:23:24 +0200353 return len;
354}
355
Radek Krejcid972c252018-09-25 13:23:39 +0200356size_t
357LY_VCODE_INSTREXP_len(const char *str)
358{
359 size_t len = 0;
360 if (!str) {
361 return len;
362 } else if (!str[0]) {
363 return 1;
364 }
Radek Krejci1e008d22020-08-17 11:37:37 +0200365 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
Radek Krejcid972c252018-09-25 13:23:39 +0200366 return len;
367}
368
Radek Krejcif345c012018-09-19 11:12:59 +0200369LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200370ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200371{
Radek Krejci86d106e2018-10-18 09:53:19 +0200372 struct stat sb;
373 long pagesize;
374 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200375
Radek Krejci86d106e2018-10-18 09:53:19 +0200376 assert(length);
377 assert(addr);
378 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200379
Radek Krejci86d106e2018-10-18 09:53:19 +0200380 if (fstat(fd, &sb) == -1) {
381 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
382 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200383 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200384 if (!S_ISREG(sb.st_mode)) {
385 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
386 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200387 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200388 if (!sb.st_size) {
389 *addr = NULL;
390 return LY_SUCCESS;
391 }
392 pagesize = sysconf(_SC_PAGESIZE);
393
394 m = sb.st_size % pagesize;
395 if (m && pagesize - m >= 1) {
396 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
397 *length = sb.st_size + 1;
398 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
399 } else {
400 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
401 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
402 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
403 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
404 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
405 * where the anonymous mapping starts. */
406 *length = sb.st_size + pagesize;
407 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
408 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
409 }
410 if (*addr == MAP_FAILED) {
411 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
412 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200413 }
414
Radek Krejcif345c012018-09-19 11:12:59 +0200415 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200416}
Michal Vasko841d1a92018-09-07 15:40:31 +0200417
Radek Krejci86d106e2018-10-18 09:53:19 +0200418LY_ERR
419ly_munmap(void *addr, size_t length)
420{
421 if (munmap(addr, length)) {
422 return LY_ESYS;
423 }
424 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200425}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100426
427LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200428ly_strcat(char **dest, const char *format, ...)
429{
430 va_list fp;
431 char *addition = NULL;
432 size_t len;
433
434 va_start(fp, format);
435 len = vasprintf(&addition, format, fp);
436 len += (*dest ? strlen(*dest) : 0) + 1;
437
438 if (*dest) {
439 *dest = ly_realloc(*dest, len);
440 if (!*dest) {
441 return LY_EMEM;
442 }
443 *dest = strcat(*dest, addition);
444 free(addition);
445 } else {
446 *dest = addition;
447 }
448
449 va_end(fp);
450 return LY_SUCCESS;
451}
452
453LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200454ly_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 +0100455{
456 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200457 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100458
Radek Krejci249973a2019-06-10 10:50:54 +0200459 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100460
461 /* convert to 64-bit integer, all the redundant characters are handled */
462 errno = 0;
463 strptr = NULL;
464
465 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200466 i = strtoll(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200467 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100468 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200469 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100470 return LY_EDENIED;
471 } else if (strptr && *strptr) {
472 while (isspace(*strptr)) {
473 ++strptr;
474 }
Radek Krejci249973a2019-06-10 10:50:54 +0200475 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100476 return LY_EVALID;
477 }
478 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200479
480 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100481 return LY_SUCCESS;
482}
483
484LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200485ly_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 +0100486{
487 char *strptr;
488 uint64_t u;
489
490 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
491
492 errno = 0;
493 strptr = NULL;
494 u = strtoull(val_str, &strptr, base);
Radek Krejci249973a2019-06-10 10:50:54 +0200495 if (errno || strptr == val_str) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100496 return LY_EVALID;
497 } else if ((u > max) || (u && val_str[0] == '-')) {
498 return LY_EDENIED;
499 } else if (strptr && *strptr) {
500 while (isspace(*strptr)) {
501 ++strptr;
502 }
Radek Krejci249973a2019-06-10 10:50:54 +0200503 if (*strptr && strptr < val_str + val_len) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100504 return LY_EVALID;
505 }
506 }
507
508 *ret = u;
509 return LY_SUCCESS;
510}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200511
512/**
513 * @brief Parse an identifier.
514 *
515 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
516 * identifier = (ALPHA / "_")
517 * *(ALPHA / DIGIT / "_" / "-" / ".")
518 *
519 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
520 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
521 */
522static LY_ERR
523lys_parse_id(const char **id)
524{
525 assert(id && *id);
526
527 if (!is_yangidentstartchar(**id)) {
528 return LY_EINVAL;
529 }
530 ++(*id);
531
532 while (is_yangidentchar(**id)) {
533 ++(*id);
534 }
535 return LY_SUCCESS;
536}
537
538LY_ERR
539ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
540{
541 assert(id && *id);
542 assert(prefix && prefix_len);
543 assert(name && name_len);
544
545 *prefix = *id;
546 *prefix_len = 0;
547 *name = NULL;
548 *name_len = 0;
549
550 LY_CHECK_RET(lys_parse_id(id));
551 if (**id == ':') {
552 /* there is prefix */
553 *prefix_len = *id - *prefix;
554 ++(*id);
555 *name = *id;
556
557 LY_CHECK_RET(lys_parse_id(id));
558 *name_len = *id - *name;
559 } else {
560 /* there is no prefix, so what we have as prefix now is actually the name */
561 *name = *prefix;
562 *name_len = *id - *name;
563 *prefix = NULL;
564 }
565
566 return LY_SUCCESS;
567}
568
569LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200570ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
571 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
572 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200573{
574 LY_ERR ret = LY_EVALID;
575 const char *in = *pred;
576 size_t offset = 1;
577 int expr = 0;
578 char quot;
579
580 assert(in[0] == '\[');
581
582 *prefix = *id = *value = NULL;
583 *prefix_len = *id_len = *value_len = 0;
584
585 /* leading *WSP */
Radek Krejci1e008d22020-08-17 11:37:37 +0200586 for (; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200587
588 if (isdigit(in[offset])) {
589 /* pos: "[" *WSP positive-integer-value *WSP "]" */
590 if (in[offset] == '0') {
591 /* zero */
592 *errmsg = "The position predicate cannot be zero.";
593 goto error;
594 }
595
596 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200597 *value = &in[offset++];
Radek Krejci1e008d22020-08-17 11:37:37 +0200598 for (; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200599 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200600
601 } else if (in[offset] == '.') {
602 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
603 *id = &in[offset];
604 *id_len = 1;
605 offset++;
606 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200607 } else if (in[offset] == '-') {
608 /* typically negative value */
609 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
610 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200611 } else {
612 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
613 in = &in[offset];
614 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
615 *errmsg = "Invalid node-identifier.";
616 goto error;
617 }
Radek Krejci084289f2019-07-09 17:35:30 +0200618 if (format == LYD_XML && !(*prefix)) {
619 /* all node names MUST be qualified with explicit namespace prefix */
620 *errmsg = "Missing prefix of a node name.";
621 goto error;
622 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200623 offset = in - *pred;
624 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200625 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200626 }
627
628 if (expr) {
629 /* *WSP "=" *WSP quoted-string *WSP "]" */
Radek Krejci1e008d22020-08-17 11:37:37 +0200630 for (; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200631
632 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200633 if (expr == 1) {
634 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
635 } else { /* 2 */
636 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
637 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200638 goto error;
639 }
640 offset++;
Radek Krejci1e008d22020-08-17 11:37:37 +0200641 for (; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200642
643 /* quoted-string */
644 quot = in[offset++];
645 if (quot != '\'' && quot != '\"') {
646 *errmsg = "String value is not quoted.";
647 goto error;
648 }
649 *value = &in[offset];
Radek Krejci1e008d22020-08-17 11:37:37 +0200650 for (;offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200651 if (in[offset] == quot) {
652 *value_len = &in[offset] - *value;
653 offset++;
654 } else {
655 *errmsg = "Value is not terminated quoted-string.";
656 goto error;
657 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200658 }
659
660 /* *WSP "]" */
Radek Krejci1e008d22020-08-17 11:37:37 +0200661 for(; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200662 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200663 if (expr == 0) {
664 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
665 } else if (expr == 1) {
666 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
667 } else { /* 2 */
668 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
669 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200670 goto error;
671 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200672 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200673
Radek Krejci10bfdf82019-06-10 14:08:13 +0200674 if (offset <= limit) {
675 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200676 return LY_SUCCESS;
677 }
678
679 /* we read after the limit */
680 *errmsg = "Predicate is incomplete.";
681 *prefix = *id = *value = NULL;
682 *prefix_len = *id_len = *value_len = 0;
683 offset = limit;
684 ret = LY_EINVAL;
685
686error:
687 *pred = &in[offset];
688 return ret;
689}