don't leak iconv context on OOM
[libi2ncommon] / src / stringfunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
6a93d84a
TJ
20/** @file
21 *
22 * (c) Copyright 2007-2008 by Intra2net AG
6a93d84a 23 */
e93545dd
GE
24
25#include <iostream>
26#include <string>
27#include <sstream>
28#include <stdexcept>
5efd35b1 29#include <algorithm>
5cd64148 30#include <cmath> // for round()
2bb72337 31#include <climits>
e93545dd 32
a5f3af6e 33#include <wchar.h>
e93545dd
GE
34#include <stdlib.h>
35#include <iconv.h>
36#include <i18n.h>
37
5cd64148 38#include <boost/numeric/conversion/cast.hpp>
3f5c5ccd 39#include <boost/foreach.hpp>
5cd64148 40
2bb72337
TJ
41#include <boost/assert.hpp>
42#include <boost/shared_ptr.hpp>
43#include <openssl/bio.h>
44#include <openssl/evp.h>
45
e93545dd
GE
46#include <stringfunc.hxx>
47
48using namespace std;
49
6ab3bc95
RP
50namespace I2n
51{
6a93d84a
TJ
52
53
6ab3bc95
RP
54namespace
55{
6a93d84a
TJ
56
57const std::string hexDigitsLower("0123456789abcdef");
58const std::string hexDigitsUpper("0123456789ABCDEF");
59
60
61struct UpperFunc
62{
6ab3bc95
RP
63 char operator() (char c)
64 {
65 return std::toupper(c);
66 }
6a93d84a
TJ
67}; // eo struct UpperFunc
68
69
70struct LowerFunc
71{
6ab3bc95
RP
72 char operator() (char c)
73 {
74 return std::tolower(c);
75 }
6a93d84a
TJ
76}; // eo struct LowerFunc
77
78
79} // eo namespace <anonymous>
80
81
82
83/**
6ab3bc95 84 * default list of Whitespaces (" \t\r\n");
6a93d84a 85 */
6ab3bc95 86const std::string Whitespaces = " \t\r\n";
6a93d84a
TJ
87
88/**
89 * default list of lineendings ("\r\n");
90 */
6ab3bc95 91const std::string LineEndings= "\r\n";
6a93d84a
TJ
92
93
94
95/**
96 * @brief checks if a string begins with a given prefix.
97 * @param[in,out] str the string which is tested
98 * @param prefix the prefix which should be tested for.
99 * @return @a true iff the prefix is not empty and the string begins with that prefix.
100 */
6ab3bc95 101bool has_prefix(const std::string& str, const std::string& prefix)
6a93d84a 102{
6ab3bc95
RP
103 if (prefix.empty() || str.empty() || str.size() < prefix.size() )
104 {
105 return false;
106 }
107 return str.compare(0, prefix.size(), prefix) == 0;
108} // eo has_prefix(const std::string&,const std::string&)
6a93d84a
TJ
109
110
111/**
112 * @brief checks if a string ends with a given suffix.
113 * @param[in,out] str the string which is tested
114 * @param suffix the suffix which should be tested for.
115 * @return @a true iff the suffix is not empty and the string ends with that suffix.
116 */
6ab3bc95 117bool has_suffix(const std::string& str, const std::string& suffix)
6a93d84a 118{
6ab3bc95
RP
119 if (suffix.empty() || str.empty() || str.size() < suffix.size() )
120 {
121 return false;
122 }
123 return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
124} // eo has_suffix(const std::string&,const std::string&)
6a93d84a
TJ
125
126
127/**
128 * cut off characters from a given list from front and end of a string.
129 * @param[in,out] str the string which should be trimmed.
130 * @param charlist the list of characters to remove from beginning and end of string
131 * @return the result string.
132 */
6ab3bc95
RP
133std::string trim_mod(std::string& str, const std::string& charlist)
134{
135 // first: trim the beginning:
136 std::string::size_type pos= str.find_first_not_of (charlist);
137 if (pos == std::string::npos)
138 {
139 // whole string consists of charlist (or is already empty)
140 str.clear();
141 return str;
142 }
143 else if (pos>0)
144 {
145 // str starts with charlist
146 str.erase(0,pos);
147 }
148 // now let's look at the tail:
149 pos= str.find_last_not_of(charlist) +1; // note: we already know there is at least one other char!
150 if ( pos < str.size() )
151 {
152 str.erase(pos, str.size()-pos);
153 }
154 return str;
155} // eo trim_mod(std::string&,const std::string&)
6a93d84a
TJ
156
157
158
159/**
160 * removes last character from a string when it is in a list of chars to be removed.
161 * @param[in,out] str the string.
162 * @param what the list of chars which will be tested for.
163 * @return the resulting string with last char removed (if applicable)
164 */
6ab3bc95 165std::string chomp_mod(std::string& str, const std::string& what)
6a93d84a 166{
6ab3bc95
RP
167 if (str.empty() || what.empty() )
168 {
169 return str;
170 }
171 if (what.find(str.at (str.size()-1) ) != std::string::npos)
172 {
173 str.erase(str.size() - 1);
174 }
175 return str;
176} // eo chomp_mod(std::string&,const std::string&)
6a93d84a
TJ
177
178
179/**
180 * @brief converts a string to lower case.
181 * @param[in,out] str the string to modify.
182 * @return the string
183 */
6ab3bc95 184std::string to_lower_mod(std::string& str)
6a93d84a 185{
6ab3bc95
RP
186 std::transform(str.begin(), str.end(), str.begin(), LowerFunc() );
187 return str;
188} // eo to_lower_mod(std::string&)
6a93d84a
TJ
189
190
191/**
192 * @brief converts a string to upper case.
193 * @param[in,out] str the string to modify.
194 * @return the string
195 */
6ab3bc95 196std::string to_upper_mod(std::string& str)
6a93d84a 197{
6ab3bc95
RP
198 std::transform( str.begin(), str.end(), str.begin(), UpperFunc() );
199 return str;
200} // eo to_upper_mod(std::string&)
6a93d84a
TJ
201
202
203
204/**
205 * cut off characters from a given list from front and end of a string.
206 * @param str the string which should be trimmed.
207 * @param charlist the list of characters to remove from beginning and end of string
208 * @return the result string.
209 */
6ab3bc95
RP
210std::string trim (const std::string& str, const std::string& charlist)
211{
212 // first: trim the beginning:
213 std::string::size_type pos0= str.find_first_not_of(charlist);
214 if (pos0 == std::string::npos)
215 {
216 // whole string consists of charlist (or is already empty)
217 return std::string();
218 }
219 // now let's look at the end:
220 std::string::size_type pos1= str.find_last_not_of(charlist);
221 return str.substr(pos0, pos1 - pos0 + 1);
6a93d84a
TJ
222} // eo trim(const std:.string&,const std::string&)
223
224
225/**
226 * removes last character from a string when it is in a list of chars to be removed.
227 * @param str the string.
228 * @param what the list of chars which will be tested for.
229 * @return the resulting string with last char removed (if applicable)
230 */
6ab3bc95
RP
231std::string chomp (const std::string& str, const std::string& what)
232{
233 if (str.empty() || what.empty() )
234 {
235 return str;
236 }
237 if (what.find(str.at (str.size()-1) ) != std::string::npos)
238 {
239 return str.substr(0, str.size()-1);
240 }
241 return str;
6a93d84a
TJ
242} // eo chomp(const std:.string&,const std::string&)
243
244
245/**
246 * @brief returns a lower case version of a given string.
247 * @param str the string
248 * @return the lower case version of the string
249 */
6ab3bc95 250std::string to_lower (const std::string& str)
6a93d84a 251{
6ab3bc95
RP
252 std::string result(str);
253 return to_lower_mod(result);
254} // eo to_lower(const std::string&)
6a93d84a
TJ
255
256
257/**
258 * @brief returns a upper case version of a given string.
259 * @param str the string
260 * @return the upper case version of the string
261 */
6ab3bc95 262std::string to_upper(const std::string& str)
6a93d84a 263{
6ab3bc95
RP
264 std::string result(str);
265 return to_upper_mod(result);
266} // eo to_upper(const std::string&)
6a93d84a
TJ
267
268
269
270/**
271 * @brief removes a given suffix from a string.
272 * @param str the string.
273 * @param suffix the suffix which should be removed if the string ends with it.
274 * @return the string without the suffix.
275 *
276 * If the string ends with the suffix, it is removed. If the the string doesn't end
277 * with the suffix the original string is returned.
278 */
6ab3bc95 279std::string remove_suffix(const std::string& str, const std::string& suffix)
6a93d84a 280{
6ab3bc95
RP
281 if (has_suffix(str,suffix) )
282 {
283 return str.substr(0, str.size()-suffix.size() );
284 }
285 return str;
286} // eo remove_suffix(const std::string&,const std::string&)
6a93d84a
TJ
287
288
289
290/**
291 * @brief removes a given prefix from a string.
292 * @param str the string.
293 * @param prefix the prefix which should be removed if the string begins with it.
294 * @return the string without the prefix.
295 *
296 * If the string begins with the prefix, it is removed. If the the string doesn't begin
297 * with the prefix the original string is returned.
298 */
6ab3bc95 299std::string remove_prefix(const std::string& str, const std::string& prefix)
6a93d84a 300{
6ab3bc95
RP
301 if (has_prefix(str,prefix) )
302 {
303 return str.substr( prefix.size() );
304 }
305 return str;
306} // eo remove_prefix(const std::string&,const std::string&)
6a93d84a
TJ
307
308
309/**
310 * split a string to key and value delimited by a given delimiter.
6ab3bc95 311 * The resulting key and value strings are trimmed (Whitespaces removed at beginning and end).
6a93d84a
TJ
312 * @param str the string which should be splitted.
313 * @param[out] key the resulting key
314 * @param[out] value the resulting value
315 * @param delimiter the delimiter between key and value; default is '='.
316 * @return @a true if the split was successful.
317 */
6ab3bc95
RP
318bool pair_split(
319 const std::string& str,
320 std::string& key,
321 std::string& value,
322 char delimiter)
323{
324 std::string::size_type pos = str.find (delimiter);
325 if (pos == std::string::npos) return false;
326 key= str.substr(0,pos);
327 value= str.substr(pos+1);
328 trim_mod(key);
329 trim_mod(value);
330 return true;
331} // eo pair_split(const std::string&,std::string&,std::string&,char)
6a93d84a
TJ
332
333
334/**
335 * splits a string by given delimiter
336 *
337 * @param[in] str the string which should be splitted.
338 * @param[out] result the list resulting from splitting @a str.
339 * @param[in] delimiter the delimiter (word/phrase) at which @a str should be splitted.
340 * @param[in] omit_empty should empty parts not be stored?
341 * @param[in] trim_list list of characters the parts should be trimmed by.
342 * (empty string results in no trim)
343 */
6ab3bc95
RP
344void split_string(
345 const std::string& str,
346 std::list<std::string>& result,
347 const std::string& delimiter,
348 bool omit_empty,
349 const std::string& trim_list
6a93d84a
TJ
350)
351{
6ab3bc95
RP
352 std::string::size_type pos, last_pos=0;
353 bool delimiter_found= false;
354 while ( last_pos < str.size() && last_pos != std::string::npos)
355 {
356 pos= str.find(delimiter, last_pos);
357 std::string part;
358 if (pos == std::string::npos)
359 {
360 part= str.substr(last_pos);
361 delimiter_found= false;
362 }
363 else
364 {
365 part= str.substr(last_pos, pos-last_pos);
366 delimiter_found=true;
367 }
368 if (pos != std::string::npos)
369 {
370 last_pos= pos+ delimiter.size();
371 }
372 else
373 {
374 last_pos= std::string::npos;
375 }
376 if (!trim_list.empty() ) trim_mod (part, trim_list);
377 if (omit_empty && part.empty() ) continue;
378 result.push_back( part );
379 }
380 // if the string ends with a delimiter we need to append an empty string if no omit_empty
381 // was given.
382 // (this way we keep the split result consistent to a join operation)
383 if (delimiter_found && !omit_empty)
384 {
385 result.push_back("");
386 }
387} // eo split_string(const std::string&,std::list< std::string >&,const std::string&,bool,const std::string&)
6a93d84a
TJ
388
389
338da253
CH
390/** call split_string with list<string>, converts result to vector; vector is clear()-ed first
391 *
392 * Note: Uses 3 O(n)-operations: list.size, vector.resize and std::swap_ranges;
393 * not sure whether there is a better way to do this
394 * */
395void split_string(
396 const std::string& str,
397 std::vector<std::string>& result,
398 const std::string& delimiter,
399 bool omit_empty,
400 const std::string& trim_list
401)
402{
403 std::list<std::string> tmp;
404 split_string(str, tmp, delimiter, omit_empty, trim_list);
405 std::size_t size = tmp.size(); // this is O(n)
406 result.clear();
407 result.resize(size); // also O(n)
408 std::swap_ranges(tmp.begin(), tmp.end(), result.begin()); // also O(n)
409}
410
6a93d84a
TJ
411/**
412 * splits a string by a given delimiter
413 * @param str the string which should be splitted.
414 * @param delimiter delimiter the delimiter (word/phrase) at which @a str should be splitted.
415 * @param[in] omit_empty should empty parts not be stored?
416 * @param[in] trim_list list of characters the parts should be trimmed by.
417 * (empty string results in no trim)
418 * @return the list resulting from splitting @a str.
419 */
6ab3bc95
RP
420std::list<std::string> split_string(
421 const std::string& str,
422 const std::string& delimiter,
423 bool omit_empty,
424 const std::string& trim_list
6a93d84a
TJ
425)
426{
6ab3bc95
RP
427 std::list<std::string> result;
428 split_string(str, result, delimiter, omit_empty, trim_list);
429 return result;
430} // eo split_string(const std::string&,const std::string&,bool,const std::string&)
6a93d84a
TJ
431
432
4f7a7b9f
PG
433std::string join_string (
434 const char *const parts[], /* assumed NULL-terminated */
435 const std::string& delimiter
436)
437{
438 std::string result;
439
440 if (parts != NULL)
441 {
442 const char *const *cur = parts;
443
444 if (*cur != NULL) {
445 result = std::string (*cur);
446
447 while (*++cur != NULL) {
448 result += delimiter;
449 result += std::string (*cur);
450 }
451 }
452 }
453
454 return result;
455}
456
376ec4fa 457
6a93d84a
TJ
458
459/*
460** conversions
461*/
462
463
464/**
465 * @brief returns a hex string from a binary string.
466 * @param str the (binary) string
467 * @param upper_case_digits determine whether to use upper case characters for digits A-F.
468 * @return the string in hex notation.
469 */
6ab3bc95
RP
470std::string convert_binary_to_hex(
471 const std::string& str,
472 bool upper_case_digits
6a93d84a
TJ
473)
474{
6ab3bc95
RP
475 std::string result;
476 std::string hexDigits(upper_case_digits ? hexDigitsUpper : hexDigitsLower);
477 for ( std::string::const_iterator it= str.begin();
478 it != str.end();
479 ++it)
480 {
481 result.push_back( hexDigits[ ( (*it) >> 4) & 0x0f ] );
482 result.push_back( hexDigits[ (*it) & 0x0f ] );
483 }
484 return result;
485} // eo convert_binary_to_hex(const std::string&,bool)
6a93d84a
TJ
486
487
488/**
489 * @brief converts a hex digit string to binary string.
490 * @param str hex digit string
491 * @return the binary string.
492 *
493 * The hex digit string may contains white spaces or colons which are treated
494 * as delimiters between hex digit groups.
495 *
496 * @todo rework the handling of half nibbles (consistency)!
497 */
6ab3bc95
RP
498std::string convert_hex_to_binary(
499 const std::string& str
6a93d84a 500)
6ab3bc95
RP
501throw (std::runtime_error)
502{
503 std::string result;
504 char c= 0;
505 bool hasNibble= false;
506 bool lastWasWS= true;
507 for ( std::string::const_iterator it= str.begin();
508 it != str.end();
509 ++it)
510 {
511 std::string::size_type p = hexDigitsLower.find( *it );
512 if (p== std::string::npos)
513 {
514 p= hexDigitsUpper.find( *it );
515 }
516 if (p == std::string::npos)
517 {
518 if ( ( Whitespaces.find( *it ) != std::string::npos) // is it a whitespace?
6a93d84a 519 or ( *it == ':') // or a colon?
6ab3bc95
RP
520 )
521 {
522 // we treat that as a valid delimiter:
523 if (hasNibble)
6a93d84a 524 {
6ab3bc95
RP
525 // 1 nibble before WS is treate as lower part:
526 result.push_back(c);
527 // reset state:
528 hasNibble= false;
6a93d84a 529 }
6ab3bc95
RP
530 lastWasWS= true;
531 continue;
532 }
533 }
534 if (p == std::string::npos )
535 {
536 throw runtime_error("illegal character in hex digit string: " + str);
537 }
538 lastWasWS= false;
539 if (hasNibble)
540 {
541 c<<=4;
542 }
543 else
544 {
545 c=0;
546 }
547 c+= (p & 0x0f);
548 if (hasNibble)
549 {
550 //we already had a nibble, so a char is complete now:
551 result.push_back( c );
552 hasNibble=false;
553 }
554 else
555 {
556 // this is the first nibble of a new char:
557 hasNibble=true;
558 }
559 }
560 if (hasNibble)
561 {
562 //well, there is one nibble left
563 // let's do some heuristics:
564 if (lastWasWS)
565 {
566 // if the preceeding character was a white space (or a colon)
567 // we treat the nibble as lower part:
568 //( this is consistent with shortened hex notations where leading zeros are not noted)
569 result.push_back( c );
570 }
571 else
572 {
573 // if it was part of a hex digit chain, we treat it as UPPER part (!!)
574 result.push_back( c << 4 );
575 }
576 }
577 return result;
578} // eo convert_hex_to_binary(const std::string&)
579
580
1a0267e5
CH
581static list<string>& alloc_template_starts()
582{
583 static list<string> result;
584 if (result.empty())
585 {
586 result.push_back("std::list");
587 result.push_back("std::vector");
588 }
589 return result;
590}
591
592string shorten_stl_types(const string &input)
593{
594 string output = input;
595
596 // first: replace fixed string for std::string
597 replace_all(output, "std::basic_string<char, std::char_traits<char>, std::allocator<char> >",
598 "std::string");
599
600 // loop over list/vector/... that have an allocator, e.g.
601 // std::list< some_type_here, std::allocator<some_type_here> >
602 string::size_type start, comma, end, len, start_text_len;
603 int n_open_brackets;
604 string allocator_text;
605 BOOST_FOREACH(const string &start_text, alloc_template_starts())
606 {
607 start = 0;
608 comma = 0;
609 end = 0;
610 start_text_len = start_text.length();
611 while( (start=output.find(start_text+"<", start)) != string::npos )
612 {
613 len = output.length();
614 start += start_text_len+1; // start next iter and tests here after opening bracket
615
616 // now comes the tricky part: find matching ',' and the closing '>' even if "subtype" is template again
617 comma = start;
618 n_open_brackets = 1; // the bracket right after start_text counts as first
619 while (comma < len && n_open_brackets > 0)
620 {
621 if (output[comma] == ',' && n_open_brackets == 1)
622 break;
623 else if (output[comma] == '<')
624 ++n_open_brackets;
625 else if (output[comma] == '>')
626 --n_open_brackets;
627 ++comma;
628 }
629 end = comma+1;
630 while (end < len && n_open_brackets > 0)
631 {
632 if (output[end] == '<')
633 ++n_open_brackets;
634 else if (output[end] == '>')
635 {
636 --n_open_brackets;
637 if (n_open_brackets == 0)
638 break; // do not increment end
639 }
640 ++end;
641 }
642
643 // check that start < comma < end < len && n_open_brackets == 0
644 if (start >= comma || comma >= end || end >= len || n_open_brackets != 0)
645 continue; // input seems to be of unexpected form
646
647 // check that type in allocator is same as until comma
648 string type = output.substr(start, comma-start);
649 if (type[type.length()-1] == '>')
650 allocator_text = string("std::allocator<") + type + " > ";
651 else
652 allocator_text = string("std::allocator<") + type + "> ";
653 if (output.substr(comma+2, end-comma-2) == allocator_text)
654 output.replace(comma+2, end-comma-2, "_alloc_");
655 }
656 }
657
658 return output;
659}
660
2bb72337
TJ
661typedef boost::shared_ptr<BIO> BIO_Ptr;
662
663/**
664* @brief Converts openssl generic input/output to std::string
665*
666* Code adapted from keymakerd.
667*
668* @param bio Openssl's generic input/output
669* @return :string STL string
670**/
671static std::string _convert_BIO_to_string(BIO *input)
672{
673 std::string rtn;
674
675 char *output = NULL;
676 long written = BIO_get_mem_data(input, &output);
677 if (written <= 0 || output == NULL)
678 return rtn;
679
680 rtn.assign(output, written); //lint !e534 !e732
681 return rtn;
682} //lint !e1764
683
684/**
685 * @brief base64 encode a string using OpenSSL base64 functions
686 *
687 * Data size limit is 2GB on 32 bit (LONG_MAX)
688 *
689 * @param input String to encode
1ebab1e3 690 * @param one_line Encode all data as one line, no wrapping with line feeds
2bb72337
TJ
691 * @return base64 encoded string
692 */
1ebab1e3 693std::string base64_encode(const std::string &input, bool one_line)
2bb72337
TJ
694{
695 // check for empty buffer
696 if (input.empty())
697 return input;
698
699 // safety check to ensure our check afer BIO_write() works
700 if (input.size() >= LONG_MAX)
701 throw runtime_error("base64 encode: Too much data");
702
703 // setup encoder. Note: BIO_free_all frees both BIOs.
704 BIO_Ptr base64_encoder(BIO_new(BIO_f_base64()), BIO_free_all);
705 BIO *encoder_bio = base64_encoder.get();
1ebab1e3
TJ
706 if (one_line)
707 BIO_set_flags(encoder_bio, BIO_FLAGS_BASE64_NO_NL);
2bb72337
TJ
708
709 // chain output buffer and encoder together
710 BIO *encoded_result = BIO_new(BIO_s_mem());
711 BIO_push(encoder_bio, encoded_result);
712
713 // encode
714 long written = BIO_write(encoder_bio, input.c_str(), input.size());
715 if ((unsigned)written != input.size())
716 {
717 ostringstream out;
718 out << "base64 encoding failed: input size: "
719 << input.size() << " vs. output size: " << written;
720 throw runtime_error(out.str());
721 }
722 if (BIO_flush(encoder_bio) != 1)
723 throw runtime_error("base64 encode: BIO_flush() failed");
724
725 return _convert_BIO_to_string(encoded_result);
726}
727
728/**
729 * @brief base64 decode a string using OpenSSL base64 functions
730 *
731 * @param input String to decode
1ebab1e3 732 * @param one_line Expect all base64 data in one line. Input with line feeds will fail.
2bb72337
TJ
733 * @return base64 decoded string
734 */
1ebab1e3 735std::string base64_decode(const std::string &input, bool one_line)
2bb72337
TJ
736{
737 // check for empty buffer
738 if (input.empty())
739 return input;
740
741 // safety check for BIO_new_mem_buf()
742 if (input.size() >= INT_MAX)
743 throw runtime_error("base64 decode: Too much data");
744
745 // setup encoder. Note: BIO_free_all frees both BIOs.
746 BIO_Ptr base64_decoder(BIO_new(BIO_f_base64()), BIO_free_all);
747 BIO *bio_base64 = base64_decoder.get();
1ebab1e3
TJ
748 if (one_line)
749 BIO_set_flags(bio_base64, BIO_FLAGS_BASE64_NO_NL);
2bb72337
TJ
750
751 // chain input buffer and decoder together
752 BIO *bio_input = BIO_new_mem_buf((void*)input.c_str(), input.size());
753 bio_input = BIO_push(bio_base64, bio_input);
754
755 BIO_Ptr decoded_result(BIO_new(BIO_s_mem()), BIO_free_all);
756 BIO *bio_decoded = decoded_result.get();
757 const int convbuf_size = 512;
758 char convbuf[convbuf_size];
759
760 long read_bytes = 0;
761 while((read_bytes = BIO_read(bio_input, convbuf, convbuf_size)) > 0)
762 {
763 BOOST_ASSERT(read_bytes <= convbuf_size);
764 long written_bytes = BIO_write(bio_decoded, convbuf, read_bytes);
765 if (written_bytes != read_bytes)
766 {
767 ostringstream out;
768 out << "base64 decoding failed: read_bytes: "
769 << read_bytes << " vs. written_bytes: " << written_bytes;
770 throw runtime_error(out.str());
771 }
772 }
773 if (read_bytes == -2 || read_bytes == -1)
774 throw runtime_error("base64 decode: Error during decoding");
775
776 return _convert_BIO_to_string(bio_decoded);
777}
778
6ab3bc95
RP
779} // eo namespace I2n
780
781
782
6a93d84a 783
e93545dd
GE
784std::string iso_to_utf8(const std::string& isostring)
785{
6ab3bc95 786 string result;
118e216e 787
6ab3bc95 788 iconv_t i2utf8 = iconv_open("UTF-8", "ISO-8859-1");
118e216e 789
6ab3bc95
RP
790 if (iso_to_utf8 == (iconv_t)-1)
791 throw runtime_error("iconv can't convert from ISO-8859-1 to UTF-8");
118e216e 792
6ab3bc95
RP
793 size_t in_size=isostring.size();
794 size_t out_size=in_size*4;
118e216e 795
6ab3bc95
RP
796 char *buf = (char *)malloc(out_size+1);
797 if (buf == NULL)
858cd10b
PG
798 {
799 iconv_close(i2utf8);
6ab3bc95 800 throw runtime_error("out of memory for iconv buffer");
858cd10b 801 }
e93545dd 802
5a4ecb51 803 char *in = (char *)isostring.c_str();
6ab3bc95
RP
804 char *out = buf;
805 iconv(i2utf8, &in, &in_size, &out, &out_size);
118e216e 806
6ab3bc95 807 buf[isostring.size()*4-out_size]=0;
118e216e 808
6ab3bc95 809 result=buf;
118e216e 810
6ab3bc95
RP
811 free(buf);
812 iconv_close(i2utf8);
118e216e 813
6ab3bc95 814 return result;
e93545dd
GE
815}
816
817std::string utf8_to_iso(const std::string& utf8string)
818{
6ab3bc95 819 string result;
118e216e 820
6ab3bc95 821 iconv_t utf82iso = iconv_open("ISO-8859-1","UTF-8");
118e216e 822
6ab3bc95
RP
823 if (utf82iso == (iconv_t)-1)
824 throw runtime_error("iconv can't convert from UTF-8 to ISO-8859-1");
118e216e 825
6ab3bc95
RP
826 size_t in_size=utf8string.size();
827 size_t out_size=in_size;
118e216e 828
6ab3bc95
RP
829 char *buf = (char *)malloc(out_size+1);
830 if (buf == NULL)
858cd10b
PG
831 {
832 iconv_close(utf82iso);
6ab3bc95 833 throw runtime_error("out of memory for iconv buffer");
858cd10b 834 }
e93545dd 835
5a4ecb51 836 char *in = (char *)utf8string.c_str();
6ab3bc95
RP
837 char *out = buf;
838 iconv(utf82iso, &in, &in_size, &out, &out_size);
118e216e 839
6ab3bc95 840 buf[utf8string.size()-out_size]=0;
118e216e 841
6ab3bc95 842 result=buf;
118e216e 843
6ab3bc95
RP
844 free(buf);
845 iconv_close(utf82iso);
e93545dd 846
6ab3bc95 847 return result;
e93545dd
GE
848}
849
a5f3af6e
GE
850wchar_t* utf8_to_wbuf(const std::string& utf8string)
851{
6ab3bc95 852 iconv_t utf82wstr = iconv_open("UCS-4LE","UTF-8");
a5f3af6e 853
6ab3bc95
RP
854 if (utf82wstr == (iconv_t)-1)
855 throw runtime_error("iconv can't convert from UTF-8 to UCS-4");
a5f3af6e 856
6ab3bc95
RP
857 size_t in_size=utf8string.size();
858 size_t out_size= (in_size+1)*sizeof(wchar_t);
a5f3af6e 859
6ab3bc95
RP
860 wchar_t *buf = (wchar_t *)malloc(out_size);
861 if (buf == NULL)
858cd10b
PG
862 {
863 iconv_close(utf82wstr);
6ab3bc95 864 throw runtime_error("out of memory for iconv buffer");
858cd10b 865 }
a5f3af6e 866
5a4ecb51 867 char *in = (char *)utf8string.c_str();
6ab3bc95 868 char *out = (char*) buf;
dbd6d77c 869 if (iconv(utf82wstr, &in, &in_size, &out, &out_size) == (size_t)-1)
6ab3bc95 870 throw runtime_error("error converting char encodings");
a5f3af6e 871
6ab3bc95 872 buf[ ( (utf8string.size()+1)*sizeof(wchar_t)-out_size) /sizeof(wchar_t) ]=0;
a5f3af6e 873
6ab3bc95 874 iconv_close(utf82wstr);
a5f3af6e 875
6ab3bc95 876 return buf;
a5f3af6e
GE
877}
878
13cc4db1 879std::string utf7imap_to_utf8(const std::string& utf7imapstring)
d116a071 880{
6ab3bc95 881 string result;
118e216e 882
6ab3bc95 883 iconv_t utf7imap2utf8 = iconv_open("UTF-8","UTF-7-IMAP");
118e216e 884
6ab3bc95
RP
885 if (utf7imap2utf8 == (iconv_t)-1)
886 throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
118e216e 887
6ab3bc95
RP
888 size_t in_size=utf7imapstring.size();
889 size_t out_size=in_size*4;
118e216e 890
6ab3bc95
RP
891 char *buf = (char *)malloc(out_size+1);
892 if (buf == NULL)
858cd10b
PG
893 {
894 iconv_close(utf7imap2utf8);
6ab3bc95 895 throw runtime_error("out of memory for iconv buffer");
858cd10b 896 }
d116a071 897
5a4ecb51 898 char *in = (char *)utf7imapstring.c_str();
6ab3bc95
RP
899 char *out = buf;
900 iconv(utf7imap2utf8, &in, &in_size, &out, &out_size);
118e216e 901
6ab3bc95 902 buf[utf7imapstring.size()*4-out_size]=0;
118e216e 903
6ab3bc95 904 result=buf;
118e216e 905
6ab3bc95
RP
906 free(buf);
907 iconv_close(utf7imap2utf8);
118e216e 908
6ab3bc95 909 return result;
118e216e
TJ
910}
911
6a2b6dd1
TJ
912std::string utf8_to_utf7imap(const std::string& utf8string)
913{
6ab3bc95 914 string result;
6a2b6dd1 915
6ab3bc95 916 iconv_t utf82utf7imap = iconv_open("UTF-7-IMAP", "UTF-8");
6a2b6dd1 917
6ab3bc95
RP
918 if (utf82utf7imap == (iconv_t)-1)
919 throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
6a2b6dd1 920
6ab3bc95
RP
921 // UTF-7 is base64 encoded, a buffer 10x as large
922 // as the utf-8 buffer should be enough. If not the string will be truncated.
923 size_t in_size=utf8string.size();
924 size_t out_size=in_size*10;
6a2b6dd1 925
6ab3bc95
RP
926 char *buf = (char *)malloc(out_size+1);
927 if (buf == NULL)
858cd10b
PG
928 {
929 iconv_close(utf82utf7imap);
6ab3bc95 930 throw runtime_error("out of memory for iconv buffer");
858cd10b 931 }
6a2b6dd1 932
5a4ecb51 933 char *in = (char *)utf8string.c_str();
6ab3bc95
RP
934 char *out = buf;
935 iconv(utf82utf7imap, &in, &in_size, &out, &out_size);
6a2b6dd1 936
6ab3bc95 937 buf[utf8string.size()*10-out_size]= 0;
6a2b6dd1 938
6ab3bc95 939 result=buf;
6a2b6dd1 940
6ab3bc95
RP
941 free(buf);
942 iconv_close(utf82utf7imap);
6a2b6dd1 943
6ab3bc95 944 return result;
6a2b6dd1
TJ
945}
946
118e216e
TJ
947// Tokenize string by (html) tags
948void tokenize_by_tag(vector<pair<string,bool> > &tokenized, const std::string &input)
949{
6ab3bc95
RP
950 string::size_type pos, len = input.size();
951 bool inside_tag = false;
952 string current;
953
954 for (pos = 0; pos < len; pos++)
955 {
956 if (input[pos] == '<')
957 {
958 inside_tag = true;
959
960 if (!current.empty() )
961 {
962 tokenized.push_back( make_pair(current, false) );
963 current = "";
964 }
965
966 current += input[pos];
967 }
968 else if (input[pos] == '>' && inside_tag)
969 {
970 current += input[pos];
971 inside_tag = false;
972 if (!current.empty() )
973 {
974 tokenized.push_back( make_pair(current, true) );
975 current = "";
976 }
977 }
978 else
979 current += input[pos];
980 }
981
982 // String left over in buffer?
983 if (!current.empty() )
984 tokenized.push_back( make_pair(current, false) );
985} // eo tokenize_by_tag
118e216e 986
118e216e
TJ
987
988std::string strip_html_tags(const std::string &input)
989{
6ab3bc95
RP
990 // Pair first: string, second: isTag
991 vector<pair<string,bool> > tokenized;
992 tokenize_by_tag (tokenized, input);
118e216e 993
6ab3bc95
RP
994 string output;
995 vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
83d700e9 996 for (token = tokenized.begin(); token != tokens_end; ++token)
6ab3bc95
RP
997 if (!token->second)
998 output += token->first;
999
1000 return output;
1001} // eo strip_html_tags
118e216e 1002
118e216e
TJ
1003
1004// Smart-encode HTML en
1005string smart_html_entities(const std::string &input)
1006{
6ab3bc95
RP
1007 // Pair first: string, second: isTag
1008 vector<pair<string,bool> > tokenized;
1009 tokenize_by_tag (tokenized, input);
1010
1011 string output;
1012 vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
83d700e9 1013 for (token = tokenized.begin(); token != tokens_end; ++token)
6ab3bc95
RP
1014 {
1015 // keep HTML tags as they are
1016 if (token->second)
1017 output += token->first;
1018 else
1019 output += html_entities(token->first);
1020 }
1021
1022 return output;
118e216e
TJ
1023}
1024
6ab3bc95 1025
a5f3af6e
GE
1026string::size_type find_8bit(const std::string &str)
1027{
6ab3bc95
RP
1028 string::size_type l=str.size();
1029 for (string::size_type p=0; p < l; p++)
1030 if (static_cast<unsigned char>(str[p]) > 127)
1031 return p;
a5f3af6e 1032
6ab3bc95 1033 return string::npos;
a5f3af6e
GE
1034}
1035
118e216e
TJ
1036// encoded UTF-8 chars into HTML entities
1037string html_entities(std::string str)
1038{
6ab3bc95
RP
1039 // Normal chars
1040 replace_all (str, "&", "&amp;");
6ab3bc95
RP
1041 replace_all (str, "<", "&lt;");
1042 replace_all (str, ">", "&gt;");
980577e1
TJ
1043 replace_all (str, "\"", "&quot;");
1044 replace_all (str, "'", "&#x27;");
1045 replace_all (str, "/", "&#x2F;");
6ab3bc95
RP
1046
1047 // Umlauts
1048 replace_all (str, "\xC3\xA4", "&auml;");
1049 replace_all (str, "\xC3\xB6", "&ouml;");
1050 replace_all (str, "\xC3\xBC", "&uuml;");
1051 replace_all (str, "\xC3\x84", "&Auml;");
1052 replace_all (str, "\xC3\x96", "&Ouml;");
1053 replace_all (str, "\xC3\x9C", "&Uuml;");
1054
1055 // Misc
1056 replace_all (str, "\xC3\x9F", "&szlig;");
1057
1058 // conversion of remaining non-ASCII chars needed?
1059 // just do if needed because of performance
1060 if (find_8bit(str) != string::npos)
1061 {
1062 // convert to fixed-size encoding UTF-32
1063 wchar_t* wbuf=utf8_to_wbuf(str);
1064 ostringstream target;
1065
1066 // replace all non-ASCII chars with HTML representation
1067 for (int p=0; wbuf[p] != 0; p++)
1068 {
1069 unsigned int c=wbuf[p];
1070
1071 if (c <= 127)
1072 target << static_cast<unsigned char>(c);
1073 else
1074 target << "&#" << c << ';';
1075 }
1076
1077 free(wbuf);
1078
1079 str=target.str();
1080 }
1081
1082 return str;
1083} // eo html_entities(std::string)
1084
554f813d
GE
1085// convert HTML entities to something that can be viewed on a basic text console (restricted to ASCII-7)
1086string html_entities_to_console(std::string str)
1087{
1088 // Normal chars
1089 replace_all (str, "&amp;", "&");
1090 replace_all (str, "&lt;", "<");
1091 replace_all (str, "&gt;", ">");
1092 replace_all (str, "&quot;", "\"");
1093 replace_all (str, "&#x27;", "'");
1094 replace_all (str, "&#x2F;", "/");
1095
1096 // Umlauts
1097 replace_all (str, "&auml;", "ae");
1098 replace_all (str, "&ouml;", "oe");
1099 replace_all (str, "&uuml;", "ue");
1100 replace_all (str, "&Auml;", "Ae");
1101 replace_all (str, "&Ouml;", "Oe");
1102 replace_all (str, "&Uuml;", "Ue");
1103
1104 // Misc
1105 replace_all (str, "&szlig;", "ss");
1106
1107 return str;
1108}
118e216e 1109
3f5c5ccd
CH
1110// find_html_comments + remove_html_comments(str, comments)
1111void remove_html_comments(string &str)
1112{
46dd1321 1113 vector<CommentZone> comments = find_html_comments(str);
3f5c5ccd
CH
1114 remove_html_comments(str, comments);
1115}
1116
1117// find all html comments, behaving correctly if they are nested; ignores comment tags ("<!--FOO .... BAR-->")
1118// If there are invalid comments ("-->" before "<!--" or different number of closing and opening tags),
1119// then the unknown index of corresponding start/end tag will be represented by a string::npos
1120// Indices are from start of start tag until first index after closing tag
46dd1321 1121vector<CommentZone> find_html_comments(const std::string &str)
3f5c5ccd
CH
1122{
1123 static const string START = "<!--";
1124 static const string CLOSE = "-->";
1125 static const string::size_type START_LEN = START.length();
1126 static const string::size_type CLOSE_LEN = CLOSE.length();
1127
46dd1321
TJ
1128 vector<CommentZone> comments;
1129
3f5c5ccd
CH
1130 // in order to find nested comments, need either recursion or a stack
1131 vector<string::size_type> starts; // stack of start tags
1132
1133 string::size_type pos = 0;
1134 string::size_type len = str.length();
1135 string::size_type next_start, next_close;
1136
1137 while (pos < len) // not really needed but just in case
1138 {
1139 next_start = str.find(START, pos);
1140 next_close = str.find(CLOSE, pos);
1141
1142 if ( (next_start == string::npos) && (next_close == string::npos) )
1143 break; // we are done
1144
1145 else if ( (next_start == string::npos) || (next_close < next_start) ) // close one comment (pop)
1146 {
1147 if (starts.empty()) // closing tag without a start
1148 comments.push_back(CommentZone(string::npos, next_close+CLOSE_LEN));
1149 else
1150 {
1151 comments.push_back(CommentZone(starts.back(), next_close+CLOSE_LEN));
1152 starts.pop_back();
1153 }
1154 pos = next_close + CLOSE_LEN;
1155 }
1156
1157 else if ( (next_close == string::npos) || (next_start < next_close) ) // start a new comment (push)
1158 {
1159 starts.push_back(next_start);
1160 pos = next_start + START_LEN;
1161 }
1162 }
1163
1164 // add comments that have no closing tag from back to front (important for remove_html_comments!)
1165 while (!starts.empty())
1166 {
1167 comments.push_back(CommentZone(starts.back(), string::npos));
1168 starts.pop_back();
1169 }
46dd1321
TJ
1170
1171 return comments;
3f5c5ccd
CH
1172}
1173
1174// remove all html comments foundby find_html_comments
1175void remove_html_comments(std::string &str, const vector<CommentZone> &comments)
1176{
1177 // remember position where last removal started
1178 string::size_type last_removal_start = str.length();
1179
1180 // Go from back to front to not mess up indices.
1181 // This requires that bigger comments, that contain smaller comments, come AFTER
1182 // the small contained comments in the comments vector (i.e. comments are ordered by
1183 // their closing tag, not their opening tag). This is true for results from find_html_comments
1184 BOOST_REVERSE_FOREACH(const CommentZone &comment, comments)
1185 {
1186 if (comment.first == string::npos)
1187 {
1188 str = str.replace(0, comment.second, ""); // comment starts "before" str --> delete from start
1189 break; // there can be no more
1190 }
1191 else if (comment.first >= last_removal_start)
1192 {
1193 continue; // this comment is inside another comment that we have removed already
1194 }
1195 else if (comment.second == string::npos) // comment ends "after" str --> delete until end
1196 {
1197 str = str.replace(comment.first, string::npos, "");
1198 last_removal_start = comment.first;
1199 }
1200 else
1201 {
1202 str = str.replace(comment.first, comment.second-comment.first, "");
1203 last_removal_start = comment.first;
1204 }
1205 }
1206}
1207
e93545dd
GE
1208bool replace_all(string &base, const char *ist, const char *soll)
1209{
6ab3bc95
RP
1210 string i=ist;
1211 string s=soll;
1212 return replace_all(base,&i,&s);
e93545dd
GE
1213}
1214
1215bool replace_all(string &base, const string &ist, const char *soll)
1216{
6ab3bc95
RP
1217 string s=soll;
1218 return replace_all(base,&ist,&s);
e93545dd
GE
1219}
1220
1221bool replace_all(string &base, const string *ist, const string *soll)
1222{
6ab3bc95 1223 return replace_all(base,*ist,*soll);
e93545dd
GE
1224}
1225
1226bool replace_all(string &base, const char *ist, const string *soll)
1227{
6ab3bc95
RP
1228 string i=ist;
1229 return replace_all(base,&i,soll);
e93545dd
GE
1230}
1231
1232bool replace_all(string &base, const string &ist, const string &soll)
1233{
6ab3bc95
RP
1234 bool found_ist = false;
1235 string::size_type a=0;
1236
1237 if (ist.empty() )
1238 throw runtime_error ("replace_all called with empty search string");
e93545dd 1239
6ab3bc95
RP
1240 while ( (a=base.find(ist,a) ) != string::npos)
1241 {
1242 base.replace(a,ist.size(),soll);
1243 a=a+soll.size();
1244 found_ist = true;
1245 }
1ec2064e 1246
6ab3bc95 1247 return found_ist;
e93545dd
GE
1248}
1249
b953bf36
GE
1250/**
1251 * @brief replaces all characters that could be problematic or impose a security risk when being logged
1252 * @param str the original string
1253 * @param replace_with the character to replace the unsafe chars with
1254 * @return a string that is safe to send to syslog or other logfiles
1255 *
1256 * All chars between 0x20 (space) and 0x7E (~) (including) are considered safe for logging.
1257 * See e.g. RFC 5424, section 8.2 or the posix character class "printable".
1258 * This eliminates all possible problems with NUL, control characters, 8 bit chars, UTF8.
1259 *
1260 */
1261std::string sanitize_for_logging(const std::string &str, const char replace_with)
1262{
1263 std::string output=str;
1264
c0e32d64
GE
1265 const string::size_type len = output.size();
1266 for (std::string::size_type p=0; p < len; p++)
b953bf36
GE
1267 if (output[p] < 0x20 || output[p] > 0x7E)
1268 output[p]=replace_with;
1269
1270 return output;
1271}
1272
e5b21dbb 1273#if 0
e93545dd
GE
1274string to_lower(const string &src)
1275{
6ab3bc95 1276 string dst = src;
e93545dd 1277
6ab3bc95
RP
1278 string::size_type pos, end = dst.size();
1279 for (pos = 0; pos < end; pos++)
1280 dst[pos] = tolower(dst[pos]);
e93545dd 1281
6ab3bc95 1282 return dst;
e93545dd
GE
1283}
1284
1285string to_upper(const string &src)
1286{
6ab3bc95 1287 string dst = src;
e93545dd 1288
6ab3bc95
RP
1289 string::size_type pos, end = dst.size();
1290 for (pos = 0; pos < end; pos++)
1291 dst[pos] = toupper(dst[pos]);
e93545dd 1292
6ab3bc95 1293 return dst;
e93545dd 1294}
e5b21dbb 1295#endif
e93545dd 1296
83809f5e 1297const int MAX_UNIT_FORMAT_SYMBOLS = 6;
d1ea9075 1298
2cb9a9c5 1299const string shortUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
d1ea9075
GMF
1300 " B",
1301 " KB",
1302 " MB",
1303 " GB",
1304 " TB",
83809f5e 1305 " PB"
d1ea9075
GMF
1306};
1307
2cb9a9c5 1308const string longUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
5cb766b9
GMF
1309 i18n_noop(" Bytes"),
1310 i18n_noop(" KBytes"),
1311 i18n_noop(" MBytes"),
1312 i18n_noop(" GBytes"),
1313 i18n_noop(" TBytes"),
83809f5e 1314 i18n_noop(" PBytes")
d1ea9075
GMF
1315};
1316
72a94426 1317
e26f7d51 1318static long double rounding_upwards(
e91c1952 1319 const long double number,
72a94426
GMF
1320 const int rounding_multiplier
1321)
1322{
1323 long double rounded_number;
1324 rounded_number = number * rounding_multiplier;
1325 rounded_number += 0.5;
1326 rounded_number = (int64_t) (rounded_number);
1327 rounded_number = (long double) (rounded_number) / (long double) (rounding_multiplier);
1328
1329 return rounded_number;
1330}
1331
1332
81267544
GMF
1333string nice_unit_format(
1334 const int64_t input,
70fc0674
GMF
1335 const UnitFormat format,
1336 const UnitBase base
81267544 1337)
6ab3bc95 1338{
d1ea9075 1339 // select the system of units (decimal or binary)
81267544 1340 int multiple = 0;
a398513a 1341 if (base == UnitBase1000)
81267544
GMF
1342 {
1343 multiple = 1000;
1344 }
1345 else
1346 {
1347 multiple = 1024;
1348 }
1349
1350 long double size = input;
6ab3bc95 1351
d1ea9075
GMF
1352 // check the size of the input number to fit in the appropriate symbol
1353 int sizecount = 0;
81267544 1354 while (size > multiple)
6ab3bc95 1355 {
81267544
GMF
1356 size = size / multiple;
1357 sizecount++;
83809f5e
GMF
1358
1359 // rollback to the previous values and stop the loop when cannot
1360 // represent the number length.
1361 if (sizecount >= MAX_UNIT_FORMAT_SYMBOLS)
1362 {
1363 size = size * multiple;
1364 sizecount--;
1365 break;
1366 }
6ab3bc95
RP
1367 }
1368
a398513a
GMF
1369 // round the input number "half up" to multiples of 10
1370 const int rounding_multiplier = 10;
72a94426 1371 size = rounding_upwards(size, rounding_multiplier);
6ab3bc95 1372
d1ea9075 1373 // format the input number, placing the appropriate symbol
6ab3bc95 1374 ostringstream out;
6ab3bc95 1375 out.setf (ios::fixed);
a398513a 1376 if (format == ShortUnitFormat)
d1ea9075
GMF
1377 {
1378 out.precision(1);
68d37a5c 1379 out << size << i18n( shortUnitFormatSymbols[sizecount].c_str() );
d1ea9075
GMF
1380 }
1381 else
6ab3bc95 1382 {
d1ea9075 1383 out.precision (2);
68d37a5c 1384 out << size << i18n( longUnitFormatSymbols[sizecount].c_str() );
6ab3bc95
RP
1385 }
1386
1387 return out.str();
1388} // eo nice_unit_format(int input)
1389
e93545dd 1390
5cd64148
CH
1391string nice_unit_format(
1392 const double input,
1393 const UnitFormat format,
1394 const UnitBase base
1395)
1396{
1397 // round as double and cast to int64_t
1398 // cast raised overflow error near max val of int64_t (~9.2e18, see unittest)
1399 int64_t input_casted_and_rounded =
1400 boost::numeric_cast<int64_t>( round(input) );
1401
1402 // now call other
1403 return nice_unit_format( input_casted_and_rounded, format, base );
1404} // eo nice_unit_format(double input)
1405
1406
47c07fba
GE
1407string escape(const string &s)
1408{
6ab3bc95
RP
1409 string out(s);
1410 string::size_type p;
47c07fba 1411
6ab3bc95
RP
1412 p=0;
1413 while ( (p=out.find_first_of("\"\\",p) ) !=out.npos)
1414 {
1415 out.insert (p,"\\");
1416 p+=2;
1417 }
47c07fba 1418
6ab3bc95
RP
1419 p=0;
1420 while ( (p=out.find_first_of("\r",p) ) !=out.npos)
1421 {
1422 out.replace (p,1,"\\r");
1423 p+=2;
1424 }
47c07fba 1425
6ab3bc95
RP
1426 p=0;
1427 while ( (p=out.find_first_of("\n",p) ) !=out.npos)
1428 {
1429 out.replace (p,1,"\\n");
1430 p+=2;
1431 }
47c07fba 1432
6ab3bc95 1433 out='"'+out+'"';
47c07fba 1434
6ab3bc95
RP
1435 return out;
1436} // eo scape(const std::string&)
47c07fba 1437
47c07fba 1438
6ab3bc95
RP
1439string descape(const string &s, int startpos, int &endpos)
1440{
1441 string out;
1442
1443 if (s.at(startpos) != '"')
1444 throw out_of_range("value not type escaped string");
1445
1446 out=s.substr(startpos+1);
1447 string::size_type p=0;
1448
1449 // search for the end of the string
1450 while ( (p=out.find("\"",p) ) !=out.npos)
1451 {
1452 int e=p-1;
1453 bool escaped=false;
1454
1455 // the " might be escaped with a backslash
1456 while (e>=0 && out.at (e) =='\\')
1457 {
1458 if (escaped == false)
1459 escaped=true;
1460 else
1461 escaped=false;
1462
1463 e--;
1464 }
1465
1466 if (escaped==false)
1467 break;
1468 else
1469 p++;
1470 }
1471
1472 // we now have the end of the string
1473 out=out.substr(0,p);
1474
1475 // tell calling prog about the endposition
1476 endpos=startpos+p+1;
1477
1478 // descape all \ stuff inside the string now
1479 p=0;
1480 while ( (p=out.find_first_of("\\",p) ) !=out.npos)
1481 {
1482 switch (out.at(p+1) )
1483 {
1484 case 'r':
47c07fba
GE
1485 out.replace(p,2,"\r");
1486 break;
6ab3bc95 1487 case 'n':
47c07fba
GE
1488 out.replace(p,2,"\n");
1489 break;
6ab3bc95 1490 default:
47c07fba 1491 out.erase(p,1);
6ab3bc95
RP
1492 }
1493 p++;
1494 }
1495
1496 return out;
1497} // eo descape(const std::string&,int,int&)
47c07fba 1498
e93545dd 1499
47c07fba
GE
1500string escape_shellarg(const string &input)
1501{
6ab3bc95
RP
1502 string output = "'";
1503 string::const_iterator it, it_end = input.end();
83d700e9 1504 for (it = input.begin(); it != it_end; ++it)
6ab3bc95
RP
1505 {
1506 if ( (*it) == '\'')
1507 output += "'\\'";
1508
1509 output += *it;
1510 }
1511
1512 output += "'";
1513 return output;
47c07fba 1514}