libyui-ncurses
2.44.1
|
00001 /* 00002 Copyright (C) 2000-2012 Novell, Inc 00003 This library is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU Lesser General Public License as 00005 published by the Free Software Foundation; either version 2.1 of the 00006 License, or (at your option) version 3.0 of the License. This library 00007 is distributed in the hope that it will be useful, but WITHOUT ANY 00008 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00009 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00010 License for more details. You should have received a copy of the GNU 00011 Lesser General Public License along with this library; if not, write 00012 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 00013 Floor, Boston, MA 02110-1301 USA 00014 */ 00015 00016 00017 /*-/ 00018 00019 File: NCstring.cc 00020 00021 Author: Gabriele Strattner <gs@suse.de> 00022 00023 /-*/ 00024 00025 #include <errno.h> 00026 #include <iconv.h> 00027 #include <malloc.h> 00028 00029 #define YUILogComponent "ncurses" 00030 #include <yui/YUILog.h> 00031 #include "NCstring.h" 00032 00033 00034 // The default encoding is UTF-8. For real terminals this may be 00035 // changed with setConsoleFont(). 00036 00037 std::string NCstring::termEncoding( "UTF-8" ); 00038 00039 00040 00041 00042 00043 00044 NCstring:: NCstring() 00045 : hotk( 0 ) 00046 , hotp( std::wstring::npos ) 00047 , wstr( L"" ) 00048 00049 { 00050 } 00051 00052 00053 00054 NCstring::NCstring( const NCstring & nstr ) 00055 : hotk( nstr.hotk ) 00056 , hotp( nstr.hotp ) 00057 , wstr( nstr.wstr ) 00058 { 00059 } 00060 00061 00062 00063 NCstring::NCstring( const std::wstring & widestr ) 00064 : hotk( 0 ) 00065 , hotp( std::wstring::npos ) 00066 , wstr( widestr ) 00067 { 00068 } 00069 00070 00071 00072 NCstring::NCstring( const std::string & str ) 00073 : hotk( 0 ) 00074 , hotp( std::wstring::npos ) 00075 { 00076 bool ok = RecodeToWchar( str, "UTF-8", &wstr ); 00077 00078 if ( !ok ) 00079 { 00080 yuiError() << "ERROR: RecodeToWchar() failed" << std::endl; 00081 } 00082 } 00083 00084 00085 00086 NCstring::NCstring( const char * cstr ) 00087 : hotk( 0 ) 00088 , hotp( std::wstring::npos ) 00089 { 00090 bool ok = RecodeToWchar( cstr, "UTF-8", &wstr ); 00091 00092 if ( !ok ) 00093 { 00094 yuiError() << "ERROR: RecodeToWchar() failed" << std::endl; 00095 } 00096 } 00097 00098 00099 00100 std::ostream & operator<<( std::ostream & STREAM, const NCstring & OBJ ) 00101 { 00102 return STREAM << OBJ.Str() ; 00103 } 00104 00105 00106 00107 NCstring & NCstring::operator=( const NCstring & nstr ) 00108 { 00109 if ( &nstr != this ) 00110 { 00111 hotk = nstr.hotk; 00112 hotp = nstr.hotp; 00113 wstr = nstr.wstr; 00114 } 00115 00116 return *this; 00117 } 00118 00119 00120 00121 NCstring & NCstring::operator+=( const NCstring & nstr ) 00122 { 00123 wstr.append( nstr.wstr ); 00124 return *this; 00125 } 00126 00127 static iconv_t fromwchar_cd = ( iconv_t )( -1 ); 00128 static std::string to_name = ""; 00129 00130 00131 00132 bool NCstring::RecodeFromWchar( const std::wstring & in, const std::string & to_encoding, std::string* out ) 00133 { 00134 iconv_t cd = ( iconv_t )( -1 ); 00135 static bool complained = false; 00136 *out = ""; 00137 00138 if ( in.length() == 0 ) 00139 return true; 00140 00141 // iconv_open not yet called 00142 if ( fromwchar_cd == ( iconv_t )( -1 ) 00143 || to_name != to_encoding ) 00144 { 00145 if ( fromwchar_cd != ( iconv_t )( -1 ) ) 00146 { 00147 iconv_close( fromwchar_cd ); 00148 } 00149 00150 fromwchar_cd = iconv_open( to_encoding.c_str(), "WCHAR_T" ); 00151 00152 yuiDebug() << "iconv_open( " << to_encoding.c_str() << ", \"WCHAR_T\" )" << std::endl; 00153 00154 if ( fromwchar_cd == ( iconv_t )( -1 ) ) 00155 { 00156 if ( !complained ) 00157 { 00158 yuiError() << "ERROR: iconv_open failed" << std::endl; 00159 complained = true; 00160 } 00161 00162 return false; 00163 } 00164 else 00165 { 00166 to_name = to_encoding; 00167 } 00168 } 00169 00170 cd = fromwchar_cd; // set iconv handle 00171 00172 size_t in_len = in.length() * sizeof( std::wstring::value_type ); // number of in bytes 00173 char* in_ptr = ( char * )in.data(); 00174 00175 size_t tmp_size = ( in_len * sizeof( char ) ) * 2; 00176 // tmp buffer size: in_len bytes * 2, that means 1 wide charatcer (4 Byte) can be transformed 00177 // into an encoding which needs at most 8 Byte for one character (should be enough) 00178 00179 char* tmp = ( char* ) malloc( tmp_size + sizeof( char ) ); 00180 00181 do 00182 { 00183 00184 char *tmp_ptr = tmp; 00185 size_t tmp_len = tmp_size; 00186 *(( char* ) tmp_ptr ) = '\0'; 00187 00188 size_t iconv_ret = iconv( cd, &in_ptr, &in_len, &tmp_ptr, &tmp_len ); 00189 00190 *(( char* ) tmp_ptr ) = '\0'; 00191 *out += std::string( tmp ); 00192 00193 if ( iconv_ret == ( size_t )( -1 ) ) 00194 { 00195 if ( !complained ) 00196 { 00197 yuiError() << "ERROR iconv: " << errno << std::endl; 00198 complained = true; 00199 } 00200 00201 if ( errno == EINVAL || errno == EILSEQ ) 00202 { 00203 *out += '?'; 00204 } 00205 00206 in_ptr += sizeof( std::wstring::value_type ); 00207 00208 in_len -= sizeof( std::wstring::value_type ); 00209 } 00210 00211 } 00212 while ( in_len != 0 ); 00213 00214 free( tmp ); 00215 00216 return true; 00217 } 00218 00219 static iconv_t towchar_cd = ( iconv_t )( -1 ); 00220 static std::string from_name = ""; 00221 00222 00223 00224 bool NCstring::RecodeToWchar( const std::string& in, const std::string &from_encoding, std::wstring* out ) 00225 { 00226 iconv_t cd = ( iconv_t )( -1 ); 00227 static bool complained = false; 00228 *out = L""; 00229 00230 if ( in.length() == 0 ) 00231 return true; 00232 00233 // iconv_open not yet called 00234 if ( towchar_cd == ( iconv_t )( -1 ) 00235 || from_name != from_encoding ) 00236 { 00237 if ( towchar_cd != ( iconv_t )( -1 ) ) 00238 { 00239 iconv_close( towchar_cd ); 00240 } 00241 00242 towchar_cd = iconv_open( "WCHAR_T", from_encoding.c_str() ); 00243 00244 yuiDebug() << "iconv_open( \"WCHAR_T\", " << from_encoding.c_str() << " )" << std::endl; 00245 00246 if ( towchar_cd == ( iconv_t )( -1 ) ) 00247 { 00248 if ( !complained ) 00249 { 00250 yuiError() << "Error: RecodeToWchar iconv_open() failed" << std::endl; 00251 complained = true; 00252 } 00253 00254 return false; 00255 } 00256 else 00257 { 00258 from_name = from_encoding; 00259 } 00260 } 00261 00262 cd = towchar_cd; // set iconv handle 00263 00264 size_t in_len = in.length(); // number of bytes of input std::string 00265 char* in_ptr = const_cast <char*>( in.c_str() ); 00266 00267 size_t tmp_size = in_len * sizeof( wchar_t ); // buffer size: at most in_len wide characters 00268 char* tmp = ( char* ) malloc( tmp_size + sizeof( wchar_t ) ); // + L'\0' 00269 00270 do 00271 { 00272 00273 size_t tmp_len = tmp_size; 00274 char* tmp_ptr = tmp; 00275 00276 size_t iconv_ret = iconv( cd, &in_ptr, &in_len, &tmp_ptr, &tmp_len ); 00277 00278 *(( wchar_t* ) tmp_ptr ) = L'\0'; 00279 00280 *out += std::wstring(( wchar_t* ) tmp ); 00281 00282 if ( iconv_ret == ( size_t )( -1 ) ) 00283 { 00284 if ( !complained ) 00285 { 00286 // EILSEQ 84 Illegal byte sequence. 00287 // EINVAL 22 Invalid argument 00288 // E2BIG 7 Argument list too long 00289 yuiError() << "ERROR iconv: " << errno << std::endl; 00290 complained = true; 00291 } 00292 00293 if ( errno == EINVAL || errno == EILSEQ ) 00294 { 00295 *out += L'?'; 00296 } 00297 00298 in_ptr++; 00299 00300 in_len--; 00301 } 00302 00303 } 00304 while ( in_len != 0 ); 00305 00306 free( tmp ); 00307 00308 return true; 00309 } 00310 00311 00312 00313 std::string NCstring::Str() const 00314 { 00315 std::string utf8str; 00316 RecodeFromWchar( wstr, "UTF-8", &utf8str ); 00317 00318 return utf8str; 00319 } 00320 00321 00322 00323 void NCstring::getHotkey( ) const 00324 { 00325 00326 hotp = std::wstring::npos; 00327 const wchar_t shortcutMarker = L'&'; 00328 const wchar_t replacementShortcutMarker = L'_'; 00329 00330 // I'm not really happy with using replacement markers and copying the std::string 00331 // but is there an other way? 00332 // If hotkey is looked up before un-escaping, its position won't be up-to-date anymore 00333 // as chars got deleted from the std::string 00334 // And vice versa: if un-escaping is done before looking up hotkey position, it's no 00335 // longer possible to tell hotkey marker and regular & (previous &&) apart (this is 00336 // the 'Foo&&Bar&Geeez' case) fB. 00337 00338 bool have_shortcut = false; 00339 std::wstring::size_type len = wstr.length(); 00340 std::wstring newstr; 00341 newstr.reserve( len ); 00342 00343 for (std::wstring::iterator it = wstr.begin(); it != wstr.end(); it++) { 00344 if ( *it == shortcutMarker && 00345 (it + 1 != wstr.end()) ) { 00346 00347 // double && un-escaping - bnc#559226 00348 // foo&&bar => foo&bar 00349 if ( *(it+1) == shortcutMarker) { 00350 newstr += shortcutMarker; // add only one & 00351 it++; // .. and jump forth to skip the 2nd one 00352 } 00353 // regular hotkey &X 00354 else { 00355 // take the first one only (we can't do multiple hotkeys per 1 line 00356 // so we just discard the rest, argh) 00357 if ( !have_shortcut) { 00358 newstr += replacementShortcutMarker; 00359 have_shortcut = true; 00360 } 00361 } 00362 } 00363 else 00364 newstr += *it; 00365 } 00366 00367 wstr = newstr; 00368 00369 std::wstring::size_type tpos = wstr.find_first_of( replacementShortcutMarker ); 00370 00371 if ( tpos != std::wstring::npos && tpos != wstr.size() - 1 ) 00372 { 00373 size_t realpos = 0, t; 00374 00375 for ( t = 0; t < tpos; t++ ) 00376 realpos += wcwidth( wstr[t] ); 00377 00378 wstr.erase( tpos, 1 ); 00379 00380 hotk = wstr[tpos]; 00381 00382 hotp = realpos; 00383 } 00384 00385 } 00386 00387 00388 00389 bool NCstring::setTerminalEncoding( const std::string & encoding ) 00390 { 00391 if ( termEncoding != encoding ) 00392 { 00393 yuiMilestone() << "Terminal encoding SET to: " << encoding << std::endl; 00394 termEncoding = encoding; 00395 return true; 00396 } 00397 else 00398 { 00399 return false; 00400 } 00401 }