| /** |
| * @file parse.c |
| * @author Michal Vasko <mvasko@cesnet.cz> |
| * @brief parsing functions implementation |
| * |
| * Copyright (c) 2015 CESNET, z.s.p.o. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * 3. Neither the name of the Company nor the names of its contributors |
| * may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| */ |
| |
| #include <ctype.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| |
| #include "parse.h" |
| #include "resolve.h" |
| #include "common.h" |
| |
| /** |
| * @brief Checks the syntax of length or range statement, |
| * on success checks the semantics as well. |
| * |
| * @param[in] expr Length or range expression. |
| * @param[in] type Type with the restriction. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. |
| */ |
| int |
| check_length_range(const char *expr, struct lys_type *type) |
| { |
| struct len_ran_intv *intv = NULL, *tmp_intv; |
| const char *c = expr; |
| char *tail; |
| int ret = EXIT_FAILURE, flg = 1; /* first run flag */ |
| |
| assert(expr); |
| |
| lengthpart: |
| |
| while (isspace(*c)) { |
| c++; |
| } |
| |
| /* lower boundary or explicit number */ |
| if (!strncmp(c, "max", 3)) { |
| max: |
| c += 3; |
| while (isspace(*c)) { |
| c++; |
| } |
| if (*c != '\0') { |
| goto error; |
| } |
| |
| goto syntax_ok; |
| |
| } else if (!strncmp(c, "min", 3)) { |
| if (!flg) { |
| /* min cannot be used elsewhere than in the first length-part */ |
| goto error; |
| } else { |
| flg = 0; |
| } |
| c += 3; |
| while (isspace(*c)) { |
| c++; |
| } |
| |
| if (*c == '|') { |
| c++; |
| /* process next length-parth */ |
| goto lengthpart; |
| } else if (*c == '\0') { |
| goto syntax_ok; |
| } else if (!strncmp(c, "..", 2)) { |
| upper: |
| c += 2; |
| while (isspace(*c)) { |
| c++; |
| } |
| if (*c == '\0') { |
| goto error; |
| } |
| |
| /* upper boundary */ |
| if (!strncmp(c, "max", 3)) { |
| goto max; |
| } |
| |
| if (!isdigit(*c) && (*c != '+') && (*c != '-')) { |
| goto error; |
| } |
| |
| errno = 0; |
| strtoll(c, &tail, 10); |
| if (errno) { |
| goto error; |
| } |
| c = tail; |
| while (isspace(*c)) { |
| c++; |
| } |
| if (*c == '\0') { |
| goto syntax_ok; |
| } else if (*c == '|') { |
| c++; |
| /* process next length-parth */ |
| goto lengthpart; |
| } else { |
| goto error; |
| } |
| } else { |
| goto error; |
| } |
| |
| } else if (isdigit(*c) || (*c == '-') || (*c == '+')) { |
| /* number */ |
| errno = 0; |
| strtoll(c, &tail, 10); |
| if (errno) { |
| /* out of range value */ |
| goto error; |
| } |
| c = tail; |
| while (isspace(*c)) { |
| c++; |
| } |
| |
| if (*c == '|') { |
| c++; |
| /* process next length-parth */ |
| goto lengthpart; |
| } else if (*c == '\0') { |
| goto syntax_ok; |
| } else if (!strncmp(c, "..", 2)) { |
| goto upper; |
| } |
| |
| } else { |
| goto error; |
| } |
| |
| syntax_ok: |
| |
| if (resolve_len_ran_interval(expr, type, 1, &intv)) { |
| goto error; |
| } |
| |
| ret = EXIT_SUCCESS; |
| |
| error: |
| |
| while (intv) { |
| tmp_intv = intv->next; |
| free(intv); |
| intv = tmp_intv; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Parse an identifier. |
| * |
| * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l')) |
| * identifier = (ALPHA / "_") |
| * *(ALPHA / DIGIT / "_" / "-" / ".") |
| * |
| * @param[in] id Identifier in question. |
| * |
| * @return Number of characters successfully parsed. |
| */ |
| static int |
| parse_identifier(const char *id) |
| { |
| int parsed = 0; |
| |
| if (((id[0] == 'x') || (id[0] == 'X')) |
| && ((id[1] == 'm') || (id[0] == 'M')) |
| && ((id[2] == 'l') || (id[2] == 'L'))) { |
| return -parsed; |
| } |
| |
| if (!isalpha(id[0]) && (id[0] != '_')) { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isalnum(id[0]) || (id[0] == '_') || (id[0] == '-') || (id[0] == '.')) { |
| ++parsed; |
| ++id; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse a node-identifier. |
| * |
| * node-identifier = [prefix ":"] identifier |
| * |
| * @param[in] id Identifier in question. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| int |
| parse_node_identifier(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len) |
| { |
| int parsed = 0, ret; |
| |
| assert(id); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| |
| if ((ret = parse_identifier(id)) < 1) { |
| return ret; |
| } |
| |
| if (prefix) { |
| *prefix = id; |
| } |
| if (pref_len) { |
| *pref_len = ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| /* there is prefix */ |
| if (id[0] == ':') { |
| ++parsed; |
| ++id; |
| |
| /* there isn't */ |
| } else { |
| if (name && prefix) { |
| *name = *prefix; |
| } |
| if (prefix) { |
| *prefix = NULL; |
| } |
| |
| if (nam_len && pref_len) { |
| *nam_len = *pref_len; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| |
| return parsed; |
| } |
| |
| /* identifier (node name) */ |
| if ((ret = parse_identifier(id)) < 1) { |
| return -parsed+ret; |
| } |
| |
| if (name) { |
| *name = id; |
| } |
| if (nam_len) { |
| *nam_len = ret; |
| } |
| |
| return parsed+ret; |
| } |
| |
| /** |
| * @brief Parse a path-predicate (leafref). |
| * |
| * path-predicate = "[" *WSP path-equality-expr *WSP "]" |
| * path-equality-expr = node-identifier *WSP "=" *WSP path-key-expr |
| * |
| * @param[in] id Identifier in question. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] path_key_expr Points to the path-key-expr. |
| * @param[out] pke_len Length of the path-key-expr. |
| * @param[out] has_predicate Flag to mark whether there is another predicate following. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| int |
| parse_path_predicate(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, const char **path_key_expr, int *pke_len, int *has_predicate) |
| { |
| const char *ptr; |
| int parsed = 0, ret; |
| |
| assert(id); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (path_key_expr) { |
| *path_key_expr = NULL; |
| } |
| if (pke_len) { |
| *pke_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (id[0] != '[') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '=') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if ((ptr = strchr(id, ']')) == NULL) { |
| return -parsed; |
| } |
| |
| --ptr; |
| while (isspace(ptr[0])) { |
| --ptr; |
| } |
| ++ptr; |
| |
| ret = ptr-id; |
| if (path_key_expr) { |
| *path_key_expr = id; |
| } |
| if (pke_len) { |
| *pke_len = ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| assert(id[0] == ']'); |
| |
| if (id[1] == '[') { |
| *has_predicate = 1; |
| } |
| |
| return parsed+1; |
| } |
| |
| /** |
| * @brief Parse a path-key-expr (leafref). First call parses "current()", all |
| * the ".." and the first node-identifier, other calls parse a single |
| * node-identifier each. |
| * |
| * path-key-expr = current-function-invocation *WSP "/" *WSP |
| * rel-path-keyexpr |
| * rel-path-keyexpr = 1*(".." *WSP "/" *WSP) |
| * *(node-identifier *WSP "/" *WSP) |
| * node-identifier |
| * |
| * @param[in] id Identifier in question. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] parent_times Number of ".." in the path. Must be 0 on the first call, |
| * must not be changed between consecutive calls. |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| int |
| parse_path_key_expr(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, int *parent_times) |
| { |
| int parsed = 0, ret, par_times = 0; |
| |
| assert(id); |
| assert(parent_times); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| |
| if (!*parent_times) { |
| /* current-function-invocation *WSP "/" *WSP rel-path-keyexpr */ |
| if (strncmp(id, "current()", 9)) { |
| return -parsed; |
| } |
| |
| parsed += 9; |
| id += 9; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| /* rel-path-keyexpr */ |
| if (strncmp(id, "..", 2)) { |
| return -parsed; |
| } |
| ++par_times; |
| |
| parsed += 2; |
| id += 2; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| } |
| |
| /* 1*(".." *WSP "/" *WSP) *(node-identifier *WSP "/" *WSP) node-identifier |
| * |
| * first parent reference with whitespaces already parsed |
| */ |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| while (!strncmp(id, "..", 2) && !*parent_times) { |
| ++par_times; |
| |
| parsed += 2; |
| id += 2; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| } |
| |
| if (!*parent_times) { |
| *parent_times = par_times; |
| } |
| |
| /* all parent references must be parsed at this point */ |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse path-arg (leafref). |
| * |
| * path-arg = absolute-path / relative-path |
| * absolute-path = 1*("/" (node-identifier *path-predicate)) |
| * relative-path = 1*(".." "/") descendant-path |
| * |
| * @param[in] id Identifier in question. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] parent_times Number of ".." in the path. Must be 0 on the first call, |
| * must not be changed between consecutive calls. -1 if the |
| * path is relative. |
| * @param[out] has_predicate Flag to mark whether there is a predicate specified. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| int |
| parse_path_arg(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, int *parent_times, int *has_predicate) |
| { |
| int parsed = 0, ret, par_times = 0; |
| |
| assert(id); |
| assert(parent_times); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (!*parent_times && !strncmp(id, "..", 2)) { |
| ++par_times; |
| |
| parsed += 2; |
| id += 2; |
| |
| while (!strncmp(id, "/..", 3)) { |
| ++par_times; |
| |
| parsed += 3; |
| id += 3; |
| } |
| } |
| |
| if (!*parent_times) { |
| if (par_times) { |
| *parent_times = par_times; |
| } else { |
| *parent_times = -1; |
| } |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| /* skip '/' */ |
| ++parsed; |
| ++id; |
| |
| /* node-identifier ([prefix:]identifier) */ |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed-ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| /* there is no predicate */ |
| if ((id[0] == '/') || !id[0]) { |
| return parsed; |
| } else if (id[0] != '[') { |
| return -parsed; |
| } |
| |
| if (has_predicate) { |
| *has_predicate = 1; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse instance-identifier (instance-identifier). |
| * |
| * instance-identifier = 1*("/" (node-identifier *predicate)) |
| * |
| * @param[in] id Identifier in question. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] has_predicate Flag to mark whether there is a predicate specified. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| int |
| parse_instance_identifier(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, int *has_predicate) |
| { |
| int parsed = 0, ret; |
| |
| assert(id); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| if ((id[0] == '[') && has_predicate) { |
| *has_predicate = 1; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse predicate (instance-identifier). |
| * |
| * predicate = "[" *WSP (predicate-expr / pos) *WSP "]" |
| * predicate-expr = (node-identifier / ".") *WSP "=" *WSP |
| * ((DQUOTE string DQUOTE) / |
| * (SQUOTE string SQUOTE)) |
| * pos = non-negative-integer-value |
| * |
| * @param[in] id Identifier in question. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. Can be identifier (from node-identifier), "." or pos. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] value Value the node-identifier must have (string from the grammar), |
| * NULL if there is not any. |
| * @param[out] val_len Length of the value, 0 if there is not any. |
| * @param[out] has_predicate Flag to mark whether there is a predicate specified. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| int |
| parse_predicate(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, const char **value, int *val_len, int *has_predicate) |
| { |
| const char *ptr; |
| int parsed = 0, ret; |
| char quote; |
| |
| assert(id); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (value) { |
| *value = NULL; |
| } |
| if (val_len) { |
| *val_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (id[0] != '[') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| /* pos */ |
| if (isdigit(id[0])) { |
| if (name) { |
| *name = id; |
| } |
| |
| if (id[0] == '0') { |
| ++parsed; |
| ++id; |
| |
| if (isdigit(id[0])) { |
| return -parsed; |
| } |
| } |
| |
| while (isdigit(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (nam_len) { |
| *nam_len = id-(*name); |
| } |
| |
| /* "." */ |
| } else if (id[0] == '.') { |
| if (name) { |
| *name = id; |
| } |
| if (nam_len) { |
| *nam_len = 1; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| /* node-identifier */ |
| } else { |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| } |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '=') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| /* ((DQUOTE string DQUOTE) / (SQUOTE string SQUOTE)) */ |
| if ((id[0] == '\"') || (id[0] == '\'')) { |
| quote = id[0]; |
| |
| ++parsed; |
| ++id; |
| |
| if ((ptr = strchr(id, quote)) == NULL) { |
| return -parsed; |
| } |
| ret = ptr-id; |
| |
| if (value) { |
| *value = id; |
| } |
| if (val_len) { |
| *val_len = ret; |
| } |
| |
| parsed += ret+1; |
| id += ret+1; |
| } else { |
| return -parsed; |
| } |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != ']') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| if ((id[0] == '[') && has_predicate) { |
| *has_predicate = 1; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse schema-nodeid. |
| * |
| * schema-nodeid = absolute-schema-nodeid / |
| * descendant-schema-nodeid |
| * absolute-schema-nodeid = 1*("/" node-identifier) |
| * descendant-schema-nodeid = |
| * node-identifier |
| * absolute-schema-nodeid |
| * |
| * @param[in] id Identifier in question. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. Can be identifier (from node-identifier), "." or pos. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] is_relative Flag to mark whether the nodeid is absolute or descendant. Must be -1 |
| * on the first call, must not be changed between consecutive calls. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| int |
| parse_schema_nodeid(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, int *is_relative) |
| { |
| int parsed = 0, ret; |
| |
| assert(id); |
| assert(is_relative); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| |
| if (id[0] != '/') { |
| if (*is_relative != -1) { |
| return -parsed; |
| } else { |
| *is_relative = 1; |
| } |
| } else { |
| if (*is_relative == -1) { |
| *is_relative = 0; |
| } |
| ++parsed; |
| ++id; |
| } |
| |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| |
| return parsed+ret; |
| } |