@matheusbsbs / matheus-bagliano / commits / 44b25b872f
Fork 0

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>

Junio C Hamano committed Jan 2, 2008 at 01:49 UTC 44b25b872f926ba92bae9ddf2495e7f59d0c96e2
2 files changed +54 -34
utf8.c
+52 -32
@@ -153,74 +153,94 @@ static int git_wcwidth(ucs_char_t ch)
153 /*
154 * Pick one ucs character starting from the location *start points at,
155 * and return it, while updating the *start pointer to point at the
156 - * end of that character.
156 + * end of that character. When remainder_p is not NULL, the location
157 + * holds the number of bytes remaining in the string that we are allowed
158 + * to pick from. Otherwise we are allowed to pick up to the NUL that
159 + * would eventually appear in the string. *remainder_p is also reduced
160 + * by the number of bytes we have consumed.
161 *
162 * If the string was not a valid UTF-8, *start pointer is set to NULL
163 * and the return value is undefined.
164 */
161 -ucs_char_t pick_one_utf8_char(const char **start)
165 +ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p)
166 {
167 unsigned char *s = (unsigned char *)*start;
168 ucs_char_t ch;
169 + size_t remainder, incr;
170
166 - if (*s < 0x80) {
171 + /*
172 + * A caller that assumes NUL terminated text can choose
173 + * not to bother with the remainder length. We will
174 + * stop at the first NUL.
175 + */
176 + remainder = (remainder_p ? *remainder_p : 999);
177 +
178 + if (remainder < 1) {
179 + goto invalid;
180 + } else if (*s < 0x80) {
181 /* 0xxxxxxx */
182 ch = *s;
169 - *start += 1;
183 + incr = 1;
184 } else if ((s[0] & 0xe0) == 0xc0) {
185 /* 110XXXXx 10xxxxxx */
172 - if ((s[1] & 0xc0) != 0x80 ||
173 - /* overlong? */
174 - (s[0] & 0xfe) == 0xc0)
186 + if (remainder < 2 ||
187 + (s[1] & 0xc0) != 0x80 ||
188 + (s[0] & 0xfe) == 0xc0)
189 goto invalid;
190 ch = ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
177 - *start += 2;
191 + incr = 2;
192 } else if ((s[0] & 0xf0) == 0xe0) {
193 /* 1110XXXX 10Xxxxxx 10xxxxxx */
180 - if ((s[1] & 0xc0) != 0x80 ||
181 - (s[2] & 0xc0) != 0x80 ||
182 - /* overlong? */
183 - (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) ||
184 - /* surrogate? */
185 - (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) ||
186 - /* U+FFFE or U+FFFF? */
187 - (s[0] == 0xef && s[1] == 0xbf &&
188 - (s[2] & 0xfe) == 0xbe))
194 + if (remainder < 3 ||
195 + (s[1] & 0xc0) != 0x80 ||
196 + (s[2] & 0xc0) != 0x80 ||
197 + /* overlong? */
198 + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) ||
199 + /* surrogate? */
200 + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) ||
201 + /* U+FFFE or U+FFFF? */
202 + (s[0] == 0xef && s[1] == 0xbf &&
203 + (s[2] & 0xfe) == 0xbe))
204 goto invalid;
205 ch = ((s[0] & 0x0f) << 12) |
206 ((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
192 - *start += 3;
207 + incr = 3;
208 } else if ((s[0] & 0xf8) == 0xf0) {
209 /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
195 - if ((s[1] & 0xc0) != 0x80 ||
196 - (s[2] & 0xc0) != 0x80 ||
197 - (s[3] & 0xc0) != 0x80 ||
198 - /* overlong? */
199 - (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) ||
200 - /* > U+10FFFF? */
201 - (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4)
210 + if (remainder < 4 ||
211 + (s[1] & 0xc0) != 0x80 ||
212 + (s[2] & 0xc0) != 0x80 ||
213 + (s[3] & 0xc0) != 0x80 ||
214 + /* overlong? */
215 + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) ||
216 + /* > U+10FFFF? */
217 + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4)
218 goto invalid;
219 ch = ((s[0] & 0x07) << 18) | ((s[1] & 0x3f) << 12) |
220 ((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
205 - *start += 4;
221 + incr = 4;
222 } else {
223 invalid:
224 *start = NULL;
225 return 0;
226 }
227
228 + *start += incr;
229 + if (remainder_p)
230 + *remainder_p = remainder - incr;
231 return ch;
232 }
233
234 /*
235 * This function returns the number of columns occupied by the character
236 * pointed to by the variable start. The pointer is updated to point at
218 - * the next character. If it was not valid UTF-8, the pointer is set to
219 - * NULL.
237 + * the next character. When remainder_p is not NULL, it points at the
238 + * location that stores the number of remaining bytes we can use to pick
239 + * a character (see pick_one_utf8_char() above).
240 */
221 -int utf8_width(const char **start)
241 +int utf8_width(const char **start, size_t *remainder_p)
242 {
223 - ucs_char_t ch = pick_one_utf8_char(start);
243 + ucs_char_t ch = pick_one_utf8_char(start, remainder_p);
244 if (!*start)
245 return 0;
246 return git_wcwidth(ch);
@@ -233,7 +253,7 @@ int is_utf8(const char *text)
253 text++;
254 continue;
255 }
236 - utf8_width(&text);
256 + utf8_width(&text, NULL);
257 if (!text)
258 return 0;
259 }
@@ -293,7 +313,7 @@ int print_wrapped_text(const char *text, int indent, int indent2, int width)
313 continue;
314 }
315 if (assume_utf8)
296 - w += utf8_width(&text);
316 + w += utf8_width(&text, NULL);
317 else {
318 w++;
319 text++;
utf8.h
+2 -2
@@ -3,8 +3,8 @@
3
4 typedef unsigned int ucs_char_t; /* assuming 32bit int */
5
6 -ucs_char_t pick_one_utf8_char(const char **start);
7 -int utf8_width(const char **start);
6 +ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p);
7 +int utf8_width(const char **start, size_t *remainder_p);
8 int is_utf8(const char *text);
9 int is_encoding_utf8(const char *name);
10