core: Factor out common URI decode functions from Wayland and X11

This commit is contained in:
Frank Praznik
2024-06-26 18:13:29 -04:00
parent 0ae4fd0ec9
commit 71200e94a8
4 changed files with 185 additions and 246 deletions

View File

@@ -0,0 +1,123 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_uri_decode.h"
#include <errno.h>
#include <unistd.h>
int SDL_URIDecode(const char *src, char *dst, int len)
{
int ri, wi, di;
char decode = '\0';
if (!src || !dst || len < 0) {
errno = EINVAL;
return -1;
}
if (len == 0) {
len = SDL_strlen(src);
}
for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) {
if (di == 0) {
/* start decoding */
if (src[ri] == '%') {
decode = '\0';
di += 1;
continue;
}
/* normal write */
dst[wi] = src[ri];
wi += 1;
continue;
} else if (di == 1 || di == 2) {
char off = '\0';
char isa = src[ri] >= 'a' && src[ri] <= 'f';
char isA = src[ri] >= 'A' && src[ri] <= 'F';
char isn = src[ri] >= '0' && src[ri] <= '9';
if (!(isa || isA || isn)) {
/* not a hexadecimal */
int sri;
for (sri = ri - di; sri <= ri; sri += 1) {
dst[wi] = src[sri];
wi += 1;
}
di = 0;
continue;
}
/* itsy bitsy magicsy */
if (isn) {
off = 0 - '0';
} else if (isa) {
off = 10 - 'a';
} else if (isA) {
off = 10 - 'A';
}
decode |= (src[ri] + off) << (2 - di) * 4;
if (di == 2) {
dst[wi] = decode;
wi += 1;
di = 0;
} else {
di += 1;
}
continue;
}
}
dst[wi] = '\0';
return wi;
}
int SDL_URIToLocal(const char *src, char *dst)
{
if (SDL_memcmp(src, "file:/", 6) == 0) {
src += 6; /* local file? */
} else if (SDL_strstr(src, ":/") != NULL) {
return -1; /* wrong scheme */
}
SDL_bool local = src[0] != '/' || (src[0] != '\0' && src[1] == '/');
/* got a hostname? */
if (!local && src[0] == '/' && src[2] != '/') {
char *hostname_end = SDL_strchr(src + 1, '/');
if (hostname_end) {
char hostname[257];
if (gethostname(hostname, 255) == 0) {
hostname[256] = '\0';
if (SDL_memcmp(src + 1, hostname, hostname_end - (src + 1)) == 0) {
src = hostname_end + 1;
local = SDL_TRUE;
}
}
}
}
if (local) {
/* Convert URI escape sequences to real characters */
if (src[0] == '/') {
src++;
} else {
src--;
}
return SDL_URIDecode(src, dst, 0);
}
return -1;
}

View File

@@ -0,0 +1,56 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_uri_decode_h_
#define SDL_uri_decode_h_
/* Decodes URI escape sequences in string src of len bytes
* (excluding the terminating NULL byte) into the dst buffer.
* Since URI-encoded characters take three times the space of
* normal characters, src and dst can safely point to the same
* buffer for in situ conversion.
*
* The buffer is guaranteed to be NULL-terminated, but
* may contain embedded NULL bytes.
*
* Returns the number of decoded bytes that wound up in
* the destination buffer, excluding the terminating NULL byte.
*
* On error, -1 is returned.
*/
int SDL_URIDecode(const char *src, char *dst, int len);
/* Convert URI to a local filename, stripping the "file://"
* preamble and hostname if present, and writes the result
* to the dst buffer. Since URI-encoded characters take
* three times the space of normal characters, src and dst
* can safely point to the same buffer for in situ conversion.
*
* Returns the number of decoded bytes that wound up in
* the destination buffer, excluding the terminating NULL byte.
*
* On error, -1 is returned;
*/
int SDL_URIToLocal(const char *src, char *dst);
#endif /* SDL_uri_decode_h_ */

View File

