blob: b923bb379ce0e647e489ea8f6a426b745dd90221 [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) {
442 return LY_EMEM;
443 }
444 *dest = strcat(*dest, addition);
445 free(addition);
446 } else {
447 *dest = addition;
448 }
449
450 va_end(fp);
451 return LY_SUCCESS;
452}
453
454LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200455ly_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 +0100456{
457 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200458 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100459
Radek Krejci249973a2019-06-10 10:50:54 +0200460 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100461
462 /* convert to 64-bit integer, all the redundant characters are handled */
463 errno = 0;
464 strptr = NULL;
465
466 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200467 i = strtoll(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200468 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100469 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200470 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100471 return LY_EDENIED;
472 } else if (strptr && *strptr) {
473 while (isspace(*strptr)) {
474 ++strptr;
475 }
Michal Vasko69730152020-10-09 16:30:07 +0200476 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100477 return LY_EVALID;
478 }
479 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200480
481 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100482 return LY_SUCCESS;
483}
484
485LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200486ly_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 +0100487{
488 char *strptr;
489 uint64_t u;
490
491 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
492
493 errno = 0;
494 strptr = NULL;
495 u = strtoull(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200496 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100497 return LY_EVALID;
Michal Vasko69730152020-10-09 16:30:07 +0200498 } else if ((u > max) || (u && (val_str[0] == '-'))) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100499 return LY_EDENIED;
500 } else if (strptr && *strptr) {
501 while (isspace(*strptr)) {
502 ++strptr;
503 }
Michal Vasko69730152020-10-09 16:30:07 +0200504 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100505 return LY_EVALID;
506 }
507 }
508
509 *ret = u;
510 return LY_SUCCESS;
511}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200512
513/**
514 * @brief Parse an identifier.
515 *
516 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
517 * identifier = (ALPHA / "_")
518 * *(ALPHA / DIGIT / "_" / "-" / ".")
519 *
520 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
521 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
522 */
523static LY_ERR
524lys_parse_id(const char **id)
525{
526 assert(id && *id);
527
528 if (!is_yangidentstartchar(**id)) {
529 return LY_EINVAL;
530 }
531 ++(*id);
532
533 while (is_yangidentchar(**id)) {
534 ++(*id);
535 }
536 return LY_SUCCESS;
537}
538
539LY_ERR
540ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
541{
542 assert(id && *id);
543 assert(prefix && prefix_len);
544 assert(name && name_len);
545
546 *prefix = *id;
547 *prefix_len = 0;
548 *name = NULL;
549 *name_len = 0;
550
551 LY_CHECK_RET(lys_parse_id(id));
552 if (**id == ':') {
553 /* there is prefix */
554 *prefix_len = *id - *prefix;
555 ++(*id);
556 *name = *id;
557
558 LY_CHECK_RET(lys_parse_id(id));
559 *name_len = *id - *name;
560 } else {
561 /* there is no prefix, so what we have as prefix now is actually the name */
562 *name = *prefix;
563 *name_len = *id - *name;
564 *prefix = NULL;
565 }
566
567 return LY_SUCCESS;
568}
569
570LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200571ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
Radek Krejci0f969882020-08-21 16:56:47 +0200572 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
573 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200574{
575 LY_ERR ret = LY_EVALID;
576 const char *in = *pred;
577 size_t offset = 1;
Radek Krejci857189e2020-09-01 13:26:36 +0200578 uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
Radek Krejcib4a4a272019-06-10 12:44:52 +0200579 char quot;
580
581 assert(in[0] == '\[');
582
583 *prefix = *id = *value = NULL;
584 *prefix_len = *id_len = *value_len = 0;
585
586 /* leading *WSP */
Michal Vaskod989ba02020-08-24 10:59:24 +0200587 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200588
589 if (isdigit(in[offset])) {
590 /* pos: "[" *WSP positive-integer-value *WSP "]" */
591 if (in[offset] == '0') {
592 /* zero */
593 *errmsg = "The position predicate cannot be zero.";
594 goto error;
595 }
596
597 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200598 *value = &in[offset++];
Michal Vaskod989ba02020-08-24 10:59:24 +0200599 for ( ; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200600 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200601
602 } else if (in[offset] == '.') {
603 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
604 *id = &in[offset];
605 *id_len = 1;
606 offset++;
607 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200608 } else if (in[offset] == '-') {
609 /* typically negative value */
610 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
611 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200612 } else {
613 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
614 in = &in[offset];
615 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
616 *errmsg = "Invalid node-identifier.";
617 goto error;
618 }
Michal Vasko69730152020-10-09 16:30:07 +0200619 if ((format == LYD_XML) && !(*prefix)) {
Radek Krejci084289f2019-07-09 17:35:30 +0200620 /* all node names MUST be qualified with explicit namespace prefix */
621 *errmsg = "Missing prefix of a node name.";
622 goto error;
623 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200624 offset = in - *pred;
625 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200626 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200627 }
628
629 if (expr) {
630 /* *WSP "=" *WSP quoted-string *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200631 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200632
633 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200634 if (expr == 1) {
635 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
636 } else { /* 2 */
637 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
638 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200639 goto error;
640 }
641 offset++;
Michal Vaskod989ba02020-08-24 10:59:24 +0200642 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200643
644 /* quoted-string */
645 quot = in[offset++];
Michal Vasko69730152020-10-09 16:30:07 +0200646 if ((quot != '\'') && (quot != '\"')) {
Radek Krejcib4a4a272019-06-10 12:44:52 +0200647 *errmsg = "String value is not quoted.";
648 goto error;
649 }
650 *value = &in[offset];
Michal Vaskod989ba02020-08-24 10:59:24 +0200651 for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200652 if (in[offset] == quot) {
653 *value_len = &in[offset] - *value;
654 offset++;
655 } else {
656 *errmsg = "Value is not terminated quoted-string.";
657 goto error;
658 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200659 }
660
661 /* *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200662 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200663 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200664 if (expr == 0) {
665 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
666 } else if (expr == 1) {
667 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
668 } else { /* 2 */
669 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
670 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200671 goto error;
672 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200673 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200674
Radek Krejci10bfdf82019-06-10 14:08:13 +0200675 if (offset <= limit) {
676 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200677 return LY_SUCCESS;
678 }
679
680 /* we read after the limit */
681 *errmsg = "Predicate is incomplete.";
682 *prefix = *id = *value = NULL;
683 *prefix_len = *id_len = *value_len = 0;
684 offset = limit;
685 ret = LY_EINVAL;
686
687error:
688 *pred = &in[offset];
689 return ret;
690}