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