xref: /minix3/external/public-domain/xz/dist/src/common/tuklib_mbstr_width.c (revision 5a645f22a86f086849945a5dd6acbf59f38c913a)
1*5a645f22SBen Gras ///////////////////////////////////////////////////////////////////////////////
2*5a645f22SBen Gras //
3*5a645f22SBen Gras /// \file       tuklib_mstr_width.c
4*5a645f22SBen Gras /// \brief      Calculate width of a multibyte string
5*5a645f22SBen Gras //
6*5a645f22SBen Gras //  Author:     Lasse Collin
7*5a645f22SBen Gras //
8*5a645f22SBen Gras //  This file has been put into the public domain.
9*5a645f22SBen Gras //  You can do whatever you want with this file.
10*5a645f22SBen Gras //
11*5a645f22SBen Gras ///////////////////////////////////////////////////////////////////////////////
12*5a645f22SBen Gras 
13*5a645f22SBen Gras #include "tuklib_mbstr.h"
14*5a645f22SBen Gras 
15*5a645f22SBen Gras #if defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
16*5a645f22SBen Gras #	include <wchar.h>
17*5a645f22SBen Gras #endif
18*5a645f22SBen Gras 
19*5a645f22SBen Gras 
20*5a645f22SBen Gras extern size_t
tuklib_mbstr_width(const char * str,size_t * bytes)21*5a645f22SBen Gras tuklib_mbstr_width(const char *str, size_t *bytes)
22*5a645f22SBen Gras {
23*5a645f22SBen Gras 	const size_t len = strlen(str);
24*5a645f22SBen Gras 	if (bytes != NULL)
25*5a645f22SBen Gras 		*bytes = len;
26*5a645f22SBen Gras 
27*5a645f22SBen Gras #if !(defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH))
28*5a645f22SBen Gras 	// In single-byte mode, the width of the string is the same
29*5a645f22SBen Gras 	// as its length.
30*5a645f22SBen Gras 	return len;
31*5a645f22SBen Gras 
32*5a645f22SBen Gras #else
33*5a645f22SBen Gras 	mbstate_t state;
34*5a645f22SBen Gras 	memset(&state, 0, sizeof(state));
35*5a645f22SBen Gras 
36*5a645f22SBen Gras 	size_t width = 0;
37*5a645f22SBen Gras 	size_t i = 0;
38*5a645f22SBen Gras 
39*5a645f22SBen Gras 	// Convert one multibyte character at a time to wchar_t
40*5a645f22SBen Gras 	// and get its width using wcwidth().
41*5a645f22SBen Gras 	while (i < len) {
42*5a645f22SBen Gras 		wchar_t wc;
43*5a645f22SBen Gras 		const size_t ret = mbrtowc(&wc, str + i, len - i, &state);
44*5a645f22SBen Gras 		if (ret < 1 || ret > len)
45*5a645f22SBen Gras 			return (size_t)-1;
46*5a645f22SBen Gras 
47*5a645f22SBen Gras 		i += ret;
48*5a645f22SBen Gras 
49*5a645f22SBen Gras 		const int wc_width = wcwidth(wc);
50*5a645f22SBen Gras 		if (wc_width < 0)
51*5a645f22SBen Gras 			return (size_t)-1;
52*5a645f22SBen Gras 
53*5a645f22SBen Gras 		width += wc_width;
54*5a645f22SBen Gras 	}
55*5a645f22SBen Gras 
56*5a645f22SBen Gras 	// Require that the string ends in the initial shift state.
57*5a645f22SBen Gras 	// This way the caller can be combine the string with other
58*5a645f22SBen Gras 	// strings without needing to worry about the shift states.
59*5a645f22SBen Gras 	if (!mbsinit(&state))
60*5a645f22SBen Gras 		return (size_t)-1;
61*5a645f22SBen Gras 
62*5a645f22SBen Gras 	return width;
63*5a645f22SBen Gras #endif
64*5a645f22SBen Gras }
65