cosmopolitan/third_party/quickjs/uri.c

281 lines
8.7 KiB
C
Raw Normal View History

/*
* QuickJS Javascript Engine
*
* Copyright (c) 2017-2021 Fabrice Bellard
* Copyright (c) 2017-2021 Charlie Gordon
*
* 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 "libc/str/str.h"
#include "third_party/quickjs/internal.h"
asm(".ident\t\"\\n\\n\
QuickJS (MIT License)\\n\
Copyright (c) 2017-2021 Fabrice Bellard\\n\
Copyright (c) 2017-2021 Charlie Gordon\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
static int string_get_hex(JSString *p, int k, int n) {
int c = 0, h;
while (n-- > 0) {
if ((h = from_hex(string_get(p, k++))) < 0)
return -1;
c = (c << 4) | h;
}
return c;
}
static int isURIReserved(int c) {
return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
}
static int hex_decode(JSContext *ctx, JSString *p, int k) {
int c;
if (k >= p->len || string_get(p, k) != '%')
return js_throw_URIError(ctx, "expecting %%");
if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
return js_throw_URIError(ctx, "expecting hex digit");
return c;
}
JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int isComponent)
{
JSValue str;
StringBuffer b_s, *b = &b_s;
JSString *p;
int k, c, c1, n, c_min;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
return str;
string_buffer_init(ctx, b, 0);
p = JS_VALUE_GET_STRING(str);
for (k = 0; k < p->len;) {
c = string_get(p, k);
if (c == '%') {
c = hex_decode(ctx, p, k);
if (c < 0)
goto fail;
k += 3;
if (c < 0x80) {
if (!isComponent && isURIReserved(c)) {
c = '%';
k -= 2;
}
} else {
/* Decode URI-encoded UTF-8 sequence */
if (c >= 0xc0 && c <= 0xdf) {
n = 1;
c_min = 0x80;
c &= 0x1f;
} else if (c >= 0xe0 && c <= 0xef) {
n = 2;
c_min = 0x800;
c &= 0xf;
} else if (c >= 0xf0 && c <= 0xf7) {
n = 3;
c_min = 0x10000;
c &= 0x7;
} else {
n = 0;
c_min = 1;
c = 0;
}
while (n-- > 0) {
c1 = hex_decode(ctx, p, k);
if (c1 < 0)
goto fail;
k += 3;
if ((c1 & 0xc0) != 0x80) {
c = 0;
break;
}
c = (c << 6) | (c1 & 0x3f);
}
if (c < c_min || c > 0x10FFFF ||
(c >= 0xd800 && c < 0xe000)) {
js_throw_URIError(ctx, "malformed UTF-8");
goto fail;
}
}
} else {
k++;
}
string_buffer_putc(b, c);
}
JS_FreeValue(ctx, str);
return string_buffer_end(b);
fail:
JS_FreeValue(ctx, str);
string_buffer_free(b);
return JS_EXCEPTION;
}
static int isUnescaped(int c) {
static char const unescaped_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"@*_+-./";
return c < 0x100 &&
memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
}
static int isURIUnescaped(int c, int isComponent) {
return c < 0x100 &&
((c >= 0x61 && c <= 0x7a) ||
(c >= 0x41 && c <= 0x5a) ||
(c >= 0x30 && c <= 0x39) ||
memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
(!isComponent && isURIReserved(c)));
}
static int encodeURI_hex(StringBuffer *b, int c) {
uint8_t buf[6];
int n = 0;
const char *hex = "0123456789ABCDEF";
buf[n++] = '%';
if (c >= 256) {
buf[n++] = 'u';
buf[n++] = hex[(c >> 12) & 15];
buf[n++] = hex[(c >> 8) & 15];
}
buf[n++] = hex[(c >> 4) & 15];
buf[n++] = hex[(c >> 0) & 15];
return string_buffer_write8(b, buf, n);
}
JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv,
int isComponent)
{
JSValue str;
StringBuffer b_s, *b = &b_s;
JSString *p;
int k, c, c1;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
return str;
p = JS_VALUE_GET_STRING(str);
string_buffer_init(ctx, b, p->len);
for (k = 0; k < p->len;) {
c = string_get(p, k);
k++;
if (isURIUnescaped(c, isComponent)) {
string_buffer_putc16(b, c);
} else {
if (c >= 0xdc00 && c <= 0xdfff) {
js_throw_URIError(ctx, "invalid character");
goto fail;
} else if (c >= 0xd800 && c <= 0xdbff) {
if (k >= p->len) {
js_throw_URIError(ctx, "expecting surrogate pair");
goto fail;
}
c1 = string_get(p, k);
k++;
if (c1 < 0xdc00 || c1 > 0xdfff) {
js_throw_URIError(ctx, "expecting surrogate pair");
goto fail;
}
c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
}
if (c < 0x80) {
encodeURI_hex(b, c);
} else {
/* XXX: use C UTF-8 conversion ? */
if (c < 0x800) {
encodeURI_hex(b, (c >> 6) | 0xc0);
} else {
if (c < 0x10000) {
encodeURI_hex(b, (c >> 12) | 0xe0);
} else {
encodeURI_hex(b, (c >> 18) | 0xf0);
encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
}
encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
}
encodeURI_hex(b, (c & 0x3f) | 0x80);
}
}
}
JS_FreeValue(ctx, str);
return string_buffer_end(b);
fail:
JS_FreeValue(ctx, str);
string_buffer_free(b);
return JS_EXCEPTION;
}
JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
JSValue str;
StringBuffer b_s, *b = &b_s;
JSString *p;
int i, len, c;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
return str;
p = JS_VALUE_GET_STRING(str);
string_buffer_init(ctx, b, p->len);
for (i = 0, len = p->len; i < len; i++) {
c = string_get(p, i);
if (isUnescaped(c)) {
string_buffer_putc16(b, c);
} else {
encodeURI_hex(b, c);
}
}
JS_FreeValue(ctx, str);
return string_buffer_end(b);
}
JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
JSValue str;
StringBuffer b_s, *b = &b_s;
JSString *p;
int i, len, c, n;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
return str;
string_buffer_init(ctx, b, 0);
p = JS_VALUE_GET_STRING(str);
for (i = 0, len = p->len; i < len; i++) {
c = string_get(p, i);
if (c == '%') {
if (i + 6 <= len
&& string_get(p, i + 1) == 'u'
&& (n = string_get_hex(p, i + 2, 4)) >= 0) {
c = n;
i += 6 - 1;
} else
if (i + 3 <= len
&& (n = string_get_hex(p, i + 1, 2)) >= 0) {
c = n;
i += 3 - 1;
}
}
string_buffer_putc16(b, c);
}
JS_FreeValue(ctx, str);
return string_buffer_end(b);
}