blob: 15595e5f6f4bdf087db2837cf8f5adcc0eb913fa [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"
Radek Krejcib4a4a272019-06-10 12:44:52 +020031#include "tree_schema_internal.h"
Michal Vasko1324b6c2018-09-07 11:16:23 +020032
33void *
34ly_realloc(void *ptr, size_t size)
35{
36 void *new_mem;
37
38 new_mem = realloc(ptr, size);
39 if (!new_mem) {
40 free(ptr);
41 }
42
43 return new_mem;
44}
Michal Vasko841d1a92018-09-07 15:40:31 +020045
Michal Vasko03ff5a72019-09-11 13:49:33 +020046char *
Radek Krejci1deb5be2020-08-26 16:43:36 +020047ly_strnchr(const char *s, int c, size_t len)
Michal Vasko03ff5a72019-09-11 13:49:33 +020048{
Michal Vaskod989ba02020-08-24 10:59:24 +020049 for ( ; *s != (char)c; ++s, --len) {
Michal Vasko03ff5a72019-09-11 13:49:33 +020050 if ((*s == '\0') || (!len)) {
51 return NULL;
52 }
53 }
54 return (char *)s;
55}
56
Radek Krejci7f9b6512019-09-18 13:11:09 +020057int
58ly_strncmp(const char *refstr, const char *str, size_t str_len)
59{
60 int rc = strncmp(refstr, str, str_len);
Michal Vasko69730152020-10-09 16:30:07 +020061
62 if (!rc && (refstr[str_len] == '\0')) {
Radek Krejci7f9b6512019-09-18 13:11:09 +020063 return 0;
64 } else {
65 return rc ? rc : 1;
66 }
67}
68
Radek Krejcib416be62018-10-01 14:51:45 +020069LY_ERR
Michal Vaskob36053d2020-03-26 15:49:30 +010070ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
Radek Krejcib416be62018-10-01 14:51:45 +020071{
Radek Krejci1deb5be2020-08-26 16:43:36 +020072 uint32_t c, aux;
73 size_t len;
Radek Krejcib416be62018-10-01 14:51:45 +020074
Radek Krejcicc6a45c2019-05-13 10:16:14 +020075 if (bytes_read) {
76 (*bytes_read) = 0;
77 }
78
Radek Krejcib416be62018-10-01 14:51:45 +020079 c = (*input)[0];
80 LY_CHECK_RET(!c, LY_EINVAL);
81
82 if (!(c & 0x80)) {
83 /* one byte character */
84 len = 1;
85
Michal Vasko69730152020-10-09 16:30:07 +020086 if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
Radek Krejcib416be62018-10-01 14:51:45 +020087 return LY_EINVAL;
88 }
89 } else if ((c & 0xe0) == 0xc0) {
90 /* two bytes character */
91 len = 2;
92
93 aux = (*input)[1];
94 if ((aux & 0xc0) != 0x80) {
95 return LY_EINVAL;
96 }
97 c = ((c & 0x1f) << 6) | (aux & 0x3f);
98
99 if (c < 0x80) {
100 return LY_EINVAL;
101 }
102 } else if ((c & 0xf0) == 0xe0) {
103 /* three bytes character */
104 len = 3;
105
106 c &= 0x0f;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200107 for (uint64_t i = 1; i <= 2; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200108 aux = (*input)[i];
109 if ((aux & 0xc0) != 0x80) {
110 return LY_EINVAL;
111 }
112
113 c = (c << 6) | (aux & 0x3f);
114 }
115
Michal Vasko69730152020-10-09 16:30:07 +0200116 if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200117 return LY_EINVAL;
118 }
119 } else if ((c & 0xf8) == 0xf0) {
120 /* four bytes character */
121 len = 4;
122
123 c &= 0x07;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200124 for (uint64_t i = 1; i <= 3; i++) {
Radek Krejcib416be62018-10-01 14:51:45 +0200125 aux = (*input)[i];
126 if ((aux & 0xc0) != 0x80) {
127 return LY_EINVAL;
128 }
129
130 c = (c << 6) | (aux & 0x3f);
131 }
132
Michal Vasko69730152020-10-09 16:30:07 +0200133 if ((c < 0x1000) || (c > 0x10ffff)) {
Radek Krejcib416be62018-10-01 14:51:45 +0200134 return LY_EINVAL;
135 }
136 } else {
137 return LY_EINVAL;
138 }
139
140 (*utf8_char) = c;
141 (*input) += len;
142 if (bytes_read) {
143 (*bytes_read) = len;
144 }
145 return LY_SUCCESS;
146}
147
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200148LY_ERR
149ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
150{
151 if (value < 0x80) {
152 /* one byte character */
Michal Vasko69730152020-10-09 16:30:07 +0200153 if ((value < 0x20) &&
154 (value != 0x09) &&
155 (value != 0x0a) &&
156 (value != 0x0d)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200157 return LY_EINVAL;
158 }
159
160 dst[0] = value;
161 (*bytes_written) = 1;
162 } else if (value < 0x800) {
163 /* two bytes character */
164 dst[0] = 0xc0 | (value >> 6);
165 dst[1] = 0x80 | (value & 0x3f);
166 (*bytes_written) = 2;
167 } else if (value < 0xfffe) {
168 /* three bytes character */
169 if (((value & 0xf800) == 0xd800) ||
Michal Vasko69730152020-10-09 16:30:07 +0200170 ((value >= 0xfdd0) && (value <= 0xfdef))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200171 /* exclude surrogate blocks %xD800-DFFF */
172 /* exclude noncharacters %xFDD0-FDEF */
173 return LY_EINVAL;
174 }
175
176 dst[0] = 0xe0 | (value >> 12);
177 dst[1] = 0x80 | ((value >> 6) & 0x3f);
178 dst[2] = 0x80 | (value & 0x3f);
179
180 (*bytes_written) = 3;
181 } else if (value < 0x10fffe) {
182 if ((value & 0xffe) == 0xffe) {
183 /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
184 * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
185 * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
186 return LY_EINVAL;
187 }
188 /* four bytes character */
189 dst[0] = 0xf0 | (value >> 18);
190 dst[1] = 0x80 | ((value >> 12) & 0x3f);
191 dst[2] = 0x80 | ((value >> 6) & 0x3f);
192 dst[3] = 0x80 | (value & 0x3f);
193
194 (*bytes_written) = 4;
195 } else {
196 return LY_EINVAL;
197 }
198 return LY_SUCCESS;
199}
200
Radek Krejci76c98012019-08-14 11:23:24 +0200201/**
202 * @brief Static table of the UTF8 characters lengths according to their first byte.
203 */
Radek Krejcif6a11002020-08-21 13:29:07 +0200204static const unsigned char utf8_char_length_table[] = {
Radek Krejci76c98012019-08-14 11:23:24 +0200205 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
206 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 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
218 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
219 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
220 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
221};
222
223size_t
224ly_utf8len(const char *str, size_t bytes)
225{
Radek Krejci1e008d22020-08-17 11:37:37 +0200226 size_t len = 0;
227 const char *ptr = str;
Radek Krejci76c98012019-08-14 11:23:24 +0200228
Michal Vaskod989ba02020-08-24 10:59:24 +0200229 while (*ptr && (size_t)(ptr - str) < bytes) {
Radek Krejci1e008d22020-08-17 11:37:37 +0200230 ++len;
231 ptr += utf8_char_length_table[((unsigned char)(*ptr))];
232 }
Radek Krejci76c98012019-08-14 11:23:24 +0200233 return len;
234}
235
Radek Krejcid972c252018-09-25 13:23:39 +0200236size_t
237LY_VCODE_INSTREXP_len(const char *str)
238{
239 size_t len = 0;
Michal Vasko69730152020-10-09 16:30:07 +0200240
Radek Krejcid972c252018-09-25 13:23:39 +0200241 if (!str) {
242 return len;
243 } else if (!str[0]) {
244 return 1;
245 }
Radek Krejci1e008d22020-08-17 11:37:37 +0200246 for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
Radek Krejcid972c252018-09-25 13:23:39 +0200247 return len;
248}
249
Radek Krejcif345c012018-09-19 11:12:59 +0200250LY_ERR
Radek Krejci86d106e2018-10-18 09:53:19 +0200251ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
Michal Vasko841d1a92018-09-07 15:40:31 +0200252{
Radek Krejci86d106e2018-10-18 09:53:19 +0200253 struct stat sb;
254 long pagesize;
255 size_t m;
Michal Vasko841d1a92018-09-07 15:40:31 +0200256
Radek Krejci86d106e2018-10-18 09:53:19 +0200257 assert(length);
258 assert(addr);
259 assert(fd >= 0);
Michal Vasko841d1a92018-09-07 15:40:31 +0200260
Radek Krejci86d106e2018-10-18 09:53:19 +0200261 if (fstat(fd, &sb) == -1) {
262 LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
263 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200264 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200265 if (!S_ISREG(sb.st_mode)) {
266 LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
267 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200268 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200269 if (!sb.st_size) {
270 *addr = NULL;
271 return LY_SUCCESS;
272 }
273 pagesize = sysconf(_SC_PAGESIZE);
274
275 m = sb.st_size % pagesize;
Michal Vasko69730152020-10-09 16:30:07 +0200276 if (m && (pagesize - m >= 1)) {
Radek Krejci86d106e2018-10-18 09:53:19 +0200277 /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
278 *length = sb.st_size + 1;
279 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
280 } else {
281 /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
282 * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
283 * Therefore we have to do the following hack with double mapping. First, the required number of bytes
284 * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
285 * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
286 * where the anonymous mapping starts. */
287 *length = sb.st_size + pagesize;
288 *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
289 *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
290 }
291 if (*addr == MAP_FAILED) {
292 LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
293 return LY_ESYS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200294 }
295
Radek Krejcif345c012018-09-19 11:12:59 +0200296 return LY_SUCCESS;
Radek Krejci86d106e2018-10-18 09:53:19 +0200297}
Michal Vasko841d1a92018-09-07 15:40:31 +0200298
Radek Krejci86d106e2018-10-18 09:53:19 +0200299LY_ERR
300ly_munmap(void *addr, size_t length)
301{
302 if (munmap(addr, length)) {
303 return LY_ESYS;
304 }
305 return LY_SUCCESS;
Michal Vasko841d1a92018-09-07 15:40:31 +0200306}
Radek Krejci4f28eda2018-11-12 11:46:16 +0100307
308LY_ERR
Radek Krejci4546aa62019-07-15 16:53:32 +0200309ly_strcat(char **dest, const char *format, ...)
310{
311 va_list fp;
312 char *addition = NULL;
313 size_t len;
314
315 va_start(fp, format);
316 len = vasprintf(&addition, format, fp);
317 len += (*dest ? strlen(*dest) : 0) + 1;
318
319 if (*dest) {
320 *dest = ly_realloc(*dest, len);
321 if (!*dest) {
Radek Krejci1cd812f2020-12-01 12:17:53 +0100322 va_end(fp);
Radek Krejci4546aa62019-07-15 16:53:32 +0200323 return LY_EMEM;
324 }
325 *dest = strcat(*dest, addition);
326 free(addition);
327 } else {
328 *dest = addition;
329 }
330
331 va_end(fp);
332 return LY_SUCCESS;
333}
334
335LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200336ly_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 +0100337{
338 char *strptr;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200339 int64_t i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100340
Radek Krejci249973a2019-06-10 10:50:54 +0200341 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
Radek Krejci4f28eda2018-11-12 11:46:16 +0100342
343 /* convert to 64-bit integer, all the redundant characters are handled */
344 errno = 0;
345 strptr = NULL;
346
347 /* parse the value */
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200348 i = strtoll(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200349 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100350 return LY_EVALID;
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200351 } else if ((i < min) || (i > max)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100352 return LY_EDENIED;
353 } else if (strptr && *strptr) {
354 while (isspace(*strptr)) {
355 ++strptr;
356 }
Michal Vasko69730152020-10-09 16:30:07 +0200357 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100358 return LY_EVALID;
359 }
360 }
Radek Krejci9ea8ca12019-06-10 13:11:55 +0200361
362 *ret = i;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100363 return LY_SUCCESS;
364}
365
366LY_ERR
Radek Krejci249973a2019-06-10 10:50:54 +0200367ly_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 +0100368{
369 char *strptr;
370 uint64_t u;
371
372 LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
373
374 errno = 0;
375 strptr = NULL;
376 u = strtoull(val_str, &strptr, base);
Michal Vasko69730152020-10-09 16:30:07 +0200377 if (errno || (strptr == val_str)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100378 return LY_EVALID;
Michal Vasko69730152020-10-09 16:30:07 +0200379 } else if ((u > max) || (u && (val_str[0] == '-'))) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100380 return LY_EDENIED;
381 } else if (strptr && *strptr) {
382 while (isspace(*strptr)) {
383 ++strptr;
384 }
Michal Vasko69730152020-10-09 16:30:07 +0200385 if (*strptr && (strptr < val_str + val_len)) {
Radek Krejci4f28eda2018-11-12 11:46:16 +0100386 return LY_EVALID;
387 }
388 }
389
390 *ret = u;
391 return LY_SUCCESS;
392}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200393
394/**
395 * @brief Parse an identifier.
396 *
397 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
398 * identifier = (ALPHA / "_")
399 * *(ALPHA / DIGIT / "_" / "-" / ".")
400 *
401 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
402 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
403 */
404static LY_ERR
405lys_parse_id(const char **id)
406{
407 assert(id && *id);
408
409 if (!is_yangidentstartchar(**id)) {
410 return LY_EINVAL;
411 }
412 ++(*id);
413
414 while (is_yangidentchar(**id)) {
415 ++(*id);
416 }
417 return LY_SUCCESS;
418}
419
420LY_ERR
421ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
422{
423 assert(id && *id);
424 assert(prefix && prefix_len);
425 assert(name && name_len);
426
427 *prefix = *id;
428 *prefix_len = 0;
429 *name = NULL;
430 *name_len = 0;
431
432 LY_CHECK_RET(lys_parse_id(id));
433 if (**id == ':') {
434 /* there is prefix */
435 *prefix_len = *id - *prefix;
436 ++(*id);
437 *name = *id;
438
439 LY_CHECK_RET(lys_parse_id(id));
440 *name_len = *id - *name;
441 } else {
442 /* there is no prefix, so what we have as prefix now is actually the name */
443 *name = *prefix;
444 *name_len = *id - *name;
445 *prefix = NULL;
446 }
447
448 return LY_SUCCESS;
449}
450
451LY_ERR
Radek Krejci084289f2019-07-09 17:35:30 +0200452ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
Radek Krejci0f969882020-08-21 16:56:47 +0200453 const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
454 const char **errmsg)
Radek Krejcib4a4a272019-06-10 12:44:52 +0200455{
456 LY_ERR ret = LY_EVALID;
457 const char *in = *pred;
458 size_t offset = 1;
Radek Krejci857189e2020-09-01 13:26:36 +0200459 uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
Radek Krejcib4a4a272019-06-10 12:44:52 +0200460 char quot;
461
Radek Krejci4607f542020-12-01 12:18:49 +0100462 assert(in[0] == '[');
Radek Krejcib4a4a272019-06-10 12:44:52 +0200463
464 *prefix = *id = *value = NULL;
465 *prefix_len = *id_len = *value_len = 0;
466
467 /* leading *WSP */
Michal Vaskod989ba02020-08-24 10:59:24 +0200468 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200469
470 if (isdigit(in[offset])) {
471 /* pos: "[" *WSP positive-integer-value *WSP "]" */
472 if (in[offset] == '0') {
473 /* zero */
474 *errmsg = "The position predicate cannot be zero.";
475 goto error;
476 }
477
478 /* positive-integer-value */
Radek Krejci10bfdf82019-06-10 14:08:13 +0200479 *value = &in[offset++];
Michal Vaskod989ba02020-08-24 10:59:24 +0200480 for ( ; isdigit(in[offset]); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200481 *value_len = &in[offset] - *value;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200482
483 } else if (in[offset] == '.') {
484 /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
485 *id = &in[offset];
486 *id_len = 1;
487 offset++;
488 expr = 1;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200489 } else if (in[offset] == '-') {
490 /* typically negative value */
491 *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
492 goto error;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200493 } else {
494 /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
495 in = &in[offset];
496 if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
497 *errmsg = "Invalid node-identifier.";
498 goto error;
499 }
Michal Vasko69730152020-10-09 16:30:07 +0200500 if ((format == LYD_XML) && !(*prefix)) {
Radek Krejci084289f2019-07-09 17:35:30 +0200501 /* all node names MUST be qualified with explicit namespace prefix */
502 *errmsg = "Missing prefix of a node name.";
503 goto error;
504 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200505 offset = in - *pred;
506 in = *pred;
Radek Krejci10bfdf82019-06-10 14:08:13 +0200507 expr = 2;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200508 }
509
510 if (expr) {
511 /* *WSP "=" *WSP quoted-string *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200512 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200513
514 if (in[offset] != '=') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200515 if (expr == 1) {
516 *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
517 } else { /* 2 */
518 *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
519 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200520 goto error;
521 }
522 offset++;
Michal Vaskod989ba02020-08-24 10:59:24 +0200523 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200524
525 /* quoted-string */
526 quot = in[offset++];
Michal Vasko69730152020-10-09 16:30:07 +0200527 if ((quot != '\'') && (quot != '\"')) {
Radek Krejcib4a4a272019-06-10 12:44:52 +0200528 *errmsg = "String value is not quoted.";
529 goto error;
530 }
531 *value = &in[offset];
Michal Vaskod989ba02020-08-24 10:59:24 +0200532 for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
Radek Krejci10bfdf82019-06-10 14:08:13 +0200533 if (in[offset] == quot) {
534 *value_len = &in[offset] - *value;
535 offset++;
536 } else {
537 *errmsg = "Value is not terminated quoted-string.";
538 goto error;
539 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200540 }
541
542 /* *WSP "]" */
Michal Vaskod989ba02020-08-24 10:59:24 +0200543 for ( ; isspace(in[offset]); offset++) {}
Radek Krejcib4a4a272019-06-10 12:44:52 +0200544 if (in[offset] != ']') {
Radek Krejci10bfdf82019-06-10 14:08:13 +0200545 if (expr == 0) {
546 *errmsg = "Predicate (pos) is not terminated by \']\' character.";
547 } else if (expr == 1) {
548 *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
549 } else { /* 2 */
550 *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
551 }
Radek Krejcib4a4a272019-06-10 12:44:52 +0200552 goto error;
553 }
Radek Krejci10bfdf82019-06-10 14:08:13 +0200554 offset++;
Radek Krejcib4a4a272019-06-10 12:44:52 +0200555
Radek Krejci10bfdf82019-06-10 14:08:13 +0200556 if (offset <= limit) {
557 *pred = &in[offset];
Radek Krejcib4a4a272019-06-10 12:44:52 +0200558 return LY_SUCCESS;
559 }
560
561 /* we read after the limit */
562 *errmsg = "Predicate is incomplete.";
563 *prefix = *id = *value = NULL;
564 *prefix_len = *id_len = *value_len = 0;
565 offset = limit;
566 ret = LY_EINVAL;
567
568error:
569 *pred = &in[offset];
570 return ret;
571}