blob: 3cd347c7ec3daef24cd3397442a581a2428008be [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "common/iconv.h"
#include <guacamole/unicode.h>
#include <endian.h>
#include <stdint.h>
#include <stdlib.h>
/**
* CP1252 lookup tables.
*
* __GUAC_CP1252_CODEPOINT maps CP1252 byte values in the 0x80-0x9F exception
* range to Unicode codepoints.
*
* __GUAC_UNICODE_TO_CP1252 maps the Unicode codepoints representable in that
* exception range back to their byte values. Because only 27 codepoints in the
* range differ from their Unicode byte values, the reverse mapping is stored
* as a sparse array sorted by codepoint, allowing the CP1252 byte to be found
* with bsearch() in O(log n) time.
*/
const static int __GUAC_CP1252_CODEPOINT[32] = {
0x20AC, /* 0x80 */
0xFFFD, /* 0x81 */
0x201A, /* 0x82 */
0x0192, /* 0x83 */
0x201E, /* 0x84 */
0x2026, /* 0x85 */
0x2020, /* 0x86 */
0x2021, /* 0x87 */
0x02C6, /* 0x88 */
0x2030, /* 0x89 */
0x0160, /* 0x8A */
0x2039, /* 0x8B */
0x0152, /* 0x8C */
0xFFFD, /* 0x8D */
0x017D, /* 0x8E */
0xFFFD, /* 0x8F */
0xFFFD, /* 0x90 */
0x2018, /* 0x91 */
0x2019, /* 0x92 */
0x201C, /* 0x93 */
0x201D, /* 0x94 */
0x2022, /* 0x95 */
0x2013, /* 0x96 */
0x2014, /* 0x97 */
0x02DC, /* 0x98 */
0x2122, /* 0x99 */
0x0161, /* 0x9A */
0x203A, /* 0x9B */
0x0153, /* 0x9C */
0xFFFD, /* 0x9D */
0x017E, /* 0x9E */
0x0178, /* 0x9F */
};
typedef struct {
int codepoint;
unsigned char byte;
} guac_cp1252_reverse_t;
const static guac_cp1252_reverse_t __GUAC_UNICODE_TO_CP1252[27] = {
{ 0x0152, 0x8C }, /* 0x8C */
{ 0x0153, 0x9C }, /* 0x9C */
{ 0x0160, 0x8A }, /* 0x8A */
{ 0x0161, 0x9A }, /* 0x9A */
{ 0x0178, 0x9F }, /* 0x9F */
{ 0x017D, 0x8E }, /* 0x8E */
{ 0x017E, 0x9E }, /* 0x9E */
{ 0x0192, 0x83 }, /* 0x83 */
{ 0x02C6, 0x88 }, /* 0x88 */
{ 0x02DC, 0x98 }, /* 0x98 */
{ 0x2013, 0x96 }, /* 0x96 */
{ 0x2014, 0x97 }, /* 0x97 */
{ 0x2018, 0x91 }, /* 0x91 */
{ 0x2019, 0x92 }, /* 0x92 */
{ 0x201A, 0x82 }, /* 0x82 */
{ 0x201C, 0x93 }, /* 0x93 */
{ 0x201D, 0x94 }, /* 0x94 */
{ 0x201E, 0x84 }, /* 0x84 */
{ 0x2020, 0x86 }, /* 0x86 */
{ 0x2021, 0x87 }, /* 0x87 */
{ 0x2022, 0x95 }, /* 0x95 */
{ 0x2026, 0x85 }, /* 0x85 */
{ 0x2030, 0x89 }, /* 0x89 */
{ 0x2039, 0x8B }, /* 0x8B */
{ 0x203A, 0x9B }, /* 0x9B */
{ 0x20AC, 0x80 }, /* 0x80 */
{ 0x2122, 0x99 } /* 0x99 */
};
/*
* MacRoman lookup tables.
*
* __GUAC_MACROMAN_CODEPOINT maps MacRoman byte values to Unicode codepoints,
* allowing the codepoint to be determined by direct array access.
*
* __GUAC_UNICODE_TO_MACROMAN maps the Unicode codepoints representable in
* MacRoman back to their byte values. Because the Unicode lookup space is much
* larger than 256 bytes, the reverse mapping is stored as a sparse array
* sorted by codepoint, allowing the MacRoman byte to be found with bsearch()
* in O(log n) time.
*
* To regenerate both tables, run:
* python3 generate_macroman_table.py
*
* where generate_macroman_table.py contains:
* MACROMAN_HIGH_RANGE = range(0x80, 0x100)
*
* # Forward table: MacRoman byte -> Unicode codepoint
* pairs = []
* print("const static int __GUAC_MACROMAN_CODEPOINT[128] = {")
* for mac_byte in MACROMAN_HIGH_RANGE:
* unicode_char = bytes([mac_byte]).decode('mac_roman')
* unicode_codepoint = ord(unicode_char)
* pairs.append((unicode_codepoint, mac_byte))
* trailing_comma = "," if mac_byte < 0xFF else ""
* print(f" 0x{unicode_codepoint:04X}{trailing_comma} "
* f"/ * 0x{mac_byte:02X} - {unicode_char} * /")
* print("};")
* print()
*
* # Reverse table: Unicode codepoint -> MacRoman byte, sorted by codepoint
* print("const static struct {")
* print(" int codepoint;")
* print(" unsigned char byte;")
* print("} __GUAC_UNICODE_TO_MACROMAN[ARRAY_SIZE(__GUAC_MACROMAN_CODEPOINT)] = {")
* for unicode_codepoint, mac_byte in sorted(pairs):
* unicode_char = chr(unicode_codepoint)
* print(f" {{ 0x{unicode_codepoint:04X}, 0x{mac_byte:02X} }}, "
* f"/ * {unicode_char} * /")
* print("};")
*/
const static int __GUAC_MACROMAN_CODEPOINT[128] = {
0x00C4, /* 0x80 - Ä */
0x00C5, /* 0x81 - Å */
0x00C7, /* 0x82 - Ç */
0x00C9, /* 0x83 - É */
0x00D1, /* 0x84 - Ñ */
0x00D6, /* 0x85 - Ö */
0x00DC, /* 0x86 - Ü */
0x00E1, /* 0x87 - á */
0x00E0, /* 0x88 - à */
0x00E2, /* 0x89 - â */
0x00E4, /* 0x8A - ä */
0x00E3, /* 0x8B - ã */
0x00E5, /* 0x8C - å */
0x00E7, /* 0x8D - ç */
0x00E9, /* 0x8E - é */
0x00E8, /* 0x8F - è */
0x00EA, /* 0x90 - ê */
0x00EB, /* 0x91 - ë */
0x00ED, /* 0x92 - í */
0x00EC, /* 0x93 - ì */
0x00EE, /* 0x94 - î */
0x00EF, /* 0x95 - ï */
0x00F1, /* 0x96 - ñ */
0x00F3, /* 0x97 - ó */
0x00F2, /* 0x98 - ò */
0x00F4, /* 0x99 - ô */
0x00F6, /* 0x9A - ö */
0x00F5, /* 0x9B - õ */
0x00FA, /* 0x9C - ú */
0x00F9, /* 0x9D - ù */
0x00FB, /* 0x9E - û */
0x00FC, /* 0x9F - ü */
0x2020, /* 0xA0 - † */
0x00B0, /* 0xA1 - ° */
0x00A2, /* 0xA2 - ¢ */
0x00A3, /* 0xA3 - £ */
0x00A7, /* 0xA4 - § */
0x2022, /* 0xA5 - • */
0x00B6, /* 0xA6 - ¶ */
0x00DF, /* 0xA7 - ß */
0x00AE, /* 0xA8 - ® */
0x00A9, /* 0xA9 - © */
0x2122, /* 0xAA - ™ */
0x00B4, /* 0xAB - ´ */
0x00A8, /* 0xAC - ¨ */
0x2260, /* 0xAD - ≠ */
0x00C6, /* 0xAE - Æ */
0x00D8, /* 0xAF - Ø */
0x221E, /* 0xB0 - ∞ */
0x00B1, /* 0xB1 - ± */
0x2264, /* 0xB2 - ≤ */
0x2265, /* 0xB3 - ≥ */
0x00A5, /* 0xB4 - ¥ */
0x00B5, /* 0xB5 - µ */
0x2202, /* 0xB6 - ∂ */
0x2211, /* 0xB7 - ∑ */
0x220F, /* 0xB8 - ∏ */
0x03C0, /* 0xB9 - π */
0x222B, /* 0xBA - ∫ */
0x00AA, /* 0xBB - ª */
0x00BA, /* 0xBC - º */
0x03A9, /* 0xBD - Ω */
0x00E6, /* 0xBE - æ */
0x00F8, /* 0xBF - ø */
0x00BF, /* 0xC0 - ¿ */
0x00A1, /* 0xC1 - ¡ */
0x00AC, /* 0xC2 - ¬ */
0x221A, /* 0xC3 - √ */
0x0192, /* 0xC4 - ƒ */
0x2248, /* 0xC5 - ≈ */
0x2206, /* 0xC6 - ∆ */
0x00AB, /* 0xC7 - « */
0x00BB, /* 0xC8 - » */
0x2026, /* 0xC9 - … */
0x00A0, /* 0xCA -   */
0x00C0, /* 0xCB - À */
0x00C3, /* 0xCC - Ã */
0x00D5, /* 0xCD - Õ */
0x0152, /* 0xCE - Œ */
0x0153, /* 0xCF - œ */
0x2013, /* 0xD0 - – */
0x2014, /* 0xD1 - — */
0x201C, /* 0xD2 - “ */
0x201D, /* 0xD3 - ” */
0x2018, /* 0xD4 - ‘ */
0x2019, /* 0xD5 - ’ */
0x00F7, /* 0xD6 - ÷ */
0x25CA, /* 0xD7 - ◊ */
0x00FF, /* 0xD8 - ÿ */
0x0178, /* 0xD9 - Ÿ */
0x2044, /* 0xDA - ⁄ */
0x20AC, /* 0xDB - € */
0x2039, /* 0xDC - ‹ */
0x203A, /* 0xDD - › */
0xFB01, /* 0xDE - fi */
0xFB02, /* 0xDF - fl */
0x2021, /* 0xE0 - ‡ */
0x00B7, /* 0xE1 - · */
0x201A, /* 0xE2 - ‚ */
0x201E, /* 0xE3 - „ */
0x2030, /* 0xE4 - ‰ */
0x00C2, /* 0xE5 - Â */
0x00CA, /* 0xE6 - Ê */
0x00C1, /* 0xE7 - Á */
0x00CB, /* 0xE8 - Ë */
0x00C8, /* 0xE9 - È */
0x00CD, /* 0xEA - Í */
0x00CE, /* 0xEB - Î */
0x00CF, /* 0xEC - Ï */
0x00CC, /* 0xED - Ì */
0x00D3, /* 0xEE - Ó */
0x00D4, /* 0xEF - Ô */
0xF8FF, /* 0xF0 -  */
0x00D2, /* 0xF1 - Ò */
0x00DA, /* 0xF2 - Ú */
0x00DB, /* 0xF3 - Û */
0x00D9, /* 0xF4 - Ù */
0x0131, /* 0xF5 - ı */
0x02C6, /* 0xF6 - ˆ */
0x02DC, /* 0xF7 - ˜ */
0x00AF, /* 0xF8 - ¯ */
0x02D8, /* 0xF9 - ˘ */
0x02D9, /* 0xFA - ˙ */
0x02DA, /* 0xFB - ˚ */
0x00B8, /* 0xFC - ¸ */
0x02DD, /* 0xFD - ˝ */
0x02DB, /* 0xFE - ˛ */
0x02C7 /* 0xFF - ˇ */
};
typedef struct {
int codepoint;
unsigned char byte;
} guac_macroman_reverse_t;
const static guac_macroman_reverse_t __GUAC_UNICODE_TO_MACROMAN[ARRAY_SIZE(__GUAC_MACROMAN_CODEPOINT)] = {
{ 0x00A0, 0xCA }, /* 0xCA -   */
{ 0x00A1, 0xC1 }, /* 0xC1 - ¡ */
{ 0x00A2, 0xA2 }, /* 0xA2 - ¢ */
{ 0x00A3, 0xA3 }, /* 0xA3 - £ */
{ 0x00A5, 0xB4 }, /* 0xB4 - ¥ */
{ 0x00A7, 0xA4 }, /* 0xA4 - § */
{ 0x00A8, 0xAC }, /* 0xAC - ¨ */
{ 0x00A9, 0xA9 }, /* 0xA9 - © */
{ 0x00AA, 0xBB }, /* 0xBB - ª */
{ 0x00AB, 0xC7 }, /* 0xC7 - « */
{ 0x00AC, 0xC2 }, /* 0xC2 - ¬ */
{ 0x00AE, 0xA8 }, /* 0xA8 - ® */
{ 0x00AF, 0xF8 }, /* 0xF8 - ¯ */
{ 0x00B0, 0xA1 }, /* 0xA1 - ° */
{ 0x00B1, 0xB1 }, /* 0xB1 - ± */
{ 0x00B4, 0xAB }, /* 0xAB - ´ */
{ 0x00B5, 0xB5 }, /* 0xB5 - µ */
{ 0x00B6, 0xA6 }, /* 0xA6 - ¶ */
{ 0x00B7, 0xE1 }, /* 0xE1 - · */
{ 0x00B8, 0xFC }, /* 0xFC - ¸ */
{ 0x00BA, 0xBC }, /* 0xBC - º */
{ 0x00BB, 0xC8 }, /* 0xC8 - » */
{ 0x00BF, 0xC0 }, /* 0xC0 - ¿ */
{ 0x00C0, 0xCB }, /* 0xCB - À */
{ 0x00C1, 0xE7 }, /* 0xE7 - Á */
{ 0x00C2, 0xE5 }, /* 0xE5 - Â */
{ 0x00C3, 0xCC }, /* 0xCC - Ã */
{ 0x00C4, 0x80 }, /* 0x80 - Ä */
{ 0x00C5, 0x81 }, /* 0x81 - Å */
{ 0x00C6, 0xAE }, /* 0xAE - Æ */
{ 0x00C7, 0x82 }, /* 0x82 - Ç */
{ 0x00C8, 0xE9 }, /* 0xE9 - È */
{ 0x00C9, 0x83 }, /* 0x83 - É */
{ 0x00CA, 0xE6 }, /* 0xE6 - Ê */
{ 0x00CB, 0xE8 }, /* 0xE8 - Ë */
{ 0x00CC, 0xED }, /* 0xED - Ì */
{ 0x00CD, 0xEA }, /* 0xEA - Í */
{ 0x00CE, 0xEB }, /* 0xEB - Î */
{ 0x00CF, 0xEC }, /* 0xEC - Ï */
{ 0x00D1, 0x84 }, /* 0x84 - Ñ */
{ 0x00D2, 0xF1 }, /* 0xF1 - Ò */
{ 0x00D3, 0xEE }, /* 0xEE - Ó */
{ 0x00D4, 0xEF }, /* 0xEF - Ô */
{ 0x00D5, 0xCD }, /* 0xCD - Õ */
{ 0x00D6, 0x85 }, /* 0x85 - Ö */
{ 0x00D8, 0xAF }, /* 0xAF - Ø */
{ 0x00D9, 0xF4 }, /* 0xF4 - Ù */
{ 0x00DA, 0xF2 }, /* 0xF2 - Ú */
{ 0x00DB, 0xF3 }, /* 0xF3 - Û */
{ 0x00DC, 0x86 }, /* 0x86 - Ü */
{ 0x00DF, 0xA7 }, /* 0xA7 - ß */
{ 0x00E0, 0x88 }, /* 0x88 - à */
{ 0x00E1, 0x87 }, /* 0x87 - á */
{ 0x00E2, 0x89 }, /* 0x89 - â */
{ 0x00E3, 0x8B }, /* 0x8B - ã */
{ 0x00E4, 0x8A }, /* 0x8A - ä */
{ 0x00E5, 0x8C }, /* 0x8C - å */
{ 0x00E6, 0xBE }, /* 0xBE - æ */
{ 0x00E7, 0x8D }, /* 0x8D - ç */
{ 0x00E8, 0x8F }, /* 0x8F - è */
{ 0x00E9, 0x8E }, /* 0x8E - é */
{ 0x00EA, 0x90 }, /* 0x90 - ê */
{ 0x00EB, 0x91 }, /* 0x91 - ë */
{ 0x00EC, 0x93 }, /* 0x93 - ì */
{ 0x00ED, 0x92 }, /* 0x92 - í */
{ 0x00EE, 0x94 }, /* 0x94 - î */
{ 0x00EF, 0x95 }, /* 0x95 - ï */
{ 0x00F1, 0x96 }, /* 0x96 - ñ */
{ 0x00F2, 0x98 }, /* 0x98 - ò */
{ 0x00F3, 0x97 }, /* 0x97 - ó */
{ 0x00F4, 0x99 }, /* 0x99 - ô */
{ 0x00F5, 0x9B }, /* 0x9B - õ */
{ 0x00F6, 0x9A }, /* 0x9A - ö */
{ 0x00F7, 0xD6 }, /* 0xD6 - ÷ */
{ 0x00F8, 0xBF }, /* 0xBF - ø */
{ 0x00F9, 0x9D }, /* 0x9D - ù */
{ 0x00FA, 0x9C }, /* 0x9C - ú */
{ 0x00FB, 0x9E }, /* 0x9E - û */
{ 0x00FC, 0x9F }, /* 0x9F - ü */
{ 0x00FF, 0xD8 }, /* 0xD8 - ÿ */
{ 0x0131, 0xF5 }, /* 0xF5 - ı */
{ 0x0152, 0xCE }, /* 0xCE - Œ */
{ 0x0153, 0xCF }, /* 0xCF - œ */
{ 0x0178, 0xD9 }, /* 0xD9 - Ÿ */
{ 0x0192, 0xC4 }, /* 0xC4 - ƒ */
{ 0x02C6, 0xF6 }, /* 0xF6 - ˆ */
{ 0x02C7, 0xFF }, /* 0xFF - ˇ */
{ 0x02D8, 0xF9 }, /* 0xF9 - ˘ */
{ 0x02D9, 0xFA }, /* 0xFA - ˙ */
{ 0x02DA, 0xFB }, /* 0xFB - ˚ */
{ 0x02DB, 0xFE }, /* 0xFE - ˛ */
{ 0x02DC, 0xF7 }, /* 0xF7 - ˜ */
{ 0x02DD, 0xFD }, /* 0xFD - ˝ */
{ 0x03A9, 0xBD }, /* 0xBD - Ω */
{ 0x03C0, 0xB9 }, /* 0xB9 - π */
{ 0x2013, 0xD0 }, /* 0xD0 - – */
{ 0x2014, 0xD1 }, /* 0xD1 - — */
{ 0x2018, 0xD4 }, /* 0xD4 - ‘ */
{ 0x2019, 0xD5 }, /* 0xD5 - ’ */
{ 0x201A, 0xE2 }, /* 0xE2 - ‚ */
{ 0x201C, 0xD2 }, /* 0xD2 - “ */
{ 0x201D, 0xD3 }, /* 0xD3 - ” */
{ 0x201E, 0xE3 }, /* 0xE3 - „ */
{ 0x2020, 0xA0 }, /* 0xA0 - † */
{ 0x2021, 0xE0 }, /* 0xE0 - ‡ */
{ 0x2022, 0xA5 }, /* 0xA5 - • */
{ 0x2026, 0xC9 }, /* 0xC9 - … */
{ 0x2030, 0xE4 }, /* 0xE4 - ‰ */
{ 0x2039, 0xDC }, /* 0xDC - ‹ */
{ 0x203A, 0xDD }, /* 0xDD - › */
{ 0x2044, 0xDA }, /* 0xDA - ⁄ */
{ 0x20AC, 0xDB }, /* 0xDB - € */
{ 0x2122, 0xAA }, /* 0xAA - ™ */
{ 0x2202, 0xB6 }, /* 0xB6 - ∂ */
{ 0x2206, 0xC6 }, /* 0xC6 - ∆ */
{ 0x220F, 0xB8 }, /* 0xB8 - ∏ */
{ 0x2211, 0xB7 }, /* 0xB7 - ∑ */
{ 0x221A, 0xC3 }, /* 0xC3 - √ */
{ 0x221E, 0xB0 }, /* 0xB0 - ∞ */
{ 0x222B, 0xBA }, /* 0xBA - ∫ */
{ 0x2248, 0xC5 }, /* 0xC5 - ≈ */
{ 0x2260, 0xAD }, /* 0xAD - ≠ */
{ 0x2264, 0xB2 }, /* 0xB2 - ≤ */
{ 0x2265, 0xB3 }, /* 0xB3 - ≥ */
{ 0x25CA, 0xD7 }, /* 0xD7 - ◊ */
{ 0xF8FF, 0xF0 }, /* 0xF0 -  */
{ 0xFB01, 0xDE }, /* 0xDE - fi */
{ 0xFB02, 0xDF }, /* 0xDF - fl */
};
int guac_iconv(guac_iconv_read* reader, const char** input, int in_remaining,
guac_iconv_write* writer, char** output, int out_remaining) {
while (in_remaining > 0 && out_remaining > 0) {
int value;
const char* read_start;
char* write_start;
/* Read character */
read_start = *input;
value = reader(input, in_remaining);
in_remaining -= *input - read_start;
/* Write character */
write_start = *output;
writer(output, out_remaining, value);
out_remaining -= *output - write_start;
/* Stop if null terminator reached */
if (value == 0)
return 1;
}
/* Null terminator not reached */
return 0;
}
int GUAC_READ_UTF8(const char** input, int remaining) {
int value;
*input += guac_utf8_read(*input, remaining, &value);
return value;
}
int GUAC_READ_UTF16(const char** input, int remaining) {
int value;
/* Bail if not enough data */
if (remaining < 2)
return 0;
/* Read two bytes as integer */
value = le16toh(*((uint16_t*) *input));
*input += 2;
return value;
}
int GUAC_READ_CP1252(const char** input, int remaining) {
int value = *((unsigned char*) *input);
/* Replace value with exception if not identical to ISO-8859-1 */
if (value >= 0x80 && value <= 0x9F)
value = __GUAC_CP1252_CODEPOINT[value - 0x80];
(*input)++;
return value;
}
int GUAC_READ_ISO8859_1(const char** input, int remaining) {
int value = *((unsigned char*) *input);
(*input)++;
return value;
}
/**
* Invokes the given reader function, automatically normalizing newline
* sequences as Unix-style newline characters ('\n'). All other characters are
* read verbatim.
*
* @param reader
* The reader to use to read the given character.
*
* @param input
* Pointer to the location within the input buffer that the next character
* should be read from.
*
* @param remaining
* The number of bytes remaining in the input buffer.
*
* @return
* The codepoint that was read, or zero if the end of the input string has
* been reached.
*/
static int guac_iconv_read_normalized(guac_iconv_read* reader,
const char** input, int remaining) {
/* Read requested character */
const char* input_start = *input;
int value = reader(input, remaining);
/* Automatically translate CRLF pairs to simple newlines */
if (value == '\r') {
/* Peek ahead by one character, adjusting remaining bytes relative to
* last read */
int peek_remaining = remaining - (*input - input_start);
const char* peek_input = *input;
int peek_value = reader(&peek_input, peek_remaining);
/* Consider read value to be a newline if we have encountered a "\r\n"
* (CRLF) pair */
if (peek_value == '\n') {
value = '\n';
*input = peek_input;
}
}
return value;
}
int GUAC_READ_UTF8_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_UTF8, input, remaining);
}
int GUAC_READ_UTF16_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_UTF16, input, remaining);
}
int GUAC_READ_CP1252_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_CP1252, input, remaining);
}
int GUAC_READ_ISO8859_1_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_ISO8859_1, input, remaining);
}
int GUAC_READ_MACROMAN(const char** input, int remaining) {
/* MacRoman is a single-byte encoding: each character is one byte */
int value = (unsigned char) **input;
(*input)++;
/* Bytes 0x00-0x7F are identical to ASCII/Unicode; bytes 0x80-0xFF are
* remapped via the lookup table. ARRAY_SIZE guards against the table
* being resized without updating the encoding range. */
if (value >= 0x80 && (size_t)(value - 0x80) < ARRAY_SIZE(__GUAC_MACROMAN_CODEPOINT))
value = __GUAC_MACROMAN_CODEPOINT[value - 0x80];
return value;
}
int GUAC_READ_MACROMAN_NORMALIZED(const char** input, int remaining) {
const char* input_start = *input;
int value = GUAC_READ_MACROMAN(input, remaining);
/* Translate both bare CR (\r) and CRLF (\r\n) to Unix newline (\n).
* Classic Mac OS uses bare CR as its line separator, so bare CR must be
* normalized in addition to the CRLF pairs handled by other encodings. */
if (value == '\r') {
int peek_remaining = remaining - (*input - input_start);
const char* peek_input = *input;
int peek_value = GUAC_READ_MACROMAN(&peek_input, peek_remaining);
/* Consume the following LF if this is a CRLF pair */
if (peek_value == '\n')
*input = peek_input;
value = '\n';
}
return value;
}
void GUAC_WRITE_UTF8(char** output, int remaining, int value) {
*output += guac_utf8_write(value, *output, remaining);
}
void GUAC_WRITE_UTF16(char** output, int remaining, int value) {
/* Bail if not enough data */
if (remaining < 2)
return;
/* Write two bytes as integer */
*((uint16_t*) *output) = htole16(value);
*output += 2;
}
/**
* Compares a Unicode codepoint against a CP1252 reverse-lookup table entry.
*
* @param key
* Pointer to the Unicode codepoint being searched for.
*
* @param elem
* Pointer to the CP1252 reverse-lookup table entry being compared.
*
* @return
* A negative value if the requested codepoint sorts before the table
* entry, a positive value if it sorts after the table entry, or zero if
* the codepoints are equal.
*/
static int guac_cp1252_reverse_cmp(const void* key, const void* elem) {
int value = *(const int*) key;
const guac_cp1252_reverse_t* entry = elem;
return (value > entry->codepoint) - (value < entry->codepoint);
}
void GUAC_WRITE_CP1252(char** output, int remaining, int value) {
/* CP1252 matches Unicode directly for 0x00-0x7F and 0xA0-0xFF. */
if ((value >= 0x00 && value < 0x80) || (value >= 0xA0 && value <= 0xFF)) {
*((unsigned char*) *output) = (unsigned char) value;
(*output)++;
return;
}
/* The 0x80-0x9F range is a sparse remapping, so the reverse mapping is
* stored as a codepoint-sorted table and searched with bsearch(). Values
* outside the direct-write ranges also land here: representable exception
* codepoints map to 0x80-0x9F, while everything else falls back to '?'. */
const guac_cp1252_reverse_t* match = bsearch(
&value, /* key */
__GUAC_UNICODE_TO_CP1252, /* base */
ARRAY_SIZE(__GUAC_UNICODE_TO_CP1252), /* nmemb */
sizeof(*__GUAC_UNICODE_TO_CP1252), /* size */
guac_cp1252_reverse_cmp /* compar */
);
/* Write the matched byte, or '?' if the codepoint is not representable. */
*((unsigned char*) *output) = (match != NULL) ? match->byte : '?';
(*output)++;
}
void GUAC_WRITE_ISO8859_1(char** output, int remaining, int value) {
/* Translate to question mark if out of range */
if (value > 0xFF)
value = '?';
*((unsigned char*) *output) = (unsigned char) value;
(*output)++;
}
/**
* Invokes the given writer function, automatically writing newline characters
* ('\n') as CRLF ("\r\n"). All other characters are written verbatim.
*
* @param writer
* The writer to use to write the given character.
*
* @param output
* Pointer to the location within the output buffer that the next character
* should be written.
*
* @param remaining
* The number of bytes remaining in the output buffer.
*
* @param value
* The codepoint of the character to write.
*/
static void guac_iconv_write_crlf(guac_iconv_write* writer, char** output,
int remaining, int value) {
if (value != '\n') {
writer(output, remaining, value);
return;
}
char* output_start = *output;
writer(output, remaining, '\r');
remaining -= *output - output_start;
if (remaining > 0)
writer(output, remaining, '\n');
}
void GUAC_WRITE_UTF8_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_UTF8, output, remaining, value);
}
void GUAC_WRITE_UTF16_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_UTF16, output, remaining, value);
}
void GUAC_WRITE_CP1252_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_CP1252, output, remaining, value);
}
void GUAC_WRITE_ISO8859_1_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_ISO8859_1, output, remaining, value);
}
/**
* Compares a Unicode codepoint against a MacRoman reverse-lookup table entry.
*
* @param key
* Pointer to the Unicode codepoint being searched for.
*
* @param elem
* Pointer to the MacRoman reverse-lookup table entry being compared.
*
* @return
* A negative value if the requested codepoint sorts before the table
* entry, a positive value if it sorts after the table entry, or zero if
* the codepoints are equal.
*/
static int guac_macroman_reverse_cmp(const void* key, const void* elem) {
int value = *(const int*) key;
const guac_macroman_reverse_t* entry = elem;
return (value > entry->codepoint) - (value < entry->codepoint);
}
void GUAC_WRITE_MACROMAN(char** output, int remaining, int value) {
/* MacRoman is a single-byte encoding: each character is one byte */
/* ASCII range is identical to Unicode; write directly */
if (value >= 0x00 && value < 0x80) {
*((unsigned char*) *output) = (unsigned char) value;
(*output)++;
return;
}
/* The reverse mapping is stored as a sparse array of representable Unicode
* codepoints sorted by codepoint, allowing bsearch() to find the MacRoman
* byte in O(log n) time. */
const guac_macroman_reverse_t* match = bsearch(
&value, /* key */
__GUAC_UNICODE_TO_MACROMAN, /* base */
ARRAY_SIZE(__GUAC_UNICODE_TO_MACROMAN), /* nmemb */
sizeof(*__GUAC_UNICODE_TO_MACROMAN), /* size */
guac_macroman_reverse_cmp /* compar */
);
/* Write the matched byte, or '?' if the codepoint is not representable. */
*((unsigned char*) *output) = (match != NULL) ? match->byte : '?';
(*output)++;
}
void GUAC_WRITE_MACROMAN_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_MACROMAN, output, remaining, value);
}