add char** overloads for join_string
[libi2ncommon] / src / stringfunc.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /** @file
21  *
22  * (c) Copyright 2007-2008 by Intra2net AG
23  */
24
25 #include <iostream>
26 #include <string>
27 #include <sstream>
28 #include <stdexcept>
29 #include <algorithm>
30 #include <cmath>    // for round()
31 #include <climits>
32
33 #include <wchar.h>
34 #include <stdlib.h>
35 #include <iconv.h>
36 #include <i18n.h>
37
38 #include <boost/numeric/conversion/cast.hpp>
39 #include <boost/foreach.hpp>
40
41 #include <boost/assert.hpp>
42 #include <boost/shared_ptr.hpp>
43 #include <openssl/bio.h>
44 #include <openssl/evp.h>
45
46 #include <stringfunc.hxx>
47
48 using namespace std;
49
50 namespace I2n
51 {
52
53
54 namespace
55 {
56
57 const std::string hexDigitsLower("0123456789abcdef");
58 const std::string hexDigitsUpper("0123456789ABCDEF");
59
60
61 struct UpperFunc
62 {
63    char operator() (char c)
64    {
65       return std::toupper(c);
66    }
67 }; // eo struct UpperFunc
68
69
70 struct LowerFunc
71 {
72    char operator() (char c)
73    {
74       return std::tolower(c);
75    }
76 }; // eo struct LowerFunc
77
78
79 } // eo namespace <anonymous>
80
81
82
83 /**
84  * default list of Whitespaces (" \t\r\n");
85  */
86 const std::string Whitespaces = " \t\r\n";
87
88 /**
89  * default list of lineendings ("\r\n");
90  */
91 const std::string LineEndings= "\r\n";
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  */
101 bool has_prefix(const std::string& str, const std::string& prefix)
102 {
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&)
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  */
117 bool has_suffix(const std::string& str, const std::string& suffix)
118 {
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&)
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  */
133 std::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&)
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  */
165 std::string chomp_mod(std::string& str, const std::string& what)
166 {
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&)
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  */
184 std::string to_lower_mod(std::string& str)
185 {
186    std::transform(str.begin(), str.end(), str.begin(), LowerFunc() );
187    return str;
188 } // eo to_lower_mod(std::string&)
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  */
196 std::string to_upper_mod(std::string& str)
197 {
198    std::transform( str.begin(), str.end(), str.begin(), UpperFunc() );
199    return str;
200 } // eo to_upper_mod(std::string&)
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  */
210 std::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);
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  */
231 std::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;
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  */
250 std::string to_lower (const std::string& str)
251 {
252    std::string result(str);
253    return to_lower_mod(result);
254 } // eo to_lower(const std::string&)
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  */
262 std::string to_upper(const std::string& str)
263 {
264    std::string result(str);
265    return to_upper_mod(result);
266 } // eo to_upper(const std::string&)
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  */
279 std::string remove_suffix(const std::string& str, const std::string& suffix)
280 {
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&)
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  */
299 std::string remove_prefix(const std::string& str, const std::string& prefix)
300 {
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&)
307
308
309 /**
310  * split a string to key and value delimited by a given delimiter.
311  * The resulting key and value strings are trimmed (Whitespaces removed at beginning and end).
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  */
318 bool 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)
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  */
344 void 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
350 )
351 {
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&)
388
389
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  * */
395 void 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
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  */
420 std::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
425 )
426 {
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&)
431
432
433 /**
434  * @brief joins a list of strings into a single string.
435  *
436  * This funtion is (basically) the reverse operation of @a split_string.
437  *
438  * @param parts the list of strings.
439  * @param delimiter the delimiter which is inserted between the strings.
440  * @return the joined string.
441  */
442 std::string join_string(
443    const std::list< std::string >& parts,
444    const std::string& delimiter
445 )
446 {
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&)
460
461
462 /** @brief same as join_string for list, except uses a vector */
463 std::string join_string(
464    const std::vector< std::string >& parts,
465    const std::string& delimiter
466 )
467 {
468    std::string result;
469    if (! parts.empty() )
470    {
471       std::vector< std::string >::const_iterator it= parts.begin();
472       result = *it;
473       while ( ++it != parts.end() )
474       {
475          result+= delimiter;
476          result+= *it;
477       }
478    }
479    return result;
480 } // eo join_string(const std::vector< std::string >&,const std::string&)
481
482 std::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
506
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  */
519 std::string convert_binary_to_hex(
520    const std::string& str,
521    bool upper_case_digits
522 )
523 {
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)
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  */
547 std::string convert_hex_to_binary(
548    const std::string& str
549 )
550 throw (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?
568                 or ( *it == ':') // or a colon?
569             )
570          {
571             // we treat that as a valid delimiter:
572             if (hasNibble)
573             {
574                // 1 nibble before WS is treate as lower part:
575                result.push_back(c);
576                // reset state:
577                hasNibble= false;
578             }
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
630 static 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
641 string 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
710 typedef 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 **/
720 static 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
739     * @param one_line Encode all data as one line, no wrapping with line feeds
740     * @return base64 encoded string
741     */
742 std::string base64_encode(const std::string &input, bool one_line)
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();
755     if (one_line)
756         BIO_set_flags(encoder_bio, BIO_FLAGS_BASE64_NO_NL);
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
781     * @param one_line Expect all base64 data in one line. Input with line feeds will fail.
782     * @return base64 decoded string
783     */
784 std::string base64_decode(const std::string &input, bool one_line)
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();
797     if (one_line)
798         BIO_set_flags(bio_base64, BIO_FLAGS_BASE64_NO_NL);
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
828 } // eo namespace I2n
829
830
831
832
833 std::string iso_to_utf8(const std::string& isostring)
834 {
835    string result;
836
837    iconv_t i2utf8 = iconv_open("UTF-8", "ISO-8859-1");
838
839    if (iso_to_utf8 == (iconv_t)-1)
840       throw runtime_error("iconv can't convert from ISO-8859-1 to UTF-8");
841
842    size_t in_size=isostring.size();
843    size_t out_size=in_size*4;
844
845    char *buf = (char *)malloc(out_size+1);
846    if (buf == NULL)
847       throw runtime_error("out of memory for iconv buffer");
848
849    char *in = (char *)isostring.c_str();
850    char *out = buf;
851    iconv(i2utf8, &in, &in_size, &out, &out_size);
852
853    buf[isostring.size()*4-out_size]=0;
854
855    result=buf;
856
857    free(buf);
858    iconv_close(i2utf8);
859
860    return result;
861 }
862
863 std::string utf8_to_iso(const std::string& utf8string)
864 {
865    string result;
866
867    iconv_t utf82iso = iconv_open("ISO-8859-1","UTF-8");
868
869    if (utf82iso == (iconv_t)-1)
870       throw runtime_error("iconv can't convert from UTF-8 to ISO-8859-1");
871
872    size_t in_size=utf8string.size();
873    size_t out_size=in_size;
874
875    char *buf = (char *)malloc(out_size+1);
876    if (buf == NULL)
877       throw runtime_error("out of memory for iconv buffer");
878
879    char *in = (char *)utf8string.c_str();
880    char *out = buf;
881    iconv(utf82iso, &in, &in_size, &out, &out_size);
882
883    buf[utf8string.size()-out_size]=0;
884
885    result=buf;
886
887    free(buf);
888    iconv_close(utf82iso);
889
890    return result;
891 }
892
893 wchar_t* utf8_to_wbuf(const std::string& utf8string)
894 {
895    iconv_t utf82wstr = iconv_open("UCS-4LE","UTF-8");
896
897    if (utf82wstr == (iconv_t)-1)
898       throw runtime_error("iconv can't convert from UTF-8 to UCS-4");
899
900    size_t in_size=utf8string.size();
901    size_t out_size= (in_size+1)*sizeof(wchar_t);
902
903    wchar_t *buf = (wchar_t *)malloc(out_size);
904    if (buf == NULL)
905       throw runtime_error("out of memory for iconv buffer");
906
907    char *in = (char *)utf8string.c_str();
908    char *out = (char*) buf;
909    if (iconv(utf82wstr, &in, &in_size, &out, &out_size) == (size_t)-1)
910       throw runtime_error("error converting char encodings");
911
912    buf[ ( (utf8string.size()+1)*sizeof(wchar_t)-out_size) /sizeof(wchar_t) ]=0;
913
914    iconv_close(utf82wstr);
915
916    return buf;
917 }
918
919 std::string utf7imap_to_utf8(const std::string& utf7imapstring)
920 {
921    string result;
922
923    iconv_t utf7imap2utf8 = iconv_open("UTF-8","UTF-7-IMAP");
924
925    if (utf7imap2utf8 == (iconv_t)-1)
926       throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
927
928    size_t in_size=utf7imapstring.size();
929    size_t out_size=in_size*4;
930
931    char *buf = (char *)malloc(out_size+1);
932    if (buf == NULL)
933       throw runtime_error("out of memory for iconv buffer");
934
935    char *in = (char *)utf7imapstring.c_str();
936    char *out = buf;
937    iconv(utf7imap2utf8, &in, &in_size, &out, &out_size);
938
939    buf[utf7imapstring.size()*4-out_size]=0;
940
941    result=buf;
942
943    free(buf);
944    iconv_close(utf7imap2utf8);
945
946    return result;
947 }
948
949 std::string utf8_to_utf7imap(const std::string& utf8string)
950 {
951    string result;
952
953    iconv_t utf82utf7imap = iconv_open("UTF-7-IMAP", "UTF-8");
954
955    if (utf82utf7imap == (iconv_t)-1)
956       throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
957
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;
962
963    char *buf = (char *)malloc(out_size+1);
964    if (buf == NULL)
965       throw runtime_error("out of memory for iconv buffer");
966
967    char *in = (char *)utf8string.c_str();
968    char *out = buf;
969    iconv(utf82utf7imap, &in, &in_size, &out, &out_size);
970
971    buf[utf8string.size()*10-out_size]= 0;
972
973    result=buf;
974
975    free(buf);
976    iconv_close(utf82utf7imap);
977
978    return result;
979 }
980
981 // Tokenize string by (html) tags
982 void tokenize_by_tag(vector<pair<string,bool> > &tokenized, const std::string &input)
983 {
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
1020
1021
1022 std::string strip_html_tags(const std::string &input)
1023 {
1024    // Pair first: string, second: isTag
1025    vector<pair<string,bool> > tokenized;
1026    tokenize_by_tag (tokenized, input);
1027
1028    string output;
1029    vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
1030    for (token = tokenized.begin(); token != tokens_end; ++token)
1031       if (!token->second)
1032          output += token->first;
1033
1034    return output;
1035 } // eo strip_html_tags
1036
1037
1038 // Smart-encode HTML en
1039 string smart_html_entities(const std::string &input)
1040 {
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();
1047    for (token = tokenized.begin(); token != tokens_end; ++token)
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;
1057 }
1058
1059
1060 string::size_type find_8bit(const std::string &str)
1061 {
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;
1066
1067    return string::npos;
1068 }
1069
1070 // encoded UTF-8 chars into HTML entities
1071 string html_entities(std::string str)
1072 {
1073    // Normal chars
1074    replace_all (str, "&", "&amp;");
1075    replace_all (str, "<", "&lt;");
1076    replace_all (str, ">", "&gt;");
1077    replace_all (str, "\"", "&quot;");
1078    replace_all (str, "'", "&#x27;");
1079    replace_all (str, "/", "&#x2F;");
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
1119 // convert HTML entities to something that can be viewed on a basic text console (restricted to ASCII-7)
1120 string 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 }
1143
1144 // find_html_comments + remove_html_comments(str, comments)
1145 void remove_html_comments(string &str)
1146 {
1147     vector<CommentZone> comments = find_html_comments(str);
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
1155 vector<CommentZone> find_html_comments(const std::string &str)
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
1162     vector<CommentZone> comments;
1163
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     }
1204
1205     return comments;
1206 }
1207
1208 // remove all html comments foundby find_html_comments
1209 void 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
1242 bool replace_all(string &base, const char *ist, const char *soll)
1243 {
1244    string i=ist;
1245    string s=soll;
1246    return replace_all(base,&i,&s);
1247 }
1248
1249 bool replace_all(string &base, const string &ist, const char *soll)
1250 {
1251    string s=soll;
1252    return replace_all(base,&ist,&s);
1253 }
1254
1255 bool replace_all(string &base, const string *ist, const string *soll)
1256 {
1257    return replace_all(base,*ist,*soll);
1258 }
1259
1260 bool replace_all(string &base, const char *ist, const string *soll)
1261 {
1262    string i=ist;
1263    return replace_all(base,&i,soll);
1264 }
1265
1266 bool replace_all(string &base, const string &ist, const string &soll)
1267 {
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");
1273
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    }
1280
1281    return found_ist;
1282 }
1283
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  */
1295 std::string sanitize_for_logging(const std::string &str, const char replace_with)
1296 {
1297     std::string output=str;
1298
1299     const string::size_type len = output.size();
1300     for (std::string::size_type p=0; p < len; p++)
1301         if (output[p] < 0x20 || output[p] > 0x7E)
1302             output[p]=replace_with;
1303
1304     return output;
1305 }
1306
1307 #if 0
1308 string to_lower(const string &src)
1309 {
1310    string dst = src;
1311
1312    string::size_type pos, end = dst.size();
1313    for (pos = 0; pos < end; pos++)
1314       dst[pos] = tolower(dst[pos]);
1315
1316    return dst;
1317 }
1318
1319 string to_upper(const string &src)
1320 {
1321    string dst = src;
1322
1323    string::size_type pos, end = dst.size();
1324    for (pos = 0; pos < end; pos++)
1325       dst[pos] = toupper(dst[pos]);
1326
1327    return dst;
1328 }
1329 #endif
1330
1331 const int MAX_UNIT_FORMAT_SYMBOLS = 6;
1332
1333 const string shortUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
1334         " B",
1335         " KB",
1336         " MB",
1337         " GB",
1338         " TB",
1339         " PB"
1340 };
1341
1342 const string longUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
1343         i18n_noop(" Bytes"),
1344         i18n_noop(" KBytes"),
1345         i18n_noop(" MBytes"),
1346         i18n_noop(" GBytes"),
1347         i18n_noop(" TBytes"),
1348         i18n_noop(" PBytes")
1349 };
1350
1351
1352 static long double rounding_upwards(
1353         const long double number,
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
1367 string nice_unit_format(
1368         const int64_t input,
1369         const UnitFormat format,
1370         const UnitBase base
1371 )
1372 {
1373    // select the system of units (decimal or binary)
1374    int multiple = 0;
1375    if (base == UnitBase1000)
1376    {
1377        multiple = 1000;
1378    }
1379    else
1380    {
1381        multiple = 1024;
1382    }
1383
1384    long double size = input;
1385
1386    // check the size of the input number to fit in the appropriate symbol
1387    int sizecount = 0;
1388    while (size > multiple)
1389    {
1390        size = size / multiple;
1391        sizecount++;
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        }
1401    }
1402
1403    // round the input number "half up" to multiples of 10
1404    const int rounding_multiplier = 10;
1405    size = rounding_upwards(size, rounding_multiplier);
1406
1407    // format the input number, placing the appropriate symbol
1408    ostringstream out;
1409    out.setf (ios::fixed);
1410    if (format == ShortUnitFormat)
1411    {
1412        out.precision(1);
1413        out << size << i18n( shortUnitFormatSymbols[sizecount].c_str() );
1414    }
1415    else
1416    {
1417        out.precision (2);
1418        out << size << i18n( longUnitFormatSymbols[sizecount].c_str() );
1419    }
1420
1421    return out.str();
1422 } // eo nice_unit_format(int input)
1423
1424
1425 string 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
1441 string escape(const string &s)
1442 {
1443    string out(s);
1444    string::size_type p;
1445
1446    p=0;
1447    while ( (p=out.find_first_of("\"\\",p) ) !=out.npos)
1448    {
1449       out.insert (p,"\\");
1450       p+=2;
1451    }
1452
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    }
1459
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    }
1466
1467    out='"'+out+'"';
1468
1469    return out;
1470 } // eo scape(const std::string&)
1471
1472
1473 string 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':
1519             out.replace(p,2,"\r");
1520             break;
1521          case 'n':
1522             out.replace(p,2,"\n");
1523             break;
1524          default:
1525             out.erase(p,1);
1526       }
1527       p++;
1528    }
1529
1530    return out;
1531 } // eo descape(const std::string&,int,int&)
1532
1533
1534 string escape_shellarg(const string &input)
1535 {
1536    string output = "'";
1537    string::const_iterator it, it_end = input.end();
1538    for (it = input.begin(); it != it_end; ++it)
1539    {
1540       if ( (*it) == '\'')
1541          output += "'\\'";
1542
1543       output += *it;
1544    }
1545
1546    output += "'";
1547    return output;
1548 }