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