feat: ports - netsurf with the monkey frontend
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Ae69wJS7sueQMUwNxV3nX
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* iconv() for the MontaukOS NetSurf port, implemented over libparserutils'
|
||||
* charset codecs. See iconv.h for why.
|
||||
*
|
||||
* Structure: one codec decodes the source charset to UCS-4, a second encodes
|
||||
* UCS-4 to the destination charset. iconv() pumps a small stack buffer of
|
||||
* UCS-4 between them so neither side needs an unbounded intermediate.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <parserutils/charset/codec.h>
|
||||
#include <parserutils/errors.h>
|
||||
|
||||
#include "iconv.h"
|
||||
|
||||
/* UCS-4 code points staged between decode and encode, per inner iteration. */
|
||||
#define STAGE_POINTS 64
|
||||
|
||||
struct montauk_iconv {
|
||||
parserutils_charset_codec *from; /* source charset -> UCS-4 */
|
||||
parserutils_charset_codec *to; /* UCS-4 -> dest charset */
|
||||
};
|
||||
|
||||
/*
|
||||
* Strip glibc's "//TRANSLIT" / "//IGNORE" suffixes, which libparserutils does
|
||||
* not understand. Returns a malloc'd bare charset name.
|
||||
*/
|
||||
static char *strip_suffix(const char *name)
|
||||
{
|
||||
const char *sep;
|
||||
size_t len;
|
||||
char *out;
|
||||
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
sep = strstr(name, "//");
|
||||
len = (sep != NULL) ? (size_t) (sep - name) : strlen(name);
|
||||
|
||||
out = malloc(len + 1);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(out, name, len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
static parserutils_charset_codec *make_codec(const char *name)
|
||||
{
|
||||
parserutils_charset_codec *codec = NULL;
|
||||
parserutils_charset_codec_optparams params;
|
||||
char *bare;
|
||||
|
||||
bare = strip_suffix(name);
|
||||
if (bare == NULL)
|
||||
return NULL;
|
||||
|
||||
if (parserutils_charset_codec_create(bare, &codec) != PARSERUTILS_OK) {
|
||||
free(bare);
|
||||
return NULL;
|
||||
}
|
||||
free(bare);
|
||||
|
||||
/*
|
||||
* LOOSE: substitute unrepresentable characters instead of aborting.
|
||||
* NetSurf renders best-effort; a hard failure mid-document is worse
|
||||
* than a replacement character.
|
||||
*/
|
||||
params.error_mode.mode = PARSERUTILS_CHARSET_CODEC_ERROR_LOOSE;
|
||||
(void) parserutils_charset_codec_setopt(codec,
|
||||
PARSERUTILS_CHARSET_CODEC_ERROR_MODE, ¶ms);
|
||||
|
||||
return codec;
|
||||
}
|
||||
|
||||
iconv_t iconv_open(const char *tocode, const char *fromcode)
|
||||
{
|
||||
struct montauk_iconv *cd;
|
||||
|
||||
cd = calloc(1, sizeof(*cd));
|
||||
if (cd == NULL) {
|
||||
errno = ENOMEM;
|
||||
return (iconv_t) -1;
|
||||
}
|
||||
|
||||
cd->from = make_codec(fromcode);
|
||||
cd->to = make_codec(tocode);
|
||||
|
||||
if (cd->from == NULL || cd->to == NULL) {
|
||||
if (cd->from != NULL)
|
||||
parserutils_charset_codec_destroy(cd->from);
|
||||
if (cd->to != NULL)
|
||||
parserutils_charset_codec_destroy(cd->to);
|
||||
free(cd);
|
||||
errno = EINVAL; /* unsupported conversion */
|
||||
return (iconv_t) -1;
|
||||
}
|
||||
|
||||
return (iconv_t) cd;
|
||||
}
|
||||
|
||||
int iconv_close(iconv_t handle)
|
||||
{
|
||||
struct montauk_iconv *cd = (struct montauk_iconv *) handle;
|
||||
|
||||
if (cd == NULL || handle == (iconv_t) -1) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
parserutils_charset_codec_destroy(cd->from);
|
||||
parserutils_charset_codec_destroy(cd->to);
|
||||
free(cd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t iconv(iconv_t handle, char **inbuf, size_t *inbytesleft,
|
||||
char **outbuf, size_t *outbytesleft)
|
||||
{
|
||||
struct montauk_iconv *cd = (struct montauk_iconv *) handle;
|
||||
size_t converted = 0;
|
||||
|
||||
if (cd == NULL || handle == (iconv_t) -1) {
|
||||
errno = EBADF;
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
/* Reset request: iconv(cd, NULL, NULL, ...) returns to initial state. */
|
||||
if (inbuf == NULL || *inbuf == NULL) {
|
||||
parserutils_charset_codec_reset(cd->from);
|
||||
parserutils_charset_codec_reset(cd->to);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*inbytesleft > 0) {
|
||||
uint8_t stage[STAGE_POINTS * 4];
|
||||
const uint8_t *src = (const uint8_t *) *inbuf;
|
||||
size_t srclen = *inbytesleft;
|
||||
uint8_t *mid = stage;
|
||||
size_t midlen = sizeof(stage);
|
||||
size_t staged;
|
||||
const uint8_t *midsrc;
|
||||
size_t midsrclen;
|
||||
uint8_t *dst;
|
||||
size_t dstlen;
|
||||
parserutils_error perr;
|
||||
|
||||
/* ---- source charset -> UCS-4 ---- */
|
||||
perr = parserutils_charset_codec_decode(cd->from,
|
||||
&src, &srclen, &mid, &midlen);
|
||||
|
||||
staged = sizeof(stage) - midlen;
|
||||
|
||||
if (staged == 0) {
|
||||
/* nothing decoded: classify why and stop */
|
||||
if (perr == PARSERUTILS_NEEDDATA)
|
||||
errno = EINVAL; /* incomplete tail sequence */
|
||||
else if (perr == PARSERUTILS_INVALID)
|
||||
errno = EILSEQ; /* malformed input */
|
||||
else if (perr == PARSERUTILS_NOMEM)
|
||||
errno = E2BIG;
|
||||
else
|
||||
errno = EILSEQ;
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
/* ---- UCS-4 -> destination charset ---- */
|
||||
midsrc = stage;
|
||||
midsrclen = staged;
|
||||
dst = (uint8_t *) *outbuf;
|
||||
dstlen = *outbytesleft;
|
||||
|
||||
perr = parserutils_charset_codec_encode(cd->to,
|
||||
&midsrc, &midsrclen, &dst, &dstlen);
|
||||
|
||||
/*
|
||||
* Advance the caller's input only by what was both decoded AND
|
||||
* encoded. If the encoder ran out of output room mid-stage,
|
||||
* the undrained UCS-4 must not be counted as consumed -- the
|
||||
* caller will grow its buffer and call again.
|
||||
*/
|
||||
{
|
||||
size_t encoded_points = (staged - midsrclen) / 4;
|
||||
size_t consumed_in;
|
||||
|
||||
*outbytesleft = dstlen;
|
||||
*outbuf = (char *) dst;
|
||||
|
||||
if (midsrclen == 0) {
|
||||
/* whole stage drained: input consumed as decoded */
|
||||
consumed_in = (size_t) ((const char *) src - *inbuf);
|
||||
} else {
|
||||
/*
|
||||
* Partial drain. Re-decode from the original
|
||||
* position next time; consume nothing this
|
||||
* round beyond what actually made it out.
|
||||
*/
|
||||
if (encoded_points == 0) {
|
||||
errno = E2BIG;
|
||||
return (size_t) -1;
|
||||
}
|
||||
consumed_in = 0;
|
||||
}
|
||||
|
||||
if (consumed_in > 0) {
|
||||
*inbuf += consumed_in;
|
||||
*inbytesleft -= consumed_in;
|
||||
}
|
||||
|
||||
converted += encoded_points;
|
||||
|
||||
if (midsrclen != 0) {
|
||||
/* output was too small to take the rest */
|
||||
errno = E2BIG;
|
||||
return (size_t) -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (perr == PARSERUTILS_NOMEM) {
|
||||
errno = E2BIG;
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
if (*outbytesleft == 0 && *inbytesleft > 0) {
|
||||
errno = E2BIG;
|
||||
return (size_t) -1;
|
||||
}
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
Reference in New Issue
Block a user