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