libyui-ncurses  2.44.1
 All Classes Functions Variables
NCstring.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: NCstring.cc
20 
21  Author: Gabriele Strattner <gs@suse.de>
22 
23 /-*/
24 
25 #include <errno.h>
26 #include <iconv.h>
27 #include <malloc.h>
28 
29 #define YUILogComponent "ncurses"
30 #include <yui/YUILog.h>
31 #include "NCstring.h"
32 
33 
34 // The default encoding is UTF-8. For real terminals this may be
35 // changed with setConsoleFont().
36 
37 std::string NCstring::termEncoding( "UTF-8" );
38 
39 
40 
41 
42 
43 
44 NCstring:: NCstring()
45  : hotk( 0 )
46  , hotp( std::wstring::npos )
47  , wstr( L"" )
48 
49 {
50 }
51 
52 
53 
54 NCstring::NCstring( const NCstring & nstr )
55  : hotk( nstr.hotk )
56  , hotp( nstr.hotp )
57  , wstr( nstr.wstr )
58 {
59 }
60 
61 
62 
63 NCstring::NCstring( const std::wstring & widestr )
64  : hotk( 0 )
65  , hotp( std::wstring::npos )
66  , wstr( widestr )
67 {
68 }
69 
70 
71 
72 NCstring::NCstring( const std::string & str )
73  : hotk( 0 )
74  , hotp( std::wstring::npos )
75 {
76  bool ok = RecodeToWchar( str, "UTF-8", &wstr );
77 
78  if ( !ok )
79  {
80  yuiError() << "ERROR: RecodeToWchar() failed" << std::endl;
81  }
82 }
83 
84 
85 
86 NCstring::NCstring( const char * cstr )
87  : hotk( 0 )
88  , hotp( std::wstring::npos )
89 {
90  bool ok = RecodeToWchar( cstr, "UTF-8", &wstr );
91 
92  if ( !ok )
93  {
94  yuiError() << "ERROR: RecodeToWchar() failed" << std::endl;
95  }
96 }
97 
98 
99 
100 std::ostream & operator<<( std::ostream & STREAM, const NCstring & OBJ )
101 {
102  return STREAM << OBJ.Str() ;
103 }
104 
105 
106 
107 NCstring & NCstring::operator=( const NCstring & nstr )
108 {
109  if ( &nstr != this )
110  {
111  hotk = nstr.hotk;
112  hotp = nstr.hotp;
113  wstr = nstr.wstr;
114  }
115 
116  return *this;
117 }
118 
119 
120 
121 NCstring & NCstring::operator+=( const NCstring & nstr )
122 {
123  wstr.append( nstr.wstr );
124  return *this;
125 }
126 
127 static iconv_t fromwchar_cd = ( iconv_t )( -1 );
128 static std::string to_name = "";
129 
130 
131 
132 bool NCstring::RecodeFromWchar( const std::wstring & in, const std::string & to_encoding, std::string* out )
133 {
134  iconv_t cd = ( iconv_t )( -1 );
135  static bool complained = false;
136  *out = "";
137 
138  if ( in.length() == 0 )
139  return true;
140 
141  // iconv_open not yet called
142  if ( fromwchar_cd == ( iconv_t )( -1 )
143  || to_name != to_encoding )
144  {
145  if ( fromwchar_cd != ( iconv_t )( -1 ) )
146  {
147  iconv_close( fromwchar_cd );
148  }
149 
150  fromwchar_cd = iconv_open( to_encoding.c_str(), "WCHAR_T" );
151 
152  yuiDebug() << "iconv_open( " << to_encoding.c_str() << ", \"WCHAR_T\" )" << std::endl;
153 
154  if ( fromwchar_cd == ( iconv_t )( -1 ) )
155  {
156  if ( !complained )
157  {
158  yuiError() << "ERROR: iconv_open failed" << std::endl;
159  complained = true;
160  }
161 
162  return false;
163  }
164  else
165  {
166  to_name = to_encoding;
167  }
168  }
169 
170  cd = fromwchar_cd; // set iconv handle
171 
172  size_t in_len = in.length() * sizeof( std::wstring::value_type ); // number of in bytes
173  char* in_ptr = ( char * )in.data();
174 
175  size_t tmp_size = ( in_len * sizeof( char ) ) * 2;
176  // tmp buffer size: in_len bytes * 2, that means 1 wide charatcer (4 Byte) can be transformed
177  // into an encoding which needs at most 8 Byte for one character (should be enough)
178 
179  char* tmp = ( char* ) malloc( tmp_size + sizeof( char ) );
180 
181  do
182  {
183 
184  char *tmp_ptr = tmp;
185  size_t tmp_len = tmp_size;
186  *(( char* ) tmp_ptr ) = '\0';
187 
188  size_t iconv_ret = iconv( cd, &in_ptr, &in_len, &tmp_ptr, &tmp_len );
189 
190  *(( char* ) tmp_ptr ) = '\0';
191  *out += std::string( tmp );
192 
193  if ( iconv_ret == ( size_t )( -1 ) )
194  {
195  if ( !complained )
196  {
197  yuiError() << "ERROR iconv: " << errno << std::endl;
198  complained = true;
199  }
200 
201  if ( errno == EINVAL || errno == EILSEQ )
202  {
203  *out += '?';
204  }
205 
206  in_ptr += sizeof( std::wstring::value_type );
207 
208  in_len -= sizeof( std::wstring::value_type );
209  }
210 
211  }
212  while ( in_len != 0 );
213 
214  free( tmp );
215 
216  return true;
217 }
218 
219 static iconv_t towchar_cd = ( iconv_t )( -1 );
220 static std::string from_name = "";
221 
222 
223 
224 bool NCstring::RecodeToWchar( const std::string& in, const std::string &from_encoding, std::wstring* out )
225 {
226  iconv_t cd = ( iconv_t )( -1 );
227  static bool complained = false;
228  *out = L"";
229 
230  if ( in.length() == 0 )
231  return true;
232 
233  // iconv_open not yet called
234  if ( towchar_cd == ( iconv_t )( -1 )
235  || from_name != from_encoding )
236  {
237  if ( towchar_cd != ( iconv_t )( -1 ) )
238  {
239  iconv_close( towchar_cd );
240  }
241 
242  towchar_cd = iconv_open( "WCHAR_T", from_encoding.c_str() );
243 
244  yuiDebug() << "iconv_open( \"WCHAR_T\", " << from_encoding.c_str() << " )" << std::endl;
245 
246  if ( towchar_cd == ( iconv_t )( -1 ) )
247  {
248  if ( !complained )
249  {
250  yuiError() << "Error: RecodeToWchar iconv_open() failed" << std::endl;
251  complained = true;
252  }
253 
254  return false;
255  }
256  else
257  {
258  from_name = from_encoding;
259  }
260  }
261 
262  cd = towchar_cd; // set iconv handle
263 
264  size_t in_len = in.length(); // number of bytes of input std::string
265  char* in_ptr = const_cast <char*>( in.c_str() );
266 
267  size_t tmp_size = in_len * sizeof( wchar_t ); // buffer size: at most in_len wide characters
268  char* tmp = ( char* ) malloc( tmp_size + sizeof( wchar_t ) ); // + L'\0'
269 
270  do
271  {
272 
273  size_t tmp_len = tmp_size;
274  char* tmp_ptr = tmp;
275 
276  size_t iconv_ret = iconv( cd, &in_ptr, &in_len, &tmp_ptr, &tmp_len );
277 
278  *(( wchar_t* ) tmp_ptr ) = L'\0';
279 
280  *out += std::wstring(( wchar_t* ) tmp );
281 
282  if ( iconv_ret == ( size_t )( -1 ) )
283  {
284  if ( !complained )
285  {
286  // EILSEQ 84 Illegal byte sequence.
287  // EINVAL 22 Invalid argument
288  // E2BIG 7 Argument list too long
289  yuiError() << "ERROR iconv: " << errno << std::endl;
290  complained = true;
291  }
292 
293  if ( errno == EINVAL || errno == EILSEQ )
294  {
295  *out += L'?';
296  }
297 
298  in_ptr++;
299 
300  in_len--;
301  }
302 
303  }
304  while ( in_len != 0 );
305 
306  free( tmp );
307 
308  return true;
309 }
310 
311 
312 
313 std::string NCstring::Str() const
314 {
315  std::string utf8str;
316  RecodeFromWchar( wstr, "UTF-8", &utf8str );
317 
318  return utf8str;
319 }
320 
321 
322 
323 void NCstring::getHotkey( ) const
324 {
325 
326  hotp = std::wstring::npos;
327  const wchar_t shortcutMarker = L'&';
328  const wchar_t replacementShortcutMarker = L'_';
329 
330  // I'm not really happy with using replacement markers and copying the std::string
331  // but is there an other way?
332  // If hotkey is looked up before un-escaping, its position won't be up-to-date anymore
333  // as chars got deleted from the std::string
334  // And vice versa: if un-escaping is done before looking up hotkey position, it's no
335  // longer possible to tell hotkey marker and regular & (previous &&) apart (this is
336  // the 'Foo&&Bar&Geeez' case) fB.
337 
338  bool have_shortcut = false;
339  std::wstring::size_type len = wstr.length();
340  std::wstring newstr;
341  newstr.reserve( len );
342 
343  for (std::wstring::iterator it = wstr.begin(); it != wstr.end(); it++) {
344  if ( *it == shortcutMarker &&
345  (it + 1 != wstr.end()) ) {
346 
347  // double && un-escaping - bnc#559226
348  // foo&&bar => foo&bar
349  if ( *(it+1) == shortcutMarker) {
350  newstr += shortcutMarker; // add only one &
351  it++; // .. and jump forth to skip the 2nd one
352  }
353  // regular hotkey &X
354  else {
355  // take the first one only (we can't do multiple hotkeys per 1 line
356  // so we just discard the rest, argh)
357  if ( !have_shortcut) {
358  newstr += replacementShortcutMarker;
359  have_shortcut = true;
360  }
361  }
362  }
363  else
364  newstr += *it;
365  }
366 
367  wstr = newstr;
368 
369  std::wstring::size_type tpos = wstr.find_first_of( replacementShortcutMarker );
370 
371  if ( tpos != std::wstring::npos && tpos != wstr.size() - 1 )
372  {
373  size_t realpos = 0, t;
374 
375  for ( t = 0; t < tpos; t++ )
376  realpos += wcwidth( wstr[t] );
377 
378  wstr.erase( tpos, 1 );
379 
380  hotk = wstr[tpos];
381 
382  hotp = realpos;
383  }
384 
385 }
386 
387 
388 
389 bool NCstring::setTerminalEncoding( const std::string & encoding )
390 {
391  if ( termEncoding != encoding )
392  {
393  yuiMilestone() << "Terminal encoding SET to: " << encoding << std::endl;
394  termEncoding = encoding;
395  return true;
396  }
397  else
398  {
399  return false;
400  }
401 }