blob: c976de4ad129b21deea72a57788c564d6054326b [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
179 c = (*input)[0];
Radek Krejcib416be62018-10-01 14:51:45 +0200180
181 if (!(c & 0x80)) {
182 /* one byte character */
183 len = 1;
184
Michal Vasko69730152020-10-09 16:30:07 +0200185 if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200186 return LY_EINVAL;
187 }
188 } else if ((c & 0xe0) == 0xc0) {
189 /* two bytes character */
190 len = 2;
191
192 aux = (*input)[1];
193 if ((aux & 0xc0) != 0x80) {
194 return LY_EINVAL;
195 }
196 c = ((c & 0x1f) << 6) | (aux & 0x3f);
197
198 if (c < 0x80) {
199 return LY_EINVAL;
200 }
201 } else if ((c & 0xf0) == 0xe0) {
202 /* three bytes character */
203 len = 3;
204
205 c &= 0x0f;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200206 for (uint64_t i = 1; i <= 2; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200207 aux = (*input)[i];
208 if ((aux & 0xc0) != 0x80) {
209 return LY_EINVAL;
210 }
211
212 c = (c << 6) | (aux & 0x3f);
213 }
214
Michal Vasko69730152020-10-09 16:30:07 +0200215 if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200216 return LY_EINVAL;
217 }
218 } else if ((c & 0xf8) == 0xf0) {
219 /* four bytes character */
220 len = 4;
221
222 c &= 0x07;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200223 for (uint64_t i = 1; i <= 3; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200224 aux = (*input)[i];
225 if ((aux & 0xc0) != 0x80) {
226 return LY_EINVAL;
227 }
228
229 c = (c << 6) | (aux & 0x3f);
230 }
231
Michal Vasko69730152020-10-09 16:30:07 +0200232 if ((c < 0x1000) || (c > 0x10ffff)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200233 return LY_EINVAL;
234 }
235 } else {
Michal Vaskobe9136a2023-10-06 15:25:20 +0200236 if (bytes_read) {
237 (*bytes_read) = 0;
238 }
Radek Krejcib416be62018-10-01 14:51:45 +0200239 return LY_EINVAL;
240 }
241
242 (*utf8_char) = c;
243 (*input) += len;
244 if (bytes_read) {
245 (*bytes_read) = len;
246 }
247 return LY_SUCCESS;
248}
249
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200250LY_ERR
251ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
252{
253 if (value < 0x80) {
254 /* one byte character */
Michal Vasko69730152020-10-09 16:30:07 +0200255 if ((value < 0x20) &&
256 (value != 0x09) &&
257 (value != 0x0a) &&
258 (value != 0x0d)) {
Michal Vasko519097f2023-05-25 10:00:44 +0200259 /* valid UTF8 but not YANG string character */
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200260 return LY_EINVAL;
261 }
262
263 dst[0] = value;
264 (*bytes_written) = 1;
265 } else if (value < 0x800) {
266 /* two bytes character */
267 dst[0] = 0xc0 | (value >> 6);
268 dst[1] = 0x80 | (value & 0x3f);
269 (*bytes_written) = 2;
270 } else if (value < 0xfffe) {
271 /* three bytes character */
272 if (((value & 0xf800) == 0xd800) ||
Michal Vasko69730152020-10-09 16:30:07 +0200273 ((value >= 0xfdd0) && (value <= 0xfdef))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200274 /* exclude surrogate blocks %xD800-DFFF */
275 /* exclude noncharacters %xFDD0-FDEF */
276 return LY_EINVAL;
277 }
278
279 dst[0] = 0xe0 | (value >> 12);
280 dst[1] = 0x80 | ((value >> 6) & 0x3f);
281 dst[2] = 0x80 | (value & 0x3f);
282
283 (*bytes_written) = 3;
284 } else if (value < 0x10fffe) {
285 if ((value & 0xffe) == 0xffe) {
286 /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
287 * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
288 * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
289 return LY_EINVAL;
290 }
291 /* four bytes character */
292 dst[0] = 0xf0 | (value >> 18);
293 dst[1] = 0x80 | ((value >> 12) & 0x3f);
294 dst[2] = 0x80 | ((value >> 6) & 0x3f);
295 dst[3] = 0x80 | (value & 0x3f);
296
297 (*bytes_written) = 4;
298 } else {
299 return LY_EINVAL;
300 }
301 return LY_SUCCESS;
302}
303
Radek Krejci76c98012019-08-14 11:23:24 +0200304/**
305 * @brief Static table of the UTF8 characters lengths according to their first byte.
306 */
Radek Krejcif6a11002020-08-21 13:29:07 +0200307static const unsigned char utf8_char_length_table[] = {
Radek Krejci76c98012019-08-14 11:23:24 +0200308 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
309 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
310 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 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
321 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
322 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
323 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
324};
325
326size_t
327ly_utf8len(const char *str, size_t bytes)
328{
Radek Krejci1e008d22020-08-17 11:37:37 +0200329 size_t len = 0;
330 const char *ptr = str;
Radek Krejci76c98012019-08-14 11:23:24 +0200331
Michal Vaskob4d40d62021-05-04 11:42:44 +0200332 while (((size_t)(ptr - str) < bytes) && *ptr) {
Radek Krejci1e008d22020-08-17 11:37:37 +0200333 ++len;
334 ptr += utf8_char_length_table[((unsigned char)(*ptr))];
335 }
Radek Krejci76c98012019-08-14 11:23:24 +0200336 return len;
337}
338
Michal Vasko7b3a00e2023-08-09 11:58:03 +0200339int
Radek Krejcid972c252018-09-25 13:23:39 +0200340LY_VCODE_INSTREXP_len(const char *str)
341{
Michal Vasko7b3a00e2023-08-09 11:58:03 +0200342 int len = 0;
Michal Vasko69730152020-10-09 16:30:07 +0200343
Radek Krejcid972c252018-09-25 13:23:39 +0200344 if (!str) {
345 return len;
346 } else if (!str[0]) {
347 return 1;
348 }
Radek Krejci1e008d22020-08-17 11:37:37 +0200349 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
Radek Krejcid972c252018-09-25 13:23:39 +0200350 return len;
351}
352
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100353#ifdef HAVE_MMAP
Radek Krejcif345c012018-09-19 11:12:59 +0200354LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200355ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200356{
Radek Krejci86d106e2018-10-18 09:53:19 +0200357 struct stat sb;
358 long pagesize;
359 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200360
Radek Krejci86d106e2018-10-18 09:53:19 +0200361 assert(length);
362 assert(addr);
363 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200364
Radek Krejci86d106e2018-10-18 09:53:19 +0200365 if (fstat(fd, &sb) == -1) {
366 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
367 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200368 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200369 if (!S_ISREG(sb.st_mode)) {
370 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
371 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200372 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200373 if (!sb.st_size) {
374 *addr = NULL;
375 return LY_SUCCESS;
376 }
377 pagesize = sysconf(_SC_PAGESIZE);
378
379 m = sb.st_size % pagesize;
Michal Vasko69730152020-10-09 16:30:07 +0200380 if (m && (pagesize - m >= 1)) {
Radek Krejci86d106e2018-10-18 09:53:19 +0200381 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
382 *length = sb.st_size + 1;
383 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
384 } else {
385 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
386 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
387 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
388 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
389 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
390 * where the anonymous mapping starts. */
391 *length = sb.st_size + pagesize;
392 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
393 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
394 }
395 if (*addr == MAP_FAILED) {
396 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
397 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200398 }
399
Radek Krejcif345c012018-09-19 11:12:59 +0200400 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200401}
Michal Vasko841d1a92018-09-07 15:40:31 +0200402
Radek Krejci86d106e2018-10-18 09:53:19 +0200403LY_ERR
404ly_munmap(void *addr, size_t length)
405{
406 if (munmap(addr, length)) {
407 return LY_ESYS;
408 }
409 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200410}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100411
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100412#else
413
414LY_ERR
415ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
416{
417 struct stat sb;
418 size_t m;
419
420 assert(length);
421 assert(addr);
422 assert(fd >= 0);
423
Jan Kundrátd31adc12022-07-07 21:36:15 +0200424#if _WIN32
425 if (_setmode(fd, _O_BINARY) == -1) {
426 LOGERR(ctx, LY_ESYS, "Failed to switch the file descriptor to binary mode.", strerror(errno));
427 return LY_ESYS;
428 }
429#endif
430
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100431 if (fstat(fd, &sb) == -1) {
432 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
433 return LY_ESYS;
434 }
435 if (!S_ISREG(sb.st_mode)) {
436 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
437 return LY_ESYS;
438 }
439 if (!sb.st_size) {
440 *addr = NULL;
441 return LY_SUCCESS;
442 }
443 /* On Windows, the mman-win32 mmap() emulation uses CreateFileMapping and MapViewOfFile, and these functions
444 * do not allow mapping more than "length of file" bytes for PROT_READ. Remapping existing mappings is not allowed, either.
445 * At that point the path of least resistance is just reading the file in as-is. */
446 m = sb.st_size + 1;
447 char *buf = calloc(m, 1);
448
449 if (!buf) {
450 LOGERR(ctx, LY_ESYS, "ly_mmap: malloc() failed (%s).", strerror(errno));
451 }
452 *addr = buf;
453 *length = m;
454
455 lseek(fd, 0, SEEK_SET);
456 ssize_t to_read = m - 1;
457
458 while (to_read > 0) {
459 ssize_t n = read(fd, buf, to_read);
Michal Vasko2bf4af42023-01-04 12:08:38 +0100460
Jan Kundrátf1960dc2021-12-12 03:12:23 +0100461 if (n == 0) {
462 return LY_SUCCESS;
463 } else if (n < 0) {
464 if (errno == EINTR) {
465 continue; // can I get this on Windows?
466 }
467 LOGERR(ctx, LY_ESYS, "ly_mmap: read() failed (%s).", strerror(errno));
468 }
469 to_read -= n;
470 buf += n;
471 }
472 return LY_SUCCESS;
473}
474
475LY_ERR
476ly_munmap(void *addr, size_t length)
477{
478 (void)length;
479 free(addr);
480 return LY_SUCCESS;
481}
482
483#endif
484
Radek Krejci4f28eda2018-11-12 11:46:16 +0100485LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200486ly_strcat(char **dest, const char *format, ...)
487{
488 va_list fp;
489 char *addition = NULL;
490 size_t len;
491
492 va_start(fp, format);
493 len = vasprintf(&addition, format, fp);
494 len += (*dest ? strlen(*dest) : 0) + 1;
495
496 if (*dest) {
497 *dest = ly_realloc(*dest, len);
498 if (!*dest) {
Radek Krejci1cd812f2020-12-01 12:17:53 +0100499 va_end(fp);
Radek Krejci4546aa62019-07-15 16:53:32 +0200500 return LY_EMEM;
501 }
502 *dest = strcat(*dest, addition);
503 free(addition);
504 } else {
505 *dest = addition;
506 }
507
508 va_end(fp);
509 return LY_SUCCESS;
510}
511
512LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200513ly_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 +0100514{
Michal Vaskob4d40d62021-05-04 11:42:44 +0200515 LY_ERR rc = LY_SUCCESS;
516 char *ptr, *str;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200517 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100518
Radek Krejci249973a2019-06-10 10:50:54 +0200519 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100520
Michal Vaskob4d40d62021-05-04 11:42:44 +0200521 /* duplicate the value */
522 str = strndup(val_str, val_len);
523 LY_CHECK_RET(!str, LY_EMEM);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100524
Michal Vaskob4d40d62021-05-04 11:42:44 +0200525 /* parse the value to avoid accessing following bytes */
526 errno = 0;
527 i = strtoll(str, &ptr, base);
528 if (errno || (ptr == str)) {
529 /* invalid string */
530 rc = LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200531 } else if ((i < min) || (i > max)) {
Michal Vaskob4d40d62021-05-04 11:42:44 +0200532 /* invalid number */
533 rc = LY_EDENIED;
534 } else if (*ptr) {
535 while (isspace(*ptr)) {
536 ++ptr;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100537 }
Michal Vaskob4d40d62021-05-04 11:42:44 +0200538 if (*ptr) {
539 /* invalid characters after some number */
540 rc = LY_EVALID;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100541 }
542 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200543
Michal Vaskob4d40d62021-05-04 11:42:44 +0200544 /* cleanup */
545 free(str);
546 if (!rc) {
547 *ret = i;
548 }
549 return rc;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100550}
551
552LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200553ly_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 +0100554{
Michal Vaskob4d40d62021-05-04 11:42:44 +0200555 LY_ERR rc = LY_SUCCESS;
556 char *ptr, *str;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100557 uint64_t u;
558
Michal Vaskob4d40d62021-05-04 11:42:44 +0200559 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100560
Michal Vaskob4d40d62021-05-04 11:42:44 +0200561 /* duplicate the value to avoid accessing following bytes */
562 str = strndup(val_str, val_len);
563 LY_CHECK_RET(!str, LY_EMEM);
564
565 /* parse the value */
Radek Krejci4f28eda2018-11-12 11:46:16 +0100566 errno = 0;
Michal Vaskob4d40d62021-05-04 11:42:44 +0200567 u = strtoull(str, &ptr, base);
568 if (errno || (ptr == str)) {
569 /* invalid string */
570 rc = LY_EVALID;
571 } else if ((u > max) || (u && (str[0] == '-'))) {
572 /* invalid number */
573 rc = LY_EDENIED;
574 } else if (*ptr) {
575 while (isspace(*ptr)) {
576 ++ptr;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100577 }
Michal Vaskob4d40d62021-05-04 11:42:44 +0200578 if (*ptr) {
579 /* invalid characters after some number */
580 rc = LY_EVALID;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100581 }
582 }
583
Michal Vaskob4d40d62021-05-04 11:42:44 +0200584 /* cleanup */
585 free(str);
586 if (!rc) {
587 *ret = u;
588 }
589 return rc;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100590}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200591
592/**
593 * @brief Parse an identifier.
594 *
595 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
596 * identifier = (ALPHA / "_")
597 * *(ALPHA / DIGIT / "_" / "-" / ".")
598 *
599 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
600 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
601 */
602static LY_ERR
603lys_parse_id(const char **id)
604{
605 assert(id && *id);
606
607 if (!is_yangidentstartchar(**id)) {
608 return LY_EINVAL;
609 }
610 ++(*id);
611
612 while (is_yangidentchar(**id)) {
613 ++(*id);
614 }
615 return LY_SUCCESS;
616}
617
618LY_ERR
619ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
620{
621 assert(id && *id);
622 assert(prefix && prefix_len);
623 assert(name && name_len);
624
625 *prefix = *id;
626 *prefix_len = 0;
627 *name = NULL;
628 *name_len = 0;
629
630 LY_CHECK_RET(lys_parse_id(id));
631 if (**id == ':') {
632 /* there is prefix */
633 *prefix_len = *id - *prefix;
634 ++(*id);
635 *name = *id;
636
637 LY_CHECK_RET(lys_parse_id(id));
638 *name_len = *id - *name;
639 } else {
640 /* there is no prefix, so what we have as prefix now is actually the name */
641 *name = *prefix;
642 *name_len = *id - *name;
643 *prefix = NULL;
644 }
645
646 return LY_SUCCESS;
647}
648
649LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200650ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
Radek Krejci0f969882020-08-21 16:56:47 +0200651 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
652 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200653{
654 LY_ERR ret = LY_EVALID;
655 const char *in = *pred;
656 size_t offset = 1;
Radek Krejci857189e2020-09-01 13:26:36 +0200657 uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
Radek Krejcib4a4a272019-06-10 12:44:52 +0200658 char quot;
659
Radek Krejci4607f542020-12-01 12:18:49 +0100660 assert(in[0] == '[');
Radek Krejcib4a4a272019-06-10 12:44:52 +0200661
662 *prefix = *id = *value = NULL;
663 *prefix_len = *id_len = *value_len = 0;
664
665 /* leading *WSP */
Michal Vaskod989ba02020-08-24 10:59:24 +0200666 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200667
668 if (isdigit(in[offset])) {
669 /* pos: "[" *WSP positive-integer-value *WSP "]" */
670 if (in[offset] == '0') {
671 /* zero */
672 *errmsg = "The position predicate cannot be zero.";
673 goto error;
674 }
675
676 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200677 *value = &in[offset++];
Michal Vaskod989ba02020-08-24 10:59:24 +0200678 for ( ; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200679 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200680
681 } else if (in[offset] == '.') {
682 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
683 *id = &in[offset];
684 *id_len = 1;
685 offset++;
686 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200687 } else if (in[offset] == '-') {
688 /* typically negative value */
689 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
690 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200691 } else {
692 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
693 in = &in[offset];
694 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
695 *errmsg = "Invalid node-identifier.";
696 goto error;
697 }
Michal Vasko69730152020-10-09 16:30:07 +0200698 if ((format == LYD_XML) && !(*prefix)) {
Radek Krejci084289f2019-07-09 17:35:30 +0200699 /* all node names MUST be qualified with explicit namespace prefix */
700 *errmsg = "Missing prefix of a node name.";
701 goto error;
702 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200703 offset = in - *pred;
704 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200705 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200706 }
707
708 if (expr) {
709 /* *WSP "=" *WSP quoted-string *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200710 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200711
712 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200713 if (expr == 1) {
714 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
715 } else { /* 2 */
716 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
717 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200718 goto error;
719 }
720 offset++;
Michal Vaskod989ba02020-08-24 10:59:24 +0200721 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200722
723 /* quoted-string */
724 quot = in[offset++];
Michal Vasko69730152020-10-09 16:30:07 +0200725 if ((quot != '\'') && (quot != '\"')) {
Radek Krejcib4a4a272019-06-10 12:44:52 +0200726 *errmsg = "String value is not quoted.";
727 goto error;
728 }
729 *value = &in[offset];
Michal Vaskod989ba02020-08-24 10:59:24 +0200730 for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200731 if (in[offset] == quot) {
732 *value_len = &in[offset] - *value;
733 offset++;
734 } else {
735 *errmsg = "Value is not terminated quoted-string.";
736 goto error;
737 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200738 }
739
740 /* *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200741 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200742 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200743 if (expr == 0) {
744 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
745 } else if (expr == 1) {
746 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
747 } else { /* 2 */
748 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
749 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200750 goto error;
751 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200752 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200753
Radek Krejci10bfdf82019-06-10 14:08:13 +0200754 if (offset <= limit) {
755 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200756 return LY_SUCCESS;
757 }
758
759 /* we read after the limit */
760 *errmsg = "Predicate is incomplete.";
761 *prefix = *id = *value = NULL;
762 *prefix_len = *id_len = *value_len = 0;
763 offset = limit;
764 ret = LY_EINVAL;
765
766error:
767 *pred = &in[offset];
768 return ret;
769}