blob: 3bf0e1ab00df8bcb0db24f9c0a6faeca821bb17a [file] [log] [blame]
/**
* @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;
}