1 /*
2 * This file is part of Dune Legacy.
3 *
4 * Dune Legacy is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * Dune Legacy is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Dune Legacy. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <misc/string_util.h>
19
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <vector>
23 #include <algorithm>
24
25 /**
26 Splits a string into several substrings. This strings are separated with ','.
27 Example:<br>
28 String first, second;<br>
29 SplitString("abc,xyz",2,&first, &second);<br>
30 \param parseString the string to parse
31 \param numStringPointers the number of pointers to strings following after this parameter
32 \return true if successful, false otherwise.
33 */
splitString(const std::string & parseString,unsigned int numStringPointers,...)34 bool splitString(const std::string& parseString, unsigned int numStringPointers, ...) {
35 va_list arg_ptr;
36 va_start(arg_ptr, numStringPointers);
37
38 std::string** pStr;
39
40 if(numStringPointers == 0) {
41 va_end(arg_ptr);
42 return false;
43 }
44
45 pStr = new std::string*[numStringPointers];
46
47 for(unsigned int i = 0; i < numStringPointers; i++) {
48 pStr[i] = va_arg(arg_ptr, std::string* );
49 }
50 va_end(arg_ptr);
51
52 int startpos = 0;
53 unsigned int index = 0;
54
55 for(unsigned int i = 0; i < parseString.size(); i++) {
56 if(parseString[i] == ',') {
57 *(pStr[index]) = parseString.substr(startpos,i-startpos);
58 startpos = i + 1;
59 index++;
60 if(index >= numStringPointers) {
61 delete [] pStr;
62 return false;
63 }
64 }
65 }
66
67 *(pStr[index]) = parseString.substr(startpos,parseString.size()-startpos);
68 delete [] pStr;
69 return true;
70 }
71
72 /*
73 Splits a string into several substrings. This strings are separated with ',' by default.
74 */
splitString(const std::string & parseString,const std::string & delim,bool keepDelim)75 std::vector<std::string> splitString(const std::string& parseString, const std::string& delim, bool keepDelim) {
76 std::vector<std::string> retVector;
77 if(delim.empty()) {
78 retVector.push_back(parseString);
79 return retVector;
80 }
81
82 std::string::const_iterator iter = parseString.begin();
83
84 while(true) {
85 std::string::const_iterator foundPos = std::search(iter, parseString.end(), delim.begin(), delim.end());
86 std::string part(iter, (foundPos == parseString.end()) ? foundPos : foundPos + (keepDelim ? delim.length() : 0));
87 retVector.push_back(part);
88
89 if(foundPos == parseString.end()) {
90 break;
91 }
92
93 iter = foundPos + delim.length();
94 }
95
96 return retVector;
97 }
98
99 /**
100 Replaces multiple strings at one. The mapping is specified by replacementMap
101 \param str the string to apply the replacement to
102 \param replacementMap a map of replacements
103 \return the modified strings
104 */
replaceAll(const std::string & str,const std::map<std::string,std::string> & replacementMap)105 std::string replaceAll(const std::string& str, const std::map<std::string, std::string>& replacementMap) {
106
107 std::string result = str;
108 size_t currentPos = 0;
109
110 while(true) {
111
112 size_t bestNextPos = std::string::npos;
113 std::string bestNextKey;
114 std::string bestNextValue;
115
116 for(const auto& replacement : replacementMap) {
117
118 std::string nextKey = replacement.first;
119 size_t nextPos = result.find(nextKey, currentPos);
120
121 if((nextPos != std::string::npos)
122 && ( (nextPos < bestNextPos)
123 || ((nextPos == bestNextPos) && (nextKey.length() > bestNextKey.length())) ) ) {
124
125 // best match so far (either smaller position or same position but longer match)
126 bestNextPos = nextPos;
127 bestNextKey = nextKey;
128 bestNextValue = replacement.second;
129 }
130
131 }
132
133 if(bestNextPos == std::string::npos) {
134 break;
135 }
136
137 result.replace(bestNextPos, bestNextKey.length(), bestNextValue);
138
139 currentPos = bestNextPos + bestNextValue.length();
140 }
141
142
143 return result;
144 }
145
146
greedyWordWrap(const std::string & text,int linewidth,std::function<int (const std::string &)> pGetTextWidth)147 std::vector<std::string> greedyWordWrap(const std::string& text, int linewidth, std::function<int (const std::string&)> pGetTextWidth) {
148 //split text into single lines at every '\n'
149 size_t startpos = 0;
150 size_t nextpos;
151 std::vector<std::string> hardLines;
152 do {
153 nextpos = text.find("\n",startpos);
154 if(nextpos == std::string::npos) {
155 hardLines.push_back(text.substr(startpos,text.length()-startpos));
156 } else {
157 hardLines.push_back(text.substr(startpos,nextpos-startpos));
158 startpos = nextpos+1;
159 }
160 } while(nextpos != std::string::npos);
161
162 std::vector<std::string> textLines;
163 for(const std::string& hardLine : hardLines) {
164 if(hardLine == "") {
165 textLines.push_back(" ");
166 continue;
167 }
168
169 bool bEndOfLine = false;
170 size_t warppos = 0;
171 size_t oldwarppos = 0;
172 size_t lastwarp = 0;
173
174 while(bEndOfLine == false) {
175 while(true) {
176 warppos = hardLine.find(" ", oldwarppos);
177 std::string tmp;
178 if(warppos == std::string::npos) {
179 tmp = hardLine.substr(lastwarp,hardLine.length()-lastwarp);
180 warppos = hardLine.length();
181 bEndOfLine = true;
182 } else {
183 tmp = hardLine.substr(lastwarp,warppos-lastwarp);
184 }
185
186 if( pGetTextWidth(tmp) > linewidth) {
187 // this line would be too big => in oldwarppos is the last correct word warp pos
188 bEndOfLine = false;
189 break;
190 } else {
191 if(bEndOfLine == true) {
192 oldwarppos = warppos;
193 break;
194 } else {
195 oldwarppos = warppos + 1;
196 }
197 }
198 }
199
200 if(oldwarppos == lastwarp) {
201 // linewidth is too small for the next word => split the word
202
203 warppos = lastwarp;
204 while(true) {
205 std::string tmp = hardLine.substr(lastwarp,warppos-lastwarp);
206 if( pGetTextWidth(tmp) > linewidth) {
207 // this line would be too big => in oldwarppos is the last correct warp pos
208 break;
209 } else {
210 oldwarppos = warppos;
211 }
212
213 warppos++;
214
215 if(warppos > hardLine.length()) {
216 oldwarppos = hardLine.length();
217 break;
218 }
219 }
220
221 if(warppos != lastwarp) {
222 textLines.push_back(hardLine.substr(lastwarp,oldwarppos-lastwarp));
223 lastwarp = oldwarppos;
224 } else {
225 // linewidth is too small for the next character => create a dummy entry
226 textLines.push_back(" ");
227 lastwarp++;
228 oldwarppos++;
229 }
230 } else {
231 textLines.push_back(hardLine.substr(lastwarp,oldwarppos-lastwarp));
232 lastwarp = oldwarppos;
233 }
234 }
235 }
236
237 return textLines;
238 }
239
240
241
convertCP850ToISO8859_1(const std::string & text)242 std::string convertCP850ToISO8859_1(const std::string& text)
243 {
244 // contains the upper half of cp850 (128 - 255)
245 static const unsigned char cp850toISO8859_1[] = {
246 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5,
247 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, 0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, 0x3f,
248 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb,
249 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xc1, 0xc2, 0xc0, 0xa9, 0x3f, 0x3f, 0x3f, 0x3f, 0xa2, 0xa5, 0x3f,
250 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xe3, 0xc3, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xa4,
251 0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0x3f, 0xcd, 0xce, 0xcf, 0x3f, 0x3f, 0x3f, 0x3f, 0xa6, 0xcc, 0x3f,
252 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe, 0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0xb4,
253 0xad, 0xb1, 0x3f, 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, 0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, 0x3f, 0xa0
254 };
255
256 std::string result;
257 for(unsigned int i = 0; i < text.size(); i++) {
258 unsigned char c = (unsigned char) text[i];
259 if(c == 0x0D) {
260 result += "\n";
261 } else if(c < 128) {
262 result += c;
263 } else {
264 result += cp850toISO8859_1[c-128];
265 }
266 }
267 return result;
268 }
269
convertUTF8ToISO8859_1(const std::string & text)270 std::string convertUTF8ToISO8859_1(const std::string& text)
271 {
272 unsigned char savedChar = 0;
273
274 std::string result;
275 for(unsigned int i = 0; i < text.size(); i++) {
276 unsigned char c = (unsigned char) text[i];
277
278 if(savedChar != 0) {
279 unsigned char newChar = ((savedChar & 0x1F) << 6) | (c & 0x3F);
280 result += newChar;
281 savedChar = 0;
282 } else {
283
284 if(c < 128) {
285 // 1 byte
286 result += c;
287 } else if( (c & 0xE0) == 0xC0) {
288 // 2 byte
289 savedChar = c;
290 } else if( (c & 0xF0) == 0xE0) {
291 // 3 byte
292 result += 0x01; // cannot handle this
293 i += 2;
294 } else if( (c & 0xF8) == 0xF0) {
295 // 4 byte
296 result += 0x01; // cannot handle this
297 i += 3;
298 } else if( (c & 0xFC) == 0xF8) {
299 // 5 byte
300 result += 0x01; // cannot handle this
301 i += 4;
302 } else if( (c & 0xFE) == 0xFC) {
303 // 6 byte
304 result += 0x01; // cannot handle this
305 i += 5;
306 }
307 }
308 }
309 return result;
310 }
311
decodeString(const std::string & text)312 std::string decodeString(const std::string& text) {
313 std::string out = "";
314
315 static const char decodeTable1[16] = { ' ','e','t','a','i','n','o','s','r','l','h','c','d','u','p','m' };
316 static const char decodeTable2[16][9] = { { 't','a','s','i','o',' ','w','b' },
317 { ' ','r','n','s','d','a','l','m' },
318 { 'h',' ','i','e','o','r','a','s' },
319 { 'n','r','t','l','c',' ','s','y' },
320 { 'n','s','t','c','l','o','e','r' },
321 { ' ','d','t','g','e','s','i','o' },
322 { 'n','r',' ','u','f','m','s','w' },
323 { ' ','t','e','p','.','i','c','a' },
324 { 'e',' ','o','i','a','d','u','r' },
325 { ' ','l','a','e','i','y','o','d' },
326 { 'e','i','a',' ','o','t','r','u' },
327 { 'e','t','o','a','k','h','l','r' },
328 { ' ','e','i','u',',','.','o','a' },
329 { 'n','s','r','c','t','l','a','i' },
330 { 'l','e','o','i','r','a','t','p' },
331 { 'e','a','o','i','p',' ','b','m' } };
332
333 for(unsigned int i = 0; i < text.length(); i++) {
334 unsigned char databyte = text[i];
335
336 if(databyte & 0x80) {
337 unsigned char index1 = (databyte >> 3) & 0xF;
338 unsigned char index2 = databyte & 0x7;
339
340 out += decodeTable1[index1];
341 out += decodeTable2[index1][index2];
342 } else {
343 if(databyte == 0x1B) {
344 // special character
345 // These characters are encoded as CP850 but from the actual CP850 code 0x7F is subtracted
346
347 i++;
348 if(i == text.length()) {
349 THROW(std::invalid_argument, "decodeString(): Special character escape sequence at end of string!");
350 }
351
352 unsigned char special = text[i] + 0x7F;
353
354 out += special;
355 } else if(databyte == '\r') {
356 out += '\n';
357 } else if(databyte == 0x0C) {
358 out += '\n';
359 } else if(databyte == 0x1F) {
360 out += '.';
361 } else {
362 out += databyte;
363 }
364 }
365 }
366 return out;
367 }
368