500% performance gain in to_lower / to_upper
[libi2ncommon] / src / stringfunc.cpp
1 /***************************************************************************
2                           escape.cpp  -  escaping of strings
3                              -------------------
4     begin                : Sun Nov 14 1999
5     copyright            : (C) 1999 by Intra2net AG
6     email                : info@intra2net.com
7  ***************************************************************************/
8
9 #include <iostream>
10 #include <string>
11 #include <sstream>
12 #include <stdexcept>
13
14 #include <stdlib.h>
15 #include <iconv.h>
16 #include <i18n.h>
17
18 #include <stringfunc.hxx>
19
20 using namespace std;
21
22 std::string iso_to_utf8(const std::string& isostring)
23 {
24     string result;
25
26     iconv_t i2utf8 = iconv_open ("UTF-8", "ISO-8859-1");
27
28     if (iso_to_utf8 == (iconv_t)-1)
29         throw runtime_error("iconv can't convert from ISO-8859-1 to UTF-8");
30
31     size_t in_size=isostring.size();
32     size_t out_size=in_size*4;
33
34     char *buf = (char *)malloc(out_size+1);
35     if (buf == NULL)
36         throw runtime_error("out of memory for iconv buffer");
37
38     const char *in = isostring.c_str();
39     char *out = buf;
40     iconv (i2utf8, &in, &in_size, &out, &out_size);
41
42     buf[isostring.size()*4-out_size]=0;
43
44     result=buf;
45
46     free(buf);
47     iconv_close (i2utf8);
48
49     return result;
50 }
51
52 std::string utf8_to_iso(const std::string& utf8string)
53 {
54     string result;
55
56     iconv_t utf82iso = iconv_open ("ISO-8859-1","UTF-8");
57
58     if (utf82iso == (iconv_t)-1)
59         throw runtime_error("iconv can't convert from UTF-8 to ISO-8859-1");
60
61     size_t in_size=utf8string.size();
62     size_t out_size=in_size;
63
64     char *buf = (char *)malloc(out_size+1);
65     if (buf == NULL)
66         throw runtime_error("out of memory for iconv buffer");
67
68     const char *in = utf8string.c_str();
69     char *out = buf;
70     iconv (utf82iso, &in, &in_size, &out, &out_size);
71
72     buf[utf8string.size()-out_size]=0;
73
74     result=buf;
75
76     free(buf);
77     iconv_close (utf82iso);
78
79     return result;
80 }
81
82 std::string utf7imap_to_utf8(const std::string& utf7imapstring)
83 {
84     string result;
85
86     iconv_t utf7imap2utf8 = iconv_open ("UTF-8","UTF-7-IMAP");
87
88     if (utf7imap2utf8 == (iconv_t)-1)
89         throw runtime_error("iconv can't convert from UTF-7-IMAP to UTF-8");
90
91     size_t in_size=utf7imapstring.size();
92     size_t out_size=in_size*4;
93
94     char *buf = (char *)malloc(out_size+1);
95     if (buf == NULL)
96         throw runtime_error("out of memory for iconv buffer");
97
98     const char *in = utf7imapstring.c_str();
99     char *out = buf;
100     iconv (utf7imap2utf8, &in, &in_size, &out, &out_size);
101
102     buf[utf7imapstring.size()*4-out_size]=0;
103
104     result=buf;
105
106     free(buf);
107     iconv_close (utf7imap2utf8);
108
109     return result;
110 }
111
112 // Tokenize string by (html) tags
113 void tokenize_by_tag(vector<pair<string,bool> > &tokenized, const std::string &input)
114 {
115     string::size_type pos, len = input.size();
116     bool inside_tag = false;
117     string current;
118
119     for (pos = 0; pos < len; pos++) {
120         if (input[pos] == '<') {
121             inside_tag = true;
122
123             if (!current.empty()) {
124                 tokenized.push_back(make_pair(current, false));
125                 current = "";
126             }
127
128             current += input[pos];
129         } else if (input[pos] == '>' && inside_tag) {
130             current += input[pos];
131             inside_tag = false;
132             if (!current.empty()) {
133                 tokenized.push_back(make_pair(current, true));
134                 current = "";
135             }
136         } else
137             current += input[pos];
138     }
139
140     // String left over in buffer?
141     if (!current.empty())
142         tokenized.push_back(make_pair(current, false));
143 }
144
145 std::string strip_html_tags(const std::string &input)
146 {
147     // Pair first: string, second: isTag
148     vector<pair<string,bool> > tokenized;
149     tokenize_by_tag(tokenized, input);
150
151     string output;
152     vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
153     for (token = tokenized.begin(); token != tokens_end; token++)
154         if (!token->second)
155             output += token->first;
156
157     return output;
158 }
159
160 // Smart-encode HTML en
161 string smart_html_entities(const std::string &input)
162 {
163     // Pair first: string, second: isTag
164     vector<pair<string,bool> > tokenized;
165     tokenize_by_tag(tokenized, input);
166
167     string output;
168     vector<pair<string,bool> >::const_iterator token, tokens_end = tokenized.end();
169     for (token = tokenized.begin(); token != tokens_end; token++) {
170         // keep HTML tags as they are
171         if (token->second)
172             output += token->first;
173         else
174             output += html_entities(token->first);
175     }
176
177     return output;
178 }
179
180 // encoded UTF-8 chars into HTML entities
181 string html_entities(std::string str)
182 {
183     // Normal chars
184     replace_all (str, "&", "&amp;");
185     replace_all (str, "\"", "&quot;");
186     replace_all (str, "<", "&lt;");
187     replace_all (str, ">", "&gt;");
188
189     // Umlauts
190     replace_all (str, "ä", "&auml;");
191     replace_all (str, "ö", "&ouml;");
192     replace_all (str, "ü", "&uuml;");
193     replace_all (str, "Ä", "&Auml;");
194     replace_all (str, "Ö", "&Ouml;");
195     replace_all (str, "Ü", "&Uuml;");
196
197     // Misc
198     replace_all (str, "ß", "&szlig;");
199
200     return str;
201 }
202
203 bool replace_all(string &base, const char *ist, const char *soll)
204 {
205     string i=ist;
206     string s=soll;
207     return replace_all(base,&i,&s);
208 }
209
210 bool replace_all(string &base, const string &ist, const char *soll)
211 {
212     string s=soll;
213     return replace_all(base,&ist,&s);
214 }
215
216 bool replace_all(string &base, const string *ist, const string *soll)
217 {
218     return replace_all(base,*ist,*soll);
219 }
220
221 bool replace_all(string &base, const char *ist, const string *soll)
222 {
223     string i=ist;
224     return replace_all(base,&i,soll);
225 }
226
227 bool replace_all(string &base, const string &ist, const string &soll)
228 {
229     bool found_ist = false;
230     string::size_type a=0;
231
232     if (ist.empty())
233         throw runtime_error("replace_all called with empty search string");
234
235     while((a=base.find(ist,a))!=string::npos)
236     {
237         base.replace(a,ist.size(),soll);
238         a=a+soll.size();
239         found_ist = true;
240     }
241     
242     return found_ist;
243 }
244
245 string to_lower(const string &src)
246 {
247     string dst = src;
248
249     string::size_type pos, end = dst.size();
250     for (pos = 0; pos < end; pos++)
251         dst[pos] = tolower(dst[pos]);
252
253     return dst;
254 }
255
256 string to_upper(const string &src)
257 {
258     string dst = src;
259
260     string::size_type pos, end = dst.size();
261     for (pos = 0; pos < end; pos++)
262         dst[pos] = toupper(dst[pos]);
263
264     return dst;
265 }
266
267 string nice_unit_format (int input) {
268     float size = input;
269     int sizecount = 0;
270
271     while (size > 1000) {
272         size = size / 1000;
273         sizecount++;
274     }
275
276     float tmp;                       // round
277     tmp = size*10;
278     tmp += 0.5;
279     tmp = int (tmp);
280     tmp = float(tmp)/float(10);
281     size = tmp;
282
283     ostringstream out;
284
285     out.setf (ios::fixed);
286     out.precision(2);
287     switch (sizecount) {
288     case 1:
289         out << size << i18n(" KBytes");
290         break;
291     case 2:
292         out << size << i18n(" MBytes");
293         break;
294     case 3:
295         out << size << i18n(" Gbytes");
296         break;
297     default:
298         out << size << i18n(" Bytes");
299         break;
300     }
301
302     return out.str();
303 }
304
305 string escape(const string &s)
306 {
307     string out(s);
308     string::size_type p;
309
310     p=0;
311     while ((p=out.find_first_of("\"\\",p))!=out.npos)
312     {
313         out.insert(p,"\\");
314         p+=2;
315     }
316
317     p=0;
318     while ((p=out.find_first_of("\r",p))!=out.npos)
319     {
320         out.replace(p,1,"\\r");
321         p+=2;
322     }
323
324     p=0;
325     while ((p=out.find_first_of("\n",p))!=out.npos)
326     {
327         out.replace(p,1,"\\n");
328         p+=2;
329     }
330
331     out='"'+out+'"';
332
333     return out;
334 }
335
336 string descape(const string &s, int startpos, int &endpos)
337 {
338     string out;
339
340     if (s.at(startpos) != '"')
341         throw out_of_range("value not type escaped string");
342
343     out=s.substr(startpos+1);
344     string::size_type p=0;
345
346     // search for the end of the string
347     while((p=out.find("\"",p))!=out.npos)
348     {
349         int e=p-1;
350         bool escaped=false;
351
352         // the " might be escaped with a backslash
353         while(e>=0 && out.at(e)=='\\')
354         {
355             if (escaped == false)
356                 escaped=true;
357             else
358                 escaped=false;
359
360             e--;
361         }
362
363         if (escaped==false)
364             break;
365         else
366             p++;
367     }
368
369     // we now have the end of the string
370     out=out.substr(0,p);
371
372     // tell calling prog about the endposition
373     endpos=startpos+p+1;
374
375     // descape all \ stuff inside the string now
376     p=0;
377     while((p=out.find_first_of("\\",p))!=out.npos)
378     {
379         switch(out.at(p+1))
380         {
381         case 'r':
382             out.replace(p,2,"\r");
383             break;
384         case 'n':
385             out.replace(p,2,"\n");
386             break;
387         default:
388             out.erase(p,1);
389         }
390         p++;
391     }
392
393     return out;
394 }
395
396 string escape_shellarg(const string &input)
397 {
398     if (!input.size())
399         return "";
400     
401     string output = "'";
402     string::const_iterator it, it_end = input.end();
403     for (it = input.begin(); it != it_end; it++) {
404         if ((*it) == '\'')
405             output += "'\\'";
406         
407         output += *it;
408     }
409     
410     output += "'";
411     return output;
412 }