2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
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.
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.
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.
22 * (c) Copyright 2007-2008 by Intra2net AG
30 #include <cmath> // for round()
37 #include <boost/numeric/conversion/cast.hpp>
38 #include <boost/foreach.hpp>
40 #include <stringfunc.hxx>
51 const std::string hexDigitsLower("0123456789abcdef");
52 const std::string hexDigitsUpper("0123456789ABCDEF");
57 char operator() (char c)
59 return std::toupper(c);
61 }; // eo struct UpperFunc
66 char operator() (char c)
68 return std::tolower(c);
70 }; // eo struct LowerFunc
73 } // eo namespace <anonymous>
78 * default list of Whitespaces (" \t\r\n");
80 const std::string Whitespaces = " \t\r\n";
83 * default list of lineendings ("\r\n");
85 const std::string LineEndings= "\r\n";
90 * @brief checks if a string begins with a given prefix.
91 * @param[in,out] str the string which is tested
92 * @param prefix the prefix which should be tested for.
93 * @return @a true iff the prefix is not empty and the string begins with that prefix.
95 bool has_prefix(const std::string& str, const std::string& prefix)
97 if (prefix.empty() || str.empty() || str.size() < prefix.size() )
101 return str.compare(0, prefix.size(), prefix) == 0;
102 } // eo has_prefix(const std::string&,const std::string&)
106 * @brief checks if a string ends with a given suffix.
107 * @param[in,out] str the string which is tested
108 * @param suffix the suffix which should be tested for.
109 * @return @a true iff the suffix is not empty and the string ends with that suffix.
111 bool has_suffix(const std::string& str, const std::string& suffix)
113 if (suffix.empty() || str.empty() || str.size() < suffix.size() )
117 return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
118 } // eo has_suffix(const std::string&,const std::string&)
122 * cut off characters from a given list from front and end of a string.
123 * @param[in,out] str the string which should be trimmed.
124 * @param charlist the list of characters to remove from beginning and end of string
125 * @return the result string.
127 std::string trim_mod(std::string& str, const std::string& charlist)
129 // first: trim the beginning:
130 std::string::size_type pos= str.find_first_not_of (charlist);
131 if (pos == std::string::npos)
133 // whole string consists of charlist (or is already empty)
139 // str starts with charlist
142 // now let's look at the tail:
143 pos= str.find_last_not_of(charlist) +1; // note: we already know there is at least one other char!
144 if ( pos < str.size() )
146 str.erase(pos, str.size()-pos);
149 } // eo trim_mod(std::string&,const std::string&)
154 * removes last character from a string when it is in a list of chars to be removed.
155 * @param[in,out] str the string.
156 * @param what the list of chars which will be tested for.
157 * @return the resulting string with last char removed (if applicable)
159 std::string chomp_mod(std::string& str, const std::string& what)
161 if (str.empty() || what.empty() )
165 if (what.find(str.at (str.size()-1) ) != std::string::npos)
167 str.erase(str.size() - 1);
170 } // eo chomp_mod(std::string&,const std::string&)
174 * @brief converts a string to lower case.
175 * @param[in,out] str the string to modify.
178 std::string to_lower_mod(std::string& str)
180 std::transform(str.begin(), str.end(), str.begin(), LowerFunc() );
182 } // eo to_lower_mod(std::string&)
186 * @brief converts a string to upper case.
187 * @param[in,out] str the string to modify.
190 std::string to_upper_mod(std::string& str)
192 std::transform( str.begin(), str.end(), str.begin(), UpperFunc() );
194 } // eo to_upper_mod(std::string&)
199 * cut off characters from a given list from front and end of a string.
200 * @param str the string which should be trimmed.
201 * @param charlist the list of characters to remove from beginning and end of string
202 * @return the result string.
204 std::string trim (const std::string& str, const std::string& charlist)
206 // first: trim the beginning:
207 std::string::size_type pos0= str.find_first_not_of(charlist);
208 if (pos0 == std::string::npos)
210 // whole string consists of charlist (or is already empty)
211 return std::string();
213 // now let's look at the end:
214 std::string::size_type pos1= str.find_last_not_of(charlist);
215 return str.substr(pos0, pos1 - pos0 + 1);
216 } // eo trim(const std:.string&,const std::string&)
220 * removes last character from a string when it is in a list of chars to be removed.
221 * @param str the string.
222 * @param what the list of chars which will be tested for.
223 * @return the resulting string with last char removed (if applicable)
225 std::string chomp (const std::string& str, const std::string& what)
227 if (str.empty() || what.empty() )
231 if (what.find(str.at (str.size()-1) ) != std::string::npos)
233 return str.substr(0, str.size()-1);
236 } // eo chomp(const std:.string&,const std::string&)
240 * @brief returns a lower case version of a given string.
241 * @param str the string
242 * @return the lower case version of the string
244 std::string to_lower (const std::string& str)
246 std::string result(str);
247 return to_lower_mod(result);
248 } // eo to_lower(const std::string&)
252 * @brief returns a upper case version of a given string.
253 * @param str the string
254 * @return the upper case version of the string
256 std::string to_upper(const std::string& str)
258 std::string result(str);
259 return to_upper_mod(result);
260 } // eo to_upper(const std::string&)
265 * @brief removes a given suffix from a string.
266 * @param str the string.
267 * @param suffix the suffix which should be removed if the string ends with it.
268 * @return the string without the suffix.
270 * If the string ends with the suffix, it is removed. If the the string doesn't end
271 * with the suffix the original string is returned.
273 std::string remove_suffix(const std::string& str, const std::string& suffix)
275 if (has_suffix(str,suffix) )
277 return str.substr(0, str.size()-suffix.size() );
280 } // eo remove_suffix(const std::string&,const std::string&)
285 * @brief removes a given prefix from a string.
286 * @param str the string.
287 * @param prefix the prefix which should be removed if the string begins with it.
288 * @return the string without the prefix.
290 * If the string begins with the prefix, it is removed. If the the string doesn't begin
291 * with the prefix the original string is returned.
293 std::string remove_prefix(const std::string& str, const std::string& prefix)
295 if (has_prefix(str,prefix) )
297 return str.substr( prefix.size() );
300 } // eo remove_prefix(const std::string&,const std::string&)
304 * split a string to key and value delimited by a given delimiter.
305 * The resulting key and value strings are trimmed (Whitespaces removed at beginning and end).
306 * @param str the string which should be splitted.
307 * @param[out] key the resulting key
308 * @param[out] value the resulting value
309 * @param delimiter the delimiter between key and value; default is '='.
310 * @return @a true if the split was successful.
313 const std::string& str,
318 std::string::size_type pos = str.find (delimiter);
319 if (pos == std::string::npos) return false;
320 key= str.substr(0,pos);
321 value= str.substr(pos+1);
325 } // eo pair_split(const std::string&,std::string&,std::string&,char)
329 * splits a string by given delimiter
331 * @param[in] str the string which should be splitted.
332 * @param[out] result the list resulting from splitting @a str.
333 * @param[in] delimiter the delimiter (word/phrase) at which @a str should be splitted.
334 * @param[in] omit_empty should empty parts not be stored?
335 * @param[in] trim_list list of characters the parts should be trimmed by.
336 * (empty string results in no trim)
339 const std::string& str,
340 std::list<std::string>& result,
341 const std::string& delimiter,
343 const std::string& trim_list
346 std::string::size_type pos, last_pos=0;
347 bool delimiter_found= false;
348 while ( last_pos < str.size() && last_pos != std::string::npos)
350 pos= str.find(delimiter, last_pos);
352 if (pos == std::string::npos)
354 part= str.substr(last_pos);
355 delimiter_found= false;
359 part= str.substr(last_pos, pos-last_pos);
360 delimiter_found=true;
362 if (pos != std::string::npos)
364 last_pos= pos+ delimiter.size();
368 last_pos= std::string::npos;
370 if (!trim_list.empty() ) trim_mod (part, trim_list);
371 if (omit_empty && part.empty() ) continue;
372 result.push_back( part );
374 // if the string ends with a delimiter we need to append an empty string if no omit_empty
376 // (this way we keep the split result consistent to a join operation)
377 if (delimiter_found && !omit_empty)
379 result.push_back("");
381 } // eo split_string(const std::string&,std::list< std::string >&,const std::string&,bool,const std::string&)
385 * splits a string by a given delimiter
386 * @param str the string which should be splitted.
387 * @param delimiter delimiter the delimiter (word/phrase) at which @a str should be splitted.
388 * @param[in] omit_empty should empty parts not be stored?
389 * @param[in] trim_list list of characters the parts should be trimmed by.
390 * (empty string results in no trim)
391 * @return the list resulting from splitting @a str.
393 std::list<std::string> split_string(
394 const std::string& str,
395 const std::string& delimiter,
397 const std::string& trim_list
400 std::list<std::string> result;
401 split_string(str, result, delimiter, omit_empty, trim_list);
403 } // eo split_string(const std::string&,const std::string&,bool,const std::string&)
407 * @brief joins a list of strings into a single string.
409 * This funtion is (basically) the reverse operation of @a split_string.
411 * @param parts the list of strings.
412 * @param delimiter the delimiter which is inserted between the strings.
413 * @return the joined string.
415 std::string join_string(
416 const std::list< std::string >& parts,
417 const std::string& delimiter
421 if (! parts.empty() )
423 std::list< std::string >::const_iterator it= parts.begin();
425 while ( ++it != parts.end() )
432 } // eo join_string(const std::list< std::string >&,const std::string&)
435 /** @brief same as join_string for list, except uses a vector */
436 std::string join_string(
437 const std::vector< std::string >& parts,
438 const std::string& delimiter
442 if (! parts.empty() )
444 std::vector< std::string >::const_iterator it= parts.begin();
446 while ( ++it != parts.end() )
453 } // eo join_string(const std::vector< std::string >&,const std::string&)
463 * @brief returns a hex string from a binary string.
464 * @param str the (binary) string
465 * @param upper_case_digits determine whether to use upper case characters for digits A-F.
466 * @return the string in hex notation.
468 std::string convert_binary_to_hex(
469 const std::string& str,
470 bool upper_case_digits
474 std::string hexDigits(upper_case_digits ? hexDigitsUpper : hexDigitsLower);
475 for ( std::string::const_iterator it= str.begin();
479 result.push_back( hexDigits[ ( (*it) >> 4) & 0x0f ] );
480 result.push_back( hexDigits[ (*it) & 0x0f ] );
483 } // eo convert_binary_to_hex(const std::string&,bool)
487 * @brief converts a hex digit string to binary string.
488 * @param str hex digit string
489 * @return the binary string.
491 * The hex digit string may contains white spaces or colons which are treated
492 * as delimiters between hex digit groups.
494 * @todo rework the handling of half nibbles (consistency)!
496 std::string convert_hex_to_binary(
497 const std::string& str
499 throw (std::runtime_error)
503 bool hasNibble= false;
504 bool lastWasWS= true;
505 for ( std::string::const_iterator it= str.begin();
509 std::string::size_type p = hexDigitsLower.find( *it );
510 if (p== std::string::npos)
512 p= hexDigitsUpper.find( *it );
514 if (p == std::string::npos)
516 if ( ( Whitespaces.find( *it ) != std::string::npos) // is it a whitespace?
517 or ( *it == ':') // or a colon?
520 // we treat that as a valid delimiter:
523 // 1 nibble before WS is treate as lower part:
532 if (p == std::string::npos )
534 throw runtime_error("illegal character in hex digit string: " + str);
548 //we already had a nibble, so a char is complete now:
549 result.push_back( c );
554 // this is the first nibble of a new char:
560 //well, there is one nibble left
561 // let's do some heuristics:
564 // if the preceeding character was a white space (or a colon)
565 // we treat the nibble as lower part:
566 //( this is consistent with shortened hex notations where leading zeros are not noted)
567 result.push_back( c );
571 // if it was part of a hex digit chain, we treat it as UPPER part (!!)
572 result.push_back( c << 4 );
576 } // eo convert_hex_to_binary(const std::string&)
579 } // eo namespace I2n
584 std::string iso_to_utf8(const std::string& isostring)
588 iconv_t i2utf8 = iconv_open("UTF-8", "ISO-8859-1");
590 if (iso_to_utf8 == (iconv_t)-1)
591 throw runtime_error("iconv can't convert from ISO-8859-1 to UTF-8");
593 size_t in_size=isostring.size();
594 size_t out_size=in_size*4;
596 char *buf = (char *)malloc(out_size+1);
598 throw runtime_error("out of memory for iconv buffer");
600 char *in = (char *)isostring.c_str();
602 iconv(i2utf8, &in, &in_size, &out, &out_size);
604 buf[isostring.size()*4-out_size]=0;
614 std::string utf8_to_iso(const std::string& utf8string)
618 iconv_t utf82iso = iconv_open("ISO-8859-1","UTF-8");
620 if (utf82iso == (iconv_t)-1)
621 throw runtime_error("iconv can't convert from UTF-8 to ISO-8859-1");
623 size_t in_size=utf8string.size();
624 size_t out_size=in_size;
626 char *buf = (char *)malloc(out_size+1);
628 throw runtime_error("out of memory for iconv buffer");
630 char *in = (char *)utf8string.c_str();
632 iconv(utf82iso, &in, &in_size, &out, &out_size);
634 buf[utf8string.size()-out_size]=0;
639 iconv_close(utf82iso);
644 wchar_t* utf8_to_wbuf(const std::string& utf8string)
646 iconv_t utf82wstr = iconv_open("UCS-4LE","UTF-8");
648 if (utf82wstr == (iconv_t)-1)
649 throw runtime_error("iconv can't convert from UTF-8 to UCS-4");
651 size_t in_size=utf8string.size();
652 size_t out_size= (in_size+1)*sizeof(wchar_t);
654 wchar_t *buf = (wchar_t *)malloc(out_size);
656 throw runtime_error("out of memory for iconv buffer");
658 char *in = (char *)utf8string.c_str();
659 char *out = (char*) buf;
660 if (iconv(utf82wstr, &in, &in_size, &out, &out_size) == (size_t)-1)
661 throw runtime_error("error converting char encodings");
663 buf[ ( (utf8string.size()+1)*sizeof(wchar_t)-out_size) /sizeof(wchar_t) ]=0;
665 iconv_close(utf82wstr);
670 std::string utf7imap_to_utf8(const std::string& utf7imapstring)
674 iconv_t utf7imap2utf8 = iconv_open("UTF-8","UTF-7-IMAP");
676 if (utf7imap2utf8 == (iconv_t)-1)
677 throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
679 size_t in_size=utf7imapstring.size();
680 size_t out_size=in_size*4;
682 char *buf = (char *)malloc(out_size+1);
684 throw runtime_error("out of memory for iconv buffer");
686 char *in = (char *)utf7imapstring.c_str();
688 iconv(utf7imap2utf8, &in, &in_size, &out, &out_size);
690 buf[utf7imapstring.size()*4-out_size]=0;
695 iconv_close(utf7imap2utf8);
700 std::string utf8_to_utf7imap(const std::string& utf8string)
704 iconv_t utf82utf7imap = iconv_open("UTF-7-IMAP", "UTF-8");
706 if (utf82utf7imap == (iconv_t)-1)
707 throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
709 // UTF-7 is base64 encoded, a buffer 10x as large
710 // as the utf-8 buffer should be enough. If not the string will be truncated.
711 size_t in_size=utf8string.size();
712 size_t out_size=in_size*10;
714 char *buf = (char *)malloc(out_size+1);
716 throw runtime_error("out of memory for iconv buffer");
718 char *in = (char *)utf8string.c_str();
720 iconv(utf82utf7imap, &in, &in_size, &out, &out_size);
722 buf[utf8string.size()*10-out_size]= 0;
727 iconv_close(utf82utf7imap);
732 // Tokenize string by (html) tags
733 void tokenize_by_tag(vector<pair<string,bool> > &tokenized, const std::string &input)
735 string::size_type pos, len = input.size();
736 bool inside_tag = false;
739 for (pos = 0; pos < len; pos++)
741 if (input[pos] == '<')
745 if (!current.empty() )
747 tokenized.push_back( make_pair(current, false) );
751 current += input[pos];
753 else if (input[pos] == '>' && inside_tag)
755 current += input[pos];
757 if (!current.empty() )
759 tokenized.push_back( make_pair(current, true) );
764 current += input[pos];
767 // String left over in buffer?
768 if (!current.empty() )
769 tokenized.push_back( make_pair(current, false) );
770 } // eo tokenize_by_tag
773 std::string strip_html_tags(const std::string &input)
775 // Pair first: string, second: isTag
776 vector<pair<string,bool> > tokenized;
777 tokenize_by_tag (tokenized, input);
780 vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
781 for (token = tokenized.begin(); token != tokens_end; ++token)
783 output += token->first;
786 } // eo strip_html_tags
789 // Smart-encode HTML en
790 string smart_html_entities(const std::string &input)
792 // Pair first: string, second: isTag
793 vector<pair<string,bool> > tokenized;
794 tokenize_by_tag (tokenized, input);
797 vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
798 for (token = tokenized.begin(); token != tokens_end; ++token)
800 // keep HTML tags as they are
802 output += token->first;
804 output += html_entities(token->first);
811 string::size_type find_8bit(const std::string &str)
813 string::size_type l=str.size();
814 for (string::size_type p=0; p < l; p++)
815 if (static_cast<unsigned char>(str[p]) > 127)
821 // encoded UTF-8 chars into HTML entities
822 string html_entities(std::string str)
825 replace_all (str, "&", "&");
826 replace_all (str, "<", "<");
827 replace_all (str, ">", ">");
828 replace_all (str, "\"", """);
829 replace_all (str, "'", "'");
830 replace_all (str, "/", "/");
833 replace_all (str, "\xC3\xA4", "ä");
834 replace_all (str, "\xC3\xB6", "ö");
835 replace_all (str, "\xC3\xBC", "ü");
836 replace_all (str, "\xC3\x84", "Ä");
837 replace_all (str, "\xC3\x96", "Ö");
838 replace_all (str, "\xC3\x9C", "Ü");
841 replace_all (str, "\xC3\x9F", "ß");
843 // conversion of remaining non-ASCII chars needed?
844 // just do if needed because of performance
845 if (find_8bit(str) != string::npos)
847 // convert to fixed-size encoding UTF-32
848 wchar_t* wbuf=utf8_to_wbuf(str);
849 ostringstream target;
851 // replace all non-ASCII chars with HTML representation
852 for (int p=0; wbuf[p] != 0; p++)
854 unsigned int c=wbuf[p];
857 target << static_cast<unsigned char>(c);
859 target << "&#" << c << ';';
868 } // eo html_entities(std::string)
870 // convert HTML entities to something that can be viewed on a basic text console (restricted to ASCII-7)
871 string html_entities_to_console(std::string str)
874 replace_all (str, "&", "&");
875 replace_all (str, "<", "<");
876 replace_all (str, ">", ">");
877 replace_all (str, """, "\"");
878 replace_all (str, "'", "'");
879 replace_all (str, "/", "/");
882 replace_all (str, "ä", "ae");
883 replace_all (str, "ö", "oe");
884 replace_all (str, "ü", "ue");
885 replace_all (str, "Ä", "Ae");
886 replace_all (str, "Ö", "Oe");
887 replace_all (str, "Ü", "Ue");
890 replace_all (str, "ß", "ss");
895 // find_html_comments + remove_html_comments(str, comments)
896 void remove_html_comments(string &str)
898 vector<CommentZone> comments;
899 find_html_comments(str, comments);
900 remove_html_comments(str, comments);
903 // find all html comments, behaving correctly if they are nested; ignores comment tags ("<!--FOO .... BAR-->")
904 // If there are invalid comments ("-->" before "<!--" or different number of closing and opening tags),
905 // then the unknown index of corresponding start/end tag will be represented by a string::npos
906 // Indices are from start of start tag until first index after closing tag
907 void find_html_comments(const std::string &str, vector<CommentZone> &comments)
909 static const string START = "<!--";
910 static const string CLOSE = "-->";
911 static const string::size_type START_LEN = START.length();
912 static const string::size_type CLOSE_LEN = CLOSE.length();
914 // in order to find nested comments, need either recursion or a stack
915 vector<string::size_type> starts; // stack of start tags
917 string::size_type pos = 0;
918 string::size_type len = str.length();
919 string::size_type next_start, next_close;
921 while (pos < len) // not really needed but just in case
923 next_start = str.find(START, pos);
924 next_close = str.find(CLOSE, pos);
926 if ( (next_start == string::npos) && (next_close == string::npos) )
927 break; // we are done
929 else if ( (next_start == string::npos) || (next_close < next_start) ) // close one comment (pop)
931 if (starts.empty()) // closing tag without a start
932 comments.push_back(CommentZone(string::npos, next_close+CLOSE_LEN));
935 comments.push_back(CommentZone(starts.back(), next_close+CLOSE_LEN));
938 pos = next_close + CLOSE_LEN;
941 else if ( (next_close == string::npos) || (next_start < next_close) ) // start a new comment (push)
943 starts.push_back(next_start);
944 pos = next_start + START_LEN;
948 // add comments that have no closing tag from back to front (important for remove_html_comments!)
949 while (!starts.empty())
951 comments.push_back(CommentZone(starts.back(), string::npos));
956 // remove all html comments foundby find_html_comments
957 void remove_html_comments(std::string &str, const vector<CommentZone> &comments)
959 // remember position where last removal started
960 string::size_type last_removal_start = str.length();
962 // Go from back to front to not mess up indices.
963 // This requires that bigger comments, that contain smaller comments, come AFTER
964 // the small contained comments in the comments vector (i.e. comments are ordered by
965 // their closing tag, not their opening tag). This is true for results from find_html_comments
966 BOOST_REVERSE_FOREACH(const CommentZone &comment, comments)
968 if (comment.first == string::npos)
970 str = str.replace(0, comment.second, ""); // comment starts "before" str --> delete from start
971 break; // there can be no more
973 else if (comment.first >= last_removal_start)
975 continue; // this comment is inside another comment that we have removed already
977 else if (comment.second == string::npos) // comment ends "after" str --> delete until end
979 str = str.replace(comment.first, string::npos, "");
980 last_removal_start = comment.first;
984 str = str.replace(comment.first, comment.second-comment.first, "");
985 last_removal_start = comment.first;
990 bool replace_all(string &base, const char *ist, const char *soll)
994 return replace_all(base,&i,&s);
997 bool replace_all(string &base, const string &ist, const char *soll)
1000 return replace_all(base,&ist,&s);
1003 bool replace_all(string &base, const string *ist, const string *soll)
1005 return replace_all(base,*ist,*soll);
1008 bool replace_all(string &base, const char *ist, const string *soll)
1011 return replace_all(base,&i,soll);
1014 bool replace_all(string &base, const string &ist, const string &soll)
1016 bool found_ist = false;
1017 string::size_type a=0;
1020 throw runtime_error ("replace_all called with empty search string");
1022 while ( (a=base.find(ist,a) ) != string::npos)
1024 base.replace(a,ist.size(),soll);
1033 * @brief replaces all characters that could be problematic or impose a security risk when being logged
1034 * @param str the original string
1035 * @param replace_with the character to replace the unsafe chars with
1036 * @return a string that is safe to send to syslog or other logfiles
1038 * All chars between 0x20 (space) and 0x7E (~) (including) are considered safe for logging.
1039 * See e.g. RFC 5424, section 8.2 or the posix character class "printable".
1040 * This eliminates all possible problems with NUL, control characters, 8 bit chars, UTF8.
1043 std::string sanitize_for_logging(const std::string &str, const char replace_with)
1045 std::string output=str;
1047 const string::size_type len = output.size();
1048 for (std::string::size_type p=0; p < len; p++)
1049 if (output[p] < 0x20 || output[p] > 0x7E)
1050 output[p]=replace_with;
1056 string to_lower(const string &src)
1060 string::size_type pos, end = dst.size();
1061 for (pos = 0; pos < end; pos++)
1062 dst[pos] = tolower(dst[pos]);
1067 string to_upper(const string &src)
1071 string::size_type pos, end = dst.size();
1072 for (pos = 0; pos < end; pos++)
1073 dst[pos] = toupper(dst[pos]);
1079 const int MAX_UNIT_FORMAT_SYMBOLS = 6;
1081 const string shortUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
1090 const string longUnitFormatSymbols[MAX_UNIT_FORMAT_SYMBOLS] = {
1091 i18n_noop(" Bytes"),
1092 i18n_noop(" KBytes"),
1093 i18n_noop(" MBytes"),
1094 i18n_noop(" GBytes"),
1095 i18n_noop(" TBytes"),
1096 i18n_noop(" PBytes")
1100 long double rounding_upwards(
1101 const long double number,
1102 const int rounding_multiplier
1105 long double rounded_number;
1106 rounded_number = number * rounding_multiplier;
1107 rounded_number += 0.5;
1108 rounded_number = (int64_t) (rounded_number);
1109 rounded_number = (long double) (rounded_number) / (long double) (rounding_multiplier);
1111 return rounded_number;
1115 string nice_unit_format(
1116 const int64_t input,
1117 const UnitFormat format,
1121 // select the system of units (decimal or binary)
1123 if (base == UnitBase1000)
1132 long double size = input;
1134 // check the size of the input number to fit in the appropriate symbol
1136 while (size > multiple)
1138 size = size / multiple;
1141 // rollback to the previous values and stop the loop when cannot
1142 // represent the number length.
1143 if (sizecount >= MAX_UNIT_FORMAT_SYMBOLS)
1145 size = size * multiple;
1151 // round the input number "half up" to multiples of 10
1152 const int rounding_multiplier = 10;
1153 size = rounding_upwards(size, rounding_multiplier);
1155 // format the input number, placing the appropriate symbol
1157 out.setf (ios::fixed);
1158 if (format == ShortUnitFormat)
1161 out << size << i18n( shortUnitFormatSymbols[sizecount].c_str() );
1166 out << size << i18n( longUnitFormatSymbols[sizecount].c_str() );
1170 } // eo nice_unit_format(int input)
1173 string nice_unit_format(
1175 const UnitFormat format,
1179 // round as double and cast to int64_t
1180 // cast raised overflow error near max val of int64_t (~9.2e18, see unittest)
1181 int64_t input_casted_and_rounded =
1182 boost::numeric_cast<int64_t>( round(input) );
1185 return nice_unit_format( input_casted_and_rounded, format, base );
1186 } // eo nice_unit_format(double input)
1189 string escape(const string &s)
1192 string::size_type p;
1195 while ( (p=out.find_first_of("\"\\",p) ) !=out.npos)
1197 out.insert (p,"\\");
1202 while ( (p=out.find_first_of("\r",p) ) !=out.npos)
1204 out.replace (p,1,"\\r");
1209 while ( (p=out.find_first_of("\n",p) ) !=out.npos)
1211 out.replace (p,1,"\\n");
1218 } // eo scape(const std::string&)
1221 string descape(const string &s, int startpos, int &endpos)
1225 if (s.at(startpos) != '"')
1226 throw out_of_range("value not type escaped string");
1228 out=s.substr(startpos+1);
1229 string::size_type p=0;
1231 // search for the end of the string
1232 while ( (p=out.find("\"",p) ) !=out.npos)
1237 // the " might be escaped with a backslash
1238 while (e>=0 && out.at (e) =='\\')
1240 if (escaped == false)
1254 // we now have the end of the string
1255 out=out.substr(0,p);
1257 // tell calling prog about the endposition
1258 endpos=startpos+p+1;
1260 // descape all \ stuff inside the string now
1262 while ( (p=out.find_first_of("\\",p) ) !=out.npos)
1264 switch (out.at(p+1) )
1267 out.replace(p,2,"\r");
1270 out.replace(p,2,"\n");
1279 } // eo descape(const std::string&,int,int&)
1282 string escape_shellarg(const string &input)
1284 string output = "'";
1285 string::const_iterator it, it_end = input.end();
1286 for (it = input.begin(); it != it_end; ++it)