add function html_entities_to_console to stringfunc
[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
31 #include <wchar.h>
32 #include <stdlib.h>
33 #include <iconv.h>
34 #include <i18n.h>
35
36 #include <stringfunc.hxx>
37
38 using namespace std;
39
40 namespace I2n
41 {
42
43
44 namespace
45 {
46
47 const std::string hexDigitsLower("0123456789abcdef");
48 const std::string hexDigitsUpper("0123456789ABCDEF");
49
50
51 struct UpperFunc
52 {
53    char operator() (char c)
54    {
55       return std::toupper(c);
56    }
57 }; // eo struct UpperFunc
58
59
60 struct LowerFunc
61 {
62    char operator() (char c)
63    {
64       return std::tolower(c);
65    }
66 }; // eo struct LowerFunc
67
68
69 } // eo namespace <anonymous>
70
71
72
73 /**
74  * default list of Whitespaces (" \t\r\n");
75  */
76 const std::string Whitespaces = " \t\r\n";
77
78 /**
79  * default list of lineendings ("\r\n");
80  */
81 const std::string LineEndings= "\r\n";
82
83
84
85 /**
86  * @brief checks if a string begins with a given prefix.
87  * @param[in,out] str the string which is tested
88  * @param prefix the prefix which should be tested for.
89  * @return @a true iff the prefix is not empty and the string begins with that prefix.
90  */
91 bool has_prefix(const std::string& str, const std::string& prefix)
92 {
93    if (prefix.empty() || str.empty() || str.size() < prefix.size() )
94    {
95       return false;
96    }
97    return str.compare(0, prefix.size(), prefix) == 0;
98 } // eo has_prefix(const std::string&,const std::string&)
99
100
101 /**
102  * @brief checks if a string ends with a given suffix.
103  * @param[in,out] str the string which is tested
104  * @param suffix the suffix which should be tested for.
105  * @return @a true iff the suffix is not empty and the string ends with that suffix.
106  */
107 bool has_suffix(const std::string& str, const std::string& suffix)
108 {
109    if (suffix.empty() || str.empty() || str.size() < suffix.size() )
110    {
111       return false;
112    }
113    return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
114 } // eo has_suffix(const std::string&,const std::string&)
115
116
117 /**
118  * cut off characters from a given list from front and end of a string.
119  * @param[in,out] str the string which should be trimmed.
120  * @param charlist the list of characters to remove from beginning and end of string
121  * @return the result string.
122  */
123 std::string trim_mod(std::string& str, const std::string& charlist)
124 {
125    // first: trim the beginning:
126    std::string::size_type pos= str.find_first_not_of (charlist);
127    if (pos == std::string::npos)
128    {
129       // whole string consists of charlist (or is already empty)
130       str.clear();
131       return str;
132    }
133    else if (pos>0)
134    {
135       // str starts with charlist
136       str.erase(0,pos);
137    }
138    // now let's look at the tail:
139    pos= str.find_last_not_of(charlist) +1;  // note: we already know there is at least one other char!
140    if ( pos < str.size() )
141    {
142       str.erase(pos, str.size()-pos);
143    }
144    return str;
145 } // eo trim_mod(std::string&,const std::string&)
146
147
148
149 /**
150  * removes last character from a string when it is in a list of chars to be removed.
151  * @param[in,out] str the string.
152  * @param what the list of chars which will be tested for.
153  * @return the resulting string with last char removed (if applicable)
154  */
155 std::string chomp_mod(std::string& str, const std::string& what)
156 {
157    if (str.empty() || what.empty() )
158    {
159       return str;
160    }
161    if (what.find(str.at (str.size()-1) ) != std::string::npos)
162    {
163       str.erase(str.size() - 1);
164    }
165    return str;
166 } // eo chomp_mod(std::string&,const std::string&)
167
168
169 /**
170  * @brief converts a string to lower case.
171  * @param[in,out] str the string to modify.
172  * @return the string
173  */
174 std::string to_lower_mod(std::string& str)
175 {
176    std::transform(str.begin(), str.end(), str.begin(), LowerFunc() );
177    return str;
178 } // eo to_lower_mod(std::string&)
179
180
181 /**
182  * @brief converts a string to upper case.
183  * @param[in,out] str the string to modify.
184  * @return the string
185  */
186 std::string to_upper_mod(std::string& str)
187 {
188    std::transform( str.begin(), str.end(), str.begin(), UpperFunc() );
189    return str;
190 } // eo to_upper_mod(std::string&)
191
192
193
194 /**
195  * cut off characters from a given list from front and end of a string.
196  * @param str the string which should be trimmed.
197  * @param charlist the list of characters to remove from beginning and end of string
198  * @return the result string.
199  */
200 std::string trim (const std::string& str, const std::string& charlist)
201 {
202    // first: trim the beginning:
203    std::string::size_type pos0= str.find_first_not_of(charlist);
204    if (pos0 == std::string::npos)
205    {
206       // whole string consists of charlist (or is already empty)
207       return std::string();
208    }
209    // now let's look at the end:
210    std::string::size_type pos1= str.find_last_not_of(charlist);
211    return str.substr(pos0, pos1 - pos0 + 1);
212 } // eo trim(const std:.string&,const std::string&)
213
214
215 /**
216  * removes last character from a string when it is in a list of chars to be removed.
217  * @param str the string.
218  * @param what the list of chars which will be tested for.
219  * @return the resulting string with last char removed (if applicable)
220  */
221 std::string chomp (const std::string& str, const std::string& what)
222 {
223    if (str.empty() || what.empty() )
224    {
225       return str;
226    }
227    if (what.find(str.at (str.size()-1) ) != std::string::npos)
228    {
229       return str.substr(0, str.size()-1);
230    }
231    return str;
232 } // eo chomp(const std:.string&,const std::string&)
233
234
235 /**
236  * @brief returns a lower case version of a given string.
237  * @param str the string
238  * @return the lower case version of the string
239  */
240 std::string to_lower (const std::string& str)
241 {
242    std::string result(str);
243    return to_lower_mod(result);
244 } // eo to_lower(const std::string&)
245
246
247 /**
248  * @brief returns a upper case version of a given string.
249  * @param str the string
250  * @return the upper case version of the string
251  */
252 std::string to_upper(const std::string& str)
253 {
254    std::string result(str);
255    return to_upper_mod(result);
256 } // eo to_upper(const std::string&)
257
258
259
260 /**
261  * @brief removes a given suffix from a string.
262  * @param str the string.
263  * @param suffix the suffix which should be removed if the string ends with it.
264  * @return the string without the suffix.
265  *
266  * If the string ends with the suffix, it is removed. If the the string doesn't end
267  * with the suffix the original string is returned.
268  */
269 std::string remove_suffix(const std::string& str, const std::string& suffix)
270 {
271    if (has_suffix(str,suffix) )
272    {
273       return str.substr(0, str.size()-suffix.size() );
274    }
275    return str;
276 } // eo remove_suffix(const std::string&,const std::string&)
277
278
279
280 /**
281  * @brief removes a given prefix from a string.
282  * @param str the string.
283  * @param prefix the prefix which should be removed if the string begins with it.
284  * @return the string without the prefix.
285  *
286  * If the string begins with the prefix, it is removed. If the the string doesn't begin
287  * with the prefix the original string is returned.
288  */
289 std::string remove_prefix(const std::string& str, const std::string& prefix)
290 {
291    if (has_prefix(str,prefix) )
292    {
293       return str.substr( prefix.size() );
294    }
295    return str;
296 } // eo remove_prefix(const std::string&,const std::string&)
297
298
299 /**
300  * split a string to key and value delimited by a given delimiter.
301  * The resulting key and value strings are trimmed (Whitespaces removed at beginning and end).
302  * @param str the string which should be splitted.
303  * @param[out] key the resulting key
304  * @param[out] value the resulting value
305  * @param delimiter the delimiter between key and value; default is '='.
306  * @return @a true if the split was successful.
307  */
308 bool pair_split(
309    const std::string& str,
310    std::string& key,
311    std::string& value,
312    char delimiter)
313 {
314    std::string::size_type pos = str.find (delimiter);
315    if (pos == std::string::npos) return false;
316    key= str.substr(0,pos);
317    value= str.substr(pos+1);
318    trim_mod(key);
319    trim_mod(value);
320    return true;
321 } // eo pair_split(const std::string&,std::string&,std::string&,char)
322
323
324 /**
325  * splits a string by given delimiter
326  *
327  * @param[in] str the string which should be splitted.
328  * @param[out] result the list resulting from splitting  @a str.
329  * @param[in] delimiter the delimiter (word/phrase) at which @a str should be splitted.
330  * @param[in] omit_empty should empty parts not be stored?
331  * @param[in] trim_list list of characters the parts should be trimmed by.
332  *  (empty string results in no trim)
333  */
334 void split_string(
335    const std::string& str,
336    std::list<std::string>& result,
337    const std::string& delimiter,
338    bool omit_empty,
339    const std::string& trim_list
340 )
341 {
342    std::string::size_type pos, last_pos=0;
343    bool delimiter_found= false;
344    while ( last_pos < str.size()  && last_pos != std::string::npos)
345    {
346       pos= str.find(delimiter, last_pos);
347       std::string part;
348       if (pos == std::string::npos)
349       {
350          part= str.substr(last_pos);
351          delimiter_found= false;
352       }
353       else
354       {
355          part= str.substr(last_pos, pos-last_pos);
356          delimiter_found=true;
357       }
358       if (pos != std::string::npos)
359       {
360          last_pos= pos+ delimiter.size();
361       }
362       else
363       {
364          last_pos= std::string::npos;
365       }
366       if (!trim_list.empty() ) trim_mod (part, trim_list);
367       if (omit_empty && part.empty() ) continue;
368       result.push_back( part );
369    }
370    // if the string ends with a delimiter we need to append an empty string if no omit_empty
371    // was given.
372    // (this way we keep the split result consistent to a join operation)
373    if (delimiter_found && !omit_empty)
374    {
375       result.push_back("");
376    }
377 } // eo split_string(const std::string&,std::list< std::string >&,const std::string&,bool,const std::string&)
378
379
380 /**
381  * splits a string by a given delimiter
382  * @param str the string which should be splitted.
383  * @param delimiter delimiter the delimiter (word/phrase) at which @a str should be splitted.
384  * @param[in] omit_empty should empty parts not be stored?
385  * @param[in] trim_list list of characters the parts should be trimmed by.
386  *  (empty string results in no trim)
387  * @return the list resulting from splitting @a str.
388  */
389 std::list<std::string> split_string(
390    const std::string& str,
391    const std::string& delimiter,
392    bool omit_empty,
393    const std::string& trim_list
394 )
395 {
396    std::list<std::string> result;
397    split_string(str, result, delimiter, omit_empty, trim_list);
398    return result;
399 } // eo split_string(const std::string&,const std::string&,bool,const std::string&)
400
401
402 /**
403  * @brief joins a list of strings into a single string.
404  *
405  * This funtion is (basically) the reverse operation of @a split_string.
406  *
407  * @param parts the list of strings.
408  * @param delimiter the delimiter which is inserted between the strings.
409  * @return the joined string.
410  */
411 std::string join_string(
412    const std::list< std::string >& parts,
413    const std::string& delimiter
414 )
415 {
416    std::string result;
417    if (! parts.empty() )
418    {
419       std::list< std::string >::const_iterator it= parts.begin();
420       result = *it;
421       while ( ++it != parts.end() )
422       {
423          result+= delimiter;
424          result+= *it;
425       }
426    }
427    return result;
428 } // eo join_string(const std::list< std::string >&,const std::string&)
429
430
431
432 /*
433 ** conversions
434 */
435
436
437 /**
438  * @brief returns a hex string from a binary string.
439  * @param str the (binary) string
440  * @param upper_case_digits determine whether to use upper case characters for digits A-F.
441  * @return the string in hex notation.
442  */
443 std::string convert_binary_to_hex(
444    const std::string& str,
445    bool upper_case_digits
446 )
447 {
448    std::string result;
449    std::string hexDigits(upper_case_digits ? hexDigitsUpper : hexDigitsLower);
450    for ( std::string::const_iterator it= str.begin();
451          it != str.end();
452          ++it)
453    {
454       result.push_back( hexDigits[ ( (*it) >> 4) & 0x0f ] );
455       result.push_back( hexDigits[ (*it) & 0x0f ] );
456    }
457    return result;
458 } // eo convert_binary_to_hex(const std::string&,bool)
459
460
461 /**
462  * @brief converts a hex digit string to binary string.
463  * @param str hex digit string
464  * @return the binary string.
465  *
466  * The hex digit string may contains white spaces or colons which are treated
467  * as delimiters between hex digit groups.
468  *
469  * @todo rework the handling of half nibbles (consistency)!
470  */
471 std::string convert_hex_to_binary(
472    const std::string& str
473 )
474 throw (std::runtime_error)
475 {
476    std::string result;
477    char c= 0;
478    bool hasNibble= false;
479    bool lastWasWS= true;
480    for ( std::string::const_iterator it= str.begin();
481          it != str.end();
482          ++it)
483    {
484       std::string::size_type p = hexDigitsLower.find( *it );
485       if (p== std::string::npos)
486       {
487          p= hexDigitsUpper.find( *it );
488       }
489       if (p == std::string::npos)
490       {
491          if (   ( Whitespaces.find( *it ) != std::string::npos) // is it a whitespace?
492                 or ( *it == ':') // or a colon?
493             )
494          {
495             // we treat that as a valid delimiter:
496             if (hasNibble)
497             {
498                // 1 nibble before WS is treate as lower part:
499                result.push_back(c);
500                // reset state:
501                hasNibble= false;
502             }
503             lastWasWS= true;
504             continue;
505          }
506       }
507       if (p == std::string::npos )
508       {
509          throw runtime_error("illegal character in hex digit string: " + str);
510       }
511       lastWasWS= false;
512       if (hasNibble)
513       {
514          c<<=4;
515       }
516       else
517       {
518          c=0;
519       }
520       c+= (p & 0x0f);
521       if (hasNibble)
522       {
523          //we already had a nibble, so a char is complete now:
524          result.push_back( c );
525          hasNibble=false;
526       }
527       else
528       {
529          // this is the first nibble of a new char:
530          hasNibble=true;
531       }
532    }
533    if (hasNibble)
534    {
535       //well, there is one nibble left
536       // let's do some heuristics:
537       if (lastWasWS)
538       {
539          // if the preceeding character was a white space (or a colon)
540          // we treat the nibble as lower part:
541          //( this is consistent with shortened hex notations where leading zeros are not noted)
542          result.push_back( c );
543       }
544       else
545       {
546          // if it was part of a hex digit chain, we treat it as UPPER part (!!)
547          result.push_back( c << 4 );
548       }
549    }
550    return result;
551 } // eo convert_hex_to_binary(const std::string&)
552
553
554 } // eo namespace I2n
555
556
557
558
559 std::string iso_to_utf8(const std::string& isostring)
560 {
561    string result;
562
563    iconv_t i2utf8 = iconv_open("UTF-8", "ISO-8859-1");
564
565    if (iso_to_utf8 == (iconv_t)-1)
566       throw runtime_error("iconv can't convert from ISO-8859-1 to UTF-8");
567
568    size_t in_size=isostring.size();
569    size_t out_size=in_size*4;
570
571    char *buf = (char *)malloc(out_size+1);
572    if (buf == NULL)
573       throw runtime_error("out of memory for iconv buffer");
574
575    char *in = (char *)isostring.c_str();
576    char *out = buf;
577    iconv(i2utf8, &in, &in_size, &out, &out_size);
578
579    buf[isostring.size()*4-out_size]=0;
580
581    result=buf;
582
583    free(buf);
584    iconv_close(i2utf8);
585
586    return result;
587 }
588
589 std::string utf8_to_iso(const std::string& utf8string)
590 {
591    string result;
592
593    iconv_t utf82iso = iconv_open("ISO-8859-1","UTF-8");
594
595    if (utf82iso == (iconv_t)-1)
596       throw runtime_error("iconv can't convert from UTF-8 to ISO-8859-1");
597
598    size_t in_size=utf8string.size();
599    size_t out_size=in_size;
600
601    char *buf = (char *)malloc(out_size+1);
602    if (buf == NULL)
603       throw runtime_error("out of memory for iconv buffer");
604
605    char *in = (char *)utf8string.c_str();
606    char *out = buf;
607    iconv(utf82iso, &in, &in_size, &out, &out_size);
608
609    buf[utf8string.size()-out_size]=0;
610
611    result=buf;
612
613    free(buf);
614    iconv_close(utf82iso);
615
616    return result;
617 }
618
619 wchar_t* utf8_to_wbuf(const std::string& utf8string)
620 {
621    iconv_t utf82wstr = iconv_open("UCS-4LE","UTF-8");
622
623    if (utf82wstr == (iconv_t)-1)
624       throw runtime_error("iconv can't convert from UTF-8 to UCS-4");
625
626    size_t in_size=utf8string.size();
627    size_t out_size= (in_size+1)*sizeof(wchar_t);
628
629    wchar_t *buf = (wchar_t *)malloc(out_size);
630    if (buf == NULL)
631       throw runtime_error("out of memory for iconv buffer");
632
633    char *in = (char *)utf8string.c_str();
634    char *out = (char*) buf;
635    if (iconv(utf82wstr, &in, &in_size, &out, &out_size) == (size_t)-1)
636       throw runtime_error("error converting char encodings");
637
638    buf[ ( (utf8string.size()+1)*sizeof(wchar_t)-out_size) /sizeof(wchar_t) ]=0;
639
640    iconv_close(utf82wstr);
641
642    return buf;
643 }
644
645 std::string utf7imap_to_utf8(const std::string& utf7imapstring)
646 {
647    string result;
648
649    iconv_t utf7imap2utf8 = iconv_open("UTF-8","UTF-7-IMAP");
650
651    if (utf7imap2utf8 == (iconv_t)-1)
652       throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
653
654    size_t in_size=utf7imapstring.size();
655    size_t out_size=in_size*4;
656
657    char *buf = (char *)malloc(out_size+1);
658    if (buf == NULL)
659       throw runtime_error("out of memory for iconv buffer");
660
661    char *in = (char *)utf7imapstring.c_str();
662    char *out = buf;
663    iconv(utf7imap2utf8, &in, &in_size, &out, &out_size);
664
665    buf[utf7imapstring.size()*4-out_size]=0;
666
667    result=buf;
668
669    free(buf);
670    iconv_close(utf7imap2utf8);
671
672    return result;
673 }
674
675 std::string utf8_to_utf7imap(const std::string& utf8string)
676 {
677    string result;
678
679    iconv_t utf82utf7imap = iconv_open("UTF-7-IMAP", "UTF-8");
680
681    if (utf82utf7imap == (iconv_t)-1)
682       throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
683
684    // UTF-7 is base64 encoded, a buffer 10x as large
685    // as the utf-8 buffer should be enough. If not the string will be truncated.
686    size_t in_size=utf8string.size();
687    size_t out_size=in_size*10;
688
689    char *buf = (char *)malloc(out_size+1);
690    if (buf == NULL)
691       throw runtime_error("out of memory for iconv buffer");
692
693    char *in = (char *)utf8string.c_str();
694    char *out = buf;
695    iconv(utf82utf7imap, &in, &in_size, &out, &out_size);
696
697    buf[utf8string.size()*10-out_size]= 0;
698
699    result=buf;
700
701    free(buf);
702    iconv_close(utf82utf7imap);
703
704    return result;
705 }
706
707 // Tokenize string by (html) tags
708 void tokenize_by_tag(vector<pair<string,bool> > &tokenized, const std::string &input)
709 {
710    string::size_type pos, len = input.size();
711    bool inside_tag = false;
712    string current;
713
714    for (pos = 0; pos < len; pos++)
715    {
716       if (input[pos] == '<')
717       {
718          inside_tag = true;
719
720          if (!current.empty() )
721          {
722             tokenized.push_back( make_pair(current, false) );
723             current = "";
724          }
725
726          current += input[pos];
727       }
728       else if (input[pos] == '>' && inside_tag)
729       {
730          current += input[pos];
731          inside_tag = false;
732          if (!current.empty() )
733          {
734             tokenized.push_back( make_pair(current, true) );
735             current = "";
736          }
737       }
738       else
739          current += input[pos];
740    }
741
742    // String left over in buffer?
743    if (!current.empty() )
744       tokenized.push_back( make_pair(current, false) );
745 } // eo tokenize_by_tag
746
747
748 std::string strip_html_tags(const std::string &input)
749 {
750    // Pair first: string, second: isTag
751    vector<pair<string,bool> > tokenized;
752    tokenize_by_tag (tokenized, input);
753
754    string output;
755    vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
756    for (token = tokenized.begin(); token != tokens_end; ++token)
757       if (!token->second)
758          output += token->first;
759
760    return output;
761 } // eo strip_html_tags
762
763
764 // Smart-encode HTML en
765 string smart_html_entities(const std::string &input)
766 {
767    // Pair first: string, second: isTag
768    vector<pair<string,bool> > tokenized;
769    tokenize_by_tag (tokenized, input);
770
771    string output;
772    vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
773    for (token = tokenized.begin(); token != tokens_end; ++token)
774    {
775       // keep HTML tags as they are
776       if (token->second)
777          output += token->first;
778       else
779          output += html_entities(token->first);
780    }
781
782    return output;
783 }
784
785
786 string::size_type find_8bit(const std::string &str)
787 {
788    string::size_type l=str.size();
789    for (string::size_type p=0; p < l; p++)
790       if (static_cast<unsigned char>(str[p]) > 127)
791          return p;
792
793    return string::npos;
794 }
795
796 // encoded UTF-8 chars into HTML entities
797 string html_entities(std::string str)
798 {
799    // Normal chars
800    replace_all (str, "&", "&amp;");
801    replace_all (str, "<", "&lt;");
802    replace_all (str, ">", "&gt;");
803    replace_all (str, "\"", "&quot;");
804    replace_all (str, "'", "&#x27;");
805    replace_all (str, "/", "&#x2F;");
806
807    // Umlauts
808    replace_all (str, "\xC3\xA4", "&auml;");
809    replace_all (str, "\xC3\xB6", "&ouml;");
810    replace_all (str, "\xC3\xBC", "&uuml;");
811    replace_all (str, "\xC3\x84", "&Auml;");
812    replace_all (str, "\xC3\x96", "&Ouml;");
813    replace_all (str, "\xC3\x9C", "&Uuml;");
814
815    // Misc
816    replace_all (str, "\xC3\x9F", "&szlig;");
817
818    // conversion of remaining non-ASCII chars needed?
819    // just do if needed because of performance
820    if (find_8bit(str) != string::npos)
821    {
822       // convert to fixed-size encoding UTF-32
823       wchar_t* wbuf=utf8_to_wbuf(str);
824       ostringstream target;
825
826       // replace all non-ASCII chars with HTML representation
827       for (int p=0; wbuf[p] != 0; p++)
828       {
829          unsigned int c=wbuf[p];
830
831          if (c <= 127)
832             target << static_cast<unsigned char>(c);
833          else
834             target << "&#" << c << ';';
835       }
836
837       free(wbuf);
838
839       str=target.str();
840    }
841
842    return str;
843 } // eo html_entities(std::string)
844
845 // convert HTML entities to something that can be viewed on a basic text console (restricted to ASCII-7)
846 string html_entities_to_console(std::string str)
847 {
848    // Normal chars
849    replace_all (str, "&amp;", "&");
850    replace_all (str, "&lt;", "<");
851    replace_all (str, "&gt;", ">");
852    replace_all (str, "&quot;", "\"");
853    replace_all (str, "&#x27;", "'");
854    replace_all (str, "&#x2F;", "/");
855
856    // Umlauts
857    replace_all (str, "&auml;", "ae");
858    replace_all (str, "&ouml;", "oe");
859    replace_all (str, "&uuml;", "ue");
860    replace_all (str, "&Auml;", "Ae");
861    replace_all (str, "&Ouml;", "Oe");
862    replace_all (str, "&Uuml;", "Ue");
863
864    // Misc
865    replace_all (str, "&szlig;", "ss");
866
867    return str;
868 }
869
870 bool replace_all(string &base, const char *ist, const char *soll)
871 {
872    string i=ist;
873    string s=soll;
874    return replace_all(base,&i,&s);
875 }
876
877 bool replace_all(string &base, const string &ist, const char *soll)
878 {
879    string s=soll;
880    return replace_all(base,&ist,&s);
881 }
882
883 bool replace_all(string &base, const string *ist, const string *soll)
884 {
885    return replace_all(base,*ist,*soll);
886 }
887
888 bool replace_all(string &base, const char *ist, const string *soll)
889 {
890    string i=ist;
891    return replace_all(base,&i,soll);
892 }
893
894 bool replace_all(string &base, const string &ist, const string &soll)
895 {
896    bool found_ist = false;
897    string::size_type a=0;
898
899    if (ist.empty() )
900       throw runtime_error ("replace_all called with empty search string");
901
902    while ( (a=base.find(ist,a) ) != string::npos)
903    {
904       base.replace(a,ist.size(),soll);
905       a=a+soll.size();
906       found_ist = true;
907    }
908
909    return found_ist;
910 }
911
912 #if 0
913 string to_lower(const string &src)
914 {
915    string dst = src;
916
917    string::size_type pos, end = dst.size();
918    for (pos = 0; pos < end; pos++)
919       dst[pos] = tolower(dst[pos]);
920
921    return dst;
922 }
923
924 string to_upper(const string &src)
925 {
926    string dst = src;
927
928    string::size_type pos, end = dst.size();
929    for (pos = 0; pos < end; pos++)
930       dst[pos] = toupper(dst[pos]);
931
932    return dst;
933 }
934 #endif
935
936 const int MAX_UNIT_FORMAT_SYMBOLS = 6;
937
938 const string shortUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
939         " B",
940         " KB",
941         " MB",
942         " GB",
943         " TB",
944         " PB"
945 };
946
947 const string longUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
948         i18n_noop(" Bytes"),
949         i18n_noop(" KBytes"),
950         i18n_noop(" MBytes"),
951         i18n_noop(" GBytes"),
952         i18n_noop(" TBytes"),
953         i18n_noop(" PBytes")
954 };
955
956
957 long double rounding_upwards(
958         long double number,
959         const int rounding_multiplier
960 )
961 {
962     long double rounded_number;
963     rounded_number = number * rounding_multiplier;
964     rounded_number += 0.5;
965     rounded_number = (int64_t) (rounded_number);
966     rounded_number = (long double) (rounded_number) / (long double) (rounding_multiplier);
967
968     return rounded_number;
969 }
970
971
972 string nice_unit_format(
973         const int64_t input,
974         const UnitFormat format,
975         const UnitBase base
976 )
977 {
978    // select the system of units (decimal or binary)
979    int multiple = 0;
980    if (base == UnitBase1000)
981    {
982        multiple = 1000;
983    }
984    else
985    {
986        multiple = 1024;
987    }
988
989    long double size = input;
990
991    // check the size of the input number to fit in the appropriate symbol
992    int sizecount = 0;
993    while (size > multiple)
994    {
995        size = size / multiple;
996        sizecount++;
997
998        // rollback to the previous values and stop the loop when cannot
999        // represent the number length.
1000        if (sizecount >= MAX_UNIT_FORMAT_SYMBOLS)
1001        {
1002            size = size * multiple;
1003            sizecount--;
1004            break;
1005        }
1006    }
1007
1008    // round the input number "half up" to multiples of 10
1009    const int rounding_multiplier = 10;
1010    size = rounding_upwards(size, rounding_multiplier);
1011
1012    // format the input number, placing the appropriate symbol
1013    ostringstream out;
1014    out.setf (ios::fixed);
1015    if (format == ShortUnitFormat)
1016    {
1017        out.precision(1);
1018        out << size << i18n( shortUnitFormatSymbols[sizecount].c_str() );
1019    }
1020    else
1021    {
1022        out.precision (2);
1023        out << size << i18n( longUnitFormatSymbols[sizecount].c_str() );
1024    }
1025
1026    return out.str();
1027 } // eo nice_unit_format(int input)
1028
1029
1030 string escape(const string &s)
1031 {
1032    string out(s);
1033    string::size_type p;
1034
1035    p=0;
1036    while ( (p=out.find_first_of("\"\\",p) ) !=out.npos)
1037    {
1038       out.insert (p,"\\");
1039       p+=2;
1040    }
1041
1042    p=0;
1043    while ( (p=out.find_first_of("\r",p) ) !=out.npos)
1044    {
1045       out.replace (p,1,"\\r");
1046       p+=2;
1047    }
1048
1049    p=0;
1050    while ( (p=out.find_first_of("\n",p) ) !=out.npos)
1051    {
1052       out.replace (p,1,"\\n");
1053       p+=2;
1054    }
1055
1056    out='"'+out+'"';
1057
1058    return out;
1059 } // eo scape(const std::string&)
1060
1061
1062 string descape(const string &s, int startpos, int &endpos)
1063 {
1064    string out;
1065
1066    if (s.at(startpos) != '"')
1067       throw out_of_range("value not type escaped string");
1068
1069    out=s.substr(startpos+1);
1070    string::size_type p=0;
1071
1072    // search for the end of the string
1073    while ( (p=out.find("\"",p) ) !=out.npos)
1074    {
1075       int e=p-1;
1076       bool escaped=false;
1077
1078       // the " might be escaped with a backslash
1079       while (e>=0 && out.at (e) =='\\')
1080       {
1081          if (escaped == false)
1082             escaped=true;
1083          else
1084             escaped=false;
1085
1086          e--;
1087       }
1088
1089       if (escaped==false)
1090          break;
1091       else
1092          p++;
1093    }
1094
1095    // we now have the end of the string
1096    out=out.substr(0,p);
1097
1098    // tell calling prog about the endposition
1099    endpos=startpos+p+1;
1100
1101    // descape all \ stuff inside the string now
1102    p=0;
1103    while ( (p=out.find_first_of("\\",p) ) !=out.npos)
1104    {
1105       switch (out.at(p+1) )
1106       {
1107          case 'r':
1108             out.replace(p,2,"\r");
1109             break;
1110          case 'n':
1111             out.replace(p,2,"\n");
1112             break;
1113          default:
1114             out.erase(p,1);
1115       }
1116       p++;
1117    }
1118
1119    return out;
1120 } // eo descape(const std::string&,int,int&)
1121
1122
1123 string escape_shellarg(const string &input)
1124 {
1125    string output = "'";
1126    string::const_iterator it, it_end = input.end();
1127    for (it = input.begin(); it != it_end; ++it)
1128    {
1129       if ( (*it) == '\'')
1130          output += "'\\'";
1131
1132       output += *it;
1133    }
1134
1135    output += "'";
1136    return output;
1137 }