12940b44dSPeter Avalos ///////////////////////////////////////////////////////////////////////////////
22940b44dSPeter Avalos //
3*e151908bSDaniel Fojt /// \file       tuklib_mbstr_width.c
42940b44dSPeter Avalos /// \brief      Calculate width of a multibyte string
52940b44dSPeter Avalos //
62940b44dSPeter Avalos //  Author:     Lasse Collin
72940b44dSPeter Avalos //
82940b44dSPeter Avalos //  This file has been put into the public domain.
92940b44dSPeter Avalos //  You can do whatever you want with this file.
102940b44dSPeter Avalos //
112940b44dSPeter Avalos ///////////////////////////////////////////////////////////////////////////////
122940b44dSPeter Avalos 
132940b44dSPeter Avalos #include "tuklib_mbstr.h"
14*e151908bSDaniel Fojt #include <string.h>
152940b44dSPeter Avalos 
162940b44dSPeter Avalos #if defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
172940b44dSPeter Avalos #	include <wchar.h>
182940b44dSPeter Avalos #endif
192940b44dSPeter Avalos 
202940b44dSPeter Avalos 
212940b44dSPeter Avalos extern size_t
tuklib_mbstr_width(const char * str,size_t * bytes)222940b44dSPeter Avalos tuklib_mbstr_width(const char *str, size_t *bytes)
232940b44dSPeter Avalos {
242940b44dSPeter Avalos 	const size_t len = strlen(str);
252940b44dSPeter Avalos 	if (bytes != NULL)
262940b44dSPeter Avalos 		*bytes = len;
272940b44dSPeter Avalos 
282940b44dSPeter Avalos #if !(defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH))
292940b44dSPeter Avalos 	// In single-byte mode, the width of the string is the same
302940b44dSPeter Avalos 	// as its length.
312940b44dSPeter Avalos 	return len;
322940b44dSPeter Avalos 
332940b44dSPeter Avalos #else
342940b44dSPeter Avalos 	mbstate_t state;
352940b44dSPeter Avalos 	memset(&state, 0, sizeof(state));
362940b44dSPeter Avalos 
372940b44dSPeter Avalos 	size_t width = 0;
382940b44dSPeter Avalos 	size_t i = 0;
392940b44dSPeter Avalos 
402940b44dSPeter Avalos 	// Convert one multibyte character at a time to wchar_t
412940b44dSPeter Avalos 	// and get its width using wcwidth().
422940b44dSPeter Avalos 	while (i < len) {
432940b44dSPeter Avalos 		wchar_t wc;
442940b44dSPeter Avalos 		const size_t ret = mbrtowc(&wc, str + i, len - i, &state);
452940b44dSPeter Avalos 		if (ret < 1 || ret > len)
462940b44dSPeter Avalos 			return (size_t)-1;
472940b44dSPeter Avalos 
482940b44dSPeter Avalos 		i += ret;
492940b44dSPeter Avalos 
502940b44dSPeter Avalos 		const int wc_width = wcwidth(wc);
512940b44dSPeter Avalos 		if (wc_width < 0)
522940b44dSPeter Avalos 			return (size_t)-1;
532940b44dSPeter Avalos 
54*e151908bSDaniel Fojt 		width += (size_t)wc_width;
552940b44dSPeter Avalos 	}
562940b44dSPeter Avalos 
572940b44dSPeter Avalos 	// Require that the string ends in the initial shift state.
582940b44dSPeter Avalos 	// This way the caller can be combine the string with other
592940b44dSPeter Avalos 	// strings without needing to worry about the shift states.
602940b44dSPeter Avalos 	if (!mbsinit(&state))
612940b44dSPeter Avalos 		return (size_t)-1;
622940b44dSPeter Avalos 
632940b44dSPeter Avalos 	return width;
642940b44dSPeter Avalos #endif
652940b44dSPeter Avalos }
66