blob: e96f6dd92875fe2f996a1f0c7f3f432f31e96672 [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);
Michal Vasko69730152020-10-09 16:30:07 +0200181
182 if (!rc && (refstr[str_len] == '\0')) {
Radek Krejci7f9b6512019-09-18 13:11:09 +0200183 return 0;
184 } else {
185 return rc ? rc : 1;
186 }
187}
188
Radek Krejcib416be62018-10-01 14:51:45 +0200189LY_ERR
Michal Vaskob36053d2020-03-26 15:49:30 +0100190ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
Radek Krejcib416be62018-10-01 14:51:45 +0200191{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200192 uint32_t c, aux;
193 size_t len;
Radek Krejcib416be62018-10-01 14:51:45 +0200194
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
Michal Vasko69730152020-10-09 16:30:07 +0200206 if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200207 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;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200227 for (uint64_t i = 1; i <= 2; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200228 aux = (*input)[i];
229 if ((aux & 0xc0) != 0x80) {
230 return LY_EINVAL;
231 }
232
233 c = (c << 6) | (aux & 0x3f);
234 }
235
Michal Vasko69730152020-10-09 16:30:07 +0200236 if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200237 return LY_EINVAL;
238 }
239 } else if ((c & 0xf8) == 0xf0) {
240 /* four bytes character */
241 len = 4;
242
243 c &= 0x07;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200244 for (uint64_t i = 1; i <= 3; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200245 aux = (*input)[i];
246 if ((aux & 0xc0) != 0x80) {
247 return LY_EINVAL;
248 }
249
250 c = (c << 6) | (aux & 0x3f);
251 }
252
Michal Vasko69730152020-10-09 16:30:07 +0200253 if ((c < 0x1000) || (c > 0x10ffff)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200254 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 */
Michal Vasko69730152020-10-09 16:30:07 +0200273 if ((value < 0x20) &&
274 (value != 0x09) &&
275 (value != 0x0a) &&
276 (value != 0x0d)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200277 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) ||
Michal Vasko69730152020-10-09 16:30:07 +0200290 ((value >= 0xfdd0) && (value <= 0xfdef))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200291 /* 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
Michal Vaskod989ba02020-08-24 10:59:24 +0200349 while (*ptr && (size_t)(ptr - str) < bytes) {
Radek Krejci1e008d22020-08-17 11:37:37 +0200350 ++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;
Michal Vasko69730152020-10-09 16:30:07 +0200360
Radek Krejcid972c252018-09-25 13:23:39 +0200361 if (!str) {
362 return len;
363 } else if (!str[0]) {
364 return 1;
365 }
Radek Krejci1e008d22020-08-17 11:37:37 +0200366 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
Radek Krejcid972c252018-09-25 13:23:39 +0200367 return len;
368}
369
Radek Krejcif345c012018-09-19 11:12:59 +0200370LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200371ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200372{
Radek Krejci86d106e2018-10-18 09:53:19 +0200373 struct stat sb;
374 long pagesize;
375 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200376
Radek Krejci86d106e2018-10-18 09:53:19 +0200377 assert(length);
378 assert(addr);
379 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200380
Radek Krejci86d106e2018-10-18 09:53:19 +0200381 if (fstat(fd, &sb) == -1) {
382 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
383 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200384 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200385 if (!S_ISREG(sb.st_mode)) {
386 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
387 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200388 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200389 if (!sb.st_size) {
390 *addr = NULL;
391 return LY_SUCCESS;
392 }
393 pagesize = sysconf(_SC_PAGESIZE);
394
395 m = sb.st_size % pagesize;
Michal Vasko69730152020-10-09 16:30:07 +0200396 if (m && (pagesize - m >= 1)) {
Radek Krejci86d106e2018-10-18 09:53:19 +0200397 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
398 *length = sb.st_size + 1;
399 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
400 } else {
401 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
402 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
403 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
404 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
405 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
406 * where the anonymous mapping starts. */
407 *length = sb.st_size + pagesize;
408 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
409 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
410 }
411 if (*addr == MAP_FAILED) {
412 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
413 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200414 }
415
Radek Krejcif345c012018-09-19 11:12:59 +0200416 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200417}
Michal Vasko841d1a92018-09-07 15:40:31 +0200418
Radek Krejci86d106e2018-10-18 09:53:19 +0200419LY_ERR
420ly_munmap(void *addr, size_t length)
421{
422 if (munmap(addr, length)) {
423 return LY_ESYS;
424 }
425 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200426}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100427
428LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200429ly_strcat(char **dest, const char *format, ...)
430{
431 va_list fp;
432 char *addition = NULL;
433 size_t len;
434
435 va_start(fp, format);
436 len = vasprintf(&addition, format, fp);
437 len += (*dest ? strlen(*dest) : 0) + 1;
438
439 if (*dest) {
440 *dest = ly_realloc(*dest, len);
441 if (!*dest) {
Radek Krejci1cd812f2020-12-01 12:17:53 +0100442 va_end(fp);
Radek Krejci4546aa62019-07-15 16:53:32 +0200443 return LY_EMEM;
444 }
445 *dest = strcat(*dest, addition);
446 free(addition);
447 } else {
448 *dest = addition;
449 }
450
451 va_end(fp);
452 return LY_SUCCESS;
453}
454
455LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200456ly_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 +0100457{
458 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200459 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100460
Radek Krejci249973a2019-06-10 10:50:54 +0200461 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100462
463 /* convert to 64-bit integer, all the redundant characters are handled */
464 errno = 0;
465 strptr = NULL;
466
467 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200468 i = strtoll(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200469 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100470 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200471 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100472 return LY_EDENIED;
473 } else if (strptr && *strptr) {
474 while (isspace(*strptr)) {
475 ++strptr;
476 }
Michal Vasko69730152020-10-09 16:30:07 +0200477 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100478 return LY_EVALID;
479 }
480 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200481
482 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100483 return LY_SUCCESS;
484}
485
486LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200487ly_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 +0100488{
489 char *strptr;
490 uint64_t u;
491
492 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
493
494 errno = 0;
495 strptr = NULL;
496 u = strtoull(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200497 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100498 return LY_EVALID;
Michal Vasko69730152020-10-09 16:30:07 +0200499 } else if ((u > max) || (u && (val_str[0] == '-'))) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100500 return LY_EDENIED;
501 } else if (strptr && *strptr) {
502 while (isspace(*strptr)) {
503 ++strptr;
504 }
Michal Vasko69730152020-10-09 16:30:07 +0200505 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100506 return LY_EVALID;
507 }
508 }
509
510 *ret = u;
511 return LY_SUCCESS;
512}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200513
514/**
515 * @brief Parse an identifier.
516 *
517 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
518 * identifier = (ALPHA / "_")
519 * *(ALPHA / DIGIT / "_" / "-" / ".")
520 *
521 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
522 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
523 */
524static LY_ERR
525lys_parse_id(const char **id)
526{
527 assert(id && *id);
528
529 if (!is_yangidentstartchar(**id)) {
530 return LY_EINVAL;
531 }
532 ++(*id);
533
534 while (is_yangidentchar(**id)) {
535 ++(*id);
536 }
537 return LY_SUCCESS;
538}
539
540LY_ERR
541ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
542{
543 assert(id && *id);
544 assert(prefix && prefix_len);
545 assert(name && name_len);
546
547 *prefix = *id;
548 *prefix_len = 0;
549 *name = NULL;
550 *name_len = 0;
551
552 LY_CHECK_RET(lys_parse_id(id));
553 if (**id == ':') {
554 /* there is prefix */
555 *prefix_len = *id - *prefix;
556 ++(*id);
557 *name = *id;
558
559 LY_CHECK_RET(lys_parse_id(id));
560 *name_len = *id - *name;
561 } else {
562 /* there is no prefix, so what we have as prefix now is actually the name */
563 *name = *prefix;
564 *name_len = *id - *name;
565 *prefix = NULL;
566 }
567
568 return LY_SUCCESS;
569}
570
571LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200572ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
Radek Krejci0f969882020-08-21 16:56:47 +0200573 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
574 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200575{
576 LY_ERR ret = LY_EVALID;
577 const char *in = *pred;
578 size_t offset = 1;
Radek Krejci857189e2020-09-01 13:26:36 +0200579 uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
Radek Krejcib4a4a272019-06-10 12:44:52 +0200580 char quot;
581
Radek Krejci4607f542020-12-01 12:18:49 +0100582 assert(in[0] == '[');
Radek Krejcib4a4a272019-06-10 12:44:52 +0200583
584 *prefix = *id = *value = NULL;
585 *prefix_len = *id_len = *value_len = 0;
586
587 /* leading *WSP */
Michal Vaskod989ba02020-08-24 10:59:24 +0200588 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200589
590 if (isdigit(in[offset])) {
591 /* pos: "[" *WSP positive-integer-value *WSP "]" */
592 if (in[offset] == '0') {
593 /* zero */
594 *errmsg = "The position predicate cannot be zero.";
595 goto error;
596 }
597
598 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200599 *value = &in[offset++];
Michal Vaskod989ba02020-08-24 10:59:24 +0200600 for ( ; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200601 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200602
603 } else if (in[offset] == '.') {
604 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
605 *id = &in[offset];
606 *id_len = 1;
607 offset++;
608 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200609 } else if (in[offset] == '-') {
610 /* typically negative value */
611 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
612 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200613 } else {
614 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
615 in = &in[offset];
616 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
617 *errmsg = "Invalid node-identifier.";
618 goto error;
619 }
Michal Vasko69730152020-10-09 16:30:07 +0200620 if ((format == LYD_XML) && !(*prefix)) {
Radek Krejci084289f2019-07-09 17:35:30 +0200621 /* all node names MUST be qualified with explicit namespace prefix */
622 *errmsg = "Missing prefix of a node name.";
623 goto error;
624 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200625 offset = in - *pred;
626 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200627 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200628 }
629
630 if (expr) {
631 /* *WSP "=" *WSP quoted-string *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200632 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200633
634 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200635 if (expr == 1) {
636 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
637 } else { /* 2 */
638 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
639 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200640 goto error;
641 }
642 offset++;
Michal Vaskod989ba02020-08-24 10:59:24 +0200643 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200644
645 /* quoted-string */
646 quot = in[offset++];
Michal Vasko69730152020-10-09 16:30:07 +0200647 if ((quot != '\'') && (quot != '\"')) {
Radek Krejcib4a4a272019-06-10 12:44:52 +0200648 *errmsg = "String value is not quoted.";
649 goto error;
650 }
651 *value = &in[offset];
Michal Vaskod989ba02020-08-24 10:59:24 +0200652 for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200653 if (in[offset] == quot) {
654 *value_len = &in[offset] - *value;
655 offset++;
656 } else {
657 *errmsg = "Value is not terminated quoted-string.";
658 goto error;
659 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200660 }
661
662 /* *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200663 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200664 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200665 if (expr == 0) {
666 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
667 } else if (expr == 1) {
668 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
669 } else { /* 2 */
670 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
671 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200672 goto error;
673 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200674 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200675
Radek Krejci10bfdf82019-06-10 14:08:13 +0200676 if (offset <= limit) {
677 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200678 return LY_SUCCESS;
679 }
680
681 /* we read after the limit */
682 *errmsg = "Predicate is incomplete.";
683 *prefix = *id = *value = NULL;
684 *prefix_len = *id_len = *value_len = 0;
685 offset = limit;
686 ret = LY_EINVAL;
687
688error:
689 *pred = &in[offset];
690 return ret;
691}