1 /* |
1 #include <cstring> |
2 * botc source code |
2 #include "main.h" |
3 * Copyright (C) 2012 Santeri `Dusk` Piippo |
|
4 * All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright notice, |
|
10 * this list of conditions and the following disclaimer. |
|
11 * 2. Redistributions in binary form must reproduce the above copyright notice, |
|
12 * this list of conditions and the following disclaimer in the documentation |
|
13 * and/or other materials provided with the distribution. |
|
14 * 3. Neither the name of the developer nor the names of its contributors may |
|
15 * be used to endorse or promote products derived from this software without |
|
16 * specific prior written permission. |
|
17 * 4. Redistributions in any form must be accompanied by information on how to |
|
18 * obtain complete source code for the software and any accompanying |
|
19 * software that uses the software. The source code must either be included |
|
20 * in the distribution or be available for no more than the cost of |
|
21 * distribution plus a nominal fee, and must be freely redistributable |
|
22 * under reasonable conditions. For an executable file, complete source |
|
23 * code means the source code for all modules it contains. It does not |
|
24 * include source code for modules or files that typically accompany the |
|
25 * major components of the operating system on which the executable file |
|
26 * runs. |
|
27 * |
|
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
29 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
38 * POSSIBILITY OF SUCH DAMAGE. |
|
39 */ |
|
40 |
|
41 #include <stdio.h> |
|
42 #include <stdlib.h> |
|
43 #include <string.h> |
|
44 #include <stdarg.h> |
|
45 #include "array.h" |
|
46 #include "str.h" |
3 #include "str.h" |
47 #include "common.h" |
4 |
48 |
5 // ============================================================================= |
49 #define ITERATE_STRING(u) \ |
6 // |
50 for (unsigned int u = 0; u < strlen (text); u++) |
7 int string::compare (const string& other) const |
51 |
8 { |
52 // ============================================================================ |
9 return m_string.compare (other.std_string()); |
53 // vdynformat: Try to write to a formatted string with size bytes first, if |
10 } |
54 // that fails, double the size and keep recursing until it works. |
11 |
55 char* vdynformat (const char* c, va_list v, unsigned int size) { |
12 // ============================================================================= |
56 char* buffer = new char[size]; |
13 // |
57 int r = vsnprintf (buffer, size-1, c, v); |
14 void string::trim (string::length_type n) |
58 if (r > (int)size-1 || r < 0) { |
15 { |
59 delete buffer; |
16 if (n > 0) |
60 buffer = vdynformat (c, v, size*2); |
17 m_string = substring (0, length() - n).std_string(); |
61 } |
18 else |
62 return buffer; |
19 m_string = substring (n, -1).std_string(); |
63 } |
20 } |
64 |
21 |
65 // ============================================================================ |
22 // ============================================================================= |
66 str::str () { |
23 // |
67 text = new char[1]; |
24 string string::strip (list<char> unwanted) |
68 clear(); |
25 { |
69 alloclen = strlen (text); |
26 string copy (m_string); |
70 } |
27 |
71 |
28 for (char c : unwanted) |
72 str::str (const char* c) { |
29 for (int i = 0; i < copy.length(); ++i) |
73 text = new char[1]; |
30 if (copy[i] == c) |
74 clear (); |
31 copy.erase (i); |
75 alloclen = strlen (text); |
32 |
76 append (c); |
33 /* |
77 } |
34 while(( pos = copy.first( c )) != -1 ) |
78 |
35 copy.erase( pos ); |
79 str::str (char c) { |
36 */ |
80 text = new char[1]; |
37 |
81 clear (); |
38 return copy; |
82 alloclen = strlen (text); |
39 } |
83 append (c); |
40 |
84 } |
41 // ============================================================================= |
85 |
42 // |
86 // ============================================================================ |
43 string string::to_uppercase() const |
87 void str::clear () { |
44 { |
88 delete text; |
45 string newstr = m_string; |
89 text = new char[1]; |
46 |
90 text[0] = '\0'; |
47 for (char& c : newstr) |
91 curs = 0; |
48 if (c >= 'a' && c <= 'z') |
92 } |
49 c -= 'a' - 'A'; |
93 |
50 |
94 unsigned int str::len () {return strlen(text);} |
51 return newstr; |
95 |
52 } |
96 // ============================================================================ |
53 |
97 void str::resize (unsigned int len) { |
54 // ============================================================================= |
98 unsigned int oldlen = strlen (text); |
55 // |
99 char* oldtext = new char[oldlen]; |
56 string string::to_lowercase() const |
100 strncpy (oldtext, text, oldlen); |
57 { |
101 |
58 string newstr = m_string; |
102 delete text; |
59 |
103 text = new char[len+1]; |
60 for (char & c : newstr) |
104 for (unsigned int u = 0; u < len+1; u++) |
61 if (c >= 'A' && c <= 'Z') |
105 text[u] = 0; |
62 c += 'a' - 'A'; |
106 strncpy (text, oldtext, len); |
63 |
107 |
64 return newstr; |
108 alloclen = len; |
65 } |
109 } |
66 |
110 |
67 // ============================================================================= |
111 // ============================================================================ |
68 // |
112 void str::dump () { |
69 string_list string::split (char del) const |
113 for (unsigned int u = 0; u <= alloclen; u++) |
70 { |
114 printf ("\t%u. %u (%c)\n", u, text[u], text[u]); |
71 string delimstr; |
115 } |
72 delimstr += del; |
116 |
73 return split (delimstr); |
117 // ============================================================================ |
74 } |
118 // Adds a new character at the end of the string. |
75 |
119 void str::append (char c) { |
76 // ============================================================================= |
120 // Out of space, thus resize |
77 // |
121 if (curs == alloclen) |
78 string_list string::split (string del) const |
122 resize (alloclen+1); |
79 { |
123 text[curs] = c; |
80 string_list res; |
124 curs++; |
81 long a = 0; |
125 } |
82 |
126 |
83 // Find all separators and store the text left to them. |
127 void str::append (const char* c) { |
84 for (;;) |
128 resize (alloclen + strlen (c)); |
85 { |
129 |
86 long b = first (del, a); |
130 for (unsigned int u = 0; u < strlen (c); u++) { |
87 |
131 if (c[u] != 0) |
88 if (b == -1) |
132 append (c[u]); |
89 break; |
133 } |
90 |
134 } |
91 string sub = substring (a, b); |
135 |
92 |
136 void str::append (str c) { |
93 if (sub.length() > 0) |
137 append (c.chars()); |
94 res.push_back (substring (a, b)); |
138 } |
95 |
139 |
96 a = b + strlen (del); |
140 // ============================================================================ |
97 } |
141 void str::appendformat (const char* c, ...) { |
98 |
142 va_list v; |
99 // Add the string at the right of the last separator |
143 |
100 if (a < (int) length()) |
144 va_start (v, c); |
101 res.push_back (substring (a, length())); |
145 char* buf = vdynformat (c, v, 256); |
102 |
146 va_end (v); |
103 return res; |
147 |
104 } |
148 append (buf); |
105 |
149 } |
106 // ============================================================================= |
150 |
107 // |
151 void str::appendformat (str c, ...) { |
108 void string::replace (const char* a, const char* b) |
152 va_list v; |
109 { |
153 |
110 long pos; |
154 va_start (v, c); |
111 |
155 char* buf = vdynformat (c.chars(), v, 256); |
112 while ( (pos = first (a)) != -1) |
156 va_end (v); |
113 m_string = m_string.replace (pos, strlen (a), b); |
157 |
114 } |
158 append (buf); |
115 |
159 } |
116 // ============================================================================= |
160 |
117 // |
161 // ============================================================================ |
118 int string::count (const char needle) const |
162 char* str::chars () { |
119 { |
163 return text; |
120 int numNeedles = 0; |
164 } |
121 |
165 |
122 for (const char & c : m_string) |
166 // ============================================================================ |
123 if (c == needle) |
167 unsigned int str::first (const char* c, unsigned int a) { |
124 numNeedles++; |
168 unsigned int r = 0; |
125 |
169 unsigned int index = 0; |
126 return numNeedles; |
170 for (; a < alloclen; a++) { |
127 } |
171 if (text[a] == c[r]) { |
128 |
172 if (r == 0) |
129 // ============================================================================= |
173 index = a; |
130 // |
174 |
131 string string::substring (long a, long b) const |
175 r++; |
132 { |
176 if (r == strlen (c)) |
133 if (b == -1) |
177 return index; |
134 b = length(); |
178 } else { |
135 |
179 if (r != 0) { |
136 if (b == a) |
180 // If the string sequence broke at this point, we need to |
137 return ""; |
181 // check this character again, for a new sequence just |
138 |
182 // might start right here. |
139 if (b < a) |
183 a--; |
140 { |
184 } |
141 // Swap the variables |
185 |
142 int c = a; |
186 r = 0; |
143 a = b; |
187 } |
144 b = c; |
188 } |
145 } |
189 |
146 |
190 return len (); |
147 char* newString = new char[b - a + 1]; |
191 } |
148 strncpy (newString, m_string.c_str() + a, b - a); |
192 |
149 newString[b - a] = '\0'; |
193 // ============================================================================ |
150 |
194 unsigned int str::last (const char* c, int a) { |
151 string other (newString); |
195 if (a == -1) |
152 delete[] newString; |
196 a = len(); |
153 return other; |
197 |
154 } |
198 int max = strlen (c)-1; |
155 |
199 |
156 // ============================================================================= |
200 int r = max; |
157 // |
201 for (; a >= 0; a--) { |
158 string::length_type string::posof (int n) const |
202 if (text[a] == c[r]) { |
159 { |
203 r--; |
160 int count = 0; |
204 if (r == -1) |
161 |
205 return a; |
162 for (int i = 0; i < length(); ++i) |
206 } else { |
163 { |
207 if (r != max) |
164 if (m_string[i] != ' ') |
208 a++; |
165 continue; |
209 |
166 |
210 r = max; |
167 if (++count < n) |
211 } |
168 continue; |
212 } |
169 |
213 |
170 return i; |
214 return len (); |
171 } |
215 } |
172 |
216 |
173 return -1; |
217 // ============================================================================ |
174 } |
218 str str::substr (unsigned int a, unsigned int b) { |
175 |
219 if (a > len()) a = len(); |
176 // ============================================================================= |
220 if (b > len()) b = len(); |
177 // |
221 |
178 int string::first (const char* c, string::length_type a) const |
222 if (b == a) |
179 { |
223 return ""; |
180 for (; a < length(); a++) |
224 |
181 if (m_string[a] == c[0] && strncmp (m_string.c_str() + a, c, strlen (c)) == 0) |
225 if (b < a) { |
182 return a; |
226 printf ("str::substr: indices %u and %u given, should be the other way around, swapping..\n", a, b); |
183 |
227 |
184 return -1; |
228 // Swap the variables |
185 } |
229 unsigned int c = a; |
186 |
230 a = b; |
187 // ============================================================================= |
231 b = c; |
188 // |
232 } |
189 int string::last (const char* c, string::length_type a) const |
233 |
190 { |
234 char* s = new char[b-a]; |
191 if (a == -1 || a >= length()) |
235 strncpy (s, text+a, b-a); |
192 a = length() - 1; |
236 return str(s); |
193 |
237 } |
194 for (; a > 0; a--) |
238 |
195 if (m_string[a] == c[0] && strncmp (m_string.c_str() + a, c, strlen (c)) == 0) |
239 // ============================================================================ |
196 return a; |
240 void str::remove (unsigned int idx, unsigned int dellen) { |
197 |
241 str s1 = substr (0, idx); |
198 return -1; |
242 str s2 = substr (idx + dellen, len()); |
199 } |
243 |
200 |
244 clear(); |
201 // ============================================================================= |
245 |
202 // |
246 append (s1); |
203 void string::dump() const |
247 append (s2); |
204 { |
248 } |
205 print ("`%1`:\n", chars()); |
249 |
206 int i = 0; |
250 // ============================================================================ |
207 |
251 void str::trim (int dellen) { |
208 for (char u : m_string) |
252 if (!dellen) |
209 print ("\t%1. [%d2] `%3`\n", i++, u, string (u)); |
253 return; |
210 } |
254 |
211 |
255 unsigned int delpos; |
212 // ============================================================================= |
256 if (dellen > 0) { |
213 // |
257 delpos = len()-dellen; |
214 long string::to_long (bool* ok, int base) const |
258 text[delpos] = 0; |
215 { |
259 curs -= dellen; |
216 errno = 0; |
260 } else { |
217 char* endptr; |
261 str s = substr (-dellen, len()); |
218 long i = strtol (m_string.c_str(), &endptr, base); |
262 clear(); |
219 *ok = (errno == 0 && *endptr == '\0'); |
263 append (s); |
220 return i; |
264 } |
221 } |
265 } |
222 |
266 |
223 // ============================================================================= |
267 // ============================================================================ |
224 // |
268 void str::replace (const char* o, const char* n, unsigned int a) { |
225 float string::to_float (bool* ok) const |
269 unsigned int idx; |
226 { |
270 |
227 errno = 0; |
271 while ((idx = first (o, a)) != len()) { |
228 char* endptr; |
272 str s1 = substr (0, idx); |
229 float i = strtof (m_string.c_str(), &endptr); |
273 str s2 = substr (idx + strlen (o), len()); |
230 *ok = (errno == 0 && *endptr == '\0'); |
274 |
231 return i; |
275 clear(); |
232 } |
276 |
233 |
277 append (s1); |
234 // ============================================================================= |
278 append (n); |
235 // |
279 append (s2); |
236 double string::to_double (bool* ok) const |
280 } |
237 { |
281 } |
238 errno = 0; |
282 |
239 char* endptr; |
283 void str::insert (char* c, unsigned int pos) { |
240 double i = strtod (m_string.c_str(), &endptr); |
284 str s1 = substr (0, pos); |
241 *ok = (errno == 0 && *endptr == '\0'); |
285 str s2 = substr (pos, len()); |
242 return i; |
286 |
243 } |
287 clear(); |
244 |
288 append (s1); |
245 // ============================================================================= |
289 append (c); |
246 // |
290 append (s2); |
247 bool operator== (const char* a, const string& b) |
291 } |
248 { |
292 |
249 return b == a; |
293 void str::reverse () { |
250 } |
294 char* tmp = new char[alloclen]; |
251 |
295 strcpy (tmp, text); |
252 // ============================================================================= |
296 |
253 // |
297 clear(); |
254 string operator+ (const char* a, const string& b) |
298 curs = 0; |
255 { |
299 resize (alloclen); |
256 return string (a) + b; |
300 for (int i = alloclen-1; i >= 0; i--) |
257 } |
301 append (tmp[i]); |
258 |
302 } |
259 // ============================================================================= |
303 |
260 // |
304 void str::repeat (unsigned int n) { |
261 string string::operator+ (const string data) const |
305 char* tmp = new char[alloclen]; |
262 { |
306 strcpy (tmp, text); |
263 string newString = *this; |
307 |
264 newString += data; |
308 for (; n > 0; n--) |
265 return newString; |
309 append (tmp); |
266 } |
310 } |
267 |
311 |
268 // ============================================================================= |
312 // ============================================================================ |
269 // |
313 bool str::isnumber () { |
270 string string::operator+ (const char* data) const |
314 ITERATE_STRING (u) { |
271 { |
315 // Minus sign as the first character is allowed for negatives |
272 string newString = *this; |
316 if (!u && text[u] == '-') |
273 newString += data; |
317 continue; |
274 return newString; |
318 |
275 } |
319 if (text[u] < '0' || text[u] > '9') |
276 |
320 return false; |
277 // ============================================================================= |
321 } |
278 // |
322 return true; |
279 string& string::operator+= (const string data) |
323 } |
280 { |
324 |
281 append (data); |
325 // ============================================================================ |
282 return *this; |
326 bool str::isword () { |
283 } |
327 ITERATE_STRING (u) { |
284 |
328 // lowercase letters |
285 // ============================================================================= |
329 if (text[u] >= 'a' || text[u] <= 'z') |
286 // |
330 continue; |
287 string& string::operator+= (const char* data) |
331 |
288 { |
332 // uppercase letters |
289 append (data); |
333 if (text[u] >= 'A' || text[u] <= 'Z') |
290 return *this; |
334 continue; |
291 } |
335 |
292 |
336 return false; |
293 // ============================================================================= |
337 } |
294 // |
338 return true; |
295 bool string::is_numeric() const |
339 } |
296 { |
340 |
297 bool gotDot = false; |
341 // ============================================================================ |
298 |
342 int str::compare (const char* c) { |
299 for (const char & c : m_string) |
343 return strcmp (text, c); |
300 { |
344 } |
301 // Allow leading hyphen for negatives |
345 |
302 if (&c == &m_string[0] && c == '-') |
346 int str::compare (str c) { |
303 continue; |
347 return compare (c.chars()); |
304 |
348 } |
305 // Check for decimal point |
349 |
306 if (!gotDot && c == '.') |
350 int str::icompare (const char* c) { |
307 { |
351 return icompare (str ((char*)c)); |
308 gotDot = true; |
352 } |
309 continue; |
353 |
310 } |
354 int str::icompare (str b) { |
311 |
355 return strcmp (tolower().chars(), b.tolower().chars()); |
312 if (c >= '0' && c <= '9') |
356 } |
313 continue; // Digit |
357 |
314 |
358 // ============================================================================ |
315 // If the above cases didn't catch this character, it was |
359 str str::tolower () { |
316 // illegal and this is therefore not a number. |
360 str n = text; |
317 return false; |
361 |
318 } |
362 for (uint u = 0; u < len(); u++) { |
319 |
363 if (n[u] > 'A' && n[u] < 'Z') |
320 return true; |
364 n.text[u] += ('a' - 'A'); |
321 } |
365 } |
322 |
366 |
323 // ============================================================================= |
367 return n; |
324 // |
368 } |
325 bool string::ends_with (const string& other) |
369 |
326 { |
370 // ============================================================================ |
327 if (length() < other.length()) |
371 str str::toupper () { |
328 return false; |
372 str n = text; |
329 |
373 |
330 const int ofs = length() - other.length(); |
374 for (uint u = 0; u < len(); u++) { |
331 return strncmp (chars() + ofs, other.chars(), other.length()) == 0; |
375 if (n[u] > 'a' && n[u] < 'z') |
332 } |
376 n.text[u] -= ('A' - 'a'); |
333 |
377 } |
334 // ============================================================================= |
378 |
335 // |
379 return n; |
336 bool string::starts_with (const string& other) |
380 } |
337 { |
381 |
338 if (length() < other.length()) |
382 // ============================================================================ |
339 return false; |
383 unsigned int str::count (char* c) { |
340 |
384 unsigned int r = 0; |
341 return strncmp (chars(), other.chars(), other.length()) == 0; |
385 unsigned int tmp = 0; |
342 } |
386 ITERATE_STRING (u) { |
343 |
387 if (text[u] == c[r]) { |
344 // ============================================================================= |
388 r++; |
345 // |
389 if (r == strlen (c)) { |
346 void string::sprintf (const char* fmtstr, ...) |
390 r = 0; |
347 { |
391 tmp++; |
348 char* buf; |
392 } |
349 int bufsize = 256; |
393 } else { |
350 va_list va; |
394 if (r != 0) |
351 va_start (va, fmtstr); |
395 u--; |
352 |
396 r = 0; |
353 do |
397 } |
354 buf = new char[bufsize]; |
398 } |
355 |
399 |
356 while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize); |
400 return tmp; |
357 |
401 } |
358 va_end (va); |
402 |
359 m_string = buf; |
403 // ============================================================================ |
360 delete[] buf; |
404 array<str> str::split (str del) { |
361 } |
405 array<str> res; |
362 |
406 unsigned int a = 0; |
363 // ============================================================================= |
407 |
364 // |
408 // Find all separators and store the text left to them. |
365 void string::prepend (string a) |
409 while (1) { |
366 { |
410 unsigned int b = first (del, a); |
367 m_string = (a + m_string).std_string(); |
411 |
368 } |
412 if (b == len()) |
369 |
413 break; |
370 // ============================================================================= |
414 |
371 // |
415 res.push (substr (a, b)); |
372 string string_list::join (const string& delim) |
416 a = b + strlen (del); |
373 { |
417 } |
374 string result; |
418 |
375 |
419 // Add the string at the right of the last separator |
376 for (const string & it : std_deque()) |
420 res.push (substr (a, len())); |
377 { |
421 return res; |
378 if (!result.is_empty()) |
422 } |
379 result += delim; |
423 |
380 |
424 array<str> str::operator/ (str splitstring) {return split(splitstring);} |
381 result += it; |
425 array<str> str::operator/ (char* splitstring) {return split(splitstring);} |
382 } |
426 array<str> str::operator/ (const char* splitstring) {return split(splitstring);} |
383 |
|
384 return result; |
|
385 } |
|
386 |
|
387 // ============================================================================= |
|
388 // |
|
389 bool string::mask (const string& pattern) const |
|
390 { |
|
391 // Elevate to uppercase for case-insensitive matching |
|
392 string pattern_upper = pattern.to_uppercase(); |
|
393 string this_upper = to_uppercase(); |
|
394 const char* maskstring = pattern_upper.chars(); |
|
395 const char* mptr = &maskstring[0]; |
|
396 |
|
397 for (const char* sptr = this_upper.chars(); *sptr != '\0'; sptr++) |
|
398 { |
|
399 if (*mptr == '?') |
|
400 { |
|
401 if (* (sptr + 1) == '\0') |
|
402 { |
|
403 // ? demands that there's a character here and there wasn't. |
|
404 // Therefore, mask matching fails |
|
405 return false; |
|
406 } |
|
407 } |
|
408 |
|
409 elif (*mptr == '*') |
|
410 { |
|
411 char end = * (++mptr); |
|
412 |
|
413 // If '*' is the final character of the message, all of the remaining |
|
414 // string matches against the '*'. We don't need to bother checking |
|
415 // the string any further. |
|
416 if (end == '\0') |
|
417 return true; |
|
418 |
|
419 // Skip to the end character |
|
420 while (*sptr != end && *sptr != '\0') |
|
421 sptr++; |
|
422 |
|
423 // String ended while the mask still had stuff |
|
424 if (*sptr == '\0') |
|
425 return false; |
|
426 } |
|
427 elif (*sptr != *mptr) |
|
428 return false; |
|
429 |
|
430 mptr++; |
|
431 } |
|
432 |
|
433 return true; |
|
434 } |