]>
Commit | Line | Data |
---|---|---|
c0acc0f5 | 1 | /* Determine the number of screen columns needed for a string. |
02d5ffac | 2 | Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc. |
c0acc0f5 PE |
3 | |
4 | This program is free software; you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation; either version 2, or (at your option) | |
7 | any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program; if not, write to the Free Software Foundation, | |
16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
17 | ||
18 | /* Written by Bruno Haible <haible@clisp.cons.org>. */ | |
19 | ||
20 | #ifdef HAVE_CONFIG_H | |
21 | # include <config.h> | |
22 | #endif | |
23 | ||
02d5ffac PE |
24 | /* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth(). This must be |
25 | included before "mbswidth.h", because UnixWare 7.1.1 <wchar.h> | |
26 | declares its own mbswidth and we want mbswidth.h's "#define | |
27 | mbswidth gnu_mbswidth" to take effect after <wchar.h> is | |
28 | included. */ | |
29 | #if HAVE_WCHAR_H | |
30 | # include <wchar.h> | |
31 | #endif | |
32 | ||
c0acc0f5 PE |
33 | /* Specification. */ |
34 | #include "mbswidth.h" | |
35 | ||
36 | /* Get MB_CUR_MAX. */ | |
37 | #include <stdlib.h> | |
38 | ||
39 | #include <string.h> | |
40 | ||
41 | /* Get isprint(). */ | |
42 | #include <ctype.h> | |
43 | ||
c0acc0f5 PE |
44 | /* Get iswprint(), iswcntrl(). */ |
45 | #if HAVE_WCTYPE_H | |
46 | # include <wctype.h> | |
47 | #endif | |
48 | #if !defined iswprint && !HAVE_ISWPRINT | |
49 | # define iswprint(wc) 1 | |
50 | #endif | |
51 | #if !defined iswcntrl && !HAVE_ISWCNTRL | |
52 | # define iswcntrl(wc) 0 | |
53 | #endif | |
54 | ||
55 | #ifndef mbsinit | |
56 | # if !HAVE_MBSINIT | |
57 | # define mbsinit(ps) 1 | |
58 | # endif | |
59 | #endif | |
60 | ||
61 | #ifndef HAVE_DECL_WCWIDTH | |
62 | "this configure-time declaration test was not run" | |
63 | #endif | |
64 | #if !HAVE_DECL_WCWIDTH | |
65 | int wcwidth (); | |
66 | #endif | |
67 | ||
68 | #ifndef wcwidth | |
69 | # if !HAVE_WCWIDTH | |
70 | /* wcwidth doesn't exist, so assume all printable characters have | |
71 | width 1. */ | |
72 | # define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1) | |
73 | # endif | |
74 | #endif | |
75 | ||
76 | /* Get ISPRINT. */ | |
77 | #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) | |
78 | # define IN_CTYPE_DOMAIN(c) 1 | |
79 | #else | |
80 | # define IN_CTYPE_DOMAIN(c) isascii(c) | |
81 | #endif | |
82 | /* Undefine to protect against the definition in wctype.h of solaris2.6. */ | |
83 | #undef ISPRINT | |
84 | #define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c)) | |
85 | #undef ISCNTRL | |
86 | #define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c)) | |
87 | ||
88 | /* Returns the number of columns needed to represent the multibyte | |
89 | character string pointed to by STRING. If a non-printable character | |
90 | occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned. | |
91 | With flags = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE, this is | |
92 | the multibyte analogon of the wcswidth function. */ | |
93 | int | |
f6cf0f6e | 94 | mbswidth (const char *string, int flags) |
c0acc0f5 PE |
95 | { |
96 | return mbsnwidth (string, strlen (string), flags); | |
97 | } | |
98 | ||
99 | /* Returns the number of columns needed to represent the multibyte | |
100 | character string pointed to by STRING of length NBYTES. If a | |
101 | non-printable character occurs, and MBSW_REJECT_UNPRINTABLE is | |
102 | specified, -1 is returned. */ | |
103 | int | |
f6cf0f6e | 104 | mbsnwidth (const char *string, size_t nbytes, int flags) |
c0acc0f5 PE |
105 | { |
106 | const char *p = string; | |
107 | const char *plimit = p + nbytes; | |
108 | int width; | |
109 | ||
110 | width = 0; | |
111 | #if HAVE_MBRTOWC | |
112 | if (MB_CUR_MAX > 1) | |
113 | { | |
114 | while (p < plimit) | |
115 | switch (*p) | |
116 | { | |
117 | case ' ': case '!': case '"': case '#': case '%': | |
118 | case '&': case '\'': case '(': case ')': case '*': | |
119 | case '+': case ',': case '-': case '.': case '/': | |
120 | case '0': case '1': case '2': case '3': case '4': | |
121 | case '5': case '6': case '7': case '8': case '9': | |
122 | case ':': case ';': case '<': case '=': case '>': | |
123 | case '?': | |
124 | case 'A': case 'B': case 'C': case 'D': case 'E': | |
125 | case 'F': case 'G': case 'H': case 'I': case 'J': | |
126 | case 'K': case 'L': case 'M': case 'N': case 'O': | |
127 | case 'P': case 'Q': case 'R': case 'S': case 'T': | |
128 | case 'U': case 'V': case 'W': case 'X': case 'Y': | |
129 | case 'Z': | |
130 | case '[': case '\\': case ']': case '^': case '_': | |
131 | case 'a': case 'b': case 'c': case 'd': case 'e': | |
132 | case 'f': case 'g': case 'h': case 'i': case 'j': | |
133 | case 'k': case 'l': case 'm': case 'n': case 'o': | |
134 | case 'p': case 'q': case 'r': case 's': case 't': | |
135 | case 'u': case 'v': case 'w': case 'x': case 'y': | |
136 | case 'z': case '{': case '|': case '}': case '~': | |
137 | /* These characters are printable ASCII characters. */ | |
138 | p++; | |
139 | width++; | |
140 | break; | |
141 | default: | |
142 | /* If we have a multibyte sequence, scan it up to its end. */ | |
143 | { | |
144 | mbstate_t mbstate; | |
145 | memset (&mbstate, 0, sizeof mbstate); | |
146 | do | |
147 | { | |
148 | wchar_t wc; | |
149 | size_t bytes; | |
150 | int w; | |
151 | ||
152 | bytes = mbrtowc (&wc, p, plimit - p, &mbstate); | |
153 | ||
154 | if (bytes == (size_t) -1) | |
155 | /* An invalid multibyte sequence was encountered. */ | |
156 | { | |
157 | if (!(flags & MBSW_REJECT_INVALID)) | |
158 | { | |
159 | p++; | |
160 | width++; | |
161 | break; | |
162 | } | |
163 | else | |
164 | return -1; | |
165 | } | |
166 | ||
167 | if (bytes == (size_t) -2) | |
168 | /* An incomplete multibyte character at the end. */ | |
169 | { | |
170 | if (!(flags & MBSW_REJECT_INVALID)) | |
171 | { | |
172 | p = plimit; | |
173 | width++; | |
174 | break; | |
175 | } | |
176 | else | |
177 | return -1; | |
178 | } | |
179 | ||
180 | if (bytes == 0) | |
181 | /* A null wide character was encountered. */ | |
182 | bytes = 1; | |
183 | ||
184 | w = wcwidth (wc); | |
185 | if (w >= 0) | |
186 | /* A printable multibyte character. */ | |
187 | width += w; | |
188 | else | |
189 | /* An unprintable multibyte character. */ | |
190 | if (!(flags & MBSW_REJECT_UNPRINTABLE)) | |
191 | width += (iswcntrl (wc) ? 0 : 1); | |
192 | else | |
193 | return -1; | |
194 | ||
195 | p += bytes; | |
196 | } | |
197 | while (! mbsinit (&mbstate)); | |
198 | } | |
199 | break; | |
200 | } | |
201 | return width; | |
202 | } | |
203 | #endif | |
204 | ||
205 | while (p < plimit) | |
206 | { | |
207 | unsigned char c = (unsigned char) *p++; | |
208 | ||
209 | if (ISPRINT (c)) | |
210 | width++; | |
211 | else if (!(flags & MBSW_REJECT_UNPRINTABLE)) | |
212 | width += (ISCNTRL (c) ? 0 : 1); | |
213 | else | |
214 | return -1; | |
215 | } | |
216 | return width; | |
217 | } |