blob: bc5de364cea0115414453ac3753d55f6e5eae773 [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
34void *
35ly_realloc(void *ptr, size_t size)
36{
37 void *new_mem;
38
39 new_mem = realloc(ptr, size);
40 if (!new_mem) {
41 free(ptr);
42 }
43
44 return new_mem;
45}
Michal Vasko841d1a92018-09-07 15:40:31 +020046
Michal Vasko03ff5a72019-09-11 13:49:33 +020047char *
Radek Krejci1deb5be2020-08-26 16:43:36 +020048ly_strnchr(const char *s, int c, size_t len)
Michal Vasko03ff5a72019-09-11 13:49:33 +020049{
Michal Vaskod989ba02020-08-24 10:59:24 +020050 for ( ; *s != (char)c; ++s, --len) {
Michal Vasko03ff5a72019-09-11 13:49:33 +020051 if ((*s == '\0') || (!len)) {
52 return NULL;
53 }
54 }
55 return (char *)s;
56}
57
Radek Krejci7f9b6512019-09-18 13:11:09 +020058int
59ly_strncmp(const char *refstr, const char *str, size_t str_len)
60{
61 int rc = strncmp(refstr, str, str_len);
Michal Vasko69730152020-10-09 16:30:07 +020062
63 if (!rc && (refstr[str_len] == '\0')) {
Radek Krejci7f9b6512019-09-18 13:11:09 +020064 return 0;
65 } else {
66 return rc ? rc : 1;
67 }
68}
69
Radek Krejcib416be62018-10-01 14:51:45 +020070LY_ERR
Michal Vaskob36053d2020-03-26 15:49:30 +010071ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
Radek Krejcib416be62018-10-01 14:51:45 +020072{
Radek Krejci1deb5be2020-08-26 16:43:36 +020073 uint32_t c, aux;
74 size_t len;
Radek Krejcib416be62018-10-01 14:51:45 +020075
Radek Krejcicc6a45c2019-05-13 10:16:14 +020076 if (bytes_read) {
77 (*bytes_read) = 0;
78 }
79
Radek Krejcib416be62018-10-01 14:51:45 +020080 c = (*input)[0];
81 LY_CHECK_RET(!c, LY_EINVAL);
82
83 if (!(c & 0x80)) {
84 /* one byte character */
85 len = 1;
86
Michal Vasko69730152020-10-09 16:30:07 +020087 if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
Radek Krejcib416be62018-10-01 14:51:45 +020088 return LY_EINVAL;
89 }
90 } else if ((c & 0xe0) == 0xc0) {
91 /* two bytes character */
92 len = 2;
93
94 aux = (*input)[1];
95 if ((aux & 0xc0) != 0x80) {
96 return LY_EINVAL;
97 }
98 c = ((c & 0x1f) << 6) | (aux & 0x3f);
99
100 if (c < 0x80) {
101 return LY_EINVAL;
102 }
103 } else if ((c & 0xf0) == 0xe0) {
104 /* three bytes character */
105 len = 3;
106
107 c &= 0x0f;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200108 for (uint64_t i = 1; i <= 2; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200109 aux = (*input)[i];
110 if ((aux & 0xc0) != 0x80) {
111 return LY_EINVAL;
112 }
113
114 c = (c << 6) | (aux & 0x3f);
115 }
116
Michal Vasko69730152020-10-09 16:30:07 +0200117 if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200118 return LY_EINVAL;
119 }
120 } else if ((c & 0xf8) == 0xf0) {
121 /* four bytes character */
122 len = 4;
123
124 c &= 0x07;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200125 for (uint64_t i = 1; i <= 3; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200126 aux = (*input)[i];
127 if ((aux & 0xc0) != 0x80) {
128 return LY_EINVAL;
129 }
130
131 c = (c << 6) | (aux & 0x3f);
132 }
133
Michal Vasko69730152020-10-09 16:30:07 +0200134 if ((c < 0x1000) || (c > 0x10ffff)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200135 return LY_EINVAL;
136 }
137 } else {
138 return LY_EINVAL;
139 }
140
141 (*utf8_char) = c;
142 (*input) += len;
143 if (bytes_read) {
144 (*bytes_read) = len;
145 }
146 return LY_SUCCESS;
147}
148
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200149LY_ERR
150ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
151{
152 if (value < 0x80) {
153 /* one byte character */
Michal Vasko69730152020-10-09 16:30:07 +0200154 if ((value < 0x20) &&
155 (value != 0x09) &&
156 (value != 0x0a) &&
157 (value != 0x0d)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200158 return LY_EINVAL;
159 }
160
161 dst[0] = value;
162 (*bytes_written) = 1;
163 } else if (value < 0x800) {
164 /* two bytes character */
165 dst[0] = 0xc0 | (value >> 6);
166 dst[1] = 0x80 | (value & 0x3f);
167 (*bytes_written) = 2;
168 } else if (value < 0xfffe) {
169 /* three bytes character */
170 if (((value & 0xf800) == 0xd800) ||
Michal Vasko69730152020-10-09 16:30:07 +0200171 ((value >= 0xfdd0) && (value <= 0xfdef))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200172 /* exclude surrogate blocks %xD800-DFFF */
173 /* exclude noncharacters %xFDD0-FDEF */
174 return LY_EINVAL;
175 }
176
177 dst[0] = 0xe0 | (value >> 12);
178 dst[1] = 0x80 | ((value >> 6) & 0x3f);
179 dst[2] = 0x80 | (value & 0x3f);
180
181 (*bytes_written) = 3;
182 } else if (value < 0x10fffe) {
183 if ((value & 0xffe) == 0xffe) {
184 /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
185 * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
186 * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
187 return LY_EINVAL;
188 }
189 /* four bytes character */
190 dst[0] = 0xf0 | (value >> 18);
191 dst[1] = 0x80 | ((value >> 12) & 0x3f);
192 dst[2] = 0x80 | ((value >> 6) & 0x3f);
193 dst[3] = 0x80 | (value & 0x3f);
194
195 (*bytes_written) = 4;
196 } else {
197 return LY_EINVAL;
198 }
199 return LY_SUCCESS;
200}
201
Radek Krejci76c98012019-08-14 11:23:24 +0200202/**
203 * @brief Static table of the UTF8 characters lengths according to their first byte.
204 */
Radek Krejcif6a11002020-08-21 13:29:07 +0200205static const unsigned char utf8_char_length_table[] = {
Radek Krejci76c98012019-08-14 11:23:24 +0200206 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
207 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
209 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
210 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
211 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
212 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
213 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
214 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
215 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
216 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
217 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
218 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
219 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
220 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
221 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
222};
223
224size_t
225ly_utf8len(const char *str, size_t bytes)
226{
Radek Krejci1e008d22020-08-17 11:37:37 +0200227 size_t len = 0;
228 const char *ptr = str;
Radek Krejci76c98012019-08-14 11:23:24 +0200229
Michal Vaskod989ba02020-08-24 10:59:24 +0200230 while (*ptr && (size_t)(ptr - str) < bytes) {
Radek Krejci1e008d22020-08-17 11:37:37 +0200231 ++len;
232 ptr += utf8_char_length_table[((unsigned char)(*ptr))];
233 }
Radek Krejci76c98012019-08-14 11:23:24 +0200234 return len;
235}
236
Radek Krejcid972c252018-09-25 13:23:39 +0200237size_t
238LY_VCODE_INSTREXP_len(const char *str)
239{
240 size_t len = 0;
Michal Vasko69730152020-10-09 16:30:07 +0200241
Radek Krejcid972c252018-09-25 13:23:39 +0200242 if (!str) {
243 return len;
244 } else if (!str[0]) {
245 return 1;
246 }
Radek Krejci1e008d22020-08-17 11:37:37 +0200247 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
Radek Krejcid972c252018-09-25 13:23:39 +0200248 return len;
249}
250
Radek Krejcif345c012018-09-19 11:12:59 +0200251LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200252ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200253{
Radek Krejci86d106e2018-10-18 09:53:19 +0200254 struct stat sb;
255 long pagesize;
256 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200257
Radek Krejci86d106e2018-10-18 09:53:19 +0200258 assert(length);
259 assert(addr);
260 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200261
Radek Krejci86d106e2018-10-18 09:53:19 +0200262 if (fstat(fd, &sb) == -1) {
263 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
264 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200265 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200266 if (!S_ISREG(sb.st_mode)) {
267 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
268 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200269 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200270 if (!sb.st_size) {
271 *addr = NULL;
272 return LY_SUCCESS;
273 }
274 pagesize = sysconf(_SC_PAGESIZE);
275
276 m = sb.st_size % pagesize;
Michal Vasko69730152020-10-09 16:30:07 +0200277 if (m && (pagesize - m >= 1)) {
Radek Krejci86d106e2018-10-18 09:53:19 +0200278 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
279 *length = sb.st_size + 1;
280 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
281 } else {
282 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
283 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
284 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
285 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
286 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
287 * where the anonymous mapping starts. */
288 *length = sb.st_size + pagesize;
289 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
290 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
291 }
292 if (*addr == MAP_FAILED) {
293 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
294 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200295 }
296
Radek Krejcif345c012018-09-19 11:12:59 +0200297 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200298}
Michal Vasko841d1a92018-09-07 15:40:31 +0200299
Radek Krejci86d106e2018-10-18 09:53:19 +0200300LY_ERR
301ly_munmap(void *addr, size_t length)
302{
303 if (munmap(addr, length)) {
304 return LY_ESYS;
305 }
306 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200307}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100308
309LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200310ly_strcat(char **dest, const char *format, ...)
311{
312 va_list fp;
313 char *addition = NULL;
314 size_t len;
315
316 va_start(fp, format);
317 len = vasprintf(&addition, format, fp);
318 len += (*dest ? strlen(*dest) : 0) + 1;
319
320 if (*dest) {
321 *dest = ly_realloc(*dest, len);
322 if (!*dest) {
Radek Krejci1cd812f2020-12-01 12:17:53 +0100323 va_end(fp);
Radek Krejci4546aa62019-07-15 16:53:32 +0200324 return LY_EMEM;
325 }
326 *dest = strcat(*dest, addition);
327 free(addition);
328 } else {
329 *dest = addition;
330 }
331
332 va_end(fp);
333 return LY_SUCCESS;
334}
335
336LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200337ly_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 +0100338{
339 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200340 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100341
Radek Krejci249973a2019-06-10 10:50:54 +0200342 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100343
344 /* convert to 64-bit integer, all the redundant characters are handled */
345 errno = 0;
346 strptr = NULL;
347
348 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200349 i = strtoll(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200350 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100351 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200352 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100353 return LY_EDENIED;
354 } else if (strptr && *strptr) {
355 while (isspace(*strptr)) {
356 ++strptr;
357 }
Michal Vasko69730152020-10-09 16:30:07 +0200358 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100359 return LY_EVALID;
360 }
361 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200362
363 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100364 return LY_SUCCESS;
365}
366
367LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200368ly_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 +0100369{
370 char *strptr;
371 uint64_t u;
372
373 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
374
375 errno = 0;
376 strptr = NULL;
377 u = strtoull(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200378 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100379 return LY_EVALID;
Michal Vasko69730152020-10-09 16:30:07 +0200380 } else if ((u > max) || (u && (val_str[0] == '-'))) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100381 return LY_EDENIED;
382 } else if (strptr && *strptr) {
383 while (isspace(*strptr)) {
384 ++strptr;
385 }
Michal Vasko69730152020-10-09 16:30:07 +0200386 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100387 return LY_EVALID;
388 }
389 }
390
391 *ret = u;
392 return LY_SUCCESS;
393}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200394
395/**
396 * @brief Parse an identifier.
397 *
398 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
399 * identifier = (ALPHA / "_")
400 * *(ALPHA / DIGIT / "_" / "-" / ".")
401 *
402 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
403 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
404 */
405static LY_ERR
406lys_parse_id(const char **id)
407{
408 assert(id && *id);
409
410 if (!is_yangidentstartchar(**id)) {
411 return LY_EINVAL;
412 }
413 ++(*id);
414
415 while (is_yangidentchar(**id)) {
416 ++(*id);
417 }
418 return LY_SUCCESS;
419}
420
421LY_ERR
422ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
423{
424 assert(id && *id);
425 assert(prefix && prefix_len);
426 assert(name && name_len);
427
428 *prefix = *id;
429 *prefix_len = 0;
430 *name = NULL;
431 *name_len = 0;
432
433 LY_CHECK_RET(lys_parse_id(id));
434 if (**id == ':') {
435 /* there is prefix */
436 *prefix_len = *id - *prefix;
437 ++(*id);
438 *name = *id;
439
440 LY_CHECK_RET(lys_parse_id(id));
441 *name_len = *id - *name;
442 } else {
443 /* there is no prefix, so what we have as prefix now is actually the name */
444 *name = *prefix;
445 *name_len = *id - *name;
446 *prefix = NULL;
447 }
448
449 return LY_SUCCESS;
450}
451
452LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200453ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
Radek Krejci0f969882020-08-21 16:56:47 +0200454 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
455 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200456{
457 LY_ERR ret = LY_EVALID;
458 const char *in = *pred;
459 size_t offset = 1;
Radek Krejci857189e2020-09-01 13:26:36 +0200460 uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
Radek Krejcib4a4a272019-06-10 12:44:52 +0200461 char quot;
462
Radek Krejci4607f542020-12-01 12:18:49 +0100463 assert(in[0] == '[');
Radek Krejcib4a4a272019-06-10 12:44:52 +0200464
465 *prefix = *id = *value = NULL;
466 *prefix_len = *id_len = *value_len = 0;
467
468 /* leading *WSP */
Michal Vaskod989ba02020-08-24 10:59:24 +0200469 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200470
471 if (isdigit(in[offset])) {
472 /* pos: "[" *WSP positive-integer-value *WSP "]" */
473 if (in[offset] == '0') {
474 /* zero */
475 *errmsg = "The position predicate cannot be zero.";
476 goto error;
477 }
478
479 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200480 *value = &in[offset++];
Michal Vaskod989ba02020-08-24 10:59:24 +0200481 for ( ; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200482 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200483
484 } else if (in[offset] == '.') {
485 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
486 *id = &in[offset];
487 *id_len = 1;
488 offset++;
489 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200490 } else if (in[offset] == '-') {
491 /* typically negative value */
492 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
493 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200494 } else {
495 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
496 in = &in[offset];
497 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
498 *errmsg = "Invalid node-identifier.";
499 goto error;
500 }
Michal Vasko69730152020-10-09 16:30:07 +0200501 if ((format == LYD_XML) && !(*prefix)) {
Radek Krejci084289f2019-07-09 17:35:30 +0200502 /* all node names MUST be qualified with explicit namespace prefix */
503 *errmsg = "Missing prefix of a node name.";
504 goto error;
505 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200506 offset = in - *pred;
507 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200508 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200509 }
510
511 if (expr) {
512 /* *WSP "=" *WSP quoted-string *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200513 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200514
515 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200516 if (expr == 1) {
517 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
518 } else { /* 2 */
519 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
520 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200521 goto error;
522 }
523 offset++;
Michal Vaskod989ba02020-08-24 10:59:24 +0200524 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200525
526 /* quoted-string */
527 quot = in[offset++];
Michal Vasko69730152020-10-09 16:30:07 +0200528 if ((quot != '\'') && (quot != '\"')) {
Radek Krejcib4a4a272019-06-10 12:44:52 +0200529 *errmsg = "String value is not quoted.";
530 goto error;
531 }
532 *value = &in[offset];
Michal Vaskod989ba02020-08-24 10:59:24 +0200533 for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200534 if (in[offset] == quot) {
535 *value_len = &in[offset] - *value;
536 offset++;
537 } else {
538 *errmsg = "Value is not terminated quoted-string.";
539 goto error;
540 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200541 }
542
543 /* *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200544 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200545 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200546 if (expr == 0) {
547 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
548 } else if (expr == 1) {
549 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
550 } else { /* 2 */
551 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
552 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200553 goto error;
554 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200555 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200556
Radek Krejci10bfdf82019-06-10 14:08:13 +0200557 if (offset <= limit) {
558 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200559 return LY_SUCCESS;
560 }
561
562 /* we read after the limit */
563 *errmsg = "Predicate is incomplete.";
564 *prefix = *id = *value = NULL;
565 *prefix_len = *id_len = *value_len = 0;
566 offset = limit;
567 ret = LY_EINVAL;
568
569error:
570 *pred = &in[offset];
571 return ret;
572}