mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
10fd8bdb70
This change resurrects ae5d06dc53
280 lines
8.7 KiB
C
280 lines
8.7 KiB
C
/*
|
|
* 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);
|
|
}
|