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