chiark / gitweb /
dpkg (1.18.25) stretch; urgency=medium
[dpkg] / lib / dpkg / strwide.c
1 /*
2  * libdpkg - Debian packaging suite library routines
3  * strwide.c - wide character string handling routines
4  *
5  * Copyright © 2004 Changwoo Ryu <cwryu@debian.org>
6  * Copyright © 2012-2013 Guillem Jover <guillem@debian.org>
7  *
8  * This is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21
22 #include <config.h>
23 #include <compat.h>
24
25 #include <string.h>
26 #include <stdlib.h>
27 #ifdef ENABLE_NLS
28 #include <wchar.h>
29 #endif
30
31 #include <dpkg/i18n.h>
32 #include <dpkg/dpkg.h>
33 #include <dpkg/string.h>
34
35 /**
36  * Compute the screen width of a string.
37  *
38  * @param str The multibyte string.
39  *
40  * @return The width of the string.
41  */
42 int
43 str_width(const char *str)
44 {
45 #ifdef ENABLE_NLS
46         mbstate_t state;
47         wchar_t *wcs;
48         const char *mbs = str;
49         size_t len, res;
50         int width;
51
52         len = strlen(str) + 1;
53         wcs = m_malloc(sizeof(wcs[0]) * len);
54
55         memset(&state, 0, sizeof(state));
56
57         res = mbsrtowcs(wcs, &mbs, len, &state);
58         if (res == (size_t)-1) {
59 #ifdef DPKG_UNIFORM_ENCODING
60                 ohshit(_("cannot convert multibyte string '%s' "
61                          "to a wide-character string"), str);
62 #else
63                 /* Cannot convert, fallback to ASCII method. */
64                 free(wcs);
65                 return strlen(str);
66 #endif
67         }
68
69         width = wcswidth(wcs, res);
70
71         free(wcs);
72
73         return width;
74 #else
75         return strlen(str);
76 #endif
77 }
78
79 /**
80  * Generate the crop values for a string given a maximum screen width.
81  *
82  * This function analyzes the string passed and computes the correct point
83  * where to crop the string, returning the amount of string and maximum
84  * bytes to use for padding for example.
85  *
86  * On NLS enabled builds, in addition the string will be cropped on any
87  * newline.
88  *
89  * @param str        The string to crop.
90  * @param max_width  The max screen width to use.
91  * @param[out] crop  The generated crop values for the string.
92  */
93 void
94 str_gen_crop(const char *str, int max_width, struct str_crop_info *crop)
95 {
96 #ifdef ENABLE_NLS
97         mbstate_t state;
98         size_t str_bytes;
99         int mbs_bytes = 0;
100         int mbs_width = 0;
101
102         str_bytes = strlen(str) + 1;
103         memset(&state, 0, sizeof(state));
104
105         for (;;) {
106                 wchar_t wc;
107                 int wc_width;
108                 size_t mb_bytes;
109
110                 mb_bytes = mbrtowc(&wc, str, str_bytes, &state);
111                 if (mb_bytes == (size_t)-1 || mb_bytes == (size_t)-2) {
112 #ifdef DPKG_UNIFORM_ENCODING
113                         ohshit(_("cannot convert multibyte sequence '%s' "
114                                  "to a wide character"), str);
115 #else
116                         /* Cannot convert, fallback to ASCII method. */
117                         crop->str_bytes = crop->max_bytes = max_width;
118                         return;
119 #endif
120                 }
121                 if (mb_bytes == 0)
122                         break;
123
124                 wc_width = wcwidth(wc);
125                 if (wc_width < 0)
126                         break;
127                 if (mbs_width + wc_width > max_width)
128                         break;
129
130                 mbs_width += wc_width;
131                 mbs_bytes += mb_bytes;
132                 str_bytes -= mb_bytes;
133                 str += mb_bytes;
134         }
135
136         crop->str_bytes = mbs_bytes;
137         crop->max_bytes = mbs_bytes + max_width - mbs_width;
138 #else
139         crop->str_bytes = crop->max_bytes = max_width;
140 #endif
141 }