blob: b4e6045af6ce62270598ca3672a2dc234327ff9c [file] [log] [blame]
Radek Krejci335332a2019-09-05 13:03:35 +02001/**
2 * @file parser_stmt.c
3 * @author Radek Krejčí <rkrejci@cesnet.cz>
4 * @brief Parser of the extension substatements.
5 *
6 * Copyright (c) 2019 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 */
14
Radek Krejci335332a2019-09-05 13:03:35 +020015#include <assert.h>
16#include <ctype.h>
17#include <errno.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020018#include <stdint.h>
19#include <stdlib.h>
20#include <string.h>
Radek Krejci335332a2019-09-05 13:03:35 +020021
Radek Krejci535ea9f2020-05-29 16:01:05 +020022#include "common.h"
23#include "dict.h"
24#include "log.h"
Michal Vaskoafac7822020-10-20 14:22:26 +020025#include "in.h"
Radek Krejcica376bd2020-06-11 16:04:06 +020026#include "parser_schema.h"
Michal Vasko69730152020-10-09 16:30:07 +020027#include "path.h"
Michal Vasko1a7a7bd2020-10-16 14:39:15 +020028#include "schema_compile.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "tree.h"
Radek Krejci335332a2019-09-05 13:03:35 +020030#include "tree_schema.h"
31#include "tree_schema_internal.h"
32
33static LY_ERR
34lysp_stmt_validate_value(struct lys_parser_ctx *ctx, enum yang_arg val_type, const char *val)
35{
Radek Krejci857189e2020-09-01 13:26:36 +020036 uint8_t prefix = 0;
37 ly_bool first = 1;
Radek Krejci1deb5be2020-08-26 16:43:36 +020038 uint32_t c;
Radek Krejci335332a2019-09-05 13:03:35 +020039 size_t utf8_char_len;
40
41 while (*val) {
42 LY_CHECK_ERR_RET(ly_getutf8(&val, &c, &utf8_char_len),
Michal Vasko69730152020-10-09 16:30:07 +020043 LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, (val)[-utf8_char_len]), LY_EVALID);
Radek Krejci335332a2019-09-05 13:03:35 +020044
45 switch (val_type) {
46 case Y_IDENTIF_ARG:
47 LY_CHECK_RET(lysp_check_identifierchar(ctx, c, first, NULL));
48 break;
49 case Y_PREF_IDENTIF_ARG:
50 LY_CHECK_RET(lysp_check_identifierchar(ctx, c, first, &prefix));
51 break;
52 case Y_STR_ARG:
53 case Y_MAYBE_STR_ARG:
54 LY_CHECK_RET(lysp_check_stringchar(ctx, c));
55 break;
56 }
57 first = 0;
58 }
59
60 return LY_SUCCESS;
61}
62
63/**
64 * @brief Parse extension instance.
65 *
66 * @param[in] ctx yang parser context for logging.
67 * @param[in,out] data Data to read from, always moved to currently handled character.
68 * @param[in] ext_name Extension instance substatement name (keyword).
69 * @param[in] ext_name_len Extension instance substatement name length.
70 * @param[in] insubstmt Type of the keyword this extension instance is a substatement of.
71 * @param[in] insubstmt_index Index of the keyword instance this extension instance is a substatement of.
72 * @param[in,out] exts Extension instances to add to.
73 *
74 * @return LY_ERR values.
75 */
76static LY_ERR
77lysp_stmt_ext(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, LYEXT_SUBSTMT insubstmt,
Radek Krejci0f969882020-08-21 16:56:47 +020078 LY_ARRAY_COUNT_TYPE insubstmt_index, struct lysp_ext_instance **exts)
Radek Krejci335332a2019-09-05 13:03:35 +020079{
80 struct lysp_ext_instance *e;
81
Michal Vaskob36053d2020-03-26 15:49:30 +010082 LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *exts, e, LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +020083
84 /* store name and insubstmt info */
Radek Krejci011e4aa2020-09-04 15:22:31 +020085 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->stmt, 0, &e->name));
Radek Krejci335332a2019-09-05 13:03:35 +020086 e->insubstmt = insubstmt;
87 e->insubstmt_index = insubstmt_index;
88 /* TODO (duplicate) e->child = stmt->child; */
89
90 /* get optional argument */
91 if (stmt->arg) {
Radek Krejci011e4aa2020-09-04 15:22:31 +020092 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &e->argument));
Radek Krejci335332a2019-09-05 13:03:35 +020093 }
94
95 return LY_SUCCESS;
96}
97
98/**
99 * @brief Parse a generic text field without specific constraints. Those are contact, organization,
100 * description, etc...
101 *
102 * @param[in] ctx yang parser context for logging.
Michal Vasko63f3d842020-07-08 10:10:14 +0200103 * @param[in] stmt Statement structure.
Radek Krejci335332a2019-09-05 13:03:35 +0200104 * @param[in] substmt Type of this substatement.
105 * @param[in] substmt_index Index of this substatement.
106 * @param[in,out] value Place to store the parsed value.
107 * @param[in] arg Type of the YANG keyword argument (of the value).
108 * @param[in,out] exts Extension instances to add to.
109 *
110 * @return LY_ERR values.
111 */
112static LY_ERR
113lysp_stmt_text_field(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, LYEXT_SUBSTMT substmt, uint32_t substmt_index,
Radek Krejci0f969882020-08-21 16:56:47 +0200114 const char **value, enum yang_arg arg, struct lysp_ext_instance **exts)
Radek Krejci335332a2019-09-05 13:03:35 +0200115{
116 const struct lysp_stmt *child;
117
118 if (*value) {
119 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyext_substmt2str(substmt));
120 return LY_EVALID;
121 }
122
123 LY_CHECK_RET(lysp_stmt_validate_value(ctx, arg, stmt->arg));
Radek Krejci011e4aa2020-09-04 15:22:31 +0200124 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, value));
Radek Krejci335332a2019-09-05 13:03:35 +0200125
126 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200127 struct ly_in *in;
128 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
129 enum ly_stmt kw = lysp_match_kw(NULL, in);
130 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200131
132 switch (kw) {
133 case LY_STMT_EXTENSION_INSTANCE:
134 LY_CHECK_RET(lysp_stmt_ext(ctx, child, substmt, substmt_index, exts));
135 break;
136 default:
137 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
138 return LY_EVALID;
139 }
140 }
141 return LY_SUCCESS;
142}
143
144/**
Michal Vasko7f45cf22020-10-01 12:49:44 +0200145 * @brief Parse a qname that can have more instances such as if-feature.
146 *
147 * @param[in] ctx yang parser context for logging.
148 * @param[in,out] data Data to read from, always moved to currently handled character.
149 * @param[in] substmt Type of this substatement.
150 * @param[in,out] qnames Parsed qnames to add to.
151 * @param[in] arg Type of the expected argument.
152 * @param[in,out] exts Extension instances to add to.
153 *
154 * @return LY_ERR values.
155 */
156static LY_ERR
157lysp_stmt_qnames(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, LYEXT_SUBSTMT substmt,
158 struct lysp_qname **qnames, enum yang_arg arg, struct lysp_ext_instance **exts)
159{
160 struct lysp_qname *item;
161 const struct lysp_stmt *child;
162
163 LY_CHECK_RET(lysp_stmt_validate_value(ctx, arg, stmt->arg));
164
165 /* allocate new pointer */
166 LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *qnames, item, LY_EMEM);
167 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &item->str));
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200168 item->mod = ctx->parsed_mod;
Michal Vasko7f45cf22020-10-01 12:49:44 +0200169
170 for (child = stmt->child; child; child = child->next) {
171 struct ly_in *in;
172 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
173 enum ly_stmt kw = lysp_match_kw(NULL, in);
174 ly_in_free(in, 0);
175
176 switch (kw) {
177 case LY_STMT_EXTENSION_INSTANCE:
178 LY_CHECK_RET(lysp_stmt_ext(ctx, child, substmt, LY_ARRAY_COUNT(*qnames) - 1, exts));
179 break;
180 default:
181 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
182 return LY_EVALID;
183 }
184 }
185 return LY_SUCCESS;
186}
187
188/**
Radek Krejci335332a2019-09-05 13:03:35 +0200189 * @brief Parse a generic text field that can have more instances such as base.
190 *
191 * @param[in] ctx yang parser context for logging.
192 * @param[in,out] data Data to read from, always moved to currently handled character.
193 * @param[in] substmt Type of this substatement.
194 * @param[in,out] texts Parsed values to add to.
195 * @param[in] arg Type of the expected argument.
196 * @param[in,out] exts Extension instances to add to.
197 *
198 * @return LY_ERR values.
199 */
200static LY_ERR
Michal Vasko63f3d842020-07-08 10:10:14 +0200201lysp_stmt_text_fields(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, LYEXT_SUBSTMT substmt,
Radek Krejci0f969882020-08-21 16:56:47 +0200202 const char ***texts, enum yang_arg arg, struct lysp_ext_instance **exts)
Radek Krejci335332a2019-09-05 13:03:35 +0200203{
204 const char **item;
205 const struct lysp_stmt *child;
206
207 LY_CHECK_RET(lysp_stmt_validate_value(ctx, arg, stmt->arg));
208
209 /* allocate new pointer */
Michal Vaskob36053d2020-03-26 15:49:30 +0100210 LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *texts, item, LY_EMEM);
Radek Krejci011e4aa2020-09-04 15:22:31 +0200211 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, item));
Radek Krejci335332a2019-09-05 13:03:35 +0200212
213 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200214 struct ly_in *in;
215 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
216 enum ly_stmt kw = lysp_match_kw(NULL, in);
217 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200218
219 switch (kw) {
220 case LY_STMT_EXTENSION_INSTANCE:
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200221 LY_CHECK_RET(lysp_stmt_ext(ctx, child, substmt, LY_ARRAY_COUNT(*texts) - 1, exts));
Radek Krejci335332a2019-09-05 13:03:35 +0200222 break;
223 default:
224 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
225 return LY_EVALID;
226 }
227 }
228 return LY_SUCCESS;
229}
230
231/**
232 * @brief Parse the status statement.
233 *
234 * @param[in] ctx yang parser context for logging.
235 * @param[in,out] data Data to read from, always moved to currently handled character.
236 * @param[in,out] flags Flags to add to.
237 * @param[in,out] exts Extension instances to add to.
238 *
239 * @return LY_ERR values.
240 */
241static LY_ERR
242lysp_stmt_status(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
243{
244 size_t arg_len;
245 const struct lysp_stmt *child;
246
247 if (*flags & LYS_STATUS_MASK) {
248 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "status");
249 return LY_EVALID;
250 }
251
252 LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
253 arg_len = strlen(stmt->arg);
254 if ((arg_len == 7) && !strncmp(stmt->arg, "current", arg_len)) {
255 *flags |= LYS_STATUS_CURR;
256 } else if ((arg_len == 10) && !strncmp(stmt->arg, "deprecated", arg_len)) {
257 *flags |= LYS_STATUS_DEPRC;
258 } else if ((arg_len == 8) && !strncmp(stmt->arg, "obsolete", arg_len)) {
259 *flags |= LYS_STATUS_OBSLT;
260 } else {
261 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "status");
262 return LY_EVALID;
263 }
264
265 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200266 struct ly_in *in;
267 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
268 enum ly_stmt kw = lysp_match_kw(NULL, in);
269 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200270
271 switch (kw) {
272 case LY_STMT_EXTENSION_INSTANCE:
273 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_STATUS, 0, exts));
274 break;
275 default:
276 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "status");
277 return LY_EVALID;
278 }
279 }
280 return LY_SUCCESS;
281}
282
283/**
284 * @brief Parse a restriction such as range or length.
285 *
286 * @param[in] ctx yang parser context for logging.
287 * @param[in,out] data Data to read from, always moved to currently handled character.
288 * @param[in] restr_kw Type of this particular restriction.
289 * @param[in,out] exts Extension instances to add to.
290 *
291 * @return LY_ERR values.
292 */
293static LY_ERR
294lysp_stmt_restr(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt restr_kw, struct lysp_restr *restr)
295{
296 const struct lysp_stmt *child;
297
298 LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
Michal Vasko7f45cf22020-10-01 12:49:44 +0200299 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &restr->arg.str));
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200300 restr->arg.mod = ctx->parsed_mod;
Radek Krejci335332a2019-09-05 13:03:35 +0200301
302 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200303 struct ly_in *in;
304 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
305 enum ly_stmt kw = lysp_match_kw(NULL, in);
306 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200307
308 switch (kw) {
309 case LY_STMT_DESCRIPTION:
310 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
311 break;
312 case LY_STMT_REFERENCE:
313 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
314 break;
315 case LY_STMT_ERROR_APP_TAG:
316 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
317 break;
318 case LY_STMT_ERROR_MESSAGE:
319 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
320 break;
321 case LY_STMT_EXTENSION_INSTANCE:
322 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_SELF, 0, &restr->exts));
323 break;
324 default:
325 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(restr_kw));
326 return LY_EVALID;
327 }
328 }
329 return LY_SUCCESS;
330}
331
332/**
333 * @brief Parse a restriction that can have more instances such as must.
334 *
335 * @param[in] ctx yang parser context for logging.
336 * @param[in,out] data Data to read from, always moved to currently handled character.
337 * @param[in] restr_kw Type of this particular restriction.
338 * @param[in,out] restrs Restrictions to add to.
339 *
340 * @return LY_ERR values.
341 */
342static LY_ERR
343lysp_stmt_restrs(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt restr_kw, struct lysp_restr **restrs)
344{
345 struct lysp_restr *restr;
346
Michal Vaskob36053d2020-03-26 15:49:30 +0100347 LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *restrs, restr, LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200348 return lysp_stmt_restr(ctx, stmt, restr_kw, restr);
349}
350
351/**
352 * @brief Parse the value or position statement. Substatement of type enum statement.
353 *
354 * @param[in] ctx yang parser context for logging.
355 * @param[in,out] data Data to read from, always moved to currently handled character.
356 * @param[in] val_kw Type of this particular keyword.
357 * @param[in,out] value Value to write to.
358 * @param[in,out] flags Flags to write to.
359 * @param[in,out] exts Extension instances to add to.
360 *
361 * @return LY_ERR values.
362 */
363static LY_ERR
364lysp_stmt_type_enum_value_pos(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt val_kw, int64_t *value, uint16_t *flags,
Radek Krejci0f969882020-08-21 16:56:47 +0200365 struct lysp_ext_instance **exts)
Radek Krejci335332a2019-09-05 13:03:35 +0200366{
367 size_t arg_len;
368 char *ptr = NULL;
369 long int num = 0;
370 unsigned long int unum = 0;
371 struct lysp_stmt *child;
372
373 if (*flags & LYS_SET_VALUE) {
374 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(val_kw));
375 return LY_EVALID;
376 }
377 *flags |= LYS_SET_VALUE;
378
379 LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
380
381 arg_len = strlen(stmt->arg);
382 if (!arg_len || (stmt->arg[0] == '+') || ((stmt->arg[0] == '0') && (arg_len > 1)) || ((val_kw == LY_STMT_POSITION) && !strncmp(stmt->arg, "-0", 2))) {
383 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, ly_stmt2str(val_kw));
384 goto error;
385 }
386
387 errno = 0;
388 if (val_kw == LY_STMT_VALUE) {
389 num = strtol(stmt->arg, &ptr, 10);
Michal Vasko69730152020-10-09 16:30:07 +0200390 if ((num < INT64_C(-2147483648)) || (num > INT64_C(2147483647))) {
Radek Krejci335332a2019-09-05 13:03:35 +0200391 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, ly_stmt2str(val_kw));
392 goto error;
393 }
394 } else {
395 unum = strtoul(stmt->arg, &ptr, 10);
396 if (unum > UINT64_C(4294967295)) {
397 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, ly_stmt2str(val_kw));
398 goto error;
399 }
400 }
401 /* we have not parsed the whole argument */
402 if ((size_t)(ptr - stmt->arg) != arg_len) {
403 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, ly_stmt2str(val_kw));
404 goto error;
405 }
406 if (errno == ERANGE) {
407 LOGVAL_PARSER(ctx, LY_VCODE_OOB, arg_len, stmt->arg, ly_stmt2str(val_kw));
408 goto error;
409 }
410 if (val_kw == LY_STMT_VALUE) {
411 *value = num;
412 } else {
413 *value = unum;
414 }
415
416 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200417 struct ly_in *in;
418 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
419 enum ly_stmt kw = lysp_match_kw(NULL, in);
420 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200421
422 switch (kw) {
423 case LY_STMT_EXTENSION_INSTANCE:
424 LY_CHECK_RET(lysp_stmt_ext(ctx, child, val_kw == LY_STMT_VALUE ? LYEXT_SUBSTMT_VALUE : LYEXT_SUBSTMT_POSITION, 0, exts));
425 break;
426 default:
427 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(val_kw));
428 return LY_EVALID;
429 }
430 }
431 return LY_SUCCESS;
432
433error:
434 return LY_EVALID;
435}
436
437/**
438 * @brief Parse the enum or bit statement. Substatement of type statement.
439 *
440 * @param[in] ctx yang parser context for logging.
441 * @param[in,out] data Data to read from, always moved to currently handled character.
442 * @param[in] enum_kw Type of this particular keyword.
443 * @param[in,out] enums Enums or bits to add to.
444 *
445 * @return LY_ERR values.
446 */
447static LY_ERR
448lysp_stmt_type_enum(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt enum_kw, struct lysp_type_enum **enums)
449{
450 struct lysp_type_enum *enm;
451 const struct lysp_stmt *child;
452
453 LY_CHECK_RET(lysp_stmt_validate_value(ctx, enum_kw == LY_STMT_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, stmt->arg));
454
Michal Vaskob36053d2020-03-26 15:49:30 +0100455 LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *enums, enm, LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200456
457 if (enum_kw == LY_STMT_ENUM) {
458 LY_CHECK_RET(lysp_check_enum_name(ctx, stmt->arg, strlen(stmt->arg)));
459 } /* else nothing specific for YANG_BIT */
460
Radek Krejci011e4aa2020-09-04 15:22:31 +0200461 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &enm->name));
Radek Krejci335332a2019-09-05 13:03:35 +0200462 CHECK_UNIQUENESS(ctx, *enums, name, ly_stmt2str(enum_kw), enm->name);
463
464 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200465 struct ly_in *in;
466 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
467 enum ly_stmt kw = lysp_match_kw(NULL, in);
468 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200469
470 switch (kw) {
471 case LY_STMT_DESCRIPTION:
472 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_DESCRIPTION, 0, &enm->dsc, Y_STR_ARG, &enm->exts));
473 break;
474 case LY_STMT_IF_FEATURE:
475 PARSER_CHECK_STMTVER2_RET(ctx, "if-feature", ly_stmt2str(enum_kw));
Michal Vasko7f45cf22020-10-01 12:49:44 +0200476 LY_CHECK_RET(lysp_stmt_qnames(ctx, child, LYEXT_SUBSTMT_IFFEATURE, &enm->iffeatures, Y_STR_ARG, &enm->exts));
Radek Krejci335332a2019-09-05 13:03:35 +0200477 break;
478 case LY_STMT_REFERENCE:
479 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_REFERENCE, 0, &enm->ref, Y_STR_ARG, &enm->exts));
480 break;
481 case LY_STMT_STATUS:
482 LY_CHECK_RET(lysp_stmt_status(ctx, child, &enm->flags, &enm->exts));
483 break;
484 case LY_STMT_VALUE:
485 LY_CHECK_ERR_RET(enum_kw == LY_STMT_BIT, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw),
Michal Vasko69730152020-10-09 16:30:07 +0200486 ly_stmt2str(enum_kw)), LY_EVALID);
Radek Krejci335332a2019-09-05 13:03:35 +0200487 LY_CHECK_RET(lysp_stmt_type_enum_value_pos(ctx, child, kw, &enm->value, &enm->flags, &enm->exts));
488 break;
489 case LY_STMT_POSITION:
490 LY_CHECK_ERR_RET(enum_kw == LY_STMT_ENUM, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw),
Michal Vasko69730152020-10-09 16:30:07 +0200491 ly_stmt2str(enum_kw)), LY_EVALID);
Radek Krejci335332a2019-09-05 13:03:35 +0200492 LY_CHECK_RET(lysp_stmt_type_enum_value_pos(ctx, child, kw, &enm->value, &enm->flags, &enm->exts));
493 break;
494 case LY_STMT_EXTENSION_INSTANCE:
495 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_SELF, 0, &enm->exts));
496 break;
497 default:
498 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(enum_kw));
499 return LY_EVALID;
500 }
501 }
502 return LY_SUCCESS;
503}
504
505/**
506 * @brief Parse the fraction-digits statement. Substatement of type statement.
507 *
508 * @param[in] ctx yang parser context for logging.
509 * @param[in,out] data Data to read from, always moved to currently handled character.
510 * @param[in,out] fracdig Value to write to.
511 * @param[in,out] exts Extension instances to add to.
512 *
513 * @return LY_ERR values.
514 */
515static LY_ERR
516lysp_stmt_type_fracdigits(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *fracdig, struct lysp_ext_instance **exts)
517{
518 char *ptr;
519 size_t arg_len;
520 unsigned long int num;
521 const struct lysp_stmt *child;
522
523 if (*fracdig) {
524 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "fraction-digits");
525 return LY_EVALID;
526 }
527
528 LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
529 arg_len = strlen(stmt->arg);
530 if (!arg_len || (stmt->arg[0] == '0') || !isdigit(stmt->arg[0])) {
531 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "fraction-digits");
532 return LY_EVALID;
533 }
534
535 errno = 0;
536 num = strtoul(stmt->arg, &ptr, 10);
537 /* we have not parsed the whole argument */
538 if ((size_t)(ptr - stmt->arg) != arg_len) {
539 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "fraction-digits");
540 return LY_EVALID;
541 }
542 if ((errno == ERANGE) || (num > 18)) {
543 LOGVAL_PARSER(ctx, LY_VCODE_OOB, arg_len, stmt->arg, "fraction-digits");
544 return LY_EVALID;
545 }
546 *fracdig = num;
547
548 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200549 struct ly_in *in;
550 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
551 enum ly_stmt kw = lysp_match_kw(NULL, in);
552 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200553
554 switch (kw) {
555 case LY_STMT_EXTENSION_INSTANCE:
556 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_FRACDIGITS, 0, exts));
557 break;
558 default:
559 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "fraction-digits");
560 return LY_EVALID;
561 }
562 }
563 return LY_SUCCESS;
564}
565
566/**
567 * @brief Parse the require-instance statement. Substatement of type statement.
568 *
569 * @param[in] ctx yang parser context for logging.
570 * @param[in,out] data Data to read from, always moved to currently handled character.
571 * @param[in,out] reqinst Value to write to.
572 * @param[in,out] flags Flags to write to.
573 * @param[in,out] exts Extension instances to add to.
574 *
575 * @return LY_ERR values.
576 */
577static LY_ERR
578lysp_stmt_type_reqinstance(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *reqinst, uint16_t *flags,
Radek Krejci0f969882020-08-21 16:56:47 +0200579 struct lysp_ext_instance **exts)
Radek Krejci335332a2019-09-05 13:03:35 +0200580{
581 size_t arg_len;
582 struct lysp_stmt *child;
583
584 if (*flags & LYS_SET_REQINST) {
585 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "require-instance");
586 return LY_EVALID;
587 }
588 *flags |= LYS_SET_REQINST;
589
590 LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
591 arg_len = strlen(stmt->arg);
592 if ((arg_len == 4) && !strncmp(stmt->arg, "true", arg_len)) {
593 *reqinst = 1;
594 } else if ((arg_len != 5) || strncmp(stmt->arg, "false", arg_len)) {
595 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "require-instance");
596 return LY_EVALID;
597 }
598
599 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200600 struct ly_in *in;
601 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
602 enum ly_stmt kw = lysp_match_kw(NULL, in);
603 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200604
605 switch (kw) {
606 case LY_STMT_EXTENSION_INSTANCE:
607 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_REQINSTANCE, 0, exts));
608 break;
609 default:
610 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "require-instance");
611 return LY_EVALID;
612 }
613 }
614 return LY_SUCCESS;
615}
616
617/**
618 * @brief Parse the modifier statement. Substatement of type pattern statement.
619 *
620 * @param[in] ctx yang parser context for logging.
621 * @param[in,out] data Data to read from, always moved to currently handled character.
622 * @param[in,out] pat Value to write to.
623 * @param[in,out] exts Extension instances to add to.
624 *
625 * @return LY_ERR values.
626 */
627static LY_ERR
628lysp_stmt_type_pattern_modifier(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, const char **pat, struct lysp_ext_instance **exts)
629{
630 size_t arg_len;
631 char *buf;
632 const struct lysp_stmt *child;
633
634 if ((*pat)[0] == 0x15) {
635 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "modifier");
636 return LY_EVALID;
637 }
638
639 LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
640 arg_len = strlen(stmt->arg);
641 if ((arg_len != 12) || strncmp(stmt->arg, "invert-match", arg_len)) {
642 LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "modifier");
643 return LY_EVALID;
644 }
645
646 /* replace the value in the dictionary */
647 buf = malloc(strlen(*pat) + 1);
Michal Vaskob36053d2020-03-26 15:49:30 +0100648 LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200649 strcpy(buf, *pat);
Michal Vaskob36053d2020-03-26 15:49:30 +0100650 lydict_remove(PARSER_CTX(ctx), *pat);
Radek Krejci335332a2019-09-05 13:03:35 +0200651
652 assert(buf[0] == 0x06);
653 buf[0] = 0x15;
Radek Krejci011e4aa2020-09-04 15:22:31 +0200654 LY_CHECK_RET(lydict_insert_zc(PARSER_CTX(ctx), buf, pat));
Radek Krejci335332a2019-09-05 13:03:35 +0200655
656 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200657 struct ly_in *in;
658 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
659 enum ly_stmt kw = lysp_match_kw(NULL, in);
660 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200661
662 switch (kw) {
663 case LY_STMT_EXTENSION_INSTANCE:
664 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_MODIFIER, 0, exts));
665 break;
666 default:
667 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "modifier");
668 return LY_EVALID;
669 }
670 }
671 return LY_SUCCESS;
672}
673
674/**
675 * @brief Parse the pattern statement. Substatement of type statement.
676 *
677 * @param[in] ctx yang parser context for logging.
678 * @param[in,out] data Data to read from, always moved to currently handled character.
679 * @param[in,out] patterns Restrictions to add to.
680 *
681 * @return LY_ERR values.
682 */
683static LY_ERR
684lysp_stmt_type_pattern(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_restr **patterns)
685{
686 char *buf;
687 size_t arg_len;
688 const struct lysp_stmt *child;
689 struct lysp_restr *restr;
690
691 LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
Michal Vaskob36053d2020-03-26 15:49:30 +0100692 LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *patterns, restr, LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200693 arg_len = strlen(stmt->arg);
694
695 /* add special meaning first byte */
696 buf = malloc(arg_len + 2);
Michal Vaskob36053d2020-03-26 15:49:30 +0100697 LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200698 memmove(buf + 1, stmt->arg, arg_len);
699 buf[0] = 0x06; /* pattern's default regular-match flag */
700 buf[arg_len + 1] = '\0'; /* terminating NULL byte */
Michal Vasko7f45cf22020-10-01 12:49:44 +0200701 LY_CHECK_RET(lydict_insert_zc(PARSER_CTX(ctx), buf, &restr->arg.str));
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200702 restr->arg.mod = ctx->parsed_mod;
Radek Krejci335332a2019-09-05 13:03:35 +0200703
704 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200705 struct ly_in *in;
706 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
707 enum ly_stmt kw = lysp_match_kw(NULL, in);
708 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200709
710 switch (kw) {
711 case LY_STMT_DESCRIPTION:
712 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
713 break;
714 case LY_STMT_REFERENCE:
715 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
716 break;
717 case LY_STMT_ERROR_APP_TAG:
718 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
719 break;
720 case LY_STMT_ERROR_MESSAGE:
721 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
722 break;
723 case LY_STMT_MODIFIER:
724 PARSER_CHECK_STMTVER2_RET(ctx, "modifier", "pattern");
Michal Vasko7f45cf22020-10-01 12:49:44 +0200725 LY_CHECK_RET(lysp_stmt_type_pattern_modifier(ctx, child, &restr->arg.str, &restr->exts));
Radek Krejci335332a2019-09-05 13:03:35 +0200726 break;
727 case LY_STMT_EXTENSION_INSTANCE:
728 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_SELF, 0, &restr->exts));
729 break;
730 default:
731 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "pattern");
732 return LY_EVALID;
733 }
734 }
735 return LY_SUCCESS;
736}
737
738/**
739 * @brief Parse the type statement.
740 *
741 * @param[in] ctx yang parser context for logging.
742 * @param[in,out] data Data to read from, always moved to currently handled character.
743 * @param[in,out] type Type to wrote to.
744 *
745 * @return LY_ERR values.
746 */
747static LY_ERR
748lysp_stmt_type(struct lys_parser_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_type *type)
749{
750 struct lysp_type *nest_type;
751 const struct lysp_stmt *child;
Radek Krejcib1247842020-07-02 16:22:38 +0200752 const char *str_path = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +0200753 LY_ERR ret;
Radek Krejci335332a2019-09-05 13:03:35 +0200754
755 if (type->name) {
756 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "type");
757 return LY_EVALID;
758 }
Radek Krejci011e4aa2020-09-04 15:22:31 +0200759 LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &type->name));
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200760 type->pmod = ctx->parsed_mod;
Radek Krejci335332a2019-09-05 13:03:35 +0200761
762 for (child = stmt->child; child; child = child->next) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200763 struct ly_in *in;
764 LY_CHECK_RET(ly_in_new_memory(child->stmt, &in));
765 enum ly_stmt kw = lysp_match_kw(NULL, in);
766 ly_in_free(in, 0);
Radek Krejci335332a2019-09-05 13:03:35 +0200767
768 switch (kw) {
769 case LY_STMT_BASE:
770 LY_CHECK_RET(lysp_stmt_text_fields(ctx, child, LYEXT_SUBSTMT_BASE, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts));
771 type->flags |= LYS_SET_BASE;
772 break;
773 case LY_STMT_BIT:
774 LY_CHECK_RET(lysp_stmt_type_enum(ctx, child, kw, &type->bits));
775 type->flags |= LYS_SET_BIT;
776 break;
777 case LY_STMT_ENUM:
778 LY_CHECK_RET(lysp_stmt_type_enum(ctx, child, kw, &type->enums));
779 type->flags |= LYS_SET_ENUM;
780 break;
781 case LY_STMT_FRACTION_DIGITS:
782 LY_CHECK_RET(lysp_stmt_type_fracdigits(ctx, child, &type->fraction_digits, &type->exts));
783 type->flags |= LYS_SET_FRDIGITS;
784 break;
785 case LY_STMT_LENGTH:
786 if (type->length) {
787 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
788 return LY_EVALID;
789 }
790 type->length = calloc(1, sizeof *type->length);
Michal Vaskob36053d2020-03-26 15:49:30 +0100791 LY_CHECK_ERR_RET(!type->length, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200792
793 LY_CHECK_RET(lysp_stmt_restr(ctx, child, kw, type->length));
794 type->flags |= LYS_SET_LENGTH;
795 break;
796 case LY_STMT_PATH:
Michal Vasko004d3152020-06-11 19:59:22 +0200797 LY_CHECK_RET(lysp_stmt_text_field(ctx, child, LYEXT_SUBSTMT_PATH, 0, &str_path, Y_STR_ARG, &type->exts));
Michal Vasko6b26e742020-07-17 15:02:10 +0200798 ret = ly_path_parse(PARSER_CTX(ctx), NULL, str_path, 0, LY_PATH_BEGIN_EITHER, LY_PATH_LREF_TRUE,
Michal Vasko69730152020-10-09 16:30:07 +0200799 LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &type->path);
Michal Vasko004d3152020-06-11 19:59:22 +0200800 lydict_remove(PARSER_CTX(ctx), str_path);
801 LY_CHECK_RET(ret);
Radek Krejci335332a2019-09-05 13:03:35 +0200802 type->flags |= LYS_SET_PATH;
803 break;
804 case LY_STMT_PATTERN:
805 LY_CHECK_RET(lysp_stmt_type_pattern(ctx, child, &type->patterns));
806 type->flags |= LYS_SET_PATTERN;
807 break;
808 case LY_STMT_RANGE:
809 if (type->range) {
810 LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
811 return LY_EVALID;
812 }
813 type->range = calloc(1, sizeof *type->range);
Michal Vaskob36053d2020-03-26 15:49:30 +0100814 LY_CHECK_ERR_RET(!type->range, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200815
816 LY_CHECK_RET(lysp_stmt_restr(ctx, child, kw, type->range));
817 type->flags |= LYS_SET_RANGE;
818 break;
819 case LY_STMT_REQUIRE_INSTANCE:
820 LY_CHECK_RET(lysp_stmt_type_reqinstance(ctx, child, &type->require_instance, &type->flags, &type->exts));
821 /* LYS_SET_REQINST checked and set inside parse_type_reqinstance() */
822 break;
823 case LY_STMT_TYPE:
Michal Vaskob36053d2020-03-26 15:49:30 +0100824 LY_ARRAY_NEW_RET(PARSER_CTX(ctx), type->types, nest_type, LY_EMEM);
Radek Krejci335332a2019-09-05 13:03:35 +0200825 LY_CHECK_RET(lysp_stmt_type(ctx, child, nest_type));
826 type->flags |= LYS_SET_TYPE;
827 break;
828 case LY_STMT_EXTENSION_INSTANCE:
829 LY_CHECK_RET(lysp_stmt_ext(ctx, child, LYEXT_SUBSTMT_SELF, 0, &type->exts));
830 break;
831 default:
832 LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "type");
833 return LY_EVALID;
834 }
835 }
836 return LY_SUCCESS;
837}
838
839LY_ERR
Radek Krejciad5963b2019-09-06 16:03:05 +0200840lysp_stmt_parse(struct lysc_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt kw, void **result, struct lysp_ext_instance **exts)
Radek Krejci335332a2019-09-05 13:03:35 +0200841{
Radek Krejciad5963b2019-09-06 16:03:05 +0200842 LY_ERR ret = LY_SUCCESS;
Michal Vaskob36053d2020-03-26 15:49:30 +0100843 struct lys_yang_parser_ctx pctx = {0};
Radek Krejci335332a2019-09-05 13:03:35 +0200844
Michal Vaskob36053d2020-03-26 15:49:30 +0100845 pctx.format = LYS_IN_YANG;
Michal Vasko5d24f6c2020-10-13 13:49:06 +0200846 pctx.parsed_mod = ctx->pmod;
Radek Krejci335332a2019-09-05 13:03:35 +0200847 pctx.pos_type = LY_VLOG_STR;
848 pctx.path = ctx->path;
849
Michal Vaskod989ba02020-08-24 10:59:24 +0200850 switch (kw) {
Michal Vasko69730152020-10-09 16:30:07 +0200851 case LY_STMT_STATUS:
Michal Vasko22df3f02020-08-24 13:29:22 +0200852 ret = lysp_stmt_status((struct lys_parser_ctx *)&pctx, stmt, *(uint16_t **)result, exts);
Radek Krejciad5963b2019-09-06 16:03:05 +0200853 break;
Radek Krejci335332a2019-09-05 13:03:35 +0200854 case LY_STMT_TYPE: {
855 struct lysp_type *type;
856 type = calloc(1, sizeof *type);
857
Michal Vaskob36053d2020-03-26 15:49:30 +0100858 ret = lysp_stmt_type((struct lys_parser_ctx *)&pctx, stmt, type);
Radek Krejci335332a2019-09-05 13:03:35 +0200859 (*result) = type;
860 break;
Radek Krejci0f969882020-08-21 16:56:47 +0200861 }
Radek Krejci335332a2019-09-05 13:03:35 +0200862 default:
863 LOGINT(ctx->ctx);
864 return LY_EINT;
865 }
866
Radek Krejciad5963b2019-09-06 16:03:05 +0200867 return ret;
Radek Krejci335332a2019-09-05 13:03:35 +0200868}