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