diff options
author | John Mark Bell <jmb@netsurf-browser.org> | 2005-04-16 05:09:33 +0000 |
---|---|---|
committer | John Mark Bell <jmb@netsurf-browser.org> | 2005-04-16 05:09:33 +0000 |
commit | c17dc661eae89cea66959fad4fe48d77f1faf174 (patch) | |
tree | 1909b4157137e66b3f74adc75ca1a2bbf3bc3c6f | |
parent | 37a4119ab80b1a7c7e37707c4e029218bdcbe549 (diff) | |
download | netsurf-c17dc661eae89cea66959fad4fe48d77f1faf174.tar.gz netsurf-c17dc661eae89cea66959fad4fe48d77f1faf174.tar.bz2 |
[project @ 2005-04-16 05:09:32 by jmb]
Split out UTF-8 handling functions.
Submit URL-encoded forms in sensible encoding:
* First entry in accept-charset list, if present
* Document encoding, otherwise
We may want to explicitly look for UTF-8, to save converting.
Convert cnv_str_local_enc/cnv_local_enc_str to use iconv (they're now veneers for utf8_[to/from]_enc).
Provide mechanism for looking up local system charset (derived from system alphabet, under RISC OS)
svn path=/import/netsurf/; revision=1647
-rw-r--r-- | desktop/browser.c | 4 | ||||
-rw-r--r-- | desktop/textinput.c | 151 | ||||
-rw-r--r-- | makefile | 3 | ||||
-rw-r--r-- | render/box_construct.c | 30 | ||||
-rw-r--r-- | render/form.c | 35 | ||||
-rw-r--r-- | render/form.h | 6 | ||||
-rw-r--r-- | riscos/ucstables.c | 41 | ||||
-rw-r--r-- | utils/utf8.c | 280 | ||||
-rw-r--r-- | utils/utf8.h | 26 | ||||
-rw-r--r-- | utils/utils.c | 104 | ||||
-rw-r--r-- | utils/utils.h | 8 |
11 files changed, 440 insertions, 248 deletions
diff --git a/desktop/browser.c b/desktop/browser.c index abee202b7..727fdc341 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -1492,7 +1492,7 @@ void browser_form_submit(struct browser_window *bw, struct form *form, switch (form->method) { case method_GET: - data = form_url_encode(success); + data = form_url_encode(form, success); if (!data) { form_free_successful(success); warn_user("NoMemory", 0); @@ -1517,7 +1517,7 @@ void browser_form_submit(struct browser_window *bw, struct form *form, break; case method_POST_URLENC: - data = form_url_encode(success); + data = form_url_encode(form, success); if (!data) { form_free_successful(success); warn_user("NoMemory", 0); diff --git a/desktop/textinput.c b/desktop/textinput.c index afa13f76e..8e284e477 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -25,6 +25,7 @@ #define NDEBUG #include "netsurf/utils/log.h" #include "netsurf/utils/talloc.h" +#include "netsurf/utils/utf8.h" #include "netsurf/utils/utils.h" static void browser_window_textarea_callback(struct browser_window *bw, @@ -38,121 +39,6 @@ static void browser_window_place_caret(struct browser_window *bw, void *p); /** - * Convert a single UCS4 character into a UTF8 multibyte sequence - * - * Encoding of UCS values outside the UTF16 plane has been removed from - * RFC3629. This macro conforms to RFC2279, however, as it is possible - * that the platform specific keyboard input handler will generate a UCS4 - * value outside the UTF16 plane. - * - * \param c The character to process (0 <= c <= 0x7FFFFFFF) - * \param s Pointer to 6 byte long output buffer - * \param l Integer in which to store length of multibyte sequence - */ -#define ucs4_to_utf8(c, s, l) \ - do { \ - if ((c) < 0) \ - assert(0); \ - else if ((c) < 0x80) { \ - *(s) = (char)(c); \ - (l) = 1; \ - } \ - else if ((c) < 0x800) { \ - *(s) = 0xC0 | (((c) >> 6) & 0x1F); \ - *((s)+1) = 0x80 | ((c) & 0x3F); \ - (l) = 2; \ - } \ - else if ((c) < 0x10000) { \ - *(s) = 0xE0 | (((c) >> 12) & 0xF); \ - *((s)+1) = 0x80 | (((c) >> 6) & 0x3F); \ - *((s)+2) = 0x80 | ((c) & 0x3F); \ - (l) = 3; \ - } \ - else if ((c) < 0x200000) { \ - *(s) = 0xF0 | (((c) >> 18) & 0x7); \ - *((s)+1) = 0x80 | (((c) >> 12) & 0x3F); \ - *((s)+2) = 0x80 | (((c) >> 6) & 0x3F); \ - *((s)+3) = 0x80 | ((c) & 0x3F); \ - (l) = 4; \ - } \ - else if ((c) < 0x4000000) { \ - *(s) = 0xF8 | (((c) >> 24) & 0x3); \ - *((s)+1) = 0x80 | (((c) >> 18) & 0x3F); \ - *((s)+2) = 0x80 | (((c) >> 12) & 0x3F); \ - *((s)+3) = 0x80 | (((c) >> 6) & 0x3F); \ - *((s)+4) = 0x80 | ((c) & 0x3F); \ - (l) = 5; \ - } \ - else if ((c) <= 0x7FFFFFFF) { \ - *(s) = 0xFC | (((c) >> 30) & 0x1); \ - *((s)+1) = 0x80 | (((c) >> 24) & 0x3F); \ - *((s)+2) = 0x80 | (((c) >> 18) & 0x3F); \ - *((s)+3) = 0x80 | (((c) >> 12) & 0x3F); \ - *((s)+4) = 0x80 | (((c) >> 6) & 0x3F); \ - *((s)+5) = 0x80 | ((c) & 0x3F); \ - (l) = 6; \ - } \ - } while(0) - -/** - * Calculate the length (in characters) of a NULL-terminated UTF8 string - * - * \param s The string - * \param l Integer in which to store length - */ -#define utf8_length(s, l) \ - do { \ - char *__s = (s); \ - (l) = 0; \ - while (*__s != '\0') { \ - if ((*__s & 0x80) == 0x00) \ - __s += 1; \ - else if ((*__s & 0xE0) == 0xC0) \ - __s += 2; \ - else if ((*__s & 0xF0) == 0xE0) \ - __s += 3; \ - else if ((*__s & 0xF8) == 0xF0) \ - __s += 4; \ - else if ((*__s & 0xFC) == 0xF8) \ - __s += 5; \ - else if ((*__s & 0xFE) == 0xFC) \ - __s += 6; \ - else \ - assert(0); \ - (l)++; \ - } \ - } while (0) - -/** - * Find previous legal UTF8 char in string - * - * \param s The string - * \param o Offset in the string to start at (updated on exit) - */ -#define utf8_prev(s, o) \ - do { \ - while ((o) != 0 && \ - !((((s)[--(o)] & 0x80) == 0x00) || \ - (((s)[(o)] & 0xC0) == 0xC0))) \ - /* do nothing */; \ - } while(0) - -/** - * Find next legal UTF8 char in string - * - * \param s The string - * \param l Maximum offset in string - * \param o Offset in the string to start at (updated on exit) - */ -#define utf8_next(s, l, o) \ - do { \ - while ((o) != (l) && \ - !((((s)[++(o)] & 0x80) == 0x00) || \ - (((s)[(o)] & 0xC0) == 0xC0))) \ - /* do nothing */; \ - } while(0) - -/** * Handle clicks in a text area by placing the caret. * * \param bw browser window where click occurred @@ -300,7 +186,7 @@ void browser_window_textarea_callback(struct browser_window *bw, if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) { /* normal character insertion */ - ucs4_to_utf8(key, utf8, utf8_len); + utf8_len = utf8_from_ucs4(key, utf8); text = talloc_realloc(bw->current_content, text_box->text, char, text_box->length + 8); @@ -368,7 +254,7 @@ void browser_window_textarea_callback(struct browser_window *bw, } else { /* delete a character */ int prev_offset = char_offset; - utf8_prev(text_box->text, char_offset); + char_offset = utf8_prev(text_box->text, char_offset); memmove(text_box->text + char_offset, text_box->text + prev_offset, @@ -429,8 +315,8 @@ void browser_window_textarea_callback(struct browser_window *bw, case 28: /* Right cursor -> */ if ((unsigned int) char_offset != text_box->length) { - utf8_next(text_box->text, text_box->length, - char_offset); + char_offset = utf8_next(text_box->text, + text_box->length, char_offset); } else { if (!text_box->next) /* at end of text area: ignore */ @@ -445,7 +331,7 @@ void browser_window_textarea_callback(struct browser_window *bw, case 29: /* Left cursor <- */ if (char_offset != 0) { - utf8_prev(text_box->text, char_offset); + char_offset = utf8_prev(text_box->text, char_offset); } else { if (!text_box->prev) /* at start of text area: ignore */ @@ -656,14 +542,14 @@ void browser_window_input_callback(struct browser_window *bw, char *value; /* have we exceeded max length of input? */ - utf8_length(input->gadget->value, utf8_len); + utf8_len = utf8_length(input->gadget->value); if (utf8_len >= input->gadget->maxlength) return; /* normal character insertion */ /* Insert key in gadget */ - ucs4_to_utf8(key, utf8, utf8_len); + utf8_len = utf8_from_ucs4(key, utf8); value = realloc(input->gadget->value, input->gadget->length + utf8_len + 1); @@ -683,9 +569,10 @@ void browser_window_input_callback(struct browser_window *bw, /* Insert key in text box */ /* Convert space into NBSP */ - ucs4_to_utf8((input->gadget->type == GADGET_PASSWORD) ? + utf8_len = utf8_from_ucs4( + (input->gadget->type == GADGET_PASSWORD) ? '*' : (key == ' ') ? 160 : key, - utf8, utf8_len); + utf8); value = talloc_realloc(bw->current_content, text_box->text, char, text_box->length + utf8_len + 1); @@ -718,7 +605,8 @@ void browser_window_input_callback(struct browser_window *bw, /* Gadget */ prev_offset = form_offset; /* Go to the previous valid UTF-8 character */ - utf8_prev(input->gadget->value, form_offset); + form_offset = utf8_prev(input->gadget->value, + form_offset); memmove(input->gadget->value + form_offset, input->gadget->value + prev_offset, @@ -729,7 +617,7 @@ void browser_window_input_callback(struct browser_window *bw, /* Text box */ prev_offset = box_offset; /* Go to the previous valid UTF-8 character */ - utf8_prev(text_box->text, box_offset); + box_offset = utf8_prev(text_box->text, box_offset); memmove(text_box->text + box_offset, text_box->text + prev_offset, @@ -810,20 +698,21 @@ void browser_window_input_callback(struct browser_window *bw, case 28: /* Right cursor -> */ /* Text box */ /* Go to the next valid UTF-8 character */ - utf8_next(text_box->text, text_box->length, box_offset); + box_offset = utf8_next(text_box->text, text_box->length, + box_offset); /* Gadget */ /* Go to the next valid UTF-8 character */ - utf8_next(input->gadget->value, input->gadget->length, - form_offset); + form_offset = utf8_next(input->gadget->value, + input->gadget->length, form_offset); break; case 29: /* Left cursor -> */ /* Text box */ /* Go to the previous valid UTF-8 character */ - utf8_prev(text_box->text, box_offset); + box_offset = utf8_prev(text_box->text, box_offset); /* Gadget */ /* Go to the previous valid UTF-8 character */ - utf8_prev(input->gadget->value, form_offset); + form_offset = utf8_prev(input->gadget->value, form_offset); break; case 128: /* Ctrl + Left */ @@ -21,7 +21,8 @@ OBJECTS_COMMON = content.o fetch.o fetchcache.o url_store.o # content/ OBJECTS_COMMON += css.o css_enum.o parser.o ruleset.o scanner.o # css/ OBJECTS_COMMON += box.o box_construct.o box_normalise.o form.o html.o \ html_redraw.o layout.o list.o textplain.o # render/ -OBJECTS_COMMON += messages.o pool.o talloc.o url.o utils.o # utils/ +OBJECTS_COMMON += messages.o pool.o talloc.o url.o utf8.c \ + utils.o # utils/ OBJECTS_COMMON += imagemap.o loginlist.o options.o selection.o \ textinput.o tree.o # desktop/ diff --git a/render/box_construct.c b/render/box_construct.c index 9723b4de1..88a432e67 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -1575,7 +1575,7 @@ bool box_iframe(BOX_SPECIAL_PARAMS) bool box_form(BOX_SPECIAL_PARAMS) { - char *action, *method, *enctype; + char *action, *method, *enctype, *charset; form_method fmethod; struct form *form; @@ -1598,9 +1598,35 @@ bool box_form(BOX_SPECIAL_PARAMS) xmlFree(method); } - form = form_new(action, fmethod); + /* acceptable encoding(s) for form data */ + if ((charset = (char *) xmlGetProp(n, (const xmlChar *) "accept-charset"))) { + char *comma = strchr(charset, ','); + if (!comma) + /* only one => use it */ + comma = strdup(charset); + else + /* multiple => use first */ + comma = strndup(charset, comma - charset); + + xmlFree(charset); + charset = comma; + } + else if (content->data.html.encoding) + /* none specified => try document encoding */ + charset = strdup(content->data.html.encoding); + else + /* none specified and no document encoding => 8859-1 */ + charset = strdup("ISO-8859-1"); + + if (!charset) { + xmlFree(action); + return false; + } + + form = form_new(action, fmethod, charset); if (!form) { xmlFree(action); + free(charset); return false; } form->prev = content->data.html.forms; diff --git a/render/form.c b/render/form.c index 559f7e3e2..1cc7d0840 100644 --- a/render/form.c +++ b/render/form.c @@ -17,21 +17,22 @@ #include "netsurf/render/box.h" #include "netsurf/render/form.h" #include "netsurf/utils/log.h" +#include "netsurf/utils/utf8.h" #include "netsurf/utils/utils.h" static char *form_textarea_value(struct form_control *textarea); - /** * Create a struct form. * * \param action URL to submit form to, used directly (not copied) * \param method method and enctype + * \param charset characterset of form (not copied) * \return a new structure, or 0 on memory exhaustion */ -struct form *form_new(char *action, form_method method) +struct form *form_new(char *action, form_method method, char *charset) { struct form *form; @@ -40,6 +41,7 @@ struct form *form_new(char *action, form_method method) return 0; form->action = action; form->method = method; + form->charset = charset; form->controls = 0; form->last_control = 0; form->prev = 0; @@ -465,15 +467,15 @@ char *form_textarea_value(struct form_control *textarea) /** * Encode controls using application/x-www-form-urlencoded. * + * \param form form to which successful controls relate * \param control linked list of form_successful_control * \return URL-encoded form, or 0 on memory exhaustion - * - * \todo encoding conversion */ -char *form_url_encode(struct form_successful_control *control) +char *form_url_encode(struct form *form, + struct form_successful_control *control) { - char *name, *value; + char *name, *value, *n_temp, *v_temp; char *s = malloc(1), *s2; unsigned int len = 0, len1; @@ -482,11 +484,26 @@ char *form_url_encode(struct form_successful_control *control) s[0] = 0; for (; control; control = control->next) { - name = curl_escape(control->name, 0); - value = curl_escape(control->value, 0); + n_temp = utf8_to_enc(control->name, form->charset, 0); + if (!n_temp) { + free(s); + return 0; + } + v_temp = utf8_to_enc(control->value, form->charset, 0); + if (!v_temp) { + free(n_temp); + free(s); + return 0; + } + name = curl_escape(n_temp, 0); + value = curl_escape(v_temp, 0); len1 = len + strlen(name) + strlen(value) + 2; s2 = realloc(s, len1 + 1); if (!s2) { + curl_free(value); + curl_free(name); + free(v_temp); + free(n_temp); free(s); return 0; } @@ -495,6 +512,8 @@ char *form_url_encode(struct form_successful_control *control) len = len1; curl_free(name); curl_free(value); + free(v_temp); + free(n_temp); } if (len) s[len - 1] = 0; diff --git a/render/form.h b/render/form.h index ceddd96f8..c1e01ae0a 100644 --- a/render/form.h +++ b/render/form.h @@ -31,6 +31,7 @@ typedef enum { struct form { char *action; /**< URL to submit to. */ form_method method; /**< Method and enctype. */ + char *charset; /**< Charset to submit form in */ struct form_control *controls; /**< Linked list of controls. */ struct form_control *last_control; /**< Last control in list. */ struct form *prev; /**< Previous form in doc. */ @@ -101,7 +102,7 @@ struct form_successful_control { struct form_successful_control *next; /**< Next in linked list. */ }; -struct form *form_new(char *action, form_method method); +struct form *form_new(char *action, form_method method, char *charset); struct form_control *form_new_control(form_control_type type); void form_add_control(struct form *form, struct form_control *control); void form_free_control(struct form_control *control); @@ -110,7 +111,8 @@ bool form_add_option(struct form_control *control, char *value, char *text, bool form_successful_controls(struct form *form, struct form_control *submit_button, struct form_successful_control **successful_controls); -char *form_url_encode(struct form_successful_control *control); +char *form_url_encode(struct form *form, + struct form_successful_control *control); void form_free_successful(struct form_successful_control *control); #endif diff --git a/riscos/ucstables.c b/riscos/ucstables.c index 3dc38e066..b744e9c6a 100644 --- a/riscos/ucstables.c +++ b/riscos/ucstables.c @@ -9,8 +9,10 @@ * UCS conversion tables */ +#include "oslib/osbyte.h" #include "oslib/territory.h" #include "netsurf/riscos/ucstables.h" +#include "netsurf/utils/utils.h" /* Common values (ASCII) */ #define common \ @@ -331,3 +333,42 @@ int *ucstable_from_alphabet(int alphabet) return ucstable; } + +static const char *localencodings[] = { + "ISO-8859-1", /* BFont - 100 - just use Latin1, instead */ + "ISO-8859-1", /* do we want to use Acorn Latin1, instead? */ + "ISO-8859-2", + "ISO-8859-3", + "ISO-8859-4", + "ISO-8859-5", + "ISO-8859-6", + "ISO-8869-7", + "ISO-8859-8", + "ISO-8859-9", + "ISO-IR-182", + "UTF-8", + "ISO-8859-15", + "ISO-8859-10", + "ISO-8859-13", + "ISO-8859-14", + "CP866" /* Cyrillic2 - 120 */ +}; + +/** + * Retrieve local encoding name, suitable for passing to iconv + */ +const char *local_encoding_name(void) +{ + os_error *error; + int alphabet; + + error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet); + if (!error) { + if (alphabet < 116) + return localencodings[alphabet - 100]; + else if (alphabet == 120) + return localencodings[16]; + } + + return localencodings[0]; +} diff --git a/utils/utf8.c b/utils/utf8.c new file mode 100644 index 000000000..957062a37 --- /dev/null +++ b/utils/utf8.c @@ -0,0 +1,280 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk> + */ + +/** + * \file UTF-8 manipulation functions (implementation) + */ + +#include <assert.h> +#include <string.h> + +#include <iconv.h> + +#include "netsurf/utils/utf8.h" + +static char *utf8_convert(const char *string, size_t len, const char *from, + const char *to); + +/** + * Convert a UTF-8 multibyte sequence into a single UCS4 character + * + * Encoding of UCS values outside the UTF-16 plane has been removed from + * RFC3629. This function conforms to RFC2279, however, as it is possible + * that the platform specific keyboard input handler will generate a UCS4 + * value outside the UTF-16 plane. + * + * \param s The sequence to process + * \param l Length of sequence + * \return UCS4 character + */ +size_t utf8_to_ucs4(const char *s, size_t l) +{ + size_t c = 0; + + if (!s) + assert(0); + else if (l > 0 && *s < 0x80) + c = *s; + else if (l > 1 && (*s & 0xE0) == 0xC0 && (*(s+1) & 0xC0) == 0x80) + c = ((*s & 0x1F) << 6) | (*(s+1) & 0x3F); + else if (l > 2 && (*s & 0xF0) == 0xE0 && (*(s+1) & 0xC0) == 0x80 && + (*(s+2) & 0xC0) == 0x80) + c = ((*s & 0x0F) << 12) | ((*(s+1) & 0x3F) << 6) | + (*(s+2) & 0x3F); + else if (l > 3 && (*s & 0xF8) == 0xF0 && (*(s+1) & 0xC0) == 0x80 && + (*(s+2) & 0xC0) == 0x80 && (*(s+3) & 0xC0) == 0x80) + c = ((*s & 0x0F) << 18) | ((*(s+1) & 0x3F) << 12) | + ((*(s+2) & 0x3F) << 6) | (*(s+3) & 0x3F); + else if (l > 4 && (*s & 0xFC) == 0xF8 && (*(s+1) & 0xC0) == 0x80 && + (*(s+2) & 0xC0) == 0x80 && (*(s+3) & 0xC0) == 0x80 && + (*(s+4) & 0xC0) == 0x80) + c = ((*s & 0x0F) << 24) | ((*(s+1) & 0x3F) << 18) | + ((*(s+2) & 0x3F) << 12) | ((*(s+3) & 0x3F) << 6) | + (*(s+4) & 0x3F); + else if (l > 5 && (*s & 0xFE) == 0xFC && (*(s+1) & 0xC0) == 0x80 && + (*(s+2) & 0xC0) == 0x80 && (*(s+3) & 0xC0) == 0x80 && + (*(s+4) & 0xC0) == 0x80 && (*(s+5) & 0xC0) == 0x80) + c = ((*s & 0x0F) << 28) | ((*(s+1) & 0x3F) << 24) | + ((*(s+2) & 0x3F) << 18) | ((*(s+3) & 0x3F) << 12) | + ((*(s+4) & 0x3F) << 6) | (*(s+5) & 0x3F); + else + assert(0); + + return c; +} + +/** + * Convert a single UCS4 character into a UTF-8 multibyte sequence + * + * Encoding of UCS values outside the UTF-16 plane has been removed from + * RFC3629. This function conforms to RFC2279, however, as it is possible + * that the platform specific keyboard input handler will generate a UCS4 + * value outside the UTF-16 plane. + * + * \param c The character to process (0 <= c <= 0x7FFFFFFF) + * \param s Pointer to 6 byte long output buffer + * \return Length of multibyte sequence + */ +size_t utf8_from_ucs4(size_t c, char *s) +{ + size_t l = 0; + + if (c > 0x7FFFFFFF) + assert(0); + else if (c < 0x80) { + *s = (char)c; + l = 1; + } + else if (c < 0x800) { + *s = 0xC0 | ((c >> 6) & 0x1F); + *(s+1) = 0x80 | (c & 0x3F); + l = 2; + } + else if (c < 0x10000) { + *s = 0xE0 | ((c >> 12) & 0xF); + *(s+1) = 0x80 | ((c >> 6) & 0x3F); + *(s+2) = 0x80 | (c & 0x3F); + l = 3; + } + else if (c < 0x200000) { + *s = 0xF0 | ((c >> 18) & 0x7); + *(s+1) = 0x80 | ((c >> 12) & 0x3F); + *(s+2) = 0x80 | ((c >> 6) & 0x3F); + *(s+3) = 0x80 | (c & 0x3F); + l = 4; + } + else if (c < 0x4000000) { + *s = 0xF8 | ((c >> 24) & 0x3); + *(s+1) = 0x80 | ((c >> 18) & 0x3F); + *(s+2) = 0x80 | ((c >> 12) & 0x3F); + *(s+3) = 0x80 | ((c >> 6) & 0x3F); + *(s+4) = 0x80 | (c & 0x3F); + l = 5; + } + else if (c <= 0x7FFFFFFF) { + *s = 0xFC | ((c >> 30) & 0x1); + *(s+1) = 0x80 | ((c >> 24) & 0x3F); + *(s+2) = 0x80 | ((c >> 18) & 0x3F); + *(s+3) = 0x80 | ((c >> 12) & 0x3F); + *(s+4) = 0x80 | ((c >> 6) & 0x3F); + *(s+5) = 0x80 | (c & 0x3F); + l = 6; + } + + return l; +} + +/** + * Calculate the length (in characters) of a NULL-terminated UTF-8 string + * + * \param s The string + * \return Length of string + */ +size_t utf8_length(const char *s) +{ + const char *__s = s; + int l = 0; + while (*__s != '\0') { + if ((*__s & 0x80) == 0x00) + __s += 1; + else if ((*__s & 0xE0) == 0xC0) + __s += 2; + else if ((*__s & 0xF0) == 0xE0) + __s += 3; + else if ((*__s & 0xF8) == 0xF0) + __s += 4; + else if ((*__s & 0xFC) == 0xF8) + __s += 5; + else if ((*__s & 0xFE) == 0xFC) + __s += 6; + else + assert(0); + l++; + } + + return l; +} + +/** + * Find previous legal UTF-8 char in string + * + * \param s The string + * \param o Offset in the string to start at + * \return Offset of first byte of previous legal character + */ +size_t utf8_prev(const char *s, size_t o) +{ + while (o != 0 && !(((s[--o] & 0x80) == 0x00) || + ((s[o] & 0xC0) == 0xC0))) + /* do nothing */; + + return o; +} + +/** + * Find next legal UTF-8 char in string + * + * \param s The string + * \param l Maximum offset in string + * \param o Offset in the string to start at + * \return Offset of first byte of next legal character + */ +size_t utf8_next(const char *s, size_t l, size_t o) +{ + while (o != l && !(((s[++o] & 0x80) == 0x00) || + ((s[o] & 0xC0) == 0xC0))) + /* do nothing */; + + return o; +} + + +/** + * Convert a UTF8 string into the named encoding + * + * \param string The NULL-terminated string to convert + * \param encname The encoding name (suitable for passing to iconv) + * \param len Length of input string to consider (in bytes), or 0 + * \return Pointer to converted string (on heap) or NULL on error + */ +char *utf8_to_enc(const char *string, const char *encname, size_t len) +{ + return utf8_convert(string, len, "UTF-8", encname); +} + +/** + * Convert a UTF8 string into the named encoding + * + * \param string The NULL-terminated string to convert + * \param encname The encoding name (suitable for passing to iconv) + * \param len Length of input string to consider (in bytes), or 0 + * \return Pointer to converted string (on heap) or NULL on error + */ +char *utf8_from_enc(const char *string, const char *encname, size_t len) +{ + return utf8_convert(string, len, encname, "UTF-8"); +} + +/** + * Convert a string from one encoding to another + * + * \param string The NULL-terminated string to convert + * \param len Length of input string to consider (in bytes) + * \param from The encoding name to convert from + * \param to The encoding name to convert to + * \return Pointer to converted string (on heap) or NULL on error + */ +char *utf8_convert(const char *string, size_t len, const char *from, + const char *to) +{ + iconv_t cd; + char *ret, *temp, *out, *in; + size_t slen, rlen; + + if (!string || !from || !to) + return NULL; + + in = (char *)string; + + cd = iconv_open(to, from); + if (cd == (iconv_t)-1) + return NULL; + + slen = len ? len : strlen(string); + /* Worst case = ACSII -> UCS4, so allocate an output buffer + * 4 times larger than the input buffer, and add 4 bytes at + * the end for the NULL terminator + */ + rlen = slen * 4 + 4; + + temp = out = calloc(rlen, sizeof(char)); + if (!out) { + iconv_close(cd); + return NULL; + } + + /* perform conversion */ + if (iconv(cd, &in, &slen, &out, &rlen) == (size_t)-1) { + free(temp); + iconv_close(cd); + return NULL; + } + + iconv_close(cd); + + if (rlen > 64 /* allow 64bytes wasted space */) { + /* and allocate a more sensibly sized output buffer */ + ret = calloc(out - temp + 4, sizeof(char)); + memcpy(ret, temp, out - temp); + free(temp); + } + else + ret = temp; + + return ret; +} + diff --git a/utils/utf8.h b/utils/utf8.h new file mode 100644 index 000000000..02ff0322d --- /dev/null +++ b/utils/utf8.h @@ -0,0 +1,26 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk> + */ + +/** + * \file UTF-8 manipulation functions (interface) + */ + +#ifndef _NETSURF_UTILS_UTF8_H_ +#define _NETSURF_UTILS_UTF8_H_ + +size_t utf8_to_ucs4(const char *s, size_t l); +size_t utf8_from_ucs4(size_t c, char *s); + +size_t utf8_length(const char *s); + +size_t utf8_prev(const char *s, size_t o); +size_t utf8_next(const char *s, size_t l, size_t o); + +char *utf8_to_enc(const char *string, const char *encname, size_t len); +char *utf8_from_enc(const char *string, const char *encname, size_t len); + +#endif diff --git a/utils/utils.c b/utils/utils.c index 35e3b1c7e..04f24efa5 100644 --- a/utils/utils.c +++ b/utils/utils.c @@ -22,6 +22,7 @@ #define NDEBUG #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" +#include "netsurf/utils/utf8.h" #include "netsurf/utils/utils.h" @@ -110,56 +111,10 @@ char *cnv_space2nbsp(const char *s) * \param s string in local machine encoding. NUL or length terminated (which comes first). * \param length maximum number of bytes to consider at s. * \return malloc()'ed NUL termined string in UTF-8 encoding. - * - * Based on RISCOS-LATIN1 code from libiconv. - * \todo: we should use libiconv to support more local encodings instead - * of only RISCOS-LATIN1. */ char *cnv_local_enc_str(const char *s, size_t length) { - size_t l_out, l_in; - const char *s_in; - char *d, *d_out; - static const unsigned int riscos1_2uni[32] = { - /* 0x80 */ - 0x221a, 0x0174, 0x0175, 0x0083, 0x2573, 0x0176, 0x0177, 0x0087, - 0x21e6, 0x21e8, 0x21e9, 0x21e7, 0x2026, 0x2122, 0x2030, 0x2022, - /* 0x90 */ - 0x2018, 0x2019, 0x2039, 0x203a, 0x201c, 0x201d, 0x201e, 0x2013, - 0x2014, 0x2212, 0x0152, 0x0153, 0x2020, 0x2021, 0xfb01, 0xfb02, - }; - - /* We're counting on the fact that all riscos1_2uni[] values are - * between 0x80 (incl) and 0x1000 (excl). - */ - for (s_in = s, l_in = length, l_out = 1; - *s_in != '\0' && l_in != 0; - ++s_in, --l_in) - l_out += (*s_in >= 0x80 && *s_in < 0xA0) ? ((riscos1_2uni[*s_in - 0x80] < 0x800) ? 2 : 3) : 1; - if ((d_out = (char *)malloc(l_out)) == NULL) - return NULL; - for (s_in = s, l_in = length, d = d_out; - *s_in != '\0' && l_in != 0; - ++s_in, --l_in) { - unsigned int uc = (*s_in >= 0x80 && *s_in < 0xA0) ? riscos1_2uni[*s_in - 0x80] : *s_in; - const int cnt = (uc < 0x80) ? 1 : (uc < 0x800) ? 2 : 3; - switch (cnt) { - case 3: - d[2] = 0x80 | (uc & 0x3F); - uc = (uc >> 6) | 0x800; - /* fall through */ - case 2: - d[1] = 0x80 | (uc & 0x3F); - uc = (uc >> 6) | 0xC0; - /* fall through */ - case 1: - d[0] = uc; - } - d += cnt; - } - *d = '\0'; - - return d_out; + return utf8_from_enc(s, local_encoding_name(), length); } @@ -169,7 +124,7 @@ char *cnv_local_enc_str(const char *s, size_t length) */ char *cnv_str_local_enc(const char *s) { -return cnv_strn_local_enc(s, strlen(s), NULL); + return cnv_strn_local_enc(s, 0); } @@ -177,61 +132,12 @@ return cnv_strn_local_enc(s, strlen(s), NULL); * Converts UTF-8 string <s> of <length> bytes to the machine local encoding. * Caller needs to free return value. * - * When back_map is non-NULL, a ptr to a ptrdiff_t array is filled in which - * needs to be free'd by the caller. The array contains per character - * in the return string, a ptrdiff in the <s> UTF-8 encoded string. - * * \todo: we should use libiconv to support more local encodings instead * of only ISOLATIN1. */ -char *cnv_strn_local_enc(const char *s, int length, const ptrdiff_t **back_mapPP) +char *cnv_strn_local_enc(const char *s, int length) { - /* Buffer at d & back_mapP can be overdimentioned but is certainly - * big enough to carry the end result. - */ - char *d, *d0; - const char * const s0 = s; - ptrdiff_t *back_mapP = NULL; - - if (back_mapPP != NULL) { - back_mapP = calloc(length + 1, sizeof(ptrdiff_t)); - if (!back_mapP) - return NULL; - *back_mapPP = back_mapP; - } - - d = calloc(length + 1, sizeof(char)); - if (!d) - return NULL; - - d0 = d; - - while (length != 0) { - int u, chars; - - chars = length; - u = xmlGetUTF8Char(s, &chars); - if (chars <= 0) { - s += 1; - length -= 1; - continue; - } - if (back_mapP != NULL) - *back_mapP++ = s - s0; - s += chars; - length -= chars; - if (u == 0x09 || u == 0x0a || u == 0x0d || - (0x20 <= u && u <= 0x7f) || - (0xa0 <= u && u <= 0xff)) - *d++ = u; - else - *d++ = '?'; - } - if (back_mapP != NULL) - *back_mapP = s - s0; - *d = 0; - - return d0; + return utf8_to_enc(s, local_encoding_name(), length); } diff --git a/utils/utils.h b/utils/utils.h index 6dcfa1305..278446e0c 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -20,20 +20,22 @@ #define NOF_ELEMENTS(array) (sizeof(array)/sizeof(*(array))) #endif -void die(const char * const error); char * strip(char * const s); int whitespace(const char * str); char * squash_whitespace(const char * s); char *cnv_space2nbsp(const char *s); char *cnv_local_enc_str(const char *s, size_t length); char *cnv_str_local_enc(const char *s); -char *cnv_strn_local_enc(const char *s, int length, - const ptrdiff_t **back_mapPP); +char *cnv_strn_local_enc(const char *s, int length); bool is_dir(const char *path); void regcomp_wrapper(regex_t *preg, const char *regex, int cflags); void clean_cookiejar(void); void unicode_transliterate(unsigned int c, char **r); char *human_friendly_bytesize(unsigned long bytesize); + +/* Platform specific functions */ +void die(const char * const error); void warn_user(const char *warning, const char *detail); +const char *local_encoding_name(void); #endif |