X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=utf8.c;h=dc3735364f85273c2a119b994ddb405c09dc395c;hb=efb98b44536300f5daed25da3650033a1ce7fdef;hp=4efef6faf7c71f3201935f81611806af084c45d4;hpb=11bb2d4fa9ebc7481828c25752d8c23233b24c76;p=git.git diff --git a/utf8.c b/utf8.c index 4efef6faf..dc3735364 100644 --- a/utf8.c +++ b/utf8.c @@ -3,15 +3,14 @@ /* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */ -typedef unsigned int ucs_char_t; /* assuming 32bit int */ - struct interval { int first; int last; }; /* auxiliary function for binary search in interval table */ -static int bisearch(ucs_char_t ucs, const struct interval *table, int max) { +static int bisearch(ucs_char_t ucs, const struct interval *table, int max) +{ int min = 0; int mid; @@ -152,61 +151,98 @@ static int git_wcwidth(ucs_char_t ch) } /* - * This function returns the number of columns occupied by the character - * pointed to by the variable start. The pointer is updated to point at - * the next character. If it was not valid UTF-8, the pointer is set to NULL. + * Pick one ucs character starting from the location *start points at, + * and return it, while updating the *start pointer to point at the + * end of that character. When remainder_p is not NULL, the location + * holds the number of bytes remaining in the string that we are allowed + * to pick from. Otherwise we are allowed to pick up to the NUL that + * would eventually appear in the string. *remainder_p is also reduced + * by the number of bytes we have consumed. + * + * If the string was not a valid UTF-8, *start pointer is set to NULL + * and the return value is undefined. */ -int utf8_width(const char **start) +ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p) { unsigned char *s = (unsigned char *)*start; ucs_char_t ch; + size_t remainder, incr; + + /* + * A caller that assumes NUL terminated text can choose + * not to bother with the remainder length. We will + * stop at the first NUL. + */ + remainder = (remainder_p ? *remainder_p : 999); - if (*s < 0x80) { + if (remainder < 1) { + goto invalid; + } else if (*s < 0x80) { /* 0xxxxxxx */ ch = *s; - *start += 1; + incr = 1; } else if ((s[0] & 0xe0) == 0xc0) { /* 110XXXXx 10xxxxxx */ - if ((s[1] & 0xc0) != 0x80 || - /* overlong? */ - (s[0] & 0xfe) == 0xc0) + if (remainder < 2 || + (s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) goto invalid; ch = ((s[0] & 0x1f) << 6) | (s[1] & 0x3f); - *start += 2; + incr = 2; } else if ((s[0] & 0xf0) == 0xe0) { /* 1110XXXX 10Xxxxxx 10xxxxxx */ - if ((s[1] & 0xc0) != 0x80 || - (s[2] & 0xc0) != 0x80 || - /* overlong? */ - (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || - /* surrogate? */ - (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || - /* U+FFFE or U+FFFF? */ - (s[0] == 0xef && s[1] == 0xbf && - (s[2] & 0xfe) == 0xbe)) + if (remainder < 3 || + (s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + /* overlong? */ + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || + /* surrogate? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || + /* U+FFFE or U+FFFF? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) goto invalid; ch = ((s[0] & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); - *start += 3; + incr = 3; } else if ((s[0] & 0xf8) == 0xf0) { /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ - if ((s[1] & 0xc0) != 0x80 || - (s[2] & 0xc0) != 0x80 || - (s[3] & 0xc0) != 0x80 || - /* overlong? */ - (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || - /* > U+10FFFF? */ - (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) + if (remainder < 4 || + (s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + /* overlong? */ + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || + /* > U+10FFFF? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) goto invalid; ch = ((s[0] & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); - *start += 4; + incr = 4; } else { invalid: *start = NULL; return 0; } + *start += incr; + if (remainder_p) + *remainder_p = remainder - incr; + return ch; +} + +/* + * This function returns the number of columns occupied by the character + * pointed to by the variable start. The pointer is updated to point at + * the next character. When remainder_p is not NULL, it points at the + * location that stores the number of remaining bytes we can use to pick + * a character (see pick_one_utf8_char() above). + */ +int utf8_width(const char **start, size_t *remainder_p) +{ + ucs_char_t ch = pick_one_utf8_char(start, remainder_p); + if (!*start) + return 0; return git_wcwidth(ch); } @@ -217,7 +253,7 @@ int is_utf8(const char *text) text++; continue; } - utf8_width(&text); + utf8_width(&text, NULL); if (!text) return 0; } @@ -277,13 +313,12 @@ int print_wrapped_text(const char *text, int indent, int indent2, int width) continue; } if (assume_utf8) - w += utf8_width(&text); + w += utf8_width(&text, NULL); else { w++; text++; } } - return w; } int is_encoding_utf8(const char *name)