1 /* |
|
2 * LDForge: LDraw parts authoring CAD |
|
3 * Copyright (C) 2013 Santeri Piippo |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation, either version 3 of the License, or |
|
8 * (at your option) any later version. |
|
9 * |
|
10 * This program is distributed in the hope that it will be useful, |
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 * GNU General Public License for more details. |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License |
|
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
17 */ |
|
18 |
|
19 #include <stdio.h> |
|
20 #include <stdlib.h> |
|
21 #include <string.h> |
|
22 #include <stdarg.h> |
|
23 #include <assert.h> |
|
24 // #include <initializer_list> |
|
25 #include "str.h" |
|
26 #include "common.h" |
|
27 #include "misc.h" |
|
28 |
|
29 #define ITERATE_STRING(u) \ |
|
30 for (unsigned int u = 0; u < strlen (text); u++) |
|
31 |
|
32 // ============================================================================ |
|
33 // vdynformat: Try to write to a formatted string with size bytes first, if |
|
34 // that fails, double the size and keep recursing until it works. |
|
35 char* vdynformat (const char* csFormat, va_list vArgs, long lSize) { |
|
36 char* buffer = new char[lSize]; |
|
37 int r = vsnprintf (buffer, lSize - 1, csFormat, vArgs); |
|
38 if (r > (signed)(lSize - 1) || r < 0) { |
|
39 delete[] buffer; |
|
40 buffer = vdynformat (csFormat, vArgs, lSize * 2); |
|
41 } |
|
42 return buffer; |
|
43 } |
|
44 |
|
45 // ============================================================================ |
|
46 str::str () { |
|
47 text = new char[1]; |
|
48 clear(); |
|
49 alloclen = strlen (text); |
|
50 } |
|
51 |
|
52 str::str (const char* c) { |
|
53 text = new char[1]; |
|
54 text[0] = '\0'; |
|
55 curs = alloclen = 0; |
|
56 append (c); |
|
57 } |
|
58 |
|
59 str::str (char c) { |
|
60 text = new char[1]; |
|
61 text[0] = '\0'; |
|
62 curs = alloclen = 0; |
|
63 append (c); |
|
64 } |
|
65 |
|
66 str::str (QString c) { |
|
67 text = new char[1]; |
|
68 text[0] = '\0'; |
|
69 curs = alloclen = 0; |
|
70 append (c); |
|
71 } |
|
72 |
|
73 str::~str () { |
|
74 // delete[] text; |
|
75 } |
|
76 |
|
77 // ============================================================================ |
|
78 void str::clear () { |
|
79 delete[] text; |
|
80 text = new char[1]; |
|
81 text[0] = '\0'; |
|
82 curs = 0; |
|
83 alloclen = 0; |
|
84 } |
|
85 |
|
86 // ============================================================================ |
|
87 void str::resize (unsigned int len) { |
|
88 unsigned int oldlen = strlen (text); |
|
89 char* oldtext = new char[oldlen]; |
|
90 strncpy (oldtext, text, oldlen); |
|
91 |
|
92 delete[] text; |
|
93 text = new char[len+1]; |
|
94 for (unsigned int u = 0; u < len+1; u++) |
|
95 text[u] = 0; |
|
96 strncpy (text, oldtext, len); |
|
97 delete[] oldtext; |
|
98 |
|
99 alloclen = len; |
|
100 } |
|
101 |
|
102 // ============================================================================ |
|
103 void str::dump () { |
|
104 for (unsigned int u = 0; u <= alloclen; u++) |
|
105 printf ("\t%u. %u (%c)\n", u, text[u], text[u]); |
|
106 } |
|
107 |
|
108 // ============================================================================ |
|
109 // Adds a new character at the end of the string. |
|
110 void str::append (const char c) { |
|
111 // Out of space, thus resize |
|
112 if (curs == alloclen) |
|
113 resize (alloclen + 1); |
|
114 text[curs] = c; |
|
115 curs++; |
|
116 } |
|
117 |
|
118 void str::append (const char* c) { |
|
119 resize (alloclen + strlen (c)); |
|
120 |
|
121 for (unsigned int u = 0; u < strlen (c); u++) { |
|
122 if (c[u] != 0) |
|
123 append (c[u]); |
|
124 } |
|
125 } |
|
126 |
|
127 void str::append (str c) { |
|
128 append (c.chars()); |
|
129 } |
|
130 |
|
131 void str::append (QString c) { |
|
132 append (c.toUtf8 ().constData ()); |
|
133 } |
|
134 |
|
135 // ============================================================================ |
|
136 void str::appendformat (const char* c, ...) { |
|
137 va_list v; |
|
138 |
|
139 va_start (v, c); |
|
140 char* buf = vdynformat (c, v, 256); |
|
141 va_end (v); |
|
142 |
|
143 append (buf); |
|
144 delete[] buf; |
|
145 } |
|
146 |
|
147 void str::format (const char* fmt, ...) { |
|
148 clear (); |
|
149 |
|
150 va_list v; |
|
151 |
|
152 va_start (v, fmt); |
|
153 char* buf = vdynformat (fmt, v, 256); |
|
154 va_end (v); |
|
155 |
|
156 append (buf); |
|
157 delete[] buf; |
|
158 } |
|
159 |
|
160 // ============================================================================ |
|
161 char* str::chars () { |
|
162 return text; |
|
163 } |
|
164 |
|
165 // ============================================================================ |
|
166 int str::first (const char* c, unsigned int a) { |
|
167 unsigned int r = 0; |
|
168 unsigned int index = 0; |
|
169 for (; a < alloclen; a++) { |
|
170 if (text[a] == c[r]) { |
|
171 if (r == 0) |
|
172 index = a; |
|
173 |
|
174 r++; |
|
175 if (r == strlen (c)) |
|
176 return index; |
|
177 } else { |
|
178 if (r != 0) { |
|
179 // If the string sequence broke at this point, we need to |
|
180 // check this character again, for a new sequence just |
|
181 // might start right here. |
|
182 a--; |
|
183 } |
|
184 |
|
185 r = 0; |
|
186 } |
|
187 } |
|
188 |
|
189 return -1; |
|
190 } |
|
191 |
|
192 // ============================================================================ |
|
193 int str::last (const char* c, int a) { |
|
194 if (a == -1) |
|
195 a = len(); |
|
196 |
|
197 int max = strlen (c)-1; |
|
198 |
|
199 int r = max; |
|
200 for (; a >= 0; a--) { |
|
201 if (text[a] == c[r]) { |
|
202 r--; |
|
203 if (r == -1) |
|
204 return a; |
|
205 } else { |
|
206 if (r != max) |
|
207 a++; |
|
208 |
|
209 r = max; |
|
210 } |
|
211 } |
|
212 |
|
213 return -1; |
|
214 } |
|
215 |
|
216 // ============================================================================ |
|
217 str str::substr (unsigned int a, unsigned int b) { |
|
218 if (a > len()) a = len(); |
|
219 if (b > len()) b = len(); |
|
220 |
|
221 if (b == a) |
|
222 return ""; |
|
223 |
|
224 if (b < a) { |
|
225 printf ("str::substring:: indices %u and %u given, should be the other way around, swapping..\n", a, b); |
|
226 |
|
227 // Swap the variables |
|
228 unsigned int c = a; |
|
229 a = b; |
|
230 b = c; |
|
231 } |
|
232 |
|
233 char* s = new char[b - a + 1]; |
|
234 strncpy (s, text + a, b - a); |
|
235 s[b - a] = '\0'; |
|
236 |
|
237 str other = s; |
|
238 delete[] s; |
|
239 return other; |
|
240 } |
|
241 |
|
242 // ============================================================================ |
|
243 void str::remove (unsigned int idx, unsigned int dellen) { |
|
244 str s1 = substr (0, idx); |
|
245 str s2 = substr (idx + dellen, -1); |
|
246 |
|
247 clear(); |
|
248 |
|
249 append (s1); |
|
250 append (s2); |
|
251 } |
|
252 |
|
253 // ============================================================================ |
|
254 str str::trim (int dellen) { |
|
255 if (dellen > 0) |
|
256 return substr (0, len() - dellen); |
|
257 return substr (-dellen, len()); |
|
258 } |
|
259 |
|
260 // ============================================================================ |
|
261 void str::replace (const char* o, const char* n, unsigned int a) { |
|
262 for (int idx; (idx = first (o, a)) != -1;) { |
|
263 str s1 = substr (0, idx); |
|
264 str s2 = substr (idx + strlen (o), len()); |
|
265 |
|
266 clear(); |
|
267 |
|
268 append (s1); |
|
269 append (n); |
|
270 append (s2); |
|
271 } |
|
272 } |
|
273 |
|
274 // ============================================================================ |
|
275 str str::strip (char c) { |
|
276 return strip ({c}); |
|
277 } |
|
278 |
|
279 str str::strip (std::initializer_list<char> unwanted) { |
|
280 str cache = text; |
|
281 uint oldlen = len(); |
|
282 |
|
283 char* buf = new char[oldlen]; |
|
284 char* bufptr = buf; |
|
285 for (uint i = 0; i < oldlen; i++) { |
|
286 bool valid = true; |
|
287 for (const char* j = unwanted.begin(); j < unwanted.end() && valid; j++) |
|
288 if (text[i] == *j) |
|
289 valid = false; |
|
290 |
|
291 if (valid) |
|
292 *bufptr++ = text[i]; |
|
293 } |
|
294 |
|
295 *bufptr = '\0'; |
|
296 assert (bufptr <= buf + oldlen); |
|
297 |
|
298 str zResult = buf; |
|
299 delete[] buf; |
|
300 |
|
301 return zResult; |
|
302 } |
|
303 |
|
304 void str::insert (char* c, unsigned int pos) { |
|
305 str s1 = substr (0, pos); |
|
306 str s2 = substr (pos, len()); |
|
307 |
|
308 clear(); |
|
309 append (s1); |
|
310 append (c); |
|
311 append (s2); |
|
312 } |
|
313 |
|
314 str str::reverse () { |
|
315 char* buf = new char[len() + 1]; |
|
316 |
|
317 for (uint i = 0; i < len(); i++) |
|
318 buf[i] = text[len() - i - 1]; |
|
319 buf[len()] = '\0'; |
|
320 |
|
321 str other = buf; |
|
322 delete[] buf; |
|
323 return other; |
|
324 } |
|
325 |
|
326 str str::repeat (int n) { |
|
327 assert (n >= 0); |
|
328 |
|
329 str other; |
|
330 for (int i = 0; i < n; i++) |
|
331 other += text; |
|
332 return other; |
|
333 } |
|
334 |
|
335 // ============================================================================ |
|
336 bool str::isnumber () { |
|
337 ITERATE_STRING (u) { |
|
338 // Minus sign as the first character is allowed for negatives |
|
339 if (!u && text[u] == '-') |
|
340 continue; |
|
341 |
|
342 if (text[u] < '0' || text[u] > '9') |
|
343 return false; |
|
344 } |
|
345 return true; |
|
346 } |
|
347 |
|
348 // ============================================================================ |
|
349 bool str::isword () { |
|
350 ITERATE_STRING (u) { |
|
351 // lowercase letters |
|
352 if (text[u] >= 'a' || text[u] <= 'z') |
|
353 continue; |
|
354 |
|
355 // uppercase letters |
|
356 if (text[u] >= 'A' || text[u] <= 'Z') |
|
357 continue; |
|
358 |
|
359 return false; |
|
360 } |
|
361 return true; |
|
362 } |
|
363 |
|
364 int str::instanceof (const char* c, uint n) { |
|
365 unsigned int r = 0; |
|
366 unsigned int index = 0; |
|
367 unsigned int x = 0; |
|
368 for (uint a = 0; a < alloclen; a++) { |
|
369 if (text[a] == c[r]) { |
|
370 if (r == 0) |
|
371 index = a; |
|
372 |
|
373 r++; |
|
374 if (r == strlen (c)) { |
|
375 if (x++ == n) |
|
376 return index; |
|
377 r = 0; |
|
378 } |
|
379 } else { |
|
380 if (r != 0) |
|
381 a--; |
|
382 r = 0; |
|
383 } |
|
384 } |
|
385 |
|
386 return -1; |
|
387 } |
|
388 |
|
389 // ============================================================================ |
|
390 int str::compare (const char* c) { |
|
391 return strcmp (text, c); |
|
392 } |
|
393 |
|
394 int str::compare (str c) { |
|
395 return compare (c.chars()); |
|
396 } |
|
397 |
|
398 int str::icompare (const char* c) { |
|
399 return icompare (str ((char*)c)); |
|
400 } |
|
401 |
|
402 int str::icompare (str b) { |
|
403 return strcmp (tolower().chars(), b.tolower().chars()); |
|
404 } |
|
405 |
|
406 // ============================================================================ |
|
407 str str::tolower () { |
|
408 str n = text; |
|
409 |
|
410 for (uint u = 0; u < len(); u++) { |
|
411 if (n[u] >= 'A' && n[u] < 'Z') |
|
412 n.text[u] += ('a' - 'A'); |
|
413 } |
|
414 |
|
415 return n; |
|
416 } |
|
417 |
|
418 // ============================================================================ |
|
419 str str::toupper () { |
|
420 str n = text; |
|
421 |
|
422 for (uint u = 0; u < len(); u++) { |
|
423 if (n[u] >= 'a' && n[u] < 'z') |
|
424 n.text[u] -= ('a' - 'A'); |
|
425 } |
|
426 |
|
427 return n; |
|
428 } |
|
429 |
|
430 // ============================================================================ |
|
431 unsigned str::count (char c) { |
|
432 unsigned n = 0; |
|
433 ITERATE_STRING (u) |
|
434 if (text[u] == c) |
|
435 n++; |
|
436 return n; |
|
437 } |
|
438 |
|
439 unsigned str::count (char* c) { |
|
440 unsigned int r = 0; |
|
441 unsigned int tmp = 0; |
|
442 ITERATE_STRING (u) { |
|
443 if (text[u] == c[r]) { |
|
444 r++; |
|
445 if (r == strlen (c)) { |
|
446 r = 0; |
|
447 tmp++; |
|
448 } |
|
449 } else { |
|
450 if (r != 0) |
|
451 u--; |
|
452 r = 0; |
|
453 } |
|
454 } |
|
455 |
|
456 return tmp; |
|
457 } |
|
458 |
|
459 // ============================================================================ |
|
460 std::vector<str> str::split (str del, bool bNoBlanks) { |
|
461 std::vector<str> res; |
|
462 unsigned int a = 0; |
|
463 |
|
464 // Find all separators and store the text left to them. |
|
465 while (1) { |
|
466 int b = first (del, a); |
|
467 |
|
468 if (b == -1) |
|
469 break; |
|
470 |
|
471 if (!bNoBlanks || (b - a)) |
|
472 res.push_back (substr (a, b)); |
|
473 |
|
474 a = b + strlen (del); |
|
475 } |
|
476 |
|
477 // Add the string at the right of the last separator |
|
478 if (!bNoBlanks || (len () - a)) |
|
479 res.push_back (substr (a, len ())); |
|
480 return res; |
|
481 } |
|
482 |
|
483 std::vector<str> str::operator/ (str splitstring) {return split(splitstring);} |
|
484 std::vector<str> str::operator/ (char* splitstring) {return split(splitstring);} |
|
485 std::vector<str> str::operator/ (const char* splitstring) {return split(splitstring);} |
|
486 |
|
487 str& str::operator+= (vertex vrt) { |
|
488 appendformat ("%s", vrt.stringRep (false).chars()); |
|
489 return *this; |
|
490 } |
|
491 |
|
492 str fmt (const char* fmt, ...) { |
|
493 va_list va; |
|
494 char* buf; |
|
495 |
|
496 va_start (va, fmt); |
|
497 buf = vdynformat (fmt, va, 256); |
|
498 va_end (va); |
|
499 |
|
500 str val = buf; |
|
501 delete[] buf; |
|
502 return val; |
|
503 } |
|