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