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