base64 encoder/decoder: Add parameter to control linefeed handling
[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
433/**
434 * @brief joins a list of strings into a single string.
435 *
6ab3bc95
RP
436 * This funtion is (basically) the reverse operation of @a split_string.
437 *
6a93d84a
TJ
438 * @param parts the list of strings.
439 * @param delimiter the delimiter which is inserted between the strings.
440 * @return the joined string.
441 */
6ab3bc95
RP
442std::string join_string(
443 const std::list< std::string >& parts,
444 const std::string& delimiter
6a93d84a
TJ
445)
446{
6ab3bc95
RP
447 std::string result;
448 if (! parts.empty() )
449 {
450 std::list< std::string >::const_iterator it= parts.begin();
451 result = *it;
452 while ( ++it != parts.end() )
453 {
454 result+= delimiter;
455 result+= *it;
456 }
457 }
458 return result;
459} // eo join_string(const std::list< std::string >&,const std::string&)
6a93d84a
TJ
460
461
376ec4fa
CH
462/** @brief same as join_string for list, except uses a vector */
463std::string join_string(
464 const std::vector< std::string >& parts,
465 const std::string& delimiter
466)
467{
468 std::string result;
469 if (! parts.empty() )
470 {
471 std::vector< std::string >::const_iterator it= parts.begin();
472 result = *it;
473 while ( ++it != parts.end() )
474 {
475 result+= delimiter;
476 result+= *it;
477 }
478 }
479 return result;
480} // eo join_string(const std::vector< std::string >&,const std::string&)
481
482
6a93d84a
TJ
483
484/*
485** conversions
486*/
487
488
489/**
490 * @brief returns a hex string from a binary string.
491 * @param str the (binary) string
492 * @param upper_case_digits determine whether to use upper case characters for digits A-F.
493 * @return the string in hex notation.
494 */
6ab3bc95
RP
495std::string convert_binary_to_hex(
496 const std::string& str,
497 bool upper_case_digits
6a93d84a
TJ
498)
499{
6ab3bc95
RP
500 std::string result;
501 std::string hexDigits(upper_case_digits ? hexDigitsUpper : hexDigitsLower);
502 for ( std::string::const_iterator it= str.begin();
503 it != str.end();
504 ++it)
505 {
506 result.push_back( hexDigits[ ( (*it) >> 4) & 0x0f ] );
507 result.push_back( hexDigits[ (*it) & 0x0f ] );
508 }
509 return result;
510} // eo convert_binary_to_hex(const std::string&,bool)
6a93d84a
TJ
511
512
513/**
514 * @brief converts a hex digit string to binary string.
515 * @param str hex digit string
516 * @return the binary string.
517 *
518 * The hex digit string may contains white spaces or colons which are treated
519 * as delimiters between hex digit groups.
520 *
521 * @todo rework the handling of half nibbles (consistency)!
522 */
6ab3bc95
RP
523std::string convert_hex_to_binary(
524 const std::string& str
6a93d84a 525)
6ab3bc95
RP
526throw (std::runtime_error)
527{
528 std::string result;
529 char c= 0;
530 bool hasNibble= false;
531 bool lastWasWS= true;
532 for ( std::string::const_iterator it= str.begin();
533 it != str.end();
534 ++it)
535 {
536 std::string::size_type p = hexDigitsLower.find( *it );
537 if (p== std::string::npos)
538 {
539 p= hexDigitsUpper.find( *it );
540 }
541 if (p == std::string::npos)
542 {
543 if ( ( Whitespaces.find( *it ) != std::string::npos) // is it a whitespace?
6a93d84a 544 or ( *it == ':') // or a colon?
6ab3bc95
RP
545 )
546 {
547 // we treat that as a valid delimiter:
548 if (hasNibble)
6a93d84a 549 {
6ab3bc95
RP
550 // 1 nibble before WS is treate as lower part:
551 result.push_back(c);
552 // reset state:
553 hasNibble= false;
6a93d84a 554 }
6ab3bc95
RP
555 lastWasWS= true;
556 continue;
557 }
558 }
559 if (p == std::string::npos )
560 {
561 throw runtime_error("illegal character in hex digit string: " + str);
562 }
563 lastWasWS= false;
564 if (hasNibble)
565 {
566 c<<=4;
567 }
568 else
569 {
570 c=0;
571 }
572 c+= (p & 0x0f);
573 if (hasNibble)
574 {
575 //we already had a nibble, so a char is complete now:
576 result.push_back( c );
577 hasNibble=false;
578 }
579 else
580 {
581 // this is the first nibble of a new char:
582 hasNibble=true;
583 }
584 }
585 if (hasNibble)
586 {
587 //well, there is one nibble left
588 // let's do some heuristics:
589 if (lastWasWS)
590 {
591 // if the preceeding character was a white space (or a colon)
592 // we treat the nibble as lower part:
593 //( this is consistent with shortened hex notations where leading zeros are not noted)
594 result.push_back( c );
595 }
596 else
597 {
598 // if it was part of a hex digit chain, we treat it as UPPER part (!!)
599 result.push_back( c << 4 );
600 }
601 }
602 return result;
603} // eo convert_hex_to_binary(const std::string&)
604
605
1a0267e5
CH
606static list<string>& alloc_template_starts()
607{
608 static list<string> result;
609 if (result.empty())
610 {
611 result.push_back("std::list");
612 result.push_back("std::vector");
613 }
614 return result;
615}
616
617string shorten_stl_types(const string &input)
618{
619 string output = input;
620
621 // first: replace fixed string for std::string
622 replace_all(output, "std::basic_string<char, std::char_traits<char>, std::allocator<char> >",
623 "std::string");
624
625 // loop over list/vector/... that have an allocator, e.g.
626 // std::list< some_type_here, std::allocator<some_type_here> >
627 string::size_type start, comma, end, len, start_text_len;
628 int n_open_brackets;
629 string allocator_text;
630 BOOST_FOREACH(const string &start_text, alloc_template_starts())
631 {
632 start = 0;
633 comma = 0;
634 end = 0;
635 start_text_len = start_text.length();
636 while( (start=output.find(start_text+"<", start)) != string::npos )
637 {
638 len = output.length();
639 start += start_text_len+1; // start next iter and tests here after opening bracket
640
641 // now comes the tricky part: find matching ',' and the closing '>' even if "subtype" is template again
642 comma = start;
643 n_open_brackets = 1; // the bracket right after start_text counts as first
644 while (comma < len && n_open_brackets > 0)
645 {
646 if (output[comma] == ',' && n_open_brackets == 1)
647 break;
648 else if (output[comma] == '<')
649 ++n_open_brackets;
650 else if (output[comma] == '>')
651 --n_open_brackets;
652 ++comma;
653 }
654 end = comma+1;
655 while (end < len && n_open_brackets > 0)
656 {
657 if (output[end] == '<')
658 ++n_open_brackets;
659 else if (output[end] == '>')
660 {
661 --n_open_brackets;
662 if (n_open_brackets == 0)
663 break; // do not increment end
664 }
665 ++end;
666 }
667
668 // check that start < comma < end < len && n_open_brackets == 0
669 if (start >= comma || comma >= end || end >= len || n_open_brackets != 0)
670 continue; // input seems to be of unexpected form
671
672 // check that type in allocator is same as until comma
673 string type = output.substr(start, comma-start);
674 if (type[type.length()-1] == '>')
675 allocator_text = string("std::allocator<") + type + " > ";
676 else
677 allocator_text = string("std::allocator<") + type + "> ";
678 if (output.substr(comma+2, end-comma-2) == allocator_text)
679 output.replace(comma+2, end-comma-2, "_alloc_");
680 }
681 }
682
683 return output;
684}
685
2bb72337
TJ
686typedef boost::shared_ptr<BIO> BIO_Ptr;
687
688/**
689* @brief Converts openssl generic input/output to std::string
690*
691* Code adapted from keymakerd.
692*
693* @param bio Openssl's generic input/output
694* @return :string STL string
695**/
696static std::string _convert_BIO_to_string(BIO *input)
697{
698 std::string rtn;
699
700 char *output = NULL;
701 long written = BIO_get_mem_data(input, &output);
702 if (written <= 0 || output == NULL)
703 return rtn;
704
705 rtn.assign(output, written); //lint !e534 !e732
706 return rtn;
707} //lint !e1764
708
709/**
710 * @brief base64 encode a string using OpenSSL base64 functions
711 *
712 * Data size limit is 2GB on 32 bit (LONG_MAX)
713 *
714 * @param input String to encode
1ebab1e3 715 * @param one_line Encode all data as one line, no wrapping with line feeds
2bb72337
TJ
716 * @return base64 encoded string
717 */
1ebab1e3 718std::string base64_encode(const std::string &input, bool one_line)
2bb72337
TJ
719{
720 // check for empty buffer
721 if (input.empty())
722 return input;
723
724 // safety check to ensure our check afer BIO_write() works
725 if (input.size() >= LONG_MAX)
726 throw runtime_error("base64 encode: Too much data");
727
728 // setup encoder. Note: BIO_free_all frees both BIOs.
729 BIO_Ptr base64_encoder(BIO_new(BIO_f_base64()), BIO_free_all);
730 BIO *encoder_bio = base64_encoder.get();
1ebab1e3
TJ
731 if (one_line)
732 BIO_set_flags(encoder_bio, BIO_FLAGS_BASE64_NO_NL);
2bb72337
TJ
733
734 // chain output buffer and encoder together
735 BIO *encoded_result = BIO_new(BIO_s_mem());
736 BIO_push(encoder_bio, encoded_result);
737
738 // encode
739 long written = BIO_write(encoder_bio, input.c_str(), input.size());
740 if ((unsigned)written != input.size())
741 {
742 ostringstream out;
743 out << "base64 encoding failed: input size: "
744 << input.size() << " vs. output size: " << written;
745 throw runtime_error(out.str());
746 }
747 if (BIO_flush(encoder_bio) != 1)
748 throw runtime_error("base64 encode: BIO_flush() failed");
749
750 return _convert_BIO_to_string(encoded_result);
751}
752
753/**
754 * @brief base64 decode a string using OpenSSL base64 functions
755 *
756 * @param input String to decode
1ebab1e3 757 * @param one_line Expect all base64 data in one line. Input with line feeds will fail.
2bb72337
TJ
758 * @return base64 decoded string
759 */
1ebab1e3 760std::string base64_decode(const std::string &input, bool one_line)
2bb72337
TJ
761{
762 // check for empty buffer
763 if (input.empty())
764 return input;
765
766 // safety check for BIO_new_mem_buf()
767 if (input.size() >= INT_MAX)
768 throw runtime_error("base64 decode: Too much data");
769
770 // setup encoder. Note: BIO_free_all frees both BIOs.
771 BIO_Ptr base64_decoder(BIO_new(BIO_f_base64()), BIO_free_all);
772 BIO *bio_base64 = base64_decoder.get();
1ebab1e3
TJ
773 if (one_line)
774 BIO_set_flags(bio_base64, BIO_FLAGS_BASE64_NO_NL);
2bb72337
TJ
775
776 // chain input buffer and decoder together
777 BIO *bio_input = BIO_new_mem_buf((void*)input.c_str(), input.size());
778 bio_input = BIO_push(bio_base64, bio_input);
779
780 BIO_Ptr decoded_result(BIO_new(BIO_s_mem()), BIO_free_all);
781 BIO *bio_decoded = decoded_result.get();
782 const int convbuf_size = 512;
783 char convbuf[convbuf_size];
784
785 long read_bytes = 0;
786 while((read_bytes = BIO_read(bio_input, convbuf, convbuf_size)) > 0)
787 {
788 BOOST_ASSERT(read_bytes <= convbuf_size);
789 long written_bytes = BIO_write(bio_decoded, convbuf, read_bytes);
790 if (written_bytes != read_bytes)
791 {
792 ostringstream out;
793 out << "base64 decoding failed: read_bytes: "
794 << read_bytes << " vs. written_bytes: " << written_bytes;
795 throw runtime_error(out.str());
796 }
797 }
798 if (read_bytes == -2 || read_bytes == -1)
799 throw runtime_error("base64 decode: Error during decoding");
800
801 return _convert_BIO_to_string(bio_decoded);
802}
803
6ab3bc95
RP
804} // eo namespace I2n
805
806
807
6a93d84a 808
e93545dd
GE
809std::string iso_to_utf8(const std::string& isostring)
810{
6ab3bc95 811 string result;
118e216e 812
6ab3bc95 813 iconv_t i2utf8 = iconv_open("UTF-8", "ISO-8859-1");
118e216e 814
6ab3bc95
RP
815 if (iso_to_utf8 == (iconv_t)-1)
816 throw runtime_error("iconv can't convert from ISO-8859-1 to UTF-8");
118e216e 817
6ab3bc95
RP
818 size_t in_size=isostring.size();
819 size_t out_size=in_size*4;
118e216e 820
6ab3bc95
RP
821 char *buf = (char *)malloc(out_size+1);
822 if (buf == NULL)
823 throw runtime_error("out of memory for iconv buffer");
e93545dd 824
5a4ecb51 825 char *in = (char *)isostring.c_str();
6ab3bc95
RP
826 char *out = buf;
827 iconv(i2utf8, &in, &in_size, &out, &out_size);
118e216e 828
6ab3bc95 829 buf[isostring.size()*4-out_size]=0;
118e216e 830
6ab3bc95 831 result=buf;
118e216e 832
6ab3bc95
RP
833 free(buf);
834 iconv_close(i2utf8);
118e216e 835
6ab3bc95 836 return result;
e93545dd
GE
837}
838
839std::string utf8_to_iso(const std::string& utf8string)
840{
6ab3bc95 841 string result;
118e216e 842
6ab3bc95 843 iconv_t utf82iso = iconv_open("ISO-8859-1","UTF-8");
118e216e 844
6ab3bc95
RP
845 if (utf82iso == (iconv_t)-1)
846 throw runtime_error("iconv can't convert from UTF-8 to ISO-8859-1");
118e216e 847
6ab3bc95
RP
848 size_t in_size=utf8string.size();
849 size_t out_size=in_size;
118e216e 850
6ab3bc95
RP
851 char *buf = (char *)malloc(out_size+1);
852 if (buf == NULL)
853 throw runtime_error("out of memory for iconv buffer");
e93545dd 854
5a4ecb51 855 char *in = (char *)utf8string.c_str();
6ab3bc95
RP
856 char *out = buf;
857 iconv(utf82iso, &in, &in_size, &out, &out_size);
118e216e 858
6ab3bc95 859 buf[utf8string.size()-out_size]=0;
118e216e 860
6ab3bc95 861 result=buf;
118e216e 862
6ab3bc95
RP
863 free(buf);
864 iconv_close(utf82iso);
e93545dd 865
6ab3bc95 866 return result;
e93545dd
GE
867}
868
a5f3af6e
GE
869wchar_t* utf8_to_wbuf(const std::string& utf8string)
870{
6ab3bc95 871 iconv_t utf82wstr = iconv_open("UCS-4LE","UTF-8");
a5f3af6e 872
6ab3bc95
RP
873 if (utf82wstr == (iconv_t)-1)
874 throw runtime_error("iconv can't convert from UTF-8 to UCS-4");
a5f3af6e 875
6ab3bc95
RP
876 size_t in_size=utf8string.size();
877 size_t out_size= (in_size+1)*sizeof(wchar_t);
a5f3af6e 878
6ab3bc95
RP
879 wchar_t *buf = (wchar_t *)malloc(out_size);
880 if (buf == NULL)
881 throw runtime_error("out of memory for iconv buffer");
a5f3af6e 882
5a4ecb51 883 char *in = (char *)utf8string.c_str();
6ab3bc95 884 char *out = (char*) buf;
dbd6d77c 885 if (iconv(utf82wstr, &in, &in_size, &out, &out_size) == (size_t)-1)
6ab3bc95 886 throw runtime_error("error converting char encodings");
a5f3af6e 887
6ab3bc95 888 buf[ ( (utf8string.size()+1)*sizeof(wchar_t)-out_size) /sizeof(wchar_t) ]=0;
a5f3af6e 889
6ab3bc95 890 iconv_close(utf82wstr);
a5f3af6e 891
6ab3bc95 892 return buf;
a5f3af6e
GE
893}
894
13cc4db1 895std::string utf7imap_to_utf8(const std::string& utf7imapstring)
d116a071 896{
6ab3bc95 897 string result;
118e216e 898
6ab3bc95 899 iconv_t utf7imap2utf8 = iconv_open("UTF-8","UTF-7-IMAP");
118e216e 900
6ab3bc95
RP
901 if (utf7imap2utf8 == (iconv_t)-1)
902 throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
118e216e 903
6ab3bc95
RP
904 size_t in_size=utf7imapstring.size();
905 size_t out_size=in_size*4;
118e216e 906
6ab3bc95
RP
907 char *buf = (char *)malloc(out_size+1);
908 if (buf == NULL)
909 throw runtime_error("out of memory for iconv buffer");
d116a071 910
5a4ecb51 911 char *in = (char *)utf7imapstring.c_str();
6ab3bc95
RP
912 char *out = buf;
913 iconv(utf7imap2utf8, &in, &in_size, &out, &out_size);
118e216e 914
6ab3bc95 915 buf[utf7imapstring.size()*4-out_size]=0;
118e216e 916
6ab3bc95 917 result=buf;
118e216e 918
6ab3bc95
RP
919 free(buf);
920 iconv_close(utf7imap2utf8);
118e216e 921
6ab3bc95 922 return result;
118e216e
TJ
923}
924
6a2b6dd1
TJ
925std::string utf8_to_utf7imap(const std::string& utf8string)
926{
6ab3bc95 927 string result;
6a2b6dd1 928
6ab3bc95 929 iconv_t utf82utf7imap = iconv_open("UTF-7-IMAP", "UTF-8");
6a2b6dd1 930
6ab3bc95
RP
931 if (utf82utf7imap == (iconv_t)-1)
932 throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
6a2b6dd1 933
6ab3bc95
RP
934 // UTF-7 is base64 encoded, a buffer 10x as large
935 // as the utf-8 buffer should be enough. If not the string will be truncated.
936 size_t in_size=utf8string.size();
937 size_t out_size=in_size*10;
6a2b6dd1 938
6ab3bc95
RP
939 char *buf = (char *)malloc(out_size+1);
940 if (buf == NULL)
941 throw runtime_error("out of memory for iconv buffer");
6a2b6dd1 942
5a4ecb51 943 char *in = (char *)utf8string.c_str();
6ab3bc95
RP
944 char *out = buf;
945 iconv(utf82utf7imap, &in, &in_size, &out, &out_size);
6a2b6dd1 946
6ab3bc95 947 buf[utf8string.size()*10-out_size]= 0;
6a2b6dd1 948
6ab3bc95 949 result=buf;
6a2b6dd1 950
6ab3bc95
RP
951 free(buf);
952 iconv_close(utf82utf7imap);
6a2b6dd1 953
6ab3bc95 954 return result;
6a2b6dd1
TJ
955}
956
118e216e
TJ
957// Tokenize string by (html) tags
958void tokenize_by_tag(vector<pair<string,bool> > &tokenized, const std::string &input)
959{
6ab3bc95
RP
960 string::size_type pos, len = input.size();
961 bool inside_tag = false;
962 string current;
963
964 for (pos = 0; pos < len; pos++)
965 {
966 if (input[pos] == '<')
967 {
968 inside_tag = true;
969
970 if (!current.empty() )
971 {
972 tokenized.push_back( make_pair(current, false) );
973 current = "";
974 }
975
976 current += input[pos];
977 }
978 else if (input[pos] == '>' && inside_tag)
979 {
980 current += input[pos];
981 inside_tag = false;
982 if (!current.empty() )
983 {
984 tokenized.push_back( make_pair(current, true) );
985 current = "";
986 }
987 }
988 else
989 current += input[pos];
990 }
991
992 // String left over in buffer?
993 if (!current.empty() )
994 tokenized.push_back( make_pair(current, false) );
995} // eo tokenize_by_tag
118e216e 996
118e216e
TJ
997
998std::string strip_html_tags(const std::string &input)
999{
6ab3bc95
RP
1000 // Pair first: string, second: isTag
1001 vector<pair<string,bool> > tokenized;
1002 tokenize_by_tag (tokenized, input);
118e216e 1003
6ab3bc95
RP
1004 string output;
1005 vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
83d700e9 1006 for (token = tokenized.begin(); token != tokens_end; ++token)
6ab3bc95
RP
1007 if (!token->second)
1008 output += token->first;
1009
1010 return output;
1011} // eo strip_html_tags
118e216e 1012
118e216e
TJ
1013
1014// Smart-encode HTML en
1015string smart_html_entities(const std::string &input)
1016{
6ab3bc95
RP
1017 // Pair first: string, second: isTag
1018 vector<pair<string,bool> > tokenized;
1019 tokenize_by_tag (tokenized, input);
1020
1021 string output;
1022 vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
83d700e9 1023 for (token = tokenized.begin(); token != tokens_end; ++token)
6ab3bc95
RP
1024 {
1025 // keep HTML tags as they are
1026 if (token->second)
1027 output += token->first;
1028 else
1029 output += html_entities(token->first);
1030 }
1031
1032 return output;
118e216e
TJ
1033}
1034
6ab3bc95 1035
a5f3af6e
GE
1036string::size_type find_8bit(const std::string &str)
1037{
6ab3bc95
RP
1038 string::size_type l=str.size();
1039 for (string::size_type p=0; p < l; p++)
1040 if (static_cast<unsigned char>(str[p]) > 127)
1041 return p;
a5f3af6e 1042
6ab3bc95 1043 return string::npos;
a5f3af6e
GE
1044}
1045
118e216e
TJ
1046// encoded UTF-8 chars into HTML entities
1047string html_entities(std::string str)
1048{
6ab3bc95
RP
1049 // Normal chars
1050 replace_all (str, "&", "&amp;");
6ab3bc95
RP
1051 replace_all (str, "<", "&lt;");
1052 replace_all (str, ">", "&gt;");
980577e1
TJ
1053 replace_all (str, "\"", "&quot;");
1054 replace_all (str, "'", "&#x27;");
1055 replace_all (str, "/", "&#x2F;");
6ab3bc95
RP
1056
1057 // Umlauts
1058 replace_all (str, "\xC3\xA4", "&auml;");
1059 replace_all (str, "\xC3\xB6", "&ouml;");
1060 replace_all (str, "\xC3\xBC", "&uuml;");
1061 replace_all (str, "\xC3\x84", "&Auml;");
1062 replace_all (str, "\xC3\x96", "&Ouml;");
1063 replace_all (str, "\xC3\x9C", "&Uuml;");
1064
1065 // Misc
1066 replace_all (str, "\xC3\x9F", "&szlig;");
1067
1068 // conversion of remaining non-ASCII chars needed?
1069 // just do if needed because of performance
1070 if (find_8bit(str) != string::npos)
1071 {
1072 // convert to fixed-size encoding UTF-32
1073 wchar_t* wbuf=utf8_to_wbuf(str);
1074 ostringstream target;
1075
1076 // replace all non-ASCII chars with HTML representation
1077 for (int p=0; wbuf[p] != 0; p++)
1078 {
1079 unsigned int c=wbuf[p];
1080
1081 if (c <= 127)
1082 target << static_cast<unsigned char>(c);
1083 else
1084 target << "&#" << c << ';';
1085 }
1086
1087 free(wbuf);
1088
1089 str=target.str();
1090 }
1091
1092 return str;
1093} // eo html_entities(std::string)
1094
554f813d
GE
1095// convert HTML entities to something that can be viewed on a basic text console (restricted to ASCII-7)
1096string html_entities_to_console(std::string str)
1097{
1098 // Normal chars
1099 replace_all (str, "&amp;", "&");
1100 replace_all (str, "&lt;", "<");
1101 replace_all (str, "&gt;", ">");
1102 replace_all (str, "&quot;", "\"");
1103 replace_all (str, "&#x27;", "'");
1104 replace_all (str, "&#x2F;", "/");
1105
1106 // Umlauts
1107 replace_all (str, "&auml;", "ae");
1108 replace_all (str, "&ouml;", "oe");
1109 replace_all (str, "&uuml;", "ue");
1110 replace_all (str, "&Auml;", "Ae");
1111 replace_all (str, "&Ouml;", "Oe");
1112 replace_all (str, "&Uuml;", "Ue");
1113
1114 // Misc
1115 replace_all (str, "&szlig;", "ss");
1116
1117 return str;
1118}
118e216e 1119
3f5c5ccd
CH
1120// find_html_comments + remove_html_comments(str, comments)
1121void remove_html_comments(string &str)
1122{
46dd1321 1123 vector<CommentZone> comments = find_html_comments(str);
3f5c5ccd
CH
1124 remove_html_comments(str, comments);
1125}
1126
1127// find all html comments, behaving correctly if they are nested; ignores comment tags ("<!--FOO .... BAR-->")
1128// If there are invalid comments ("-->" before "<!--" or different number of closing and opening tags),
1129// then the unknown index of corresponding start/end tag will be represented by a string::npos
1130// Indices are from start of start tag until first index after closing tag
46dd1321 1131vector<CommentZone> find_html_comments(const std::string &str)
3f5c5ccd
CH
1132{
1133 static const string START = "<!--";
1134 static const string CLOSE = "-->";
1135 static const string::size_type START_LEN = START.length();
1136 static const string::size_type CLOSE_LEN = CLOSE.length();
1137
46dd1321
TJ
1138 vector<CommentZone> comments;
1139
3f5c5ccd
CH
1140 // in order to find nested comments, need either recursion or a stack
1141 vector<string::size_type> starts; // stack of start tags
1142
1143 string::size_type pos = 0;
1144 string::size_type len = str.length();
1145 string::size_type next_start, next_close;
1146
1147 while (pos < len) // not really needed but just in case
1148 {
1149 next_start = str.find(START, pos);
1150 next_close = str.find(CLOSE, pos);
1151
1152 if ( (next_start == string::npos) && (next_close == string::npos) )
1153 break; // we are done
1154
1155 else if ( (next_start == string::npos) || (next_close < next_start) ) // close one comment (pop)
1156 {
1157 if (starts.empty()) // closing tag without a start
1158 comments.push_back(CommentZone(string::npos, next_close+CLOSE_LEN));
1159 else
1160 {
1161 comments.push_back(CommentZone(starts.back(), next_close+CLOSE_LEN));
1162 starts.pop_back();
1163 }
1164 pos = next_close + CLOSE_LEN;
1165 }
1166
1167 else if ( (next_close == string::npos) || (next_start < next_close) ) // start a new comment (push)
1168 {
1169 starts.push_back(next_start);
1170 pos = next_start + START_LEN;
1171 }
1172 }
1173
1174 // add comments that have no closing tag from back to front (important for remove_html_comments!)
1175 while (!starts.empty())
1176 {
1177 comments.push_back(CommentZone(starts.back(), string::npos));
1178 starts.pop_back();
1179 }
46dd1321
TJ
1180
1181 return comments;
3f5c5ccd
CH
1182}
1183
1184// remove all html comments foundby find_html_comments
1185void remove_html_comments(std::string &str, const vector<CommentZone> &comments)
1186{
1187 // remember position where last removal started
1188 string::size_type last_removal_start = str.length();
1189
1190 // Go from back to front to not mess up indices.
1191 // This requires that bigger comments, that contain smaller comments, come AFTER
1192 // the small contained comments in the comments vector (i.e. comments are ordered by
1193 // their closing tag, not their opening tag). This is true for results from find_html_comments
1194 BOOST_REVERSE_FOREACH(const CommentZone &comment, comments)
1195 {
1196 if (comment.first == string::npos)
1197 {
1198 str = str.replace(0, comment.second, ""); // comment starts "before" str --> delete from start
1199 break; // there can be no more
1200 }
1201 else if (comment.first >= last_removal_start)
1202 {
1203 continue; // this comment is inside another comment that we have removed already
1204 }
1205 else if (comment.second == string::npos) // comment ends "after" str --> delete until end
1206 {
1207 str = str.replace(comment.first, string::npos, "");
1208 last_removal_start = comment.first;
1209 }
1210 else
1211 {
1212 str = str.replace(comment.first, comment.second-comment.first, "");
1213 last_removal_start = comment.first;
1214 }
1215 }
1216}
1217
e93545dd
GE
1218bool replace_all(string &base, const char *ist, const char *soll)
1219{
6ab3bc95
RP
1220 string i=ist;
1221 string s=soll;
1222 return replace_all(base,&i,&s);
e93545dd
GE
1223}
1224
1225bool replace_all(string &base, const string &ist, const char *soll)
1226{
6ab3bc95
RP
1227 string s=soll;
1228 return replace_all(base,&ist,&s);
e93545dd
GE
1229}
1230
1231bool replace_all(string &base, const string *ist, const string *soll)
1232{
6ab3bc95 1233 return replace_all(base,*ist,*soll);
e93545dd
GE
1234}
1235
1236bool replace_all(string &base, const char *ist, const string *soll)
1237{
6ab3bc95
RP
1238 string i=ist;
1239 return replace_all(base,&i,soll);
e93545dd
GE
1240}
1241
1242bool replace_all(string &base, const string &ist, const string &soll)
1243{
6ab3bc95
RP
1244 bool found_ist = false;
1245 string::size_type a=0;
1246
1247 if (ist.empty() )
1248 throw runtime_error ("replace_all called with empty search string");
e93545dd 1249
6ab3bc95
RP
1250 while ( (a=base.find(ist,a) ) != string::npos)
1251 {
1252 base.replace(a,ist.size(),soll);
1253 a=a+soll.size();
1254 found_ist = true;
1255 }
1ec2064e 1256
6ab3bc95 1257 return found_ist;
e93545dd
GE
1258}
1259
b953bf36
GE
1260/**
1261 * @brief replaces all characters that could be problematic or impose a security risk when being logged
1262 * @param str the original string
1263 * @param replace_with the character to replace the unsafe chars with
1264 * @return a string that is safe to send to syslog or other logfiles
1265 *
1266 * All chars between 0x20 (space) and 0x7E (~) (including) are considered safe for logging.
1267 * See e.g. RFC 5424, section 8.2 or the posix character class "printable".
1268 * This eliminates all possible problems with NUL, control characters, 8 bit chars, UTF8.
1269 *
1270 */
1271std::string sanitize_for_logging(const std::string &str, const char replace_with)
1272{
1273 std::string output=str;
1274
c0e32d64
GE
1275 const string::size_type len = output.size();
1276 for (std::string::size_type p=0; p < len; p++)
b953bf36
GE
1277 if (output[p] < 0x20 || output[p] > 0x7E)
1278 output[p]=replace_with;
1279
1280 return output;
1281}
1282
e5b21dbb 1283#if 0
e93545dd
GE
1284string to_lower(const string &src)
1285{
6ab3bc95 1286 string dst = src;
e93545dd 1287
6ab3bc95
RP
1288 string::size_type pos, end = dst.size();
1289 for (pos = 0; pos < end; pos++)
1290 dst[pos] = tolower(dst[pos]);
e93545dd 1291
6ab3bc95 1292 return dst;
e93545dd
GE
1293}
1294
1295string to_upper(const string &src)
1296{
6ab3bc95 1297 string dst = src;
e93545dd 1298
6ab3bc95
RP
1299 string::size_type pos, end = dst.size();
1300 for (pos = 0; pos < end; pos++)
1301 dst[pos] = toupper(dst[pos]);
e93545dd 1302
6ab3bc95 1303 return dst;
e93545dd 1304}
e5b21dbb 1305#endif
e93545dd 1306
83809f5e 1307const int MAX_UNIT_FORMAT_SYMBOLS = 6;
d1ea9075 1308
2cb9a9c5 1309const string shortUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
d1ea9075
GMF
1310 " B",
1311 " KB",
1312 " MB",
1313 " GB",
1314 " TB",
83809f5e 1315 " PB"
d1ea9075
GMF
1316};
1317
2cb9a9c5 1318const string longUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
5cb766b9
GMF
1319 i18n_noop(" Bytes"),
1320 i18n_noop(" KBytes"),
1321 i18n_noop(" MBytes"),
1322 i18n_noop(" GBytes"),
1323 i18n_noop(" TBytes"),
83809f5e 1324 i18n_noop(" PBytes")
d1ea9075
GMF
1325};
1326
72a94426 1327
e26f7d51 1328static long double rounding_upwards(
e91c1952 1329 const long double number,
72a94426
GMF
1330 const int rounding_multiplier
1331)
1332{
1333 long double rounded_number;
1334 rounded_number = number * rounding_multiplier;
1335 rounded_number += 0.5;
1336 rounded_number = (int64_t) (rounded_number);
1337 rounded_number = (long double) (rounded_number) / (long double) (rounding_multiplier);
1338
1339 return rounded_number;
1340}
1341
1342
81267544
GMF
1343string nice_unit_format(
1344 const int64_t input,
70fc0674
GMF
1345 const UnitFormat format,
1346 const UnitBase base
81267544 1347)
6ab3bc95 1348{
d1ea9075 1349 // select the system of units (decimal or binary)
81267544 1350 int multiple = 0;
a398513a 1351 if (base == UnitBase1000)
81267544
GMF
1352 {
1353 multiple = 1000;
1354 }
1355 else
1356 {
1357 multiple = 1024;
1358 }
1359
1360 long double size = input;
6ab3bc95 1361
d1ea9075
GMF
1362 // check the size of the input number to fit in the appropriate symbol
1363 int sizecount = 0;
81267544 1364 while (size > multiple)
6ab3bc95 1365 {
81267544
GMF
1366 size = size / multiple;
1367 sizecount++;
83809f5e
GMF
1368
1369 // rollback to the previous values and stop the loop when cannot
1370 // represent the number length.
1371 if (sizecount >= MAX_UNIT_FORMAT_SYMBOLS)
1372 {
1373 size = size * multiple;
1374 sizecount--;
1375 break;
1376 }
6ab3bc95
RP
1377 }
1378
a398513a
GMF
1379 // round the input number "half up" to multiples of 10
1380 const int rounding_multiplier = 10;
72a94426 1381 size = rounding_upwards(size, rounding_multiplier);
6ab3bc95 1382
d1ea9075 1383 // format the input number, placing the appropriate symbol
6ab3bc95 1384 ostringstream out;
6ab3bc95 1385 out.setf (ios::fixed);
a398513a 1386 if (format == ShortUnitFormat)
d1ea9075
GMF
1387 {
1388 out.precision(1);
68d37a5c 1389 out << size << i18n( shortUnitFormatSymbols[sizecount].c_str() );
d1ea9075
GMF
1390 }
1391 else
6ab3bc95 1392 {
d1ea9075 1393 out.precision (2);
68d37a5c 1394 out << size << i18n( longUnitFormatSymbols[sizecount].c_str() );
6ab3bc95
RP
1395 }
1396
1397 return out.str();
1398} // eo nice_unit_format(int input)
1399
e93545dd 1400
5cd64148
CH
1401string nice_unit_format(
1402 const double input,
1403 const UnitFormat format,
1404 const UnitBase base
1405)
1406{
1407 // round as double and cast to int64_t
1408 // cast raised overflow error near max val of int64_t (~9.2e18, see unittest)
1409 int64_t input_casted_and_rounded =
1410 boost::numeric_cast<int64_t>( round(input) );
1411
1412 // now call other
1413 return nice_unit_format( input_casted_and_rounded, format, base );
1414} // eo nice_unit_format(double input)
1415
1416
47c07fba
GE
1417string escape(const string &s)
1418{
6ab3bc95
RP
1419 string out(s);
1420 string::size_type p;
47c07fba 1421
6ab3bc95
RP
1422 p=0;
1423 while ( (p=out.find_first_of("\"\\",p) ) !=out.npos)
1424 {
1425 out.insert (p,"\\");
1426 p+=2;
1427 }
47c07fba 1428
6ab3bc95
RP
1429 p=0;
1430 while ( (p=out.find_first_of("\r",p) ) !=out.npos)
1431 {
1432 out.replace (p,1,"\\r");
1433 p+=2;
1434 }
47c07fba 1435
6ab3bc95
RP
1436 p=0;
1437 while ( (p=out.find_first_of("\n",p) ) !=out.npos)
1438 {
1439 out.replace (p,1,"\\n");
1440 p+=2;
1441 }
47c07fba 1442
6ab3bc95 1443 out='"'+out+'"';
47c07fba 1444
6ab3bc95
RP
1445 return out;
1446} // eo scape(const std::string&)
47c07fba 1447
47c07fba 1448
6ab3bc95
RP
1449string descape(const string &s, int startpos, int &endpos)
1450{
1451 string out;
1452
1453 if (s.at(startpos) != '"')
1454 throw out_of_range("value not type escaped string");
1455
1456 out=s.substr(startpos+1);
1457 string::size_type p=0;
1458
1459 // search for the end of the string
1460 while ( (p=out.find("\"",p) ) !=out.npos)
1461 {
1462 int e=p-1;
1463 bool escaped=false;
1464
1465 // the " might be escaped with a backslash
1466 while (e>=0 && out.at (e) =='\\')
1467 {
1468 if (escaped == false)
1469 escaped=true;
1470 else
1471 escaped=false;
1472
1473 e--;
1474 }
1475
1476 if (escaped==false)
1477 break;
1478 else
1479 p++;
1480 }
1481
1482 // we now have the end of the string
1483 out=out.substr(0,p);
1484
1485 // tell calling prog about the endposition
1486 endpos=startpos+p+1;
1487
1488 // descape all \ stuff inside the string now
1489 p=0;
1490 while ( (p=out.find_first_of("\\",p) ) !=out.npos)
1491 {
1492 switch (out.at(p+1) )
1493 {
1494 case 'r':
47c07fba
GE
1495 out.replace(p,2,"\r");
1496 break;
6ab3bc95 1497 case 'n':
47c07fba
GE
1498 out.replace(p,2,"\n");
1499 break;
6ab3bc95 1500 default:
47c07fba 1501 out.erase(p,1);
6ab3bc95
RP
1502 }
1503 p++;
1504 }
1505
1506 return out;
1507} // eo descape(const std::string&,int,int&)
47c07fba 1508
e93545dd 1509
47c07fba
GE
1510string escape_shellarg(const string &input)
1511{
6ab3bc95
RP
1512 string output = "'";
1513 string::const_iterator it, it_end = input.end();
83d700e9 1514 for (it = input.begin(); it != it_end; ++it)
6ab3bc95
RP
1515 {
1516 if ( (*it) == '\'')
1517 output += "'\\'";
1518
1519 output += *it;
1520 }
1521
1522 output += "'";
1523 return output;
47c07fba 1524}