@@ -24,6 +24,7 @@
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include "../../core/unix/SDL_poll.h"
#include "../../core/unix/SDL_uri_decode.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_scancode_tables_c.h"
#include "../../core/linux/SDL_system_theme.h"
@@ -89,8 +90,6 @@ struct SDL_WaylandTouchPoint
static struct wl_list touch_points;
static char *Wayland_URIToLocal(char *uri);
static void touch_add(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface)
{
struct SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint));
@@ -2060,127 +2059,6 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data
}
}
/* Decodes URI escape sequences in string buf of len bytes
* (excluding the terminating NULL byte) in-place. Since
* URI-encoded characters take three times the space of
* normal characters, this should not be an issue.
*
* Returns the number of decoded bytes that wound up in
* the buffer, excluding the terminating NULL byte.
*
* The buffer is guaranteed to be NULL-terminated but
* may contain embedded NULL bytes.
*
* On error, -1 is returned.
*
* FIXME: This was shamelessly copied from SDL_x11events.c
*/
static int Wayland_URIDecode(char *buf, int len)
{
int ri, wi, di;
char decode = '\0';
if (!buf || len < 0) {
errno = EINVAL;
return -1;
}
if (len == 0) {
len = SDL_strlen(buf);
}
for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) {
if (di == 0) {
/* start decoding */
if (buf[ri] == '%') {
decode = '\0';
di += 1;
continue;
}
/* normal write */
buf[wi] = buf[ri];
wi += 1;
continue;
} else if (di == 1 || di == 2) {
char off = '\0';
char isa = buf[ri] >= 'a' && buf[ri] <= 'f';
char isA = buf[ri] >= 'A' && buf[ri] <= 'F';
char isn = buf[ri] >= '0' && buf[ri] <= '9';
if (!(isa || isA || isn)) {
/* not a hexadecimal */
int sri;
for (sri = ri - di; sri <= ri; sri += 1) {
buf[wi] = buf[sri];
wi += 1;
}
di = 0;
continue;
}
/* itsy bitsy magicsy */
if (isn) {
off = 0 - '0';
} else if (isa) {
off = 10 - 'a';
} else if (isA) {
off = 10 - 'A';
}
decode |= (buf[ri] + off) << (2 - di) * 4;
if (di == 2) {
buf[wi] = decode;
wi += 1;
di = 0;
} else {
di += 1;
}
continue;
}
}
buf[wi] = '\0';
return wi;
}
/* Convert URI to local filename
* return filename if possible, else NULL
*
* FIXME: This was shamelessly copied from SDL_x11events.c
*/
static char *Wayland_URIToLocal(char *uri)
{
char *file = NULL;
SDL_bool local;
if (SDL_memcmp(uri, "file:/", 6) == 0) {
uri += 6; /* local file? */
} else if (SDL_strstr(uri, ":/") != NULL) {
return file; /* wrong scheme */
}
local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/');
/* got a hostname? */
if (!local && uri[0] == '/' && uri[2] != '/') {
char *hostname_end = SDL_strchr(uri + 1, '/');
if (hostname_end) {
char hostname[257];
if (gethostname(hostname, 255) == 0) {
hostname[256] = '\0';
if (SDL_memcmp(uri + 1, hostname, hostname_end - (uri + 1)) == 0) {
uri = hostname_end + 1;
local = SDL_TRUE;
}
}
}
}
if (local) {
file = uri;
/* Convert URI escape sequences to real characters */
Wayland_URIDecode(file, 0);
if (uri[1] == '/') {
file++;
} else {
file--;
}
}
return file;
}
static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_device)
{
SDL_WaylandDataDevice *data_device = data;
@@ -2226,9 +2104,8 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d
char *saveptr = NULL;
char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
while (token) {
char *fn = Wayland_URIToLocal(token);
if (fn) {
SDL_SendDropFile(data_device->dnd_window, NULL, fn);
if (SDL_URIToLocal(token, token) >= 0) {
SDL_SendDropFile(data_device->dnd_window, NULL, token);
}
token = SDL_strtok_r(NULL, "\r\n", &saveptr);
}

View File

@@ -36,6 +36,7 @@
#include "SDL_x11settings.h"
#include "../SDL_clipboard_c.h"
#include "../../core/unix/SDL_poll.h"
#include "../../core/unix/SDL_uri_decode.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_touch_c.h"
@@ -207,123 +208,6 @@ static SDL_bool X11_IsWheelEvent(Display *display, int button, int *xticks, int
return SDL_FALSE;
}
/* Decodes URI escape sequences in string buf of len bytes
(excluding the terminating NULL byte) in-place. Since
URI-encoded characters take three times the space of
normal characters, this should not be an issue.
Returns the number of decoded bytes that wound up in
the buffer, excluding the terminating NULL byte.
The buffer is guaranteed to be NULL-terminated but
may contain embedded NULL bytes.
On error, -1 is returned.
*/
static int X11_URIDecode(char *buf, int len)
{
int ri, wi, di;
char decode = '\0';
if (!buf || len < 0) {
errno = EINVAL;
return -1;
}
if (len == 0) {
len = SDL_strlen(buf);
}
for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) {
if (di == 0) {
/* start decoding */
if (buf[ri] == '%') {
decode = '\0';
di += 1;
continue;
}
/* normal write */
buf[wi] = buf[ri];
wi += 1;
continue;
} else if (di == 1 || di == 2) {
char off = '\0';
char isa = buf[ri] >= 'a' && buf[ri] <= 'f';
char isA = buf[ri] >= 'A' && buf[ri] <= 'F';
char isn = buf[ri] >= '0' && buf[ri] <= '9';
if (!(isa || isA || isn)) {
/* not a hexadecimal */
int sri;
for (sri = ri - di; sri <= ri; sri += 1) {
buf[wi] = buf[sri];
wi += 1;
}
di = 0;
continue;
}
/* itsy bitsy magicsy */
if (isn) {
off = 0 - '0';
} else if (isa) {
off = 10 - 'a';
} else if (isA) {
off = 10 - 'A';
}
decode |= (buf[ri] + off) << (2 - di) * 4;
if (di == 2) {
buf[wi] = decode;
wi += 1;
di = 0;
} else {
di += 1;
}
continue;
}
}
buf[wi] = '\0';
return wi;
}
/* Convert URI to local filename
return filename if possible, else NULL
*/
static char *X11_URIToLocal(char *uri)
{
char *file = NULL;
SDL_bool local;
if (SDL_memcmp(uri, "file:/", 6) == 0) {
uri += 6; /* local file? */
} else if (SDL_strstr(uri, ":/") != NULL) {
return file; /* wrong scheme */
}
local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/');
/* got a hostname? */
if (!local && uri[0] == '/' && uri[2] != '/') {
char *hostname_end = SDL_strchr(uri + 1, '/');
if (hostname_end) {
char hostname[257];
if (gethostname(hostname, 255) == 0) {
hostname[256] = '\0';
if (SDL_memcmp(uri + 1, hostname, hostname_end - (uri + 1)) == 0) {
uri = hostname_end + 1;
local = SDL_TRUE;
}
}
}
}
if (local) {
file = uri;
/* Convert URI escape sequences to real characters */
X11_URIDecode(file, 0);
if (uri[1] == '/') {
file++;
} else {
file--;
}
}
return file;
}
/* An X11 event hook */
static SDL_X11EventHook g_X11EventHook = NULL;
static void *g_X11EventHookData = NULL;
@@ -1886,9 +1770,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
if (SDL_strcmp("text/plain", name) == 0) {
SDL_SendDropText(data->window, token);
} else if (SDL_strcmp("text/uri-list", name) == 0) {
char *fn = X11_URIToLocal(token);
if (fn) {
SDL_SendDropFile(data->window, NULL, fn);
if (SDL_URIToLocal(token, token) >= 0) {
SDL_SendDropFile(data->window, NULL, token);
}
}
token = SDL_strtok_r(NULL, "\r\n", &saveptr);