Skip to content

Commit

Permalink
uri_parser: provide function to split query
Browse files Browse the repository at this point in the history
  • Loading branch information
miri64 committed Aug 4, 2021
1 parent 91fe57a commit af03ba4
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
40 changes: 40 additions & 0 deletions sys/include/uri_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ typedef struct {
uint16_t query_len; /**< length of @ref query */
} uri_parser_result_t;

/**
* @brief Container to represent a query parameter
*/
typedef struct {
char *name; /**< name of the query parameter */
char *value; /**< value of the query parameter */
uint16_t name_len; /**< length of @ref name */
uint16_t value_len; /**< length of @ref value */
} uri_parser_query_param_t;

/**
* @brief Checks whether @p uri is in absolute form
*
Expand Down Expand Up @@ -133,6 +143,36 @@ int uri_parser_process(uri_parser_result_t *result, const char *uri,
*/
int uri_parser_process_string(uri_parser_result_t *result, const char *uri);

/**
* @brief Provides a list of URI query parameters from a given URI parser
* result.
*
* @note The function **DOES NOT** check for duplicate query parameters.
*
* @pre `uri_parsed != NULL`
* @pre `params != NULL` and all its elements are set to zero.
*
* @param[in] uri_parsed A parsed URI result. Must not be NULL.
* @param[out] params An array of @ref uri_parser_query_param_t.
* Must not be NULL and all zero-valued on call. Will be
* filled with the name-value-pairs in
* uri_parser_result_t::query of @p uri_parsed. If the
* number of query parameters in @p uri_parsed exceeds
* @p params_len, the list will be truncated and the
* function returns -2.
* @param[in] params_len The length of @p params
*
* @return number of filled entries in @p params on success. Might be 0 if
* uri_parser_result_t::query is NULL.
* @return -1 on parsing error.
* @return -2 when the number of query parameters exceeds @p params_len.
* In that case, the array is filled with the first @p params_len
* name-value-pairs in uri_parser_result_t::query of @p uri_parsed.
*/
int uri_parser_split_query(const uri_parser_result_t *uri_parsed,
uri_parser_query_param_t *params,
size_t params_len);

#ifdef __cplusplus
}
#endif
Expand Down
72 changes: 72 additions & 0 deletions sys/uri_parser/uri_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,75 @@ int uri_parser_process_string(uri_parser_result_t *result, const char *uri)
{
return uri_parser_process(result, uri, strlen(uri));
}

int uri_parser_split_query(const uri_parser_result_t *uri,
uri_parser_query_param_t *params,
size_t params_len)
{
const char *query_end;
unsigned idx = 0;

assert(uri);
assert(params);

if ((uri->query == NULL) || (uri->query_len == 0) || (params_len == 0)) {
return 0;
}
assert(params[0].name == 0);
assert(params[0].name_len == 0);
assert(params[0].value == 0);
assert(params[0].value_len == 0);
query_end = uri->query + uri->query_len;
params[0].name = uri->query;
for (const char *c = uri->query; c < query_end; c++) {
switch (*c) {
case '#':
case '&':
if (params[idx].value == NULL) {
/* we should have picked up a parameter value by now */
return -1;
}
params[idx].value_len = c - params[idx].value;
if (*c == '#') {
/* we've reached the end of the query string, next comes an
* anchor, enforce end of loop
* XXX: can be removed when uri_parser has anchor support */
c = query_end;
}
else if ((idx + 1) < params_len) {
/* c is an ampersand (&), so mark the next char as the next
* parameter's name name */
params[++idx].name = (char *)c + 1U;
assert(params[idx].name_len == 0);
}
else {
/* c is an ampersand (&), but we exceeded param_len.
* Return -2 as per doc */
return -2;
}
break;
case '=':
/* params[idx].value != NULL picks up duplicate = in query
* parameter */
if ((params[idx].name == NULL) || params[idx].value != NULL) {
/* we should have picked up a parameter name by now */
return -1;
}
params[idx].name_len = c - params[idx].name;
/* pick next char as start of value */
params[idx].value = (char *)c + 1U;
/* make sure the precondition on params is met */
assert(params[idx].value_len == 0);
break;
default:
break;
}
}
if ((uri->query != query_end) && (params[idx].value == NULL)) {
/* we should have picked up a parameter value by now */
return -1;
}
/* set final value_len */
params[idx].value_len = query_end - params[idx].value;
return idx + 1;
}

0 comments on commit af03ba4

Please sign in to comment.