Add new examples

This commit is contained in:
2025-12-06 10:34:47 +01:00
parent 2eff3ca28c
commit c7421d8947
15 changed files with 227 additions and 157 deletions
+2 -8
View File
@@ -1,8 +1,2 @@
test
test.exe
reference.out
reference.exe
parse.out
parse.exe
decode_field.out
decode_field.exe
*.out
*.exe
+1
View File
@@ -7,3 +7,4 @@ It features
* The ability to switch between RFC 3986 and WHATWG with a flag
* Relative reference parsing and resolution
* URL normalization
* Doesn't rely on null-terminated strings
+4 -3
View File
@@ -1,3 +1,4 @@
gcc url.c examples/reference.c -o reference.exe -Wall -Wextra -ggdb
gcc url.c examples/parse.c -o parse.exe -Wall -Wextra -ggdb
gcc url.c examples/decode_field.c -o decode_field.exe -Wall -Wextra -ggdb
gcc -o 000_parse.exe examples/000_parse.c url.c -I. -Wall -Wextra -ggdb
gcc -o 001_serialize.exe examples/001_serialize.c url.c -I. -Wall -Wextra -ggdb
gcc -o 002_partial_parse.exe examples/002_partial_parse.c url.c -I. -Wall -Wextra -ggdb
gcc -o 003_references.exe examples/003_references.c url.c -I. -Wall -Wextra -ggdb
+4 -3
View File
@@ -1,3 +1,4 @@
gcc url.c examples/reference.c -o reference.out -Wall -Wextra -ggdb
gcc url.c examples/parse.c -o parse.out -Wall -Wextra -ggdb
gcc url.c examples/decode_field.c -o decode_field.out -Wall -Wextra -ggdb
gcc -o 000_parse.out examples/000_parse.c url.c -I. -Wall -Wextra -ggdb
gcc -o 001_serialize.out examples/001_serialize.c url.c -I. -Wall -Wextra -ggdb
gcc -o 002_partial_parse.out examples/002_partial_parse.c url.c -I. -Wall -Wextra -ggdb
gcc -o 003_references.out examples/003_references.c url.c -I. -Wall -Wextra -ggdb
+44
View File
@@ -0,0 +1,44 @@
#include <url.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main(void)
{
char url[] = "http://example.com";
// This is how you parse an URL
URL parsed_url;
int ret = url_parse(url, strlen(url), NULL, &parsed_url, 0);
if (ret < 0) {
printf("Invalid URL!\n");
return -1;
}
// Now the URL's component are stored in the URL
// structure. Note that the fields of URL are slices
// into the source string.
// Let's print the URL's domain. Note that any
// field except for the scheme may be percent-encoded,
// so if you want the raw representation of a
// field you should decode it:
char domain[1<<9];
int domain_len = url_percent_decode(parsed_url.host_text, domain, sizeof(domain));
// url_percent_decode may fail if the percent-encoding
// is invalid or the buffer is too small. The url_parse
// function validates the percent-encoding, so we only
// need to worry about the buffer size.
assert(domain_len > -1);
if (domain_len >= (int) sizeof(domain)) {
printf("Domain buffer is too small\n");
return -1;
}
// Note that url.c never adds null characters to outputs
domain[domain_len] = '\0';
printf("The domain is: %s\n", domain);
return 0;
}
+40
View File
@@ -0,0 +1,40 @@
#include <url.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
int main(void)
{
// We can translate parsed URLs back into strings
// using the url_serialize function
char url[] = "http://example.com";
URL parsed_url;
int ret = url_parse(url, strlen(url), NULL, &parsed_url, 0);
if (ret < 0) {
printf("Invalid URL!\n");
return -1;
}
char buf[1<<9];
ret = url_serialize(&parsed_url, NULL, buf, sizeof(buf));
// On error, url_serialize returns -1. Errors can
// only occur when relative references are involved,
// which isn't our case.
assert(ret > -1);
// We still need to worry about the buffer's capacity
// though.
if (ret >= (int) sizeof(buf)) {
printf("Serialization buffer is too small\n");
return -1;
}
// url.c never adds null terminators
buf[ret] = '\0';
printf("Serialized URL: %s\n", buf);
return 0;
}
+47
View File
@@ -0,0 +1,47 @@
#include <url.h>
#include <stdio.h>
int main(void)
{
// We can also parse URLs when they are part of a larger
// string. This is useful when we know the starting offset
// of an URL but don't know the end.
//
// For instance, say we have this string:
char str[] = " http://websiteA.com http://websiteB.com/index.html ";
int len = sizeof(str)-1;
// The "pcur" argument of url_parse allows it to tell us
// how long the URL is.
int cur = 0;
for (;;) {
// Skip whitespace preceding the URL
while (cur < len && str[cur] == ' ')
cur++;
if (cur == len)
break; // No more string
int url_off = cur;
URL url;
int ret = url_parse(str, len, &cur, &url, 0);
if (ret < 0) {
printf("Invalid URL\n");
return -1;
}
int url_len = cur - url_off;
// Thanks to the pcur argument of url_parse we can infer
// when the URL ends. We can get the original text of the
// URL my slicing the source string using the starting and
// end offsets, or use something like url_serialize to
// translate the URL structure into a string.
printf("Found an URL: %.*s\n", url_len, str + url_off);
}
return 0;
}
+60
View File
@@ -0,0 +1,60 @@
#include <url.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
// url.c also allows us to parse relative references to URLs.
// These are strings that aren't technically URLs but may be
// evaluated to one in reference to a base URL.
//
// Here's an example:
char base_url[] = "http://example.com/files/document.txt";
char relative_reference[] = "../images/cat.png";
// The url_serialize function allows us to translate the
// reference into an URL. But first, we need to parse both
// the base URL and reference.
URL parsed_base_url;
int ret = url_parse(base_url, strlen(base_url), NULL, &parsed_base_url, 0);
if (ret < 0) {
printf("Invalid base URL\n");
return -1;
}
// Note that url_parse will reject relative references by
// default. We need to pass the URL_FLAG_ALLOWREF flag.
URL parsed_relative_reference;
ret = url_parse(relative_reference, strlen(relative_reference),
NULL, &parsed_relative_reference, URL_FLAG_ALLOWREF);
if (ret < 0) {
printf("Invalid relative reference\n");
return -1;
}
// Now we can resolve the reference by serializing it with
// the base URL
char buf[1<<9];
ret = url_serialize(&parsed_relative_reference, &parsed_base_url, buf, sizeof(buf));
// Since url_serialize was called with a non-NULL base URL
// argument, it may fail. We need to check for a negative
// return value.
if (ret < 0) {
printf("Reference resolution failed\n");
return -1;
}
// Check that the buffer's capacity was enough
if (ret >= (int) sizeof(buf)) {
printf("Serialization buffer is too small\n");
return -1;
}
// All good. Now we can print the result
buf[ret] = '\0';
printf("Resolved reference: %s\n", buf);
return 0;
}
-29
View File
@@ -1,29 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "../url.h"
int main(void)
{
char src[] = "http://web%73ite.com";
URL parsed;
int ret = url_parse(src, strlen(src), NULL, 1, &parsed);
if (ret < 0) {
printf("Couldn't parse base URL\n");
return -1;
}
printf("%.*s\n",
parsed.host_text.len,
parsed.host_text.ptr);
char buf[100];
ret = url_decode_field(parsed.host_text, buf, (int) sizeof(buf)-1);
if (ret < 0 || ret >= (int) sizeof(buf)) {
printf("Couldn't decode field\n");
return -1;
}
printf("%.*s\n", ret, buf);
return 0;
}
-62
View File
@@ -1,62 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "../url.h"
int main(void)
{
char src[] = "http://website.com/users/000?filter=no#title";
URL parsed;
int ret = url_parse(src, strlen(src), NULL, 1, &parsed);
if (ret < 0) {
printf("Couldn't parse base URL\n");
return -1;
}
// http
printf("scheme=%.*s\n",
parsed.scheme.len,
parsed.scheme.ptr);
// (empty)
if (parsed.username.len == 0)
printf("username=(empty)\n");
else
printf("username=%.*s\n",
parsed.username.len,
parsed.username.ptr);
// (password)
if (parsed.password.len == 0)
printf("password=(empty)\n");
else
printf("password=%.*s\n",
parsed.password.len,
parsed.password.ptr);
if (parsed.host_type == URL_HOST_EMPTY)
printf("host=(empty)\n");
else
printf("host=%.*s\n",
parsed.host_text.len,
parsed.host_text.ptr);
if (parsed.no_port)
printf("port=(empty)\n");
else
printf("port=%d\n", parsed.port);
printf("path=%.*s\n",
parsed.path.len,
parsed.path.ptr);
printf("query=%.*s\n",
parsed.query.len,
parsed.query.ptr);
printf("fragment=%.*s\n",
parsed.fragment.len,
parsed.fragment.ptr);
return 0;
}
-27
View File
@@ -1,27 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "../url.h"
int main(void)
{
char base[] = "http://website.com/users/000";
char ref[] = "../images/cat.png";
URL parsed_base;
int ret = url_parse(base, strlen(base), NULL, 1, &parsed_base);
if (ret < 0) {
printf("Couldn't parse base URL\n");
return -1;
}
char dst[1<<10];
ret = url_resolve_reference(ref, strlen(ref), NULL, &parsed_base, 1, dst, (int) sizeof(dst));
if (ret < 0) {
printf("Couldn't resolve reference\n");
return -1;
}
// http://website.com/images/cat.png
printf("%.*s\n", ret, dst);
return 0;
}
+2 -2
View File
@@ -3,11 +3,11 @@
ifeq ($(shell uname -s),Windows_NT)
EXT = .exe
else
EXT =
EXT = .out
endif
all:
gcc -o test$(EXT) test.c ../url.c cJSON.c -Wall -Wextra -ggdb
clean:
rm test test.exe
rm test.out test.exe
BIN
View File
Binary file not shown.
+15 -5
View File
@@ -1,4 +1,3 @@
#include <string.h>
#include "url.h"
// This is free and unencumbered software released into the public domain.
@@ -35,10 +34,21 @@
#define UINT8_MAX (URL_u8) ((1 << 8)-1)
#define UINT16_MAX (URL_u16) ((1 << 16)-1)
#define NULL ((void*) 0)
#define S(X) ((URL_String) { (X), sizeof(X)-1 })
#define EMPTY (URL_String) { (void*) 0, 0 }
#define EMPTY (URL_String) { NULL, 0 }
#define SLICE(src, off, end) (URL_String) { src + off, end - off }
#if defined(__GNUC__) || defined(__clang__)
#define memcpy_ __builtin_memcpy
#else
static void memcpy_(char *dst, char *src, int len)
{
for (int i = 0; i < len; i++)
dst[i] = src[i];
}
#endif
static URL_b32 streq(URL_String a, URL_String b)
{
if (a.len != b.len)
@@ -734,7 +744,7 @@ static void append(Builder *b, URL_String s)
int copy = s.len;
if (copy > unused)
copy = unused;
memcpy(b->dst + b->len, s.ptr, copy);
memcpy_(b->dst + b->len, s.ptr, copy);
}
b->len += s.len;
}
@@ -806,7 +816,7 @@ int url_serialize(URL *url, URL *base, char *dst, int cap)
Builder b = { dst, cap, 0 };
if (url->scheme.len > 0) {
append_scheme(&b, url->scheme);
append_authority(&b, url);
append_authority(&b, *url);
append(&b, url->path);
append_query(&b, url->query);
} else {
@@ -818,7 +828,7 @@ int url_serialize(URL *url, URL *base, char *dst, int cap)
ASSERT(base->scheme.len > 0);
append_scheme(&b, base->scheme);
if (!url->no_authority) {
append_authority(&b, url);
append_authority(&b, *url);
PathComps comps = {0};
if (resolve_dots_and_append_comps(&comps, url->path) < 0)
return -1;
+8 -18
View File
@@ -25,23 +25,19 @@
//
// For more information, please refer to <https://unlicense.org>
#define URL_STATIC_ASSERT _Static_assert
typedef unsigned char URL_u8;
typedef unsigned short URL_u16;
typedef unsigned int URL_u32;
typedef unsigned int URL_b32;
URL_STATIC_ASSERT(sizeof(URL_u8) == 1, "");
URL_STATIC_ASSERT(sizeof(URL_u16) == 2, "");
URL_STATIC_ASSERT(sizeof(URL_u32) == 4, "");
URL_STATIC_ASSERT(sizeof(URL_b32) == 4, "");
typedef struct {
char *ptr;
int len;
} URL_String;
typedef struct { URL_u32 data; } URL_IPv4;
typedef struct { URL_u16 data[8]; } URL_IPv6;
typedef enum {
URL_HOST_EMPTY,
URL_HOST_IPV4,
@@ -49,17 +45,6 @@ typedef enum {
URL_HOST_NAME,
} URL_HostType;
typedef struct {
URL_u32 data;
} URL_IPv4;
typedef struct {
URL_u16 data[8];
} URL_IPv6;
URL_STATIC_ASSERT(sizeof(URL_IPv4) == 4, "");
URL_STATIC_ASSERT(sizeof(URL_IPv6) == 16, "");
typedef struct {
// These flags are used to differentiate between
@@ -79,6 +64,7 @@ typedef struct {
URL_String scheme;
// Both may be empty
// May be percent-encoded
URL_String username;
URL_String password;
@@ -88,6 +74,7 @@ typedef struct {
// (host byte order).
// Note that the host may be empty, in which
// case host_type=URL_HOST_EMPTY.
// If host_text is set, it may be percent-encoded.
URL_HostType host_type;
URL_String host_text;
union {
@@ -100,12 +87,15 @@ typedef struct {
URL_b32 no_port;
URL_u16 port;
// May be percent-encoded
URL_String path;
// May be empty
// May be percent-encoded
URL_String query;
// May be empty
// May be percent-encoded
URL_String fragment;
} URL;