Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
yuarel: import MIT licensed url parser
Baptiste Daroussin committed 2 years ago
commit 01b08f8cea9d34c11a2fb343255faffa7182fc31
parent 4547320
3 files changed +411 -1
modified libpkg/Makefile.autosetup
@@ -48,7 +48,8 @@ SRCS= backup_lib.c \
	fetch_libcurl.c \
	fetch_file.c \
	triggers.c \
-
	pkghash.c
+
	pkghash.c \
+
	yuarel.c

LOCAL_CFLAGS=	-I$(top_srcdir)/compat \
		-I$(top_srcdir)/external/blake2 \
added libpkg/yuarel.c
@@ -0,0 +1,294 @@
+
/**
+
 * Copyright (C) 2016,2017 Jack Engqvist Johansson
+
 *
+
 * Permission is hereby granted, free of charge, to any person obtaining a copy
+
 * of this software and associated documentation files (the "Software"), to deal
+
 * in the Software without restriction, including without limitation the rights
+
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+
 * copies of the Software, and to permit persons to whom the Software is
+
 * furnished to do so, subject to the following conditions:
+
 *
+
 * The above copyright notice and this permission notice shall be included in all
+
 * copies or substantial portions of the Software.
+
 *
+
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+
 * SOFTWARE.
+
 */
+
#include <stdlib.h>
+
#include <stdio.h>
+
#include <string.h>
+
#include "yuarel.h"
+

+
/**
+
 * Parse a non null terminated string into an integer.
+
 *
+
 * str: the string containing the number.
+
 * len: Number of characters to parse.
+
 */
+
static inline int
+
natoi(const char *str, size_t len)
+
{
+
	int i, r = 0;
+
	for (i = 0; i < len; i++) {
+
		r *= 10;
+
		r += str[i] - '0';
+
	}
+

+
	return r;
+
}
+

+
/**
+
 * Check if a URL is relative (no scheme and hostname).
+
 *
+
 * url: the string containing the URL to check.
+
 *
+
 * Returns 1 if relative, otherwise 0.
+
 */
+
static inline int
+
is_relative(const char *url)
+
{
+
	return (*url == '/') ? 1 : 0;
+
}
+

+
/**
+
 * Parse the scheme of a URL by inserting a null terminator after the scheme.
+
 *
+
 * str: the string containing the URL to parse. Will be modified.
+
 *
+
 * Returns a pointer to the hostname on success, otherwise NULL.
+
 */
+
static inline char *
+
parse_scheme(char *str)
+
{
+
	char *s;
+

+
	/* If not found or first in string, return error */
+
	s = strchr(str, ':');
+
	if (s == NULL || s == str) {
+
		return NULL;
+
	}
+

+
	/* If not followed by two slashes, return error */
+
	if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
+
		return NULL;
+
	}
+

+
	*s = '\0'; // Replace ':' with NULL
+

+
	return s + 3;
+
}
+

+
/**
+
 * Find a character in a string, replace it with '\0' and return the next
+
 * character in the string.
+
 *
+
 * str: the string to search in.
+
 * find: the character to search for.
+
 *
+
 * Returns a pointer to the character after the one to search for. If not
+
 * found, NULL is returned.
+
 */
+
static inline char *
+
find_and_terminate(char *str, char find)
+
{
+
	str = strchr(str, find);
+
	if (NULL == str) {
+
		return NULL;
+
	}
+

+
	*str = '\0';
+
	return str + 1;
+
}
+

+
/* Yes, the following functions could be implemented as preprocessor macros
+
     instead of inline functions, but I think that this approach will be more
+
     clean in this case. */
+
static inline char *
+
find_fragment(char *str)
+
{
+
	return find_and_terminate(str, '#');
+
}
+

+
static inline char *
+
find_query(char *str)
+
{
+
	return find_and_terminate(str, '?');
+
}
+

+
static inline char *
+
find_path(char *str)
+
{
+
	return find_and_terminate(str, '/');
+
}
+

