blob: f3b1d6bfcf3fa105e793bfe084246c6313d81243 [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>
Jan Kundrátd31adc12022-07-07 21:36:15 +020022#include <fcntl.h>
Michal Vasko15dc9fa2021-05-03 14:33:05 +020023#include <inttypes.h>
Radek Krejci4546aa62019-07-15 16:53:32 +020024#include <stdarg.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020025#include <stdio.h>
Radek Krejci4546aa62019-07-15 16:53:32 +020026#include <stdlib.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020027#include <string.h>
Jan Kundrátf1960dc2021-12-12 03:12:23 +010028#ifndef _WIN32
Radek Krejci86d106e2018-10-18 09:53:19 +020029#include <sys/mman.h>
Jan Kundrátd31adc12022-07-07 21:36:15 +020030#else
31#include <io.h>
Jan Kundrátf1960dc2021-12-12 03:12:23 +010032#endif
Radek Krejci86d106e2018-10-18 09:53:19 +020033#include <sys/stat.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020034#include <unistd.h>
Michal Vasko841d1a92018-09-07 15:40:31 +020035
Radek Krejciaa45bda2020-07-20 07:43:38 +020036#include "compat.h"
Radek Krejcib4a4a272019-06-10 12:44:52 +020037#include "tree_schema_internal.h"
aPiecek704f8e92021-08-25 13:35:05 +020038#include "xml.h"
Michal Vasko1324b6c2018-09-07 11:16:23 +020039
40void *
41ly_realloc(void *ptr, size_t size)
42{
43 void *new_mem;
44
45 new_mem = realloc(ptr, size);
46 if (!new_mem) {
47 free(ptr);
48 }
49
50 return new_mem;
51}
Michal Vasko841d1a92018-09-07 15:40:31 +020052
Michal Vasko03ff5a72019-09-11 13:49:33 +020053char *
Radek Krejci1deb5be2020-08-26 16:43:36 +020054ly_strnchr(const char *s, int c, size_t len)
Michal Vasko03ff5a72019-09-11 13:49:33 +020055{
Michal Vaskob4d40d62021-05-04 11:42:44 +020056 for ( ; len && (*s != (char)c); ++s, --len) {}
57 return len ? (char *)s : NULL;
Michal Vasko03ff5a72019-09-11 13:49:33 +020058}
59
Radek Krejci7f9b6512019-09-18 13:11:09 +020060int
61ly_strncmp(const char *refstr, const char *str, size_t str_len)
62{
63 int rc = strncmp(refstr, str, str_len);
Michal Vasko69730152020-10-09 16:30:07 +020064
65 if (!rc && (refstr[str_len] == '\0')) {
Radek Krejci7f9b6512019-09-18 13:11:09 +020066 return 0;
67 } else {
68 return rc ? rc : 1;
69 }
70}
71
Michal Vasko15dc9fa2021-05-03 14:33:05 +020072LY_ERR
73ly_strntou8(const char *nptr, size_t len, uint8_t *ret)
74{
75 uint8_t num = 0, dig, dec_pow;
76
77 if (len > 3) {
78 /* overflow for sure */
79 return LY_EDENIED;
80 }
81
82 dec_pow = 1;
83 for ( ; len && isdigit(nptr[len - 1]); --len) {
84 dig = nptr[len - 1] - 48;
85
86 if (LY_OVERFLOW_MUL(UINT8_MAX, dig, dec_pow)) {
87 return LY_EDENIED;
88 }
89 dig *= dec_pow;
90
91 if (LY_OVERFLOW_ADD(UINT8_MAX, num, dig)) {
92 return LY_EDENIED;
93 }
94 num += dig;
95
96 dec_pow *= 10;
97 }
98
99 if (len) {
100 return LY_EVALID;
101 }
102 *ret = num;
103 return LY_SUCCESS;
104}
105
aPieceke3f828d2021-05-10 15:34:41 +0200106LY_ERR
107ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix, const char **str_next)
aPiecekf102d4d2021-03-30 12:18:38 +0200108{
109 const char *stop, *prefix;
aPieceke3f828d2021-05-10 15:34:41 +0200110 size_t bytes_read;
aPiecekf102d4d2021-03-30 12:18:38 +0200111 uint32_t c;
112 ly_bool prefix_found;
aPieceke3f828d2021-05-10 15:34:41 +0200113 LY_ERR ret = LY_SUCCESS;
aPiecekf102d4d2021-03-30 12:18:38 +0200114
aPieceke3f828d2021-05-10 15:34:41 +0200115 assert(len && is_prefix && str_next);
aPiecekf102d4d2021-03-30 12:18:38 +0200116
117#define IS_AT_END(PTR, STR_END) (STR_END ? PTR == STR_END : !(*PTR))
118
119 *str_next = NULL;
120 *is_prefix = 0;
aPieceke3f828d2021-05-10 15:34:41 +0200121 *len = 0;
aPiecekf102d4d2021-03-30 12:18:38 +0200122
123 if (!str_begin || !(*str_begin) || (str_begin == str_end)) {
124 return ret;
125 }
126
127 stop = str_begin;
128 prefix = NULL;
129 prefix_found = 0;
130
131 do {
132 /* look for the beginning of the YANG value */
aPieceke3f828d2021-05-10 15:34:41 +0200133 do {
134 LY_CHECK_RET(ly_getutf8(&stop, &c, &bytes_read));
135 } while (!is_xmlqnamestartchar(c) && !IS_AT_END(stop, str_end));
aPiecekf102d4d2021-03-30 12:18:38 +0200136
137 if (IS_AT_END(stop, str_end)) {
138 break;
139 }
140
141 /* maybe the prefix was found */
aPieceke3f828d2021-05-10 15:34:41 +0200142 prefix = stop - bytes_read;
aPiecekf102d4d2021-03-30 12:18:38 +0200143
144 /* look for the the end of the prefix */
aPieceke3f828d2021-05-10 15:34:41 +0200145 do {
146 LY_CHECK_RET(ly_getutf8(&stop, &c, &bytes_read));
147 } while (is_xmlqnamechar(c) && !IS_AT_END(stop, str_end));
aPiecekf102d4d2021-03-30 12:18:38 +0200148
149 prefix_found = c == ':' ? 1 : 0;
150
151 /* if it wasn't the prefix, keep looking */
152 } while (!IS_AT_END(stop, str_end) && !prefix_found);
153
154 if ((str_begin == prefix) && prefix_found) {
155 /* prefix found at the beginning of the input string */
156 *is_prefix = 1;
157 *str_next = IS_AT_END(stop, str_end) ? NULL : stop;
aPieceke3f828d2021-05-10 15:34:41 +0200158 *len = (stop - bytes_read) - str_begin;
aPiecekf102d4d2021-03-30 12:18:38 +0200159 } else if ((str_begin != prefix) && (prefix_found)) {
160 /* there is a some string before prefix */
161 *str_next = prefix;
aPieceke3f828d2021-05-10 15:34:41 +0200162 *len = prefix - str_begin;
aPiecekf102d4d2021-03-30 12:18:38 +0200163 } else {
164 /* no prefix found */
aPieceke3f828d2021-05-10 15:34:41 +0200165 *len = stop - str_begin;
aPiecekf102d4d2021-03-30 12:18:38 +0200166 }
167
168#undef IS_AT_END
169
170 return ret;
171}
172
Radek Krejcib416be62018-10-01 14:51:45 +0200173LY_ERR
Michal Vaskob36053d2020-03-26 15:49:30 +0100174ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
Radek Krejcib416be62018-10-01 14:51:45 +0200175{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200176 uint32_t c, aux;
177 size_t len;
Radek Krejcib416be62018-10-01 14:51:45 +0200178
Radek Krejcicc6a45c2019-05-13 10:16:14 +0200179 if (bytes_read) {
180 (*bytes_read) = 0;
181 }
182
Radek Krejcib416be62018-10-01 14:51:45 +0200183 c = (*input)[0];
184 LY_CHECK_RET(!c, LY_EINVAL);
185
186 if (!(c & 0x80)) {
187 /* one byte character */
188 len = 1;
189
Michal Vasko69730152020-10-09 16:30:07 +0200190 if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200191 return LY_EINVAL;
192 }
193 } else if ((c & 0xe0) == 0xc0) {
194 /* two bytes character */
195 len = 2;
196
197 aux = (*input)[1];
198 if ((aux & 0xc0) != 0x80) {
199 return LY_EINVAL;
200 }
201 c = ((c & 0x1f) << 6) | (aux & 0x3f);
202
203 if (c < 0x80) {
204 return LY_EINVAL;
205 }
206 } else if ((c & 0xf0) == 0xe0) {
207 /* three bytes character */
208 len = 3;
209
210 c &= 0x0f;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200211 for (uint64_t i = 1; i <= 2; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200212 aux = (*input)[i];
213 if ((aux & 0xc0) != 0x80) {
214 return LY_EINVAL;
215 }
216
217 c = (c << 6) | (aux & 0x3f);
218 }
219
Michal Vasko69730152020-10-09 16:30:07 +0200220 if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200221 return LY_EINVAL;
222 }
223 } else if ((c & 0xf8) == 0xf0) {
224 /* four bytes character */
225 len = 4;
226
227 c &= 0x07;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200228 for (uint64_t i = 1; i <= 3; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200229 aux = (*input)[i];
230 if ((aux & 0xc0) != 0x80) {
231 return LY_EINVAL;
232 }
233
234 c = (c << 6) | (aux & 0x3f);
235 }
236
Michal Vasko69730152020-10-09 16:30:07 +0200237 if ((c < 0x1000) || (c > 0x10ffff)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200238 return LY_EINVAL;
239 }
240 } else {
241 return LY_EINVAL;
242 }
243
244 (*utf8_char) = c;
245 (*input) += len;
246 if (bytes_read) {
247 (*bytes_read) = len;
248 }
249 return LY_SUCCESS;
250}
251
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200252LY_ERR
253ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
254{
255 if (value < 0x80) {
256 /* one byte character */
Michal Vasko69730152020-10-09 16:30:07 +0200257 if ((value < 0x20) &&
258 (value != 0x09) &&
259 (value != 0x0a) &&
260 (value != 0x0d)) {
Michal Vasko519097f2023-05-25 10:00:44 +0200261 /* valid UTF8 but not YANG string character */
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200262 return LY_EINVAL;
263 }
264
265 dst[0] = value;
266 (*bytes_written) = 1;
267 } else if (value < 0x800) {
268 /* two bytes character */
269 dst[0] = 0xc0 | (value >> 6);
270 dst[1] = 0x80 | (value & 0x3f);
271 (*bytes_written) = 2;
272 } else if (value < 0xfffe) {
273 /* three bytes character */
274 if (((value & 0xf800) == 0xd800) ||
Michal Vasko69730152020-10-09 16:30:07 +0200275 ((value >= 0xfdd0) && (value <= 0xfdef))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200276 /* exclude surrogate blocks %xD800-DFFF */
277 /* exclude noncharacters %xFDD0-FDEF */
278 return LY_EINVAL;
279 }
280
281 dst[0] = 0xe0 | (value >> 12);
282 dst[1] = 0x80 | ((value >> 6) & 0x3f);
283 dst[2] = 0x80 | (value & 0x3f);
284
285 (*bytes_written) = 3;
286 } else if (value < 0x10fffe) {
287 if ((value & 0xffe) == 0xffe) {
288 /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
289 * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
290 * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
291 return LY_EINVAL;
292 }
293 /* four bytes character */
294 dst[0] = 0xf0 | (value >> 18);
295 dst[1] = 0x80 | ((value >> 12) & 0x3f);
296 dst[2] = 0x80 | ((value >> 6) & 0x3f);
297 dst[3] = 0x80 | (value & 0x3f);
298
299 (*bytes_written) = 4;
300 } else {
301 return LY_EINVAL;
302 }
303 return LY_SUCCESS;
304}
305
Radek Krejci76c98012019-08-14 11:23:24 +0200306/**
307 * @brief Static table of the UTF8 characters lengths according to their first byte.
308 */
Radek Krejcif6a11002020-08-21 13:29:07 +0200309static const unsigned char utf8_char_length_table[] = {
Radek Krejci76c98012019-08-14 11:23:24 +0200310 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
311 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
312 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
313 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
314 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
315 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
316 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
317 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
318 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
319 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
320 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
321 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
322 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
323 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
324 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
325 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
326};
327
328size_t
329ly_utf8len(const char *str, size_t bytes)
330{
Radek Krejci1e008d22020-08-17 11:37:37 +0200331 size_t len = 0;
332 const char *ptr = str;
Radek Krejci76c98012019-08-14 11:23:24 +0200333
Michal Vaskob4d40d62021-05-04 11:42:44 +0200334 while (((size_t)(ptr - str) < bytes) && *ptr) {
Radek Krejci1e008d22020-08-17 11:37:37 +0200335 ++len;
336 ptr += utf8_char_length_table[((unsigned char)(*ptr))];
337 }
Radek Krejci76c98012019-08-14 11:23:24 +0200338 return len;
339}
340
Radek Krejcid972c252018-09-25 13:23:39 +0200341size_t
342LY_VCODE_INSTREXP_len(const char *str)
343{
344 size_t len = 0;
Michal Vasko69730152020-10-09 16:30:07 +0200345
Radek Krejcid972c252018-09-25 13:23:39 +0200346 if (!str) {
347 return len;
348 } else if (!str[0]) {
349 return 1;
350 }
Radek Krejci1e008d22020-08-17 11:37:37 +0200351 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
Radek Krejcid972c252018-09-25 13:23:39 +0200352 return len;
353}
354
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100355#ifdef HAVE_MMAP
Radek Krejcif345c012018-09-19 11:12:59 +0200356LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200357ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200358{
Radek Krejci86d106e2018-10-18 09:53:19 +0200359 struct stat sb;
360 long pagesize;
361 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200362
Radek Krejci86d106e2018-10-18 09:53:19 +0200363 assert(length);
364 assert(addr);
365 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200366
Radek Krejci86d106e2018-10-18 09:53:19 +0200367 if (fstat(fd, &sb) == -1) {
368 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
369 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200370 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200371 if (!S_ISREG(sb.st_mode)) {
372 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
373 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200374 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200375 if (!sb.st_size) {
376 *addr = NULL;
377 return LY_SUCCESS;
378 }
379 pagesize = sysconf(_SC_PAGESIZE);
380
381 m = sb.st_size % pagesize;
Michal Vasko69730152020-10-09 16:30:07 +0200382 if (m && (pagesize - m >= 1)) {
Radek Krejci86d106e2018-10-18 09:53:19 +0200383 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
384 *length = sb.st_size + 1;
385 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
386 } else {
387 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
388 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
389 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
390 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
391 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
392 * where the anonymous mapping starts. */
393 *length = sb.st_size + pagesize;
394 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
395 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
396 }
397 if (*addr == MAP_FAILED) {
398 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
399 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200400 }
401
Radek Krejcif345c012018-09-19 11:12:59 +0200402 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200403}
Michal Vasko841d1a92018-09-07 15:40:31 +0200404
Radek Krejci86d106e2018-10-18 09:53:19 +0200405LY_ERR
406ly_munmap(void *addr, size_t length)
407{
408 if (munmap(addr, length)) {
409 return LY_ESYS;
410 }
411 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200412}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100413
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100414#else
415
416LY_ERR
417ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
418{
419 struct stat sb;
420 size_t m;
421
422 assert(length);
423 assert(addr);
424 assert(fd >= 0);
425
Jan Kundrátd31adc12022-07-07 21:36:15 +0200426#if _WIN32
427 if (_setmode(fd, _O_BINARY) == -1) {
428 LOGERR(ctx, LY_ESYS, "Failed to switch the file descriptor to binary mode.", strerror(errno));
429 return LY_ESYS;
430 }
431#endif
432
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100433 if (fstat(fd, &sb) == -1) {
434 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
435 return LY_ESYS;
436 }
437 if (!S_ISREG(sb.st_mode)) {
438 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
439 return LY_ESYS;
440 }
441 if (!sb.st_size) {
442 *addr = NULL;
443 return LY_SUCCESS;
444 }
445 /* On Windows, the mman-win32 mmap() emulation uses CreateFileMapping and MapViewOfFile, and these functions
446 * do not allow mapping more than "length of file" bytes for PROT_READ. Remapping existing mappings is not allowed, either.
447 * At that point the path of least resistance is just reading the file in as-is. */
448 m = sb.st_size + 1;
449 char *buf = calloc(m, 1);
450
451 if (!buf) {
452 LOGERR(ctx, LY_ESYS, "ly_mmap: malloc() failed (%s).", strerror(errno));
453 }
454 *addr = buf;
455 *length = m;
456
457 lseek(fd, 0, SEEK_SET);
458 ssize_t to_read = m - 1;
459
460 while (to_read > 0) {
461 ssize_t n = read(fd, buf, to_read);
Michal Vasko2bf4af42023-01-04 12:08:38 +0100462
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100463 if (n == 0) {
464 return LY_SUCCESS;
465 } else if (n < 0) {
466 if (errno == EINTR) {
467 continue; // can I get this on Windows?
468 }
469 LOGERR(ctx, LY_ESYS, "ly_mmap: read() failed (%s).", strerror(errno));
470 }
471 to_read -= n;
472 buf += n;
473 }
474 return LY_SUCCESS;
475}
476
477LY_ERR
478ly_munmap(void *addr, size_t length)
479{
480 (void)length;
481 free(addr);
482 return LY_SUCCESS;
483}
484
485#endif
486
Radek Krejci4f28eda2018-11-12 11:46:16 +0100487LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200488ly_strcat(char **dest, const char *format, ...)
489{
490 va_list fp;
491 char *addition = NULL;
492 size_t len;
493
494 va_start(fp, format);
495 len = vasprintf(&addition, format, fp);
496 len += (*dest ? strlen(*dest) : 0) + 1;
497
498 if (*dest) {
499 *dest = ly_realloc(*dest, len);
500 if (!*dest) {
Radek Krejci1cd812f2020-12-01 12:17:53 +0100501 va_end(fp);
Radek Krejci4546aa62019-07-15 16:53:32 +0200502 return LY_EMEM;
503 }
504 *dest = strcat(*dest, addition);
505 free(addition);
506 } else {
507 *dest = addition;
508 }
509
510 va_end(fp);
511 return LY_SUCCESS;
512}
513
514LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200515ly_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 +0100516{
Michal Vaskob4d40d62021-05-04 11:42:44 +0200517 LY_ERR rc = LY_SUCCESS;
518 char *ptr, *str;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200519 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100520
Radek Krejci249973a2019-06-10 10:50:54 +0200521 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100522
Michal Vaskob4d40d62021-05-04 11:42:44 +0200523 /* duplicate the value */
524 str = strndup(val_str, val_len);
525 LY_CHECK_RET(!str, LY_EMEM);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100526
Michal Vaskob4d40d62021-05-04 11:42:44 +0200527 /* parse the value to avoid accessing following bytes */
528 errno = 0;
529 i = strtoll(str, &ptr, base);
530 if (errno || (ptr == str)) {
531 /* invalid string */
532 rc = LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200533 } else if ((i < min) || (i > max)) {
Michal Vaskob4d40d62021-05-04 11:42:44 +0200534 /* invalid number */
535 rc = LY_EDENIED;
536 } else if (*ptr) {
537 while (isspace(*ptr)) {
538 ++ptr;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100539 }
Michal Vaskob4d40d62021-05-04 11:42:44 +0200540 if (*ptr) {
541 /* invalid characters after some number */
542 rc = LY_EVALID;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100543 }
544 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200545
Michal Vaskob4d40d62021-05-04 11:42:44 +0200546 /* cleanup */
547 free(str);
548 if (!rc) {
549 *ret = i;
550 }
551 return rc;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100552}
553
554LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200555ly_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 +0100556{
Michal Vaskob4d40d62021-05-04 11:42:44 +0200557 LY_ERR rc = LY_SUCCESS;
558 char *ptr, *str;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100559 uint64_t u;
560
Michal Vaskob4d40d62021-05-04 11:42:44 +0200561 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100562
Michal Vaskob4d40d62021-05-04 11:42:44 +0200563 /* duplicate the value to avoid accessing following bytes */
564 str = strndup(val_str, val_len);
565 LY_CHECK_RET(!str, LY_EMEM);
566
567 /* parse the value */
Radek Krejci4f28eda2018-11-12 11:46:16 +0100568 errno = 0;
Michal Vaskob4d40d62021-05-04 11:42:44 +0200569 u = strtoull(str, &ptr, base);
570 if (errno || (ptr == str)) {
571 /* invalid string */
572 rc = LY_EVALID;
573 } else if ((u > max) || (u && (str[0] == '-'))) {
574 /* invalid number */
575 rc = LY_EDENIED;
576 } else if (*ptr) {
577 while (isspace(*ptr)) {
578 ++ptr;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100579 }
Michal Vaskob4d40d62021-05-04 11:42:44 +0200580 if (*ptr) {
581 /* invalid characters after some number */
582 rc = LY_EVALID;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100583 }
584 }
585
Michal Vaskob4d40d62021-05-04 11:42:44 +0200586 /* cleanup */
587 free(str);
588 if (!rc) {
589 *ret = u;
590 }
591 return rc;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100592}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200593
594/**
595 * @brief Parse an identifier.
596 *
597 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
598 * identifier = (ALPHA / "_")
599 * *(ALPHA / DIGIT / "_" / "-" / ".")
600 *
601 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
602 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
603 */
604static LY_ERR
605lys_parse_id(const char **id)
606{
607 assert(id && *id);
608
609 if (!is_yangidentstartchar(**id)) {
610 return LY_EINVAL;
611 }
612 ++(*id);
613
614 while (is_yangidentchar(**id)) {
615 ++(*id);
616 }
617 return LY_SUCCESS;
618}
619
620LY_ERR
621ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
622{
623 assert(id && *id);
624 assert(prefix && prefix_len);
625 assert(name && name_len);
626
627 *prefix = *id;
628 *prefix_len = 0;
629 *name = NULL;
630 *name_len = 0;
631
632 LY_CHECK_RET(lys_parse_id(id));
633 if (**id == ':') {
634 /* there is prefix */
635 *prefix_len = *id - *prefix;
636 ++(*id);
637 *name = *id;
638
639 LY_CHECK_RET(lys_parse_id(id));
640 *name_len = *id - *name;
641 } else {
642 /* there is no prefix, so what we have as prefix now is actually the name */
643 *name = *prefix;
644 *name_len = *id - *name;
645 *prefix = NULL;
646 }
647
648 return LY_SUCCESS;
649}
650
651LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200652ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
Radek Krejci0f969882020-08-21 16:56:47 +0200653 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
654 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200655{
656 LY_ERR ret = LY_EVALID;
657 const char *in = *pred;
658 size_t offset = 1;
Radek Krejci857189e2020-09-01 13:26:36 +0200659 uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
Radek Krejcib4a4a272019-06-10 12:44:52 +0200660 char quot;
661
Radek Krejci4607f542020-12-01 12:18:49 +0100662 assert(in[0] == '[');
Radek Krejcib4a4a272019-06-10 12:44:52 +0200663
664 *prefix = *id = *value = NULL;
665 *prefix_len = *id_len = *value_len = 0;
666
667 /* leading *WSP */
Michal Vaskod989ba02020-08-24 10:59:24 +0200668 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200669
670 if (isdigit(in[offset])) {
671 /* pos: "[" *WSP positive-integer-value *WSP "]" */
672 if (in[offset] == '0') {
673 /* zero */
674 *errmsg = "The position predicate cannot be zero.";
675 goto error;
676 }
677
678 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200679 *value = &in[offset++];
Michal Vaskod989ba02020-08-24 10:59:24 +0200680 for ( ; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200681 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200682
683 } else if (in[offset] == '.') {
684 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
685 *id = &in[offset];
686 *id_len = 1;
687 offset++;
688 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200689 } else if (in[offset] == '-') {
690 /* typically negative value */
691 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
692 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200693 } else {
694 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
695 in = &in[offset];
696 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
697 *errmsg = "Invalid node-identifier.";
698 goto error;
699 }
Michal Vasko69730152020-10-09 16:30:07 +0200700 if ((format == LYD_XML) && !(*prefix)) {
Radek Krejci084289f2019-07-09 17:35:30 +0200701 /* all node names MUST be qualified with explicit namespace prefix */
702 *errmsg = "Missing prefix of a node name.";
703 goto error;
704 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200705 offset = in - *pred;
706 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200707 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200708 }
709
710 if (expr) {
711 /* *WSP "=" *WSP quoted-string *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200712 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200713
714 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200715 if (expr == 1) {
716 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
717 } else { /* 2 */
718 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
719 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200720 goto error;
721 }
722 offset++;
Michal Vaskod989ba02020-08-24 10:59:24 +0200723 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200724
725 /* quoted-string */
726 quot = in[offset++];
Michal Vasko69730152020-10-09 16:30:07 +0200727 if ((quot != '\'') && (quot != '\"')) {
Radek Krejcib4a4a272019-06-10 12:44:52 +0200728 *errmsg = "String value is not quoted.";
729 goto error;
730 }
731 *value = &in[offset];
Michal Vaskod989ba02020-08-24 10:59:24 +0200732 for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200733 if (in[offset] == quot) {
734 *value_len = &in[offset] - *value;
735 offset++;
736 } else {
737 *errmsg = "Value is not terminated quoted-string.";
738 goto error;
739 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200740 }
741
742 /* *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200743 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200744 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200745 if (expr == 0) {
746 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
747 } else if (expr == 1) {
748 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
749 } else { /* 2 */
750 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
751 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200752 goto error;
753 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200754 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200755
Radek Krejci10bfdf82019-06-10 14:08:13 +0200756 if (offset <= limit) {
757 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200758 return LY_SUCCESS;
759 }
760
761 /* we read after the limit */
762 *errmsg = "Predicate is incomplete.";
763 *prefix = *id = *value = NULL;
764 *prefix_len = *id_len = *value_len = 0;
765 offset = limit;
766 ret = LY_EINVAL;
767
768error:
769 *pred = &in[offset];
770 return ret;
771}