diff --git a/sys/Makefile.include b/sys/Makefile.include index 80fd4b458e..5f727c4bdd 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -97,3 +97,7 @@ endif ifneq (native,$(BOARD)) INCLUDES += -I$(RIOTBASE)/sys/libc/include endif + +ifneq (,$(filter clif, $(USEMODULE))) + INCLUDES += -I$(RIOTBASE)/sys/clif/include +endif diff --git a/sys/clif/Makefile b/sys/clif/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/clif/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/clif/clif.c b/sys/clif/clif.c new file mode 100644 index 0000000000..fb310291cf --- /dev/null +++ b/sys/clif/clif.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2019 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_clif + * @{ + * + * @file + * @brief CoRE Link format encoding and decoding library implementation + * + * @author Leandro Lanzieri + * @} + */ + +#include +#include +#include + +#include "clif.h" +#include "clif_internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* returns the correspondant attribute string */ +static const char *_attr_to_str[] = { + [CLIF_ATTR_ANCHOR] = LF_ATTR_ANCHOR, + [CLIF_ATTR_REL] = LF_ATTR_REL_TYPE, + [CLIF_ATTR_LANG] = LF_ATTR_LANG, + [CLIF_ATTR_MEDIA] = LF_ATTR_MEDIA, + [CLIF_ATTR_TITLE] = LF_ATTR_TITLE, + [CLIF_ATTR_TITLE_EXT] = LF_ATTR_TITLE_EXT, + [CLIF_ATTR_TYPE] = LF_ATTR_TYPE, + [CLIF_ATTR_RT] = LF_ATTR_RES_TYPE, + [CLIF_ATTR_IF] = LF_ATTR_IF_DESC, + [CLIF_ATTR_SZ] = LF_ATTR_SIZE, + [CLIF_ATTR_CT] = LF_ATTR_CT, + [CLIF_ATTR_OBS] = LF_ATTR_OBS +}; + +/* returns the correspondant attribute string size */ +static const unsigned _attr_to_size[] = { + [CLIF_ATTR_ANCHOR] = LF_ATTR_ANCHOR_S, + [CLIF_ATTR_REL] = LF_ATTR_REL_TYPE_S, + [CLIF_ATTR_LANG] = LF_ATTR_LANG_S, + [CLIF_ATTR_MEDIA] = LF_ATTR_MEDIA_S, + [CLIF_ATTR_TITLE] = LF_ATTR_TITLE_S, + [CLIF_ATTR_TITLE_EXT] = LF_ATTR_TITLE_EXT_S, + [CLIF_ATTR_TYPE] = LF_ATTR_TYPE_S, + [CLIF_ATTR_RT] = LF_ATTR_RES_TYPE_S, + [CLIF_ATTR_IF] = LF_ATTR_IF_DESC_S, + [CLIF_ATTR_SZ] = LF_ATTR_SIZE_S, + [CLIF_ATTR_CT] = LF_ATTR_CT_S, + [CLIF_ATTR_OBS] = LF_ATTR_OBS_S +}; + +/* do not count extension attr type */ +#define ATTRS_NUMOF (sizeof(_attr_to_str) / sizeof(_attr_to_str[0])) + +ssize_t clif_decode_link(clif_t *link, clif_attr_t *attrs, unsigned attrs_len, + const char *buf, size_t maxlen) +{ + + assert(buf); + assert(link); + + char *pos; + const char *end = buf + maxlen; + clif_attr_t _dummy_attr; + + ssize_t size = clif_get_target(buf, maxlen, &pos); + if (size < 0) { + return CLIF_NOT_FOUND; + } + link->target = pos; + link->target_len = size; + link->attrs_len = 0; + link->attrs = attrs; + pos += size + 1; /* escape the '>' */ + + DEBUG("Found target (%u): %.*s\n", (unsigned)size, (unsigned)size, + link->target); + + /* if there is no attr array iterate over the buffer, if not until all + * the array is used */ + while ((!attrs && pos < end) || (attrs && link->attrs_len < attrs_len)) { + clif_attr_t *attr = attrs ? &attrs[link->attrs_len] : &_dummy_attr; + size = clif_get_attr(pos, end - pos, attr); + if (size < 0) { + break; + } + pos += size; + link->attrs_len++; + } + + return pos - buf; +} + +ssize_t clif_encode_link(const clif_t *link, char *buf, size_t maxlen) +{ + assert(link); + size_t pos = 0; + ssize_t res = 0; + + res = clif_add_target(link->target, buf, maxlen); + if (res < 0) { + return res; + } + pos += res; + + for (unsigned i = 0; i < link->attrs_len; i++) { + res = clif_add_attr(&link->attrs[i], buf ? &buf[pos] : NULL, + maxlen - pos); + if (res <= 0) { + return res; + } + pos += res; + } + return pos; +} + +ssize_t clif_add_target(const char *target, char *buf, size_t maxlen) +{ + assert(target); + + size_t pos = 0; + size_t target_len = strlen(target); + + if (!buf) { + return target_len + 2; /* size after adding '<' and '>' */ + } + + if ((target_len + 2) > maxlen) { + return CLIF_NO_SPACE; + } + + buf[pos++] = LF_PATH_BEGIN_C; + + memcpy(&buf[pos], target, target_len); + pos += target_len; + + buf[pos++] = LF_PATH_END_C; + + return pos; +} + +ssize_t clif_add_link_separator(char *buf, size_t maxlen) +{ + if (!buf) { + return 1; + } + + if (maxlen < 1) { + return CLIF_NO_SPACE; + } + + *buf = LF_LINK_SEPARATOR_C; + return 1; +} + +ssize_t clif_add_attr(clif_attr_t *attr, char *buf, size_t maxlen) +{ + assert(attr); + assert(attr->key); + + /* if no length given, calculate it */ + if (!attr->key_len) { + attr->key_len = strlen(attr->key); + } + + /* count attr name size and separator ';' */ + size_t req_space = attr->key_len + 1; + size_t pos = 0; + int quoted = strcmp(attr->key, LF_ATTR_SIZE) ? 1 : 0; + + if (attr->value) { + if (!attr->value_len) { + attr->value_len = strlen(attr->value); + } + /* count also '=' */ + req_space += attr->value_len + 1; + } + + if (quoted) { + req_space += 2; + } + + if (!buf) { + return req_space; + } + + if (req_space > maxlen) { + return CLIF_NO_SPACE; + } + + /* add attribute separator ';' */ + buf[pos++] = LF_ATTR_SEPARATOR_C; + + /* add attribute name */ + memcpy(&buf[pos], attr->key, attr->key_len); + pos += attr->key_len; + + /* add attribute value if defined */ + if (attr->value) { + buf[pos++] = LF_ATTR_VAL_SEPARATOR_C; + + if (quoted) { + buf[pos++] = '"'; + } + + memcpy(&buf[pos], attr->value, attr->value_len); + pos += attr->value_len; + + if (quoted) { + buf[pos++] = '"'; + } + } + + return pos; +} + +ssize_t clif_get_target(const char *input, size_t input_len, char **output) +{ + assert(input); + char *target_end; + + *output = memchr(input, LF_PATH_BEGIN_C, input_len); + if (!*output) { + DEBUG("Path start not found\n"); + return CLIF_NOT_FOUND; + } + *output += 1; + + target_end = memchr(*output, LF_PATH_END_C, (input + input_len) - *output); + if (!target_end) { + DEBUG("Path end not found\n"); + return CLIF_NOT_FOUND; + } + ssize_t res = target_end - *output; + return res; +} + +ssize_t clif_get_attr(const char *input, size_t input_len, clif_attr_t *attr) +{ + assert(input); + assert(attr); + const char *pos = input; + const char *end = input + input_len; + bool quoted = false; + bool scan_value = false; + + /* initialize attr */ + attr->value = NULL; + attr->key = NULL; + + /* an attribute should start with the separator */ + if (*pos != LF_ATTR_SEPARATOR_C) { + DEBUG("Attribute should start with separator, found %c\n", *pos); + return CLIF_NOT_FOUND; + } + pos++; + attr->key = pos; + + /* iterate over key */ + while (pos < end) { + if (*pos == LF_ATTR_SEPARATOR_C || *pos == LF_LINK_SEPARATOR_C) { + /* key ends, no value */ + attr->key_len = pos - attr->key; + break; + } + if (*pos == LF_ATTR_VAL_SEPARATOR_C) { + /* key ends, has value */ + attr->key_len = pos - attr->key; + /* check if the value is quoted and prepare pointer for value scan */ + pos++; + if (*pos == '"') { + quoted = true; + pos++; + } + attr->value = (char *)pos; + scan_value = true; + break; + } + pos++; + } + + if (scan_value) { + /* iterate over value */ + while (pos < end) { + if (quoted) { + if (*pos == '"' && *(pos - 1) != '\\') { + /* found unescaped quote */ + attr->value_len = pos - attr->value; + pos++; + break; + } + } + else { + if (*pos == LF_ATTR_SEPARATOR_C || *pos == LF_LINK_SEPARATOR_C) { + /* value ends */ + attr->value_len = pos - attr->value; + break; + } + } + pos++; + } + } + else { + /* buffer exhausted and no special character found, calculate length of + * attribute and exit */ + attr->key_len = pos - attr->key; + } + + return pos - input; +} + +ssize_t clif_attr_type_to_str(clif_attr_type_t type, const char **str) +{ + if (type < ATTRS_NUMOF) { + *str = _attr_to_str[type]; + return _attr_to_size[type]; + } + return CLIF_NOT_FOUND; +} + +clif_attr_type_t clif_get_attr_type(const char *input, size_t input_len) +{ + assert(input); + assert(input_len > 0); + clif_attr_type_t ret = CLIF_ATTR_EXT; + for (unsigned i = 0; i < ATTRS_NUMOF; i++) { + if (input_len == _attr_to_size[i] && + !strncmp(input, _attr_to_str[i], input_len)) { + ret = i; + break; + } + } + return ret; +} + +int clif_init_attr(clif_attr_t *attr, clif_attr_type_t type) +{ + assert(attr); + attr->key_len = clif_attr_type_to_str(type, &attr->key); + return attr->key_len > 0 ? 0 : CLIF_NOT_FOUND; +} diff --git a/sys/clif/include/clif_internal.h b/sys/clif/include/clif_internal.h new file mode 100644 index 0000000000..39bd763bcf --- /dev/null +++ b/sys/clif/include/clif_internal.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_clif + * + * @{ + * + * @file + * @brief Internal definitions for CoRE Link format module + * + * @author Leandro Lanzieri + */ +#ifndef CLIF_INTERNAL_H +#define CLIF_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define _P_SIZE(p) (sizeof(p) - 1) + +/** + * @brief link format path initial character + */ +#define LF_PATH_BEGIN_C '<' + +/** + * @brief link format path final character + */ +#define LF_PATH_END_C '>' + +/** + * @brief link format link separator character + */ +#define LF_LINK_SEPARATOR_C ',' + +/** + * @brief link format attribute separator character + * + */ +#define LF_ATTR_SEPARATOR_C ';' + +/** + * @brief link format attribute value separator character + */ +#define LF_ATTR_VAL_SEPARATOR_C '=' + +/** + * @name Anchor attribute + * @{ + */ +#define LF_ATTR_ANCHOR "anchor" /**< attr name */ +#define LF_ATTR_ANCHOR_S _P_SIZE(LF_ATTR_ANCHOR) /**< attr name length */ +/** @} */ + +/** + * @name Relation type attribute + * @{ + */ +#define LF_ATTR_REL_TYPE "rel" /**< attr name */ +#define LF_ATTR_REL_TYPE_S _P_SIZE(LF_ATTR_REL_TYPE) /**< attr name length */ +/** @} */ + +/** + * @name Language attribute + * @{ + */ +#define LF_ATTR_LANG "hreflang" /**< attr name */ +#define LF_ATTR_LANG_S _P_SIZE(LF_ATTR_LANG) /**< attr name length */ +/** @} */ + +/** + * @name Media attribute + * @{ + */ +#define LF_ATTR_MEDIA "media" /**< attr name */ +#define LF_ATTR_MEDIA_S _P_SIZE(LF_ATTR_MEDIA) /**< attr name length */ +/** @} */ + +/** + * @name Title attribute + * @{ + */ +#define LF_ATTR_TITLE "title" /**< attr name */ +#define LF_ATTR_TITLE_S _P_SIZE(LF_ATTR_TITLE) /**< attr name length */ +/** @} */ + +/** + * @name Title extended attribute + * @{ + */ +#define LF_ATTR_TITLE_EXT "title*" /**< attr name */ +#define LF_ATTR_TITLE_EXT_S _P_SIZE(LF_ATTR_TITLE_EXT) /**< attr name length */ +/** @} */ + +/** + * @name Type attribute + * @{ + */ +#define LF_ATTR_TYPE "type" /**< attr name */ +#define LF_ATTR_TYPE_S _P_SIZE(LF_ATTR_TYPE) /**< attr name length */ +/** @} */ + +/** + * @name Resource type attribute + * @{ + */ +#define LF_ATTR_RES_TYPE "rt" /**< attr name */ +#define LF_ATTR_RES_TYPE_S _P_SIZE(LF_ATTR_RES_TYPE) /**< attr name length */ +/** @} */ + +/** + * @name Interface description attribute + * @{ + */ +#define LF_ATTR_IF_DESC "if" /**< attr name */ +#define LF_ATTR_IF_DESC_S _P_SIZE(LF_ATTR_IF_DESC) /**< attr name length */ +/** @} */ + +/** + * @name Size attribute + * @{ + */ +#define LF_ATTR_SIZE "sz" /**< attr name */ +#define LF_ATTR_SIZE_S _P_SIZE(LF_ATTR_SIZE) /**< attr name length */ +/** @} */ + +/** + * @name Content-format attribute + * @{ + */ +#define LF_ATTR_CT "ct" /**< attr name */ +#define LF_ATTR_CT_S _P_SIZE(LF_ATTR_CT) /**< attr name length */ +/** @} */ + +/** + * @name Observable attribute + * @{ + */ +#define LF_ATTR_OBS "obs" /**< attr name */ +#define LF_ATTR_OBS_S _P_SIZE(LF_ATTR_OBS) /**< attr name length */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* CLIF_INTERNAL_H */ +/** @} */ diff --git a/sys/include/clif.h b/sys/include/clif.h new file mode 100644 index 0000000000..14d718d2f2 --- /dev/null +++ b/sys/include/clif.h @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_clif CoRE Link Format + * @ingroup sys_serialization + * @brief Simple encoding and decoding of CoRE Link Format (RFC 6690) + * strings + * @see RFC 6690: https://tools.ietf.org/html/rfc6690 + * + * clif provides a high-level API for CoRE Link Format encoding and decoding of + * links, through the @ref clif_encode_link and @ref clif_decode_link + * respectively. + * + * The high-level API is built on top of low-level functions provided by clif, + * such as @ref clif_add_target, @ref clif_add_attr, and @ref clif_get_attr. + * Also, some convenience functions like @ref clif_get_attr_type, + * @ref clif_attr_type_to_str and @ref clif_init_attr are provided, to + * facilitate the work with links. + * + * ### Decoding + * @ref clif_decode_link takes a buffer which contains an encoded link and + * returns the information of it in a @ref clif_t structure and each attribute + * in a @ref clif_attr_t structure of a given array. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * // A buffer 'input_buf' of length 'input_len' contains the links to decode + * clif_attr_t out_attrs[ATTRS_NUM]; + * clif_t out_links[LINKS_NUM]; + * + * const char *pos = input_buf; + * unsigned links_numof = 0; + * unsigned attrs_numof = 0; + * + * while (pos < &input_buf[input_len]) { + * ssize_t res = clif_decode_link(&out_links[links_numof], + * &out_attrs[attrs_numof], + * ATTRS_NUM - attrs_numof, pos, + * &input_buf[input_len]- pos); + * if (res < 0) { + * break; + * } + * pos += res; + * attrs_numof += out_links[links_numof].attrs_len; + * links_numof++; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * ### Encoding + * @ref clif_encode_link encodes a given link into a buffer, it can be called + * with a NULL pointer, in that case it will only calculate the amount of bytes + * needed to encode the link. After every call to this function a separator + * needs to be added to the buffer. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * // A 'links' array of clif_t, of length 'links_len' contains the links to encode + * char out[OUT_SIZE]; + * ssize_t res; + * size_t pos = 0; + * + * for (unsigned i = 0; i < links_len; i++) { + * if (i) { + * res = clif_add_link_separator(&out[pos], OUT_SIZE - pos); + * if (res <= 0) { + * break; + * } + * pos += res; + * } + * + * res = clif_encode_link(&links[i], &out[pos], OUT_SIZE - pos); + * if (res <= 0) { + * break; + * } + * pos += res; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @note 'attribute', in this module, extends to the term 'link-param' on the + * ABNF in [section 2 of RFC 6690](https://tools.ietf.org/html/rfc6690#section-2). + * + * @{ + * + * @file + * @brief CoRE Link Format encoding and decoding library public + * definitions + * + * @author Leandro Lanzieri + */ + +#ifndef CLIF_H +#define CLIF_H + +#include + +#include "clif_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Return types for the @ref sys_clif API + */ +enum { + CLIF_OK = 0, /**< success */ + CLIF_NO_SPACE = -1, /**< not enough space in the buffer */ + CLIF_NOT_FOUND = -2 /**< could not find a component in a buffer */ +}; + +/** + * @brief Types of link format attributes + */ +typedef enum { + CLIF_ATTR_ANCHOR = 0, /**< anchor */ + CLIF_ATTR_REL = 1, /**< rel */ + CLIF_ATTR_LANG = 2, /**< hreflang */ + CLIF_ATTR_MEDIA = 3, /**< media */ + CLIF_ATTR_TITLE = 4, /**< title */ + CLIF_ATTR_TITLE_EXT = 5, /**< title* */ + CLIF_ATTR_TYPE = 6, /**< type */ + CLIF_ATTR_RT = 7, /**< rt */ + CLIF_ATTR_IF = 8, /**< if */ + CLIF_ATTR_SZ = 9, /**< sz */ + CLIF_ATTR_CT = 10, /**< ct */ + CLIF_ATTR_OBS = 11, /**< obs */ + CLIF_ATTR_EXT = 12 /**< extensions */ +} clif_attr_type_t; + +/** + * @brief Link format attribute descriptor + */ +typedef struct { + char *value; /**< string with the value */ + unsigned value_len; /**< length of the value */ + const char *key; /**< attribute name */ + unsigned key_len; /**< length of the attribute name */ +} clif_attr_t; + +/** + * @brief Link format descriptor + */ +typedef struct { + char *target; /**< target string */ + unsigned target_len; /**< length of target string */ + clif_attr_t *attrs; /**< array of attributes */ + unsigned attrs_len; /**< size of array of attributes */ +} clif_t; + +/** + * @brief Encodes a given link in link format into a given buffer + * + * @pre `link != NULL` + * + * @param[in] link link to encode.Must not be NULL. + * @param[out] buf buffer to output the encoded link. Can be NULL + * @param[in] maxlen size of @p buf + * + * @note If @p buf is NULL this will return the amount of bytes that would be + * needed + * + * @return amount of bytes used from @p buf in success + * @return CLIF_NO_SPACE if there is not enough space in the buffer + */ +ssize_t clif_encode_link(const clif_t *link, char *buf, size_t maxlen); + +/** + * @brief Decodes a string of link format. It decodes the first occurrence of + * a link. + * + * @pre `(link != NULL) && (buf != NULL)` + * + * @param[out] link link to populate. Must not be NULL. + * @param[out] attrs array of attrs to populate + * @param[in] attrs_len length of @p attrs + * @param[in] buf string to decode. Must not be NULL. + * @param[in] maxlen size of @p buf + * + * @return number of bytes parsed from @p buf in success + * @return CLIF_NOT_FOUND if the string is malformed + */ +ssize_t clif_decode_link(clif_t *link, clif_attr_t *attrs, unsigned attrs_len, + const char *buf, size_t maxlen); + +/** + * @brief Adds a given @p target to a given buffer @p buf using link format + * + * @pre `target != NULL` + * + * @param[in] target string containing the path to the resource. Must not be + * NULL. + * @param[out] buf buffer to output the formatted path. Can be NULL + * @param[in] maxlen size of @p buf + * + * @note If @p buf is NULL this will return the amount of bytes that would be + * needed + * + * @return in success the amount of bytes used in the buffer + * @return CLIF_NO_SPACE if there is not enough space in the buffer + */ +ssize_t clif_add_target(const char *target, char *buf, size_t maxlen); + +/** + * @brief Adds a given @p attr to a given buffer @p buf using link format + * + * @pre `(attr != NULL) && (attr->key != NULL)` + * + * @param[in] attr pointer to the attribute to add. Must not be NULL, and + * must contain a key. + * @param[out] buf buffer to add the attribute to. Can be NULL + * @param[in] maxlen size of @p buf + * + * @note + * - If @p buf is NULL this will return the amount of bytes that would be + * needed. + * - If the lengths of the key or the value of the attribute are not + * defined a NULL-terminated string will be assumed, and it will be + * calculated. + * + * @return amount of bytes used from the buffer if successful + * @return CLIF_NO_SPACE if there is not enough space in the buffer + */ +ssize_t clif_add_attr(clif_attr_t *attr, char *buf, size_t maxlen); + +/** + * @brief Adds the link separator character to a given @p buf, using link + * format + * + * @param[out] buf buffer to add the separator to. Can be NULL + * @param[in] maxlen size of @p buf + * + * @note If @p buf is NULL this will return the amount of bytes that would be + * needed + * + * @return amount of bytes used from buffer if successful + * @return CLIF_NO_SPACE if there is not enough space in the buffer + */ +ssize_t clif_add_link_separator(char *buf, size_t maxlen); + +/** + * @brief Looks for a the target URI of a given link. + * + * @pre `input != NULL` + * + * @param[in] input string where to look for the target. It should only + * be ONE link. Must not be NULL. + * @param[in] input_len length of @p input. + * @param[out] output if a target is found this will point to the + * beginning of it + * + * @return length of the target if found + * @return CLIF_NOT_FOUND if no valid target is found + */ +ssize_t clif_get_target(const char *input, size_t input_len, char **output); +/** + * @brief Looks for the first attribute in a given link. + * + * @pre `(input != NULL) && (attr != NULL)` + * + * @note In order to consider that the string contains a valid attribute, + * @p input should start with the attribute separator character ';'. + * + * @param[in] input string where to look for the attribute. It should + * only be ONE link. Must not be NULL. + * @param[in] input_len length of @p input + * @param[out] attr pointer to store the found attribute information. + * Must not be NULL. + * + * @return length of the attribute in the buffer if found + * @return CLIF_NOT_FOUND if no valid attribute is found + */ +ssize_t clif_get_attr(const char *input, size_t input_len, clif_attr_t *attr); + +/** + * @brief Returns the attribute type of a given string. + * + * @pre `(input != NULL) && (input_len > 0)` + * + * @param[in] input string containing the attribute name. Must not be NULL. + * @param[in] input_len length of @p input. Must be greater than 0. + * + * @return type of the attribute + */ +clif_attr_type_t clif_get_attr_type(const char *input, size_t input_len); + +/** + * @brief Returns a constant string of a given attribute type + * + * @param[in] type type of the attribute + * @param[out] str pointer to store the string pointer + * + * @return length of the string + * @return CLIF_NOT_FOUND if the type is an extension or unknown + */ +ssize_t clif_attr_type_to_str(clif_attr_type_t type, const char **str); + +/** + * @brief Initializes the key of a given attribute according to a given type. + * + * @param[out] attr attribute to initialize + * @param[in] type type of the attribute + * + * @return 0 if successful + * @return <0 otherwise + */ +int clif_init_attr(clif_attr_t *attr, clif_attr_type_t type); + +#ifdef __cplusplus +} +#endif + +#endif /* CLIF_H */ +/** @} */