Fix octal escape generation in c_escape
[libs/core.git] / source / utils.cpp
1 /* $Id$
2
3 This file is part of libmspstrings
4 Copyright © 2006-2008 Mikko Rasa
5 Distributed under the LGPL
6 */
7
8 #include <algorithm>
9 #include <list>
10 #include <msp/core/except.h>
11 #include "utils.h"
12
13 using namespace std;
14
15 namespace {
16
17 template<bool long_sep, bool allow_empty>
18 vector<string> do_split(const string &str, const string &sep, int max_split)
19 {
20         vector<string> result;
21
22         unsigned start=0;
23         while(start<str.size())
24         {
25                 unsigned end=long_sep ? str.find(sep, start) : str.find_first_of(sep, start);
26                 if(end!=start || allow_empty)
27                 {
28                         if(max_split>=0 && result.size()==static_cast<unsigned>(max_split))
29                         {
30                                 result.push_back(str.substr(start));
31                                 break;
32                         }
33                         else
34                                 result.push_back(str.substr(start, end-start));
35                 }
36
37                 if(end>str.size())
38                         break;
39
40                 start=end+(long_sep ? sep.size() : 1);
41
42                 if(allow_empty && start==str.size())
43                         result.push_back(string());
44         }
45
46         return result;
47 }
48
49 bool check_str(const std::string &str, int (*pred)(int))
50 {
51         for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
52                 if(!pred(*i))
53                         return false;
54         return true;
55 }
56
57 }
58
59 namespace Msp {
60
61 int strcasecmp(const string &s1, const string &s2)
62 {
63         string::const_iterator i1=s1.begin();
64         string::const_iterator i2=s2.begin();
65         for(; (i1!=s1.end() && i2!=s2.end()); ++i1, ++i2)
66         {
67                 const char c1=::tolower(*i1);
68                 const char c2=::tolower(*i2);
69                 if(c1!=c2) return c1-c2;
70         }
71         if(i1!=s1.end()) return *i1;
72         if(i2!=s2.end()) return -*i2;
73         return 0;
74 }
75
76 string tolower(const string &str)
77 {
78         string result(str);
79         transform(result.begin(), result.end(), result.begin(), ::tolower);
80         return result;
81 }
82
83 string toupper(const string &str)
84 {
85         string result(str);
86         transform(result.begin(), result.end(), result.begin(), ::toupper);
87         return result;
88 }
89
90 bool isnumrc(const string &str)
91 {
92         return check_str(str, isdigit);
93 }
94
95 bool isalpha(const string &str)
96 {
97         return check_str(str, isalpha);
98 }
99
100 bool isalnum(const string &str)
101 {
102         return check_str(str, isalnum);
103 }
104
105 vector<string> split(const string &str, const string &sep, int max_split)
106 {
107         return do_split<false, false>(str, sep, max_split);
108 }
109
110 vector<string> split(const string &str, char sep, int max_split)
111 {
112         return split(str, string(1, sep), max_split);
113 }
114
115 vector<string> split_long(const string &str, const string &sep, int max_split)
116 {
117         return do_split<true, false>(str, sep, max_split);
118 }
119
120 vector<string> split_fields(const string &str, const string &sep, int max_split)
121 {
122         return do_split<true, true>(str, sep, max_split);
123 }
124
125 vector<string> split_fields(const string &str, char sep, int max_split)
126 {
127         return split_fields(str, string(1, sep), max_split);
128 }
129
130 /**
131 Splits a string to parts.
132
133 @param   str          String to be split
134 @param   sep          A set of separator characters
135 @param   allow_empty  Whether or not to produce empty parts for sequences of
136                       more than one separator character
137 */
138 vector<string> split(const string &str, const string &sep, bool allow_empty)
139 {
140         vector<string> result;
141         
142         unsigned start=0;
143         if(!allow_empty)
144                 start=str.find_first_not_of(sep);
145         
146         while(start<str.size())
147         {
148                 unsigned end=str.find_first_of(sep, start);
149                 result.push_back(str.substr(start, end-start));
150                 
151                 if(end==string::npos)
152                         break;
153                 
154                 if(allow_empty)
155                 {
156                         start=end+1;
157                         if(start==str.size())
158                                 result.push_back(string());
159                 }
160                 else
161                         start=str.find_first_not_of(sep, end);
162         }
163
164         return result;
165 }
166
167 vector<string> split(const string &str, char sep, bool allow_empty)
168 {
169         return split(str, string(1, sep), allow_empty);
170 }
171
172 string strip(const string &s)
173 {
174         string result=s;
175         if(!result.erase(0, result.find_first_not_of(" \t\r\n")).empty())
176                 result.erase(result.find_last_not_of(" \t\r\n")+1);
177         return result;
178 }
179
180 string c_unescape(const std::string &str)
181 {
182         bool escape=false;
183         unsigned numeric_type=0;
184         unsigned numeric_pos=0;
185         unsigned numeric_value=0;
186         string result;
187         for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
188         {
189                 if(numeric_type==16)
190                 {
191                         unsigned digit=0;
192                         if(*i>='0' && *i<='9')
193                                 digit=*i-'0';
194                         else if(*i>='a' && *i<='f')
195                                 digit=*i-'a'+10;
196                         else if(*i>='A' && *i<='F')
197                                 digit=*i-'A'+10;
198                         else
199                                 throw InvalidParameterValue("Invalid hexadecimal digit");
200
201                         numeric_value=(numeric_value<<4 | digit);
202                         ++numeric_pos;
203                         if(numeric_pos==2)
204                         {
205                                 result+=numeric_value;
206                                 numeric_type=0;
207                         }
208                 }
209                 else if(numeric_type==8)
210                 {
211                         unsigned digit=0;
212                         if(*i>='0' && *i<='7')
213                                 digit=*i-'0';
214                         else
215                                 throw InvalidParameterValue("Invalid octal digit");
216
217                         numeric_value=(numeric_value<<3 | digit);
218                         ++numeric_pos;
219                         if(numeric_pos==3)
220                         {
221                                 result+=numeric_value;
222                                 numeric_type=0;
223                         }
224                 }
225                 else if(escape)
226                 {
227                         if(*i=='x')
228                         {
229                                 numeric_type=16;
230                                 numeric_pos=0;
231                                 numeric_value=0;
232                         }
233                         else if(*i>='0' && *i<='3')
234                         {
235                                 numeric_type=8;
236                                 numeric_pos=1;
237                                 numeric_value=*i-'0';
238                         }
239                         else if(*i=='n')
240                                 result+='\n';
241                         else if(*i=='t')
242                                 result+='\t';
243                         else if(*i=='r')
244                                 result+='\r';
245                         else if(*i=='b')
246                                 result+='\b';
247                         else if(*i=='v')
248                                 result+='\v';
249                         else if(*i=='a')
250                                 result+='\a';
251                         else if(*i=='f')
252                                 result+='\f';
253                         else if(*i=='\"')
254                                 result+='\"';
255                         else if(*i=='\'')
256                                 result+='\'';
257                         else if(*i=='\\')
258                                 result+='\\';
259                         else
260                                 throw InvalidParameterValue("Invalid escape sequence");
261
262                         escape=false;
263                 }
264                 else if(*i=='\\')
265                         escape=true;
266                 else
267                         result+=*i;
268         }
269
270         if(escape)      
271                 throw InvalidParameterValue("Stray backslash at end of string");
272
273         return result;
274 }
275
276 string c_escape(const string &str, bool escape_8bit)
277 {
278         string result;
279
280         for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
281         {
282                 if(*i=='\n')
283                         result+="\\n";
284                 else if(*i=='\t')
285                         result+="\\t";
286                 else if(*i=='\r')
287                         result+="\\r";
288                 else if(*i=='\b')
289                         result+="\\b";
290                 else if(*i=='\v')
291                         result+="\\v";
292                 else if(*i=='\a')
293                         result+="\\a";
294                 else if(*i=='\f')
295                         result+="\\f";
296                 else if(*i=='\"')
297                         result+="\\\"";
298                 else if(*i=='\'')
299                         result+="\\\'";
300                 else if(*i=='\\')
301                         result+="\\\\";
302                 else if(static_cast<unsigned char>(*i)<' ' || (escape_8bit && (*i&0x80)))
303                 {
304                         char buf[4]={'\\', '0'+((*i>>6)&3), '0'+((*i>>3)&7), '0'+(*i&7)};
305                         result.append(buf, 4);
306                 }
307                 else
308                         result+=*i;
309         }
310
311         return result;
312 }
313
314 } // namespace Msp