submodule--helper: replace connect-gitdir-workingtree by ensure-core-worktree
[gitweb.git] / utf8.c
diff --git a/utf8.c b/utf8.c
index f04c24409b4e95d69ccd08473ef4c43d408d6d86..982217eec930d7495f61917ce5442016a07c5391 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -81,7 +81,7 @@ static int git_wcwidth(ucs_char_t ch)
        /*
         * 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)
@@ -401,18 +401,40 @@ void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
        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);
 }
@@ -538,6 +560,45 @@ char *reencode_string_len(const char *in, int insz,
 }
 #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[] = {'\xFE', '\xFF'};
+static const char utf16_le_bom[] = {'\xFF', '\xFE'};
+static const char utf32_be_bom[] = {'\0', '\0', '\xFE', '\xFF'};
+static const char utf32_le_bom[] = {'\xFF', '\xFE', '\0', '\0'};
+
+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`.