1 /* Determine the number of screen columns needed for a string. 2 Copyright (C) 2000-2012 Free Software Foundation, Inc. 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 3 of the License, or 7 (at your option) 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, see <http://www.gnu.org/licenses/>. */ 16 17 /* Written by Bruno Haible <haible (at) clisp.cons.org>. */ 18 19 #include <config.h> 20 21 /* Specification. */ 22 #include "mbswidth.h" 23 24 /* Get MB_CUR_MAX. */ 25 #include <stdlib.h> 26 27 #include <string.h> 28 29 /* Get isprint(). */ 30 #include <ctype.h> 31 32 /* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth(). */ 33 #include <wchar.h> 34 35 /* Get iswcntrl(). */ 36 #include <wctype.h> 37 38 /* Get INT_MAX. */ 39 #include <limits.h> 40 41 /* Returns the number of columns needed to represent the multibyte 42 character string pointed to by STRING. If a non-printable character 43 occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned. 44 With flags = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE, this is 45 the multibyte analogue of the wcswidth function. */ 46 int 47 mbswidth (const char *string, int flags) 48 { 49 return mbsnwidth (string, strlen (string), flags); 50 } 51 52 /* Returns the number of columns needed to represent the multibyte 53 character string pointed to by STRING of length NBYTES. If a 54 non-printable character occurs, and MBSW_REJECT_UNPRINTABLE is 55 specified, -1 is returned. */ 56 int 57 mbsnwidth (const char *string, size_t nbytes, int flags) 58 { 59 const char *p = string; 60 const char *plimit = p + nbytes; 61 int width; 62 63 width = 0; 64 if (MB_CUR_MAX > 1) 65 { 66 while (p < plimit) 67 switch (*p) 68 { 69 case ' ': case '!': case '"': case '#': case '%': 70 case '&': case '\'': case '(': case ')': case '*': 71 case '+': case ',': case '-': case '.': case '/': 72 case '0': case '1': case '2': case '3': case '4': 73 case '5': case '6': case '7': case '8': case '9': 74 case ':': case ';': case '<': case '=': case '>': 75 case '?': 76 case 'A': case 'B': case 'C': case 'D': case 'E': 77 case 'F': case 'G': case 'H': case 'I': case 'J': 78 case 'K': case 'L': case 'M': case 'N': case 'O': 79 case 'P': case 'Q': case 'R': case 'S': case 'T': 80 case 'U': case 'V': case 'W': case 'X': case 'Y': 81 case 'Z': 82 case '[': case '\\': case ']': case '^': case '_': 83 case 'a': case 'b': case 'c': case 'd': case 'e': 84 case 'f': case 'g': case 'h': case 'i': case 'j': 85 case 'k': case 'l': case 'm': case 'n': case 'o': 86 case 'p': case 'q': case 'r': case 's': case 't': 87 case 'u': case 'v': case 'w': case 'x': case 'y': 88 case 'z': case '{': case '|': case '}': case '~': 89 /* These characters are printable ASCII characters. */ 90 p++; 91 width++; 92 break; 93 default: 94 /* If we have a multibyte sequence, scan it up to its end. */ 95 { 96 mbstate_t mbstate; 97 memset (&mbstate, 0, sizeof mbstate); 98 do 99 { 100 wchar_t wc; 101 size_t bytes; 102 int w; 103 104 bytes = mbrtowc (&wc, p, plimit - p, &mbstate); 105 106 if (bytes == (size_t) -1) 107 /* An invalid multibyte sequence was encountered. */ 108 { 109 if (!(flags & MBSW_REJECT_INVALID)) 110 { 111 p++; 112 width++; 113 break; 114 } 115 else 116 return -1; 117 } 118 119 if (bytes == (size_t) -2) 120 /* An incomplete multibyte character at the end. */ 121 { 122 if (!(flags & MBSW_REJECT_INVALID)) 123 { 124 p = plimit; 125 width++; 126 break; 127 } 128 else 129 return -1; 130 } 131 132 if (bytes == 0) 133 /* A null wide character was encountered. */ 134 bytes = 1; 135 136 w = wcwidth (wc); 137 if (w >= 0) 138 /* A printable multibyte character. */ 139 { 140 if (w > INT_MAX - width) 141 goto overflow; 142 width += w; 143 } 144 else 145 /* An unprintable multibyte character. */ 146 if (!(flags & MBSW_REJECT_UNPRINTABLE)) 147 { 148 if (!iswcntrl (wc)) 149 { 150 if (width == INT_MAX) 151 goto overflow; 152 width++; 153 } 154 } 155 else 156 return -1; 157 158 p += bytes; 159 } 160 while (! mbsinit (&mbstate)); 161 } 162 break; 163 } 164 return width; 165 } 166 167 while (p < plimit) 168 { 169 unsigned char c = (unsigned char) *p++; 170 171 if (isprint (c)) 172 { 173 if (width == INT_MAX) 174 goto overflow; 175 width++; 176 } 177 else if (!(flags & MBSW_REJECT_UNPRINTABLE)) 178 { 179 if (!iscntrl (c)) 180 { 181 if (width == INT_MAX) 182 goto overflow; 183 width++; 184 } 185 } 186 else 187 return -1; 188 } 189 return width; 190 191 overflow: 192 return INT_MAX; 193 } 194