if (ucs < table[0].first || ucs > table[max].last)
return 0;
while (max >= min) {
- mid = (min + max) / 2;
+ mid = min + (max - min) / 2;
if (ucs > table[mid].last)
min = mid + 1;
else if (ucs < table[mid].first)
/*
* Sorted list of non-overlapping intervals of non-spacing characters,
*/
-#include "unicode_width.h"
+#include "unicode-width.h"
/* test for 8-bit control characters */
if (ch == 0)
old = src;
n = utf8_width((const char**)&src, NULL);
if (!src) /* broken utf-8, do nothing */
- return;
+ goto out;
if (n && w >= pos && w < pos + width) {
if (subst) {
memcpy(dst, subst, subst_len);
}
strbuf_setlen(&sb_dst, dst - sb_dst.buf);
strbuf_swap(sb_src, &sb_dst);
+out:
strbuf_release(&sb_dst);
}
+/*
+ * Returns true (1) if the src encoding name matches the dst encoding
+ * name directly or one of its alternative names. E.g. UTF-16BE is the
+ * same as UTF16BE.
+ */
+static int same_utf_encoding(const char *src, const char *dst)
+{
+ if (istarts_with(src, "utf") && istarts_with(dst, "utf")) {
+ /* src[3] or dst[3] might be '\0' */
+ int i = (src[3] == '-' ? 4 : 3);
+ int j = (dst[3] == '-' ? 4 : 3);
+ return !strcasecmp(src+i, dst+j);
+ }
+ return 0;
+}
+
int is_encoding_utf8(const char *name)
{
if (!name)
return 1;
- if (!strcasecmp(name, "utf-8") || !strcasecmp(name, "utf8"))
+ if (same_utf_encoding("utf-8", name))
return 1;
return 0;
}
int same_encoding(const char *src, const char *dst)
{
- if (is_encoding_utf8(src) && is_encoding_utf8(dst))
+ static const char utf8[] = "UTF-8";
+
+ if (!src)
+ src = utf8;
+ if (!dst)
+ dst = utf8;
+ if (same_utf_encoding(src, dst))
return 1;
return !strcasecmp(src, dst);
}
}
#endif
+static int has_bom_prefix(const char *data, size_t len,
+ const char *bom, size_t bom_len)
+{
+ return data && bom && (len >= bom_len) && !memcmp(data, bom, bom_len);
+}
+
+static const char utf16_be_bom[] = {0xFE, 0xFF};
+static const char utf16_le_bom[] = {0xFF, 0xFE};
+static const char utf32_be_bom[] = {0x00, 0x00, 0xFE, 0xFF};
+static const char utf32_le_bom[] = {0xFF, 0xFE, 0x00, 0x00};
+
+int has_prohibited_utf_bom(const char *enc, const char *data, size_t len)
+{
+ return (
+ (same_utf_encoding("UTF-16BE", enc) ||
+ same_utf_encoding("UTF-16LE", enc)) &&
+ (has_bom_prefix(data, len, utf16_be_bom, sizeof(utf16_be_bom)) ||
+ has_bom_prefix(data, len, utf16_le_bom, sizeof(utf16_le_bom)))
+ ) || (
+ (same_utf_encoding("UTF-32BE", enc) ||
+ same_utf_encoding("UTF-32LE", enc)) &&
+ (has_bom_prefix(data, len, utf32_be_bom, sizeof(utf32_be_bom)) ||
+ has_bom_prefix(data, len, utf32_le_bom, sizeof(utf32_le_bom)))
+ );
+}
+
+int is_missing_required_utf_bom(const char *enc, const char *data, size_t len)
+{
+ return (
+ (same_utf_encoding(enc, "UTF-16")) &&
+ !(has_bom_prefix(data, len, utf16_be_bom, sizeof(utf16_be_bom)) ||
+ has_bom_prefix(data, len, utf16_le_bom, sizeof(utf16_le_bom)))
+ ) || (
+ (same_utf_encoding(enc, "UTF-32")) &&
+ !(has_bom_prefix(data, len, utf32_be_bom, sizeof(utf32_be_bom)) ||
+ has_bom_prefix(data, len, utf32_le_bom, sizeof(utf32_le_bom)))
+ );
+}
+
/*
* Returns first character length in bytes for multi-byte `text` according to
* `encoding`.