+
/**
+
 * Parse a URL string to a struct.
+
 *
+
 * url: pointer to the struct where to store the parsed URL parts.
+
 * u:   the string containing the URL to be parsed.
+
 *
+
 * Returns 0 on success, otherwise -1.
+
 */
+
int
+
yuarel_parse(struct yuarel *url, char *u)
+
{
+
	if (NULL == url || NULL == u) {
+
		return -1;
+
	}
+

+
	memset(url, 0, sizeof (struct yuarel));
+

+
	/* (Fragment) */
+
	url->fragment = find_fragment(u);
+

+
	/* (Query) */
+
	url->query = find_query(u);
+

+
	/* Relative URL? Parse scheme and hostname */
+
	if (!is_relative(u)) {
+
		/* Scheme */
+
		url->scheme = u;
+
		u = parse_scheme(u);
+
		if (u == NULL) {
+
			return -1;
+
		}
+

+
		/* Host */
+
		if ('\0' == *u) {
+
			return -1;
+
		}
+
		url->host = u;
+

+
		/* (Path) */
+
		url->path = find_path(u);
+

+
		/* (Credentials) */
+
		u = strchr(url->host, '@');
+
		if (NULL != u) {
+
			/* Missing credentials? */
+
			if (u == url->host) {
+
				return -1;
+
			}
+

+
			url->username = url->host;
+
			url->host = u + 1;
+
			*u = '\0';
+

+
			u = strchr(url->username, ':');
+
			if (NULL == u) {
+
				return -1;
+
			}
+

+
			url->password = u + 1;
+
			*u = '\0';
+
		}
+

+
		/* Missing hostname? */
+
		if ('\0' == *url->host) {
+
			return -1;
+
		}
+

+
		/* (Port) */
+
		u = strchr(url->host, ':');
+
		if (NULL != u && (NULL == url->path || u < url->path)) {
+
			*(u++) = '\0';
+
			if ('\0' == *u) {
+
				return -1;
+
			}
+

+
			if (url->path) {
+
				url->port = natoi(u, url->path - u - 1);
+
			} else {
+
				url->port = atoi(u);
+
			}
+
		}
+

+
		/* Missing hostname? */
+
		if ('\0' == *url->host) {
+
			return -1;
+
		}
+
	} else {
+
		/* (Path) */
+
		url->path = find_path(u);
+
	}
+

+
	return 0;
+
}
+

+
/**
+
 * Split a path into several strings.
+
 *
+
 * No data is copied, the slashed are used as null terminators and then
+
 * pointers to each path part will be stored in **parts. Double slashes will be
+
 * treated as one.
+
 *
+
 * path: the path to split.
+
 * parts: a pointer to an array of (char *) where to store the result.
+
 * max_parts: max number of parts to parse.
+
 */
+
int
+
yuarel_split_path(char *path, char **parts, int max_parts)
+
{
+
	int i = 0;
+

+
	if (NULL == path || '\0' == *path) {
+
		return -1;
+
	}
+

+
	do {
+
		/* Forward to after slashes */
+
		while (*path == '/') path++;
+

+
		if ('\0' == *path) {
+
			break;
+
		}
+

+
		parts[i++] = path;
+

+
		path = strchr(path, '/');
+
		if (NULL == path) {
+
			break;
+
		}
+

+
		*(path++) = '\0';
+
	} while (i < max_parts);
+

+
	return i;
+
}
+

+
int
+
yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params)
+
{
+
	int i = 0;
+

+
	if (NULL == query || '\0' == *query) {
+
		return -1;
+
	}
+

+
	params[i++].key = query;
+
	while (i < max_params && NULL != (query = strchr(query, delimiter))) {
+
		*query = '\0';
+
		params[i].key = ++query;
+
		params[i].val = NULL;
+

+
		/* Go back and split previous param */
+
		if (i > 0) {
+
			if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
+
				*(params[i - 1].val)++ = '\0';
+
			}
+
		}
+
		i++;
+
	}
+

+
	/* Go back and split last param */
+
	if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
+
		*(params[i - 1].val)++ = '\0';
+
	}
+

+
	return i;
+
}
added libpkg/yuarel.h
@@ -0,0 +1,115 @@
+
/**
+
 * Copyright (C) 2016 Jack Engqvist Johansson
+
 *
+
 * Permission is hereby granted, free of charge, to any person obtaining a copy
+
 * of this software and associated documentation files (the "Software"), to deal
+
 * in the Software without restriction, including without limitation the rights
+
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+
 * copies of the Software, and to permit persons to whom the Software is
+
 * furnished to do so, subject to the following conditions:
+
 *
+
 * The above copyright notice and this permission notice shall be included in all
+
 * copies or substantial portions of the Software.
+
 *
+
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+
 * SOFTWARE.
+
 */
+
#ifndef INC_YUAREL_H
+
#define INC_YUAREL_H
+

+
#ifdef __cplusplus
+
extern "C" {
+
#endif
+

+
/**
+
 * The struct where the parsed values will be stored:
+
 *
+
 * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ]
+
 *
+
 * Note: to make sure that no strings are copied, the first slash "/" in the
+
 * path will be used to null terminate the hostname if no port is supplied.
+
 */
+
struct yuarel {
+
	char *scheme; /* scheme, without ":" and "//" */
+
	char *username; /* username, default: NULL */
+
	char *password; /* password, default: NULL */
+
	char *host; /* hostname or IP address */
+
	int port; /* port, default: 0 */
+
	char *path; /* path, without leading "/", default: NULL */
+
	char *query; /* query, default: NULL */
+
	char *fragment; /* fragment, default: NULL */
+
};
+

+
/* A struct to hold the query string parameter values. */
+
struct yuarel_param {
+
	char *key;
+
	char *val;
+
};
+

+
/**
+
 * Parse a URL to a struct.
+
 *
+
 * The URL string should be in one of the following formats:
+
 *
+
 * Absolute URL:
+
 * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] [ "#" fragment ]
+
 *
+
 * Relative URL:
+
 * path [ "?" query ] [ "#" fragment ]
+
 *
+
 * The following parts will be parsed to the corresponding struct member.
+
 *
+
 * *url:     a pointer to the struct where to store the parsed values.
+
 * *url_str: a pointer to the url to be parsed (null terminated). The string
+
 *           will be modified.
+
 *
+
 * Returns 0 on success, otherwise -1.
+
 */
+
extern int yuarel_parse(struct yuarel *url, char *url_str);
+

+
/**
+
 * Split a path into several strings.
+
 *
+
 * No data is copied, the slashed are used as null terminators and then
+
 * pointers to each path part will be stored in **parts. Double slashes will be
+
 * treated as one.
+
 *
+
 * *path:     the path to split. The string will be modified.
+
 * **parts:   a pointer to an array of (char *) where to store the result.
+
 * max_parts: max number of parts to parse.
+
 *
+
 * Returns the number of parsed items. -1 on error.
+
 */
+
extern int yuarel_split_path(char *path, char **parts, int max_parts);
+

+
/**
+
 * Parse a query string into a key/value struct.
+
 *
+
 * The query string should be a null terminated string of parameters separated by
+
 * a delimiter. Each parameter are checked for the equal sign character. If it
+
 * appears in the parameter, it will be used as a null terminator and the part
+
 * that comes after it will be the value of the parameter.
+
 *
+
 * No data are copied, the equal sign and delimiters are used as null
+
 * terminators and then pointers to each parameter key and value will be stored
+
 * in the yuarel_param struct.
+
 *
+
 * *query:     the query string to parse. The string will be modified.
+
 * delimiter:  the character that separates the key/value pairs from eachother.
+
 * *params:    an array of (struct yuarel_param) where to store the result.
+
 * max_values: max number of parameters to parse.
+
 *
+
 * Returns the number of parsed items. -1 on error.
+
 */
+
extern int yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params);
+

+
#ifdef __cplusplus
+
}
+
#endif
+

+
#endif /* INC_YUAREL_H */