mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
uri: add uri_parse() - new function extracts components from an URI
This commit is contained in:
parent
9580c04dd3
commit
608cc746ad
3 changed files with 286 additions and 0 deletions
|
|
@ -130,6 +130,7 @@ executable(
|
|||
'spawn.c', 'spawn.h',
|
||||
'terminal.c', 'terminal.h',
|
||||
'tokenize.c', 'tokenize.h',
|
||||
'uri.c', 'uri.h',
|
||||
'user-notification.h',
|
||||
'vt.c', 'vt.h',
|
||||
'wayland.c', 'wayland.h',
|
||||
|
|
|
|||
274
uri.c
Normal file
274
uri.c
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#include "uri.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define LOG_MODULE "uri"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
static uint8_t
|
||||
nibble2hex(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
return c - '0';
|
||||
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
return c - 'a' + 10;
|
||||
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
uri_parse(const char *uri, size_t len,
|
||||
char **scheme, char **user, char **password, char **host,
|
||||
uint16_t *port, char **path, char **query, char **fragment)
|
||||
{
|
||||
LOG_DBG("parse URI: \"%.*s\"", (int)len, uri);
|
||||
|
||||
if (scheme != NULL) *scheme = NULL;
|
||||
if (user != NULL) *user = NULL;
|
||||
if (password != NULL) *password = NULL;
|
||||
if (host != NULL) *host = NULL;
|
||||
if (port != NULL) *port = 0;
|
||||
if (path != NULL) *path = NULL;
|
||||
if (query != NULL) *query = NULL;
|
||||
if (fragment != NULL) *fragment = NULL;
|
||||
|
||||
size_t left = len;
|
||||
const char *start = uri;
|
||||
const char *end = NULL;
|
||||
|
||||
if ((end = memchr(start, ':', left)) == NULL)
|
||||
goto err;
|
||||
|
||||
size_t scheme_len = end - start;
|
||||
if (scheme_len == 0)
|
||||
goto err;
|
||||
|
||||
if (scheme != NULL)
|
||||
*scheme = xstrndup(start, scheme_len);
|
||||
|
||||
LOG_DBG("scheme: \"%.*s\"", (int)scheme_len, start);
|
||||
|
||||
start = end + 1;
|
||||
left = len - (start - uri);
|
||||
|
||||
/* Authinfo */
|
||||
if (left >= 2 && start[0] == '/' && start[1] == '/') {
|
||||
start += 2;
|
||||
left -= 2;
|
||||
|
||||
/* [user[:pasword]@]@host[:port] */
|
||||
|
||||
/* Find beginning of path segment (required component
|
||||
* following the authinfo) */
|
||||
const char *path_segment = memchr(start, '/', left);
|
||||
if (path_segment == NULL)
|
||||
goto err;
|
||||
|
||||
size_t auth_left = path_segment - start;
|
||||
|
||||
/* Do we have a user (and optionally a password)? */
|
||||
const char *user_pw_end = memchr(start, '@', auth_left);
|
||||
if (user_pw_end != NULL) {
|
||||
size_t user_pw_len = user_pw_end - start;
|
||||
|
||||
/* Do we have a password? */
|
||||
const char *user_end = memchr(start, ':', user_pw_end - start);
|
||||
if (user_end != NULL) {
|
||||
size_t user_len = user_end - start;
|
||||
if (user_len == 0)
|
||||
goto err;
|
||||
|
||||
if (user != NULL)
|
||||
*user = xstrndup(start, user_len);
|
||||
|
||||
const char *pw = user_end + 1;
|
||||
size_t pw_len = user_pw_end - pw;
|
||||
if (pw_len == 0)
|
||||
goto err;
|
||||
|
||||
if (password != NULL)
|
||||
*password = xstrndup(pw, pw_len);
|
||||
|
||||
LOG_DBG("user: \"%.*s\"", (int)user_len, start);
|
||||
LOG_DBG("password: \"%.*s\"", (int)pw_len, pw);
|
||||
} else {
|
||||
size_t user_len = user_pw_end - start;
|
||||
if (user_len == 0)
|
||||
goto err;
|
||||
|
||||
if (user != NULL)
|
||||
*user = xstrndup(start, user_len);
|
||||
|
||||
LOG_DBG("user: \"%.*s\"", (int)user_len, start);
|
||||
}
|
||||
|
||||
start = user_pw_end + 1;
|
||||
left = len - (start - uri);
|
||||
auth_left -= user_pw_len + 1;
|
||||
}
|
||||
|
||||
const char *host_end = memchr(start, ':', auth_left);
|
||||
if (host_end != NULL) {
|
||||
size_t host_len = host_end - start;
|
||||
if (host != NULL)
|
||||
*host = xstrndup(start, host_len);
|
||||
|
||||
const char *port_str = host_end + 1;
|
||||
size_t port_len = path_segment - port_str;
|
||||
if (port_len == 0)
|
||||
goto err;
|
||||
|
||||
uint16_t _port = 0;
|
||||
for (size_t i = 0; i < port_len; i++) {
|
||||
if (!(port_str[i] >= '0' && port_str[i] <= '9'))
|
||||
goto err;
|
||||
|
||||
_port *= 10;
|
||||
_port += port_str[i] - '0';
|
||||
}
|
||||
|
||||
if (port != NULL)
|
||||
*port = _port;
|
||||
|
||||
LOG_DBG("host: \"%.*s\"", (int)host_len, start);
|
||||
LOG_DBG("port: \"%.*s\" (%hu)", (int)port_len, port_str, _port);
|
||||
} else {
|
||||
size_t host_len = path_segment - start;
|
||||
if (host != NULL)
|
||||
*host = xstrndup(start, host_len);
|
||||
|
||||
LOG_DBG("host: \"%.*s\"", (int)host_len, start);
|
||||
}
|
||||
|
||||
start = path_segment;
|
||||
left = len - (start - uri);
|
||||
}
|
||||
|
||||
/* Do we have a query? */
|
||||
const char *query_start = memchr(start, '?', left);
|
||||
const char *fragment_start = memchr(start, '#', left);
|
||||
|
||||
size_t path_len =
|
||||
query_start != NULL ? query_start - start :
|
||||
fragment_start != NULL ? fragment_start - start :
|
||||
left;
|
||||
|
||||
if (path_len == 0)
|
||||
goto err;
|
||||
|
||||
/* Path - decode %xx encoded characters */
|
||||
if (path != NULL) {
|
||||
const char *encoded = start;
|
||||
char *decoded = xmalloc(path_len + 1);
|
||||
char *p = decoded;
|
||||
|
||||
size_t encoded_len = path_len;
|
||||
size_t decoded_len = 0;
|
||||
|
||||
while (true) {
|
||||
/* Find next '%' */
|
||||
const char *next = memchr(encoded, '%', encoded_len);
|
||||
|
||||
if (next == NULL) {
|
||||
strncpy(p, encoded, encoded_len);
|
||||
decoded_len += encoded_len;
|
||||
p += encoded_len;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy everything leading up to the '%' */
|
||||
size_t prefix_len = next - encoded;
|
||||
memcpy(p, encoded, prefix_len);
|
||||
|
||||
p += prefix_len;
|
||||
encoded_len -= prefix_len;
|
||||
decoded_len += prefix_len;
|
||||
|
||||
if (isxdigit(next[1]) && isxdigit(next[2])) {
|
||||
*p++ = nibble2hex(next[1]) << 4 | nibble2hex(next[2]);
|
||||
decoded_len++;
|
||||
encoded_len -= 3;
|
||||
encoded = next + 3;
|
||||
} else {
|
||||
*p++ = *next;
|
||||
decoded_len++;
|
||||
encoded_len -= 1;
|
||||
encoded = next + 1;
|
||||
}
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
*path = decoded;
|
||||
|
||||
LOG_DBG("path: encoded=\"%.*s\", decoded=\"%s\"", (int)path_len, start, decoded);
|
||||
} else
|
||||
LOG_DBG("path: encoded=\"%.*s\", decoded=<skipped>", (int)path_len, start);
|
||||
|
||||
start = query_start != NULL ? query_start + 1 : fragment_start != NULL ? fragment_start + 1 : uri + len;
|
||||
left = len - (start - uri);
|
||||
|
||||
if (query_start != NULL) {
|
||||
size_t query_len = fragment_start != NULL
|
||||
? fragment_start - start : left;
|
||||
|
||||
if (query_len == 0)
|
||||
goto err;
|
||||
|
||||
if (query != NULL)
|
||||
*query = xstrndup(start, query_len);
|
||||
|
||||
LOG_DBG("query: \"%.*s\"", (int)query_len, start);
|
||||
|
||||
start = fragment_start != NULL ? fragment_start + 1 : uri + len;
|
||||
left = len - (start - uri);
|
||||
}
|
||||
|
||||
if (fragment_start != NULL) {
|
||||
if (left == 0)
|
||||
goto err;
|
||||
|
||||
if (fragment != NULL)
|
||||
*fragment = xstrndup(start, left);
|
||||
|
||||
LOG_DBG("fragment: \"%.*s\"", (int)left, start);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
if (scheme != NULL) free(*scheme);
|
||||
if (user != NULL) free(*user);
|
||||
if (password != NULL) free(*password);
|
||||
if (host != NULL) free(*host);
|
||||
if (path != NULL) free(*path);
|
||||
if (query != NULL) free(*query);
|
||||
if (fragment != NULL) free(*fragment);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
hostname_is_localhost(const char *hostname)
|
||||
{
|
||||
char this_host[HOST_NAME_MAX];
|
||||
if (gethostname(this_host, sizeof(this_host)) < 0)
|
||||
this_host[0] = '\0';
|
||||
|
||||
return (strcmp(hostname, "") == 0 ||
|
||||
strcmp(hostname, "localhost") == 0 ||
|
||||
strcmp(hostname, this_host) == 0);
|
||||
}
|
||||
11
uri.h
Normal file
11
uri.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool uri_parse(const char *uri, size_t len,
|
||||
char **scheme, char **user, char **password, char **host,
|
||||
uint16_t *port, char **path, char **query, char **fragment);
|
||||
|
||||
bool hostname_is_localhost(const char *hostname);
|
||||
Loading…
Add table
Add a link
Reference in a new issue