utf8_width(): allow non NUL-terminated input
The original interface assumed that the input string is always terminated with a NUL, but that wasn't too useful. Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
396ccf1fcb
commit
44b25b872f
54
utf8.c
54
utf8.c
@ -153,31 +153,46 @@ static int git_wcwidth(ucs_char_t ch)
|
|||||||
/*
|
/*
|
||||||
* Pick one ucs character starting from the location *start points at,
|
* Pick one ucs character starting from the location *start points at,
|
||||||
* and return it, while updating the *start pointer to point at the
|
* and return it, while updating the *start pointer to point at the
|
||||||
* end of that character.
|
* 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
|
* If the string was not a valid UTF-8, *start pointer is set to NULL
|
||||||
* and the return value is undefined.
|
* and the return value is undefined.
|
||||||
*/
|
*/
|
||||||
ucs_char_t pick_one_utf8_char(const char **start)
|
ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p)
|
||||||
{
|
{
|
||||||
unsigned char *s = (unsigned char *)*start;
|
unsigned char *s = (unsigned char *)*start;
|
||||||
ucs_char_t ch;
|
ucs_char_t ch;
|
||||||
|
size_t remainder, incr;
|
||||||
|
|
||||||
if (*s < 0x80) {
|
/*
|
||||||
|
* 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 (remainder < 1) {
|
||||||
|
goto invalid;
|
||||||
|
} else if (*s < 0x80) {
|
||||||
/* 0xxxxxxx */
|
/* 0xxxxxxx */
|
||||||
ch = *s;
|
ch = *s;
|
||||||
*start += 1;
|
incr = 1;
|
||||||
} else if ((s[0] & 0xe0) == 0xc0) {
|
} else if ((s[0] & 0xe0) == 0xc0) {
|
||||||
/* 110XXXXx 10xxxxxx */
|
/* 110XXXXx 10xxxxxx */
|
||||||
if ((s[1] & 0xc0) != 0x80 ||
|
if (remainder < 2 ||
|
||||||
/* overlong? */
|
(s[1] & 0xc0) != 0x80 ||
|
||||||
(s[0] & 0xfe) == 0xc0)
|
(s[0] & 0xfe) == 0xc0)
|
||||||
goto invalid;
|
goto invalid;
|
||||||
ch = ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
|
ch = ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
|
||||||
*start += 2;
|
incr = 2;
|
||||||
} else if ((s[0] & 0xf0) == 0xe0) {
|
} else if ((s[0] & 0xf0) == 0xe0) {
|
||||||
/* 1110XXXX 10Xxxxxx 10xxxxxx */
|
/* 1110XXXX 10Xxxxxx 10xxxxxx */
|
||||||
if ((s[1] & 0xc0) != 0x80 ||
|
if (remainder < 3 ||
|
||||||
|
(s[1] & 0xc0) != 0x80 ||
|
||||||
(s[2] & 0xc0) != 0x80 ||
|
(s[2] & 0xc0) != 0x80 ||
|
||||||
/* overlong? */
|
/* overlong? */
|
||||||
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) ||
|
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) ||
|
||||||
@ -189,10 +204,11 @@ ucs_char_t pick_one_utf8_char(const char **start)
|
|||||||
goto invalid;
|
goto invalid;
|
||||||
ch = ((s[0] & 0x0f) << 12) |
|
ch = ((s[0] & 0x0f) << 12) |
|
||||||
((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
|
((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
|
||||||
*start += 3;
|
incr = 3;
|
||||||
} else if ((s[0] & 0xf8) == 0xf0) {
|
} else if ((s[0] & 0xf8) == 0xf0) {
|
||||||
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
|
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
|
||||||
if ((s[1] & 0xc0) != 0x80 ||
|
if (remainder < 4 ||
|
||||||
|
(s[1] & 0xc0) != 0x80 ||
|
||||||
(s[2] & 0xc0) != 0x80 ||
|
(s[2] & 0xc0) != 0x80 ||
|
||||||
(s[3] & 0xc0) != 0x80 ||
|
(s[3] & 0xc0) != 0x80 ||
|
||||||
/* overlong? */
|
/* overlong? */
|
||||||
@ -202,25 +218,29 @@ ucs_char_t pick_one_utf8_char(const char **start)
|
|||||||
goto invalid;
|
goto invalid;
|
||||||
ch = ((s[0] & 0x07) << 18) | ((s[1] & 0x3f) << 12) |
|
ch = ((s[0] & 0x07) << 18) | ((s[1] & 0x3f) << 12) |
|
||||||
((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
|
((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
|
||||||
*start += 4;
|
incr = 4;
|
||||||
} else {
|
} else {
|
||||||
invalid:
|
invalid:
|
||||||
*start = NULL;
|
*start = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*start += incr;
|
||||||
|
if (remainder_p)
|
||||||
|
*remainder_p = remainder - incr;
|
||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function returns the number of columns occupied by the character
|
* This function returns the number of columns occupied by the character
|
||||||
* pointed to by the variable start. The pointer is updated to point at
|
* 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
|
* the next character. When remainder_p is not NULL, it points at the
|
||||||
* NULL.
|
* 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)
|
int utf8_width(const char **start, size_t *remainder_p)
|
||||||
{
|
{
|
||||||
ucs_char_t ch = pick_one_utf8_char(start);
|
ucs_char_t ch = pick_one_utf8_char(start, remainder_p);
|
||||||
if (!*start)
|
if (!*start)
|
||||||
return 0;
|
return 0;
|
||||||
return git_wcwidth(ch);
|
return git_wcwidth(ch);
|
||||||
@ -233,7 +253,7 @@ int is_utf8(const char *text)
|
|||||||
text++;
|
text++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
utf8_width(&text);
|
utf8_width(&text, NULL);
|
||||||
if (!text)
|
if (!text)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -293,7 +313,7 @@ int print_wrapped_text(const char *text, int indent, int indent2, int width)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (assume_utf8)
|
if (assume_utf8)
|
||||||
w += utf8_width(&text);
|
w += utf8_width(&text, NULL);
|
||||||
else {
|
else {
|
||||||
w++;
|
w++;
|
||||||
text++;
|
text++;
|
||||||
|
4
utf8.h
4
utf8.h
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
typedef unsigned int ucs_char_t; /* assuming 32bit int */
|
typedef unsigned int ucs_char_t; /* assuming 32bit int */
|
||||||
|
|
||||||
ucs_char_t pick_one_utf8_char(const char **start);
|
ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p);
|
||||||
int utf8_width(const char **start);
|
int utf8_width(const char **start, size_t *remainder_p);
|
||||||
int is_utf8(const char *text);
|
int is_utf8(const char *text);
|
||||||
int is_encoding_utf8(const char *name);
|
int is_encoding_utf8(const char *name);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user