libstr.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "std-includes.h" | ||
| 4 | #include <cstdint> | ||
| 5 | #include <cstring> | ||
| 6 | #include <utility> | ||
| 7 | #include <string_view> | ||
| 8 | #include <cinttypes> | ||
| 9 | |||
| 10 | //ty alcaro | ||
| 11 | extern const unsigned char char_props[256]; | ||
| 12 | 1762415 | static inline int to_lower(unsigned char c) { return c|(char_props[c]&0x20); } | |
| 13 | static inline int to_upper(unsigned char c) { return c&~(char_props[c]&0x20); } | ||
| 14 | |||
| 15 | 221205 | inline bool is_space(unsigned char c) { return char_props[c] & 0x80; } // C standard says \f \v are space, but this one disagrees | |
| 16 | // TODO is this opaque table lookup really faster than c >= '0' && c <= '9'? | ||
| 17 | 47422 | inline bool is_digit(unsigned char c) { return char_props[c] & 0x40; } | |
| 18 | inline bool is_alpha(unsigned char c) { return char_props[c] & 0x20; } | ||
| 19 | ✗ | inline bool is_lower(unsigned char c) { return char_props[c] & 0x04; } | |
| 20 | 106 | inline bool is_upper(unsigned char c) { return char_props[c] & 0x02; } | |
| 21 | inline bool is_alnum(unsigned char c) { return char_props[c] & 0x60; } | ||
| 22 | 16516 | inline bool is_ualpha(unsigned char c) { return char_props[c] & 0x28; } | |
| 23 | 122773 | inline bool is_ualnum(unsigned char c) { return char_props[c] & 0x68; } | |
| 24 | 7303 | inline bool is_xdigit(unsigned char c) { return char_props[c] & 0x01; } | |
| 25 | |||
| 26 | 974596 | inline char *copy(const char *source, int copy_length, char *dest) | |
| 27 | { | ||
| 28 | 974596 | memcpy(dest, source, copy_length*sizeof(char)); | |
| 29 | 974596 | return dest; | |
| 30 | } | ||
| 31 | |||
| 32 | class string { | ||
| 33 | public: | ||
| 34 | 4610315 | const char *data() const | |
| 35 | { | ||
| 36 | 4610315 | return data_ptr; | |
| 37 | } | ||
| 38 | |||
| 39 | 71072 | char *temp_raw() const //things to cleanup and take a look at | |
| 40 | { | ||
| 41 | 71072 | return data_ptr; | |
| 42 | } | ||
| 43 | |||
| 44 | 873303 | char *raw() const | |
| 45 | { | ||
| 46 | 873303 | return data_ptr; | |
| 47 | } | ||
| 48 | |||
| 49 | 4560449 | int length() const | |
| 50 | { | ||
| 51 | 4560449 | return len; | |
| 52 | } | ||
| 53 | |||
| 54 | 1906251 | void resize(unsigned int new_length) | |
| 55 | { | ||
| 56 | 1906251 | if (new_length > capacity()) { | |
| 57 | 3542 | reallocate_capacity(new_length); | |
| 58 | } | ||
| 59 | |||
| 60 | 1906251 | len = new_length; | |
| 61 | 1906251 | data_ptr[new_length] = 0; //always ensure null terminator | |
| 62 | 1906251 | } | |
| 63 | |||
| 64 | 804 | void truncate(int newlen) | |
| 65 | { | ||
| 66 | 804 | resize(newlen); | |
| 67 | 804 | } | |
| 68 | |||
| 69 | 250535 | void assign(const char * newstr) | |
| 70 | { | ||
| 71 | 250535 | if (!newstr) newstr = ""; | |
| 72 | 250535 | assign(newstr, strlen(newstr)); | |
| 73 | 250535 | } | |
| 74 | |||
| 75 | 288094 | void assign(const string &newstr) | |
| 76 | { | ||
| 77 | 288094 | assign(newstr.data(), newstr.length()); | |
| 78 | 288094 | } | |
| 79 | |||
| 80 | 855747 | void assign(const char * newstr, int end) | |
| 81 | { | ||
| 82 | 855747 | resize(end); | |
| 83 | 855747 | copy(newstr, end, data_ptr); | |
| 84 | 855747 | } | |
| 85 | |||
| 86 | |||
| 87 | 12742 | string& operator=(const char * newstr) | |
| 88 | { | ||
| 89 | 12742 | assign(newstr); | |
| 90 | 12742 | return *this; | |
| 91 | } | ||
| 92 | |||
| 93 | 288094 | string& operator=(const string &newstr) | |
| 94 | { | ||
| 95 | 288094 | assign(newstr); | |
| 96 | 288094 | return *this; | |
| 97 | } | ||
| 98 | |||
| 99 | string& append(const string& other, int start, int end) | ||
| 100 | { | ||
| 101 | int current_end = length(); | ||
| 102 | resize(length() + end - start); | ||
| 103 | copy(other.data() + start, end - start, data_ptr + current_end); | ||
| 104 | return *this; | ||
| 105 | } | ||
| 106 | |||
| 107 | 3698 | string& append(const char *other, int start, int end) | |
| 108 | { | ||
| 109 | 3698 | int current_end = length(); | |
| 110 | 3698 | resize(length() + end - start); | |
| 111 | 3698 | copy(other + start, end - start, data_ptr + current_end); | |
| 112 | 3698 | return *this; | |
| 113 | } | ||
| 114 | |||
| 115 | 11181 | string& operator+=(const string& other) | |
| 116 | { | ||
| 117 | 11181 | int current_end = length(); | |
| 118 | 11181 | resize(length() + other.length()); | |
| 119 | 11181 | copy(other.data(), other.length(), data_ptr + current_end); | |
| 120 | 11181 | return *this; | |
| 121 | } | ||
| 122 | |||
| 123 | 100712 | string& operator+=(const char *other) | |
| 124 | { | ||
| 125 | 100712 | int current_end = length(); | |
| 126 | 100712 | int otherlen=(int)strlen(other); | |
| 127 | 100712 | resize(length() + otherlen); | |
| 128 | 100712 | copy(other, otherlen, data_ptr + current_end); | |
| 129 | 100712 | return *this; | |
| 130 | } | ||
| 131 | |||
| 132 | 934105 | string& operator+=(char c) | |
| 133 | { | ||
| 134 | 934105 | resize(length() + 1); | |
| 135 | 934105 | data_ptr[length() - 1] = c; | |
| 136 | 934105 | return *this; | |
| 137 | } | ||
| 138 | |||
| 139 | 2 | string operator+(char right) const | |
| 140 | { | ||
| 141 | 2 | string ret=*this; | |
| 142 | 2 | ret+=right; | |
| 143 | 2 | return ret; | |
| 144 | ✗ | } | |
| 145 | |||
| 146 | 45747 | string operator+(const char * right) const | |
| 147 | { | ||
| 148 | 45747 | string ret=*this; | |
| 149 | 45747 | ret+=right; | |
| 150 | 45747 | return ret; | |
| 151 | ✗ | } | |
| 152 | |||
| 153 | 2973563 | operator const char*() const | |
| 154 | { | ||
| 155 | 2973563 | return data(); | |
| 156 | } | ||
| 157 | |||
| 158 | 10356 | explicit operator bool() const | |
| 159 | { | ||
| 160 | 10356 | return length(); | |
| 161 | } | ||
| 162 | |||
| 163 | 988392 | string() | |
| 164 | 988392 | { | |
| 165 | 988392 | data_ptr = inlined.data; | |
| 166 | 988392 | len = 0; | |
| 167 | 988392 | inlined.data[0] = '\0'; | |
| 168 | 988392 | } | |
| 169 | 230592 | string(const char * newstr) : string() | |
| 170 | { | ||
| 171 | 230592 | assign(newstr); | |
| 172 | 230592 | } | |
| 173 | 30972 | string(const char * newstr, int newlen) : string() | |
| 174 | { | ||
| 175 | 30972 | assign(newstr, newlen); | |
| 176 | 30972 | } | |
| 177 | 240072 | string(const string& old) : string() | |
| 178 | { | ||
| 179 | 240072 | assign(old.data(), old.length()); | |
| 180 | 240072 | } | |
| 181 | |||
| 182 | 25108 | string(string &&move) noexcept : string() | |
| 183 | { | ||
| 184 | 25108 | *this = move; | |
| 185 | 25108 | } | |
| 186 | |||
| 187 | 112939 | string& operator=(string&& other) noexcept | |
| 188 | { | ||
| 189 | 112939 | if (other.is_inlined()) { | |
| 190 | // No resources to steal so just do a normal assignment | ||
| 191 | 112766 | *this = other; | |
| 192 | } else { | ||
| 193 | 173 | if (is_inlined()) { | |
| 194 | 125 | data_ptr = other.data_ptr; | |
| 195 | 125 | other.data_ptr = 0; | |
| 196 | } else { | ||
| 197 | // Give our old allocation back so other can free it for us | ||
| 198 | 48 | std::swap(data_ptr, other.data_ptr); | |
| 199 | } | ||
| 200 | 173 | len = other.len; | |
| 201 | 173 | allocated = other.allocated; | |
| 202 | } | ||
| 203 | 112939 | return *this; | |
| 204 | } | ||
| 205 | |||
| 206 | 938918 | ~string() | |
| 207 | { | ||
| 208 | 938918 | if(!is_inlined()){ | |
| 209 | 3377 | free(data_ptr); | |
| 210 | } | ||
| 211 | 938918 | } | |
| 212 | |||
| 213 | //maybe these should return refs to chain. but also good not to encourage chaining | ||
| 214 | 10 | void strip_prefix(char c) | |
| 215 | { | ||
| 216 | 10 | if(data()[0] == c){ | |
| 217 | 4 | std::memmove(data_ptr, data_ptr + 1, length() - 1); | |
| 218 | 4 | resize(length() - 1); | |
| 219 | } | ||
| 220 | 10 | } | |
| 221 | |||
| 222 | void strip_suffix(char c) | ||
| 223 | { | ||
| 224 | if (data()[length() - 1] == c) { | ||
| 225 | truncate(length() - 1); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | string& qreplace(const char * instr, const char * outstr); | ||
| 230 | string& qnormalize(); | ||
| 231 | |||
| 232 | // RPG Hacker: My hack shmeck to get around no longer supporting text mode. | ||
| 233 | // Symbol files are currently the only thing that use text mode, anyways, and I don't even know | ||
| 234 | // if the emulators that read them care about line endings. | ||
| 235 | ✗ | string& convert_line_endings_to_native() | |
| 236 | { | ||
| 237 | #if defined(windows) | ||
| 238 | // RPG Hacker: This is quite stinky, but doing the replacement directly will lead to a dead-lock. | ||
| 239 | // \x08 = backspace should never appear inside a string, so I'm abusing it here. | ||
| 240 | ✗ | return qreplace("\n", "\x08").qreplace("\x08", "\r\n"); | |
| 241 | #else | ||
| 242 | ✗ | return *this; | |
| 243 | #endif | ||
| 244 | } | ||
| 245 | |||
| 246 | private: | ||
| 247 | static const int scale_factor = 4; //scale sso | ||
| 248 | static const int inline_capacity = ((sizeof(char *) + sizeof(int) * 2) * scale_factor) - 2; | ||
| 249 | |||
| 250 | // Points to a malloc'd data block or to inlined.data | ||
| 251 | char *data_ptr; | ||
| 252 | unsigned int len; | ||
| 253 | union { | ||
| 254 | struct { | ||
| 255 | // Actual allocated capacity is +1 this value, to cover for the terminating NUL | ||
| 256 | unsigned int capacity; | ||
| 257 | } allocated; | ||
| 258 | struct { | ||
| 259 | char data[inline_capacity + 1]; | ||
| 260 | } inlined; | ||
| 261 | }; | ||
| 262 | |||
| 263 | void reallocate_capacity(unsigned int new_length); | ||
| 264 | |||
| 265 | 1906251 | unsigned capacity() const | |
| 266 | { | ||
| 267 | 1906251 | return is_inlined() ? inline_capacity : allocated.capacity; | |
| 268 | } | ||
| 269 | |||
| 270 | 2961823 | bool is_inlined() const | |
| 271 | { | ||
| 272 | 2961823 | return data_ptr == inlined.data; | |
| 273 | } | ||
| 274 | }; | ||
| 275 | #define STR (string) | ||
| 276 | |||
| 277 | #define ASAR_STRCMP_OPERATORS(op) \ | ||
| 278 | inline bool operator op(const string& left, const string& right) { \ | ||
| 279 | return strcmp(left, right) op 0; \ | ||
| 280 | } \ | ||
| 281 | inline bool operator op(const string& left, const char* right) { \ | ||
| 282 | return strcmp(left, right) op 0; \ | ||
| 283 | } \ | ||
| 284 | inline bool operator op(const char* left, const string& right) { \ | ||
| 285 | return strcmp(left, right) op 0; \ | ||
| 286 | } | ||
| 287 | |||
| 288 | 62964 | ASAR_STRCMP_OPERATORS(==) | |
| 289 | 10876 | ASAR_STRCMP_OPERATORS(!=) | |
| 290 | 4 | ASAR_STRCMP_OPERATORS(<) | |
| 291 | ✗ | ASAR_STRCMP_OPERATORS(<=) | |
| 292 | 2 | ASAR_STRCMP_OPERATORS(>) | |
| 293 | 2 | ASAR_STRCMP_OPERATORS(>=) | |
| 294 | #undef ASAR_STRCMP_OPERATORS | ||
| 295 | |||
| 296 | template<> | ||
| 297 | struct std::hash<string> { | ||
| 298 | 65519 | size_t operator()(const ::string& s) const { | |
| 299 | 65519 | return std::hash<std::string_view>()(std::string_view(s.data(), s.length())); | |
| 300 | } | ||
| 301 | }; | ||
| 302 | |||
| 303 | char * readfile(const char * fname, const char * basepath); | ||
| 304 | char * readfilenative(const char * fname); | ||
| 305 | bool readfile(const char * fname, const char * basepath, char ** data, int * len);//if you want an uchar*, cast it | ||
| 306 | char ** split(char * str, char key, int * len= nullptr); | ||
| 307 | char ** qsplit(char * str, char key, int * len= nullptr); | ||
| 308 | char ** qpsplit(char * str, char key, int * len= nullptr); | ||
| 309 | char ** qsplitstr(char * str, const char * key, int * len= nullptr); | ||
| 310 | bool confirmquotes(const char * str); | ||
| 311 | bool confirmqpar(const char * str); | ||
| 312 | char* strqpchr(char* str, char key); | ||
| 313 | char* strqpstr(char* str, const char* key); | ||
| 314 | 1056 | inline const char* strqpchr(const char* str, char key) { return strqpchr(const_cast<char*>(str), key); }; | |
| 315 | 114 | inline const char* strqpstr(const char* str, const char* key) { return strqpstr(const_cast<char*>(str), key); }; | |
| 316 | |||
| 317 | ✗ | inline string hex(unsigned int value) | |
| 318 | { | ||
| 319 | ✗ | char buffer[64]; | |
| 320 | if(0); | ||
| 321 | ✗ | else if (value<=0x000000FF) snprintf(buffer, sizeof(buffer), "%.2X", value); | |
| 322 | ✗ | else if (value<=0x0000FFFF) snprintf(buffer, sizeof(buffer), "%.4X", value); | |
| 323 | ✗ | else if (value<=0x00FFFFFF) snprintf(buffer, sizeof(buffer), "%.6X", value); | |
| 324 | ✗ | else snprintf(buffer, sizeof(buffer), "%.8X", value); | |
| 325 | ✗ | return buffer; | |
| 326 | } | ||
| 327 | |||
| 328 | 2 | inline string hex(unsigned int value, int width) | |
| 329 | { | ||
| 330 | 1 | char buffer[64]; | |
| 331 | 2 | snprintf(buffer, sizeof(buffer), "%.*X", width, value); | |
| 332 | 4 | return buffer; | |
| 333 | } | ||
| 334 | |||
| 335 | 3272 | inline string dec(int64_t value) | |
| 336 | { | ||
| 337 | 1685 | char buffer[64]; | |
| 338 | 3272 | snprintf(buffer, sizeof(buffer), "%" PRId64, value); | |
| 339 | 6544 | return buffer; | |
| 340 | } | ||
| 341 | |||
| 342 | // todo this function sucks, replace with std::to_chars | ||
| 343 | 258 | inline string ftostr(double value) | |
| 344 | { | ||
| 345 | // randomdude999: With 100 digits of precision, the buffer needs to be approx. 311+100, | ||
| 346 | // but let's be safe here https://stackoverflow.com/questions/7235456 | ||
| 347 | 129 | char rval[512]; | |
| 348 | // RPG Hacker: Ridiculously high precision, I know, but we're working with doubles | ||
| 349 | // here and can afford it, so no need to waste any precision | ||
| 350 | 258 | snprintf(rval, sizeof(rval), "%.100f", value); | |
| 351 | 258 | if (strchr(rval, '.'))//nuke useless zeroes | |
| 352 | { | ||
| 353 | 258 | char * end=strrchr(rval, '\0')-1; | |
| 354 | 25710 | while (*end=='0') | |
| 355 | { | ||
| 356 | 25452 | *end='\0'; | |
| 357 | 25452 | end--; | |
| 358 | } | ||
| 359 | 258 | if (*end=='.') *end='\0'; | |
| 360 | } | ||
| 361 | 516 | return rval; | |
| 362 | } | ||
| 363 | |||
| 364 | // Same as above, but with variable precision | ||
| 365 | 20 | inline string ftostrvar(double value, int precision) | |
| 366 | { | ||
| 367 | 20 | int clampedprecision = precision; | |
| 368 | 20 | if (clampedprecision < 0) clampedprecision = 0; | |
| 369 | 20 | if (clampedprecision > 100) clampedprecision = 100; | |
| 370 | |||
| 371 | // see above | ||
| 372 | 10 | char rval[512]; | |
| 373 | 20 | snprintf(rval, sizeof(rval), "%.*f", clampedprecision, (double)value); | |
| 374 | 20 | if (strchr(rval, '.'))//nuke useless zeroes | |
| 375 | { | ||
| 376 | 18 | char * end = strrchr(rval, '\0') - 1; | |
| 377 | 64 | while (*end == '0') | |
| 378 | { | ||
| 379 | 46 | *end = '\0'; | |
| 380 | 46 | end--; | |
| 381 | } | ||
| 382 | 18 | if (*end == '.') *end = '\0'; | |
| 383 | } | ||
| 384 | 40 | return rval; | |
| 385 | } | ||
| 386 | |||
| 387 | 107347 | inline bool stribegin(const char * str, const char * key) | |
| 388 | { | ||
| 389 | 435254 | for (int i=0;key[i];i++) | |
| 390 | { | ||
| 391 | 425346 | if (to_lower(str[i])!=to_lower(key[i])) return false; | |
| 392 | } | ||
| 393 | 9908 | return true; | |
| 394 | } | ||
| 395 | |||
| 396 | inline bool striend(const char * str, const char * key) | ||
| 397 | { | ||
| 398 | const char * keyend=strrchr(key, '\0'); | ||
| 399 | const char * strend=strrchr(str, '\0'); | ||
| 400 | if(keyend-key > strend-str) return false; | ||
| 401 | |||
| 402 | while (key!=keyend) | ||
| 403 | { | ||
| 404 | keyend--; | ||
| 405 | strend--; | ||
| 406 | if (to_lower(*strend)!=to_lower(*keyend)) return false; | ||
| 407 | } | ||
| 408 | return true; | ||
| 409 | } | ||
| 410 | |||
| 411 | inline bool stricmpwithupper(const char *word1, const char *word2) | ||
| 412 | { | ||
| 413 | while(*word2) | ||
| 414 | { | ||
| 415 | if(to_upper(*word1++) != *word2++) return true; | ||
| 416 | } | ||
| 417 | return *word1; | ||
| 418 | } | ||
| 419 | |||
| 420 | 27676 | inline bool stricmpwithlower(const char *word1, const char *word2) | |
| 421 | { | ||
| 422 | 32962 | while(*word2) | |
| 423 | { | ||
| 424 | 32442 | if(to_lower(*word1++) != *word2++) return true; | |
| 425 | } | ||
| 426 | 520 | return *word1; | |
| 427 | } | ||
| 428 | |||
| 429 | //function: return the string without quotes around it, if any exists | ||
| 430 | //if they don't exist, return it unaltered | ||
| 431 | //it is not guaranteed to return str | ||
| 432 | //it is not guaranteed to not edit str | ||
| 433 | //the input must be freed even though it's garbage, the output must not | ||
| 434 | 11527 | inline const char * dequote(char * str) | |
| 435 | { | ||
| 436 | 11527 | if (*str!='"') return str; | |
| 437 | 10759 | char *end = strrchr(str, '"'); | |
| 438 | 10759 | if (end) | |
| 439 | { | ||
| 440 | // make sure the closing quote is at the end of the argument | ||
| 441 | 10759 | if(end[1] != 0) return nullptr; | |
| 442 | 10759 | *end = 0; | |
| 443 | 10759 | char *quote = str+1; | |
| 444 | 10903 | while((quote = strstr(quote, "\"\""))) { | |
| 445 | 144 | memmove(quote, quote+1, strlen(quote)); | |
| 446 | 144 | quote++; | |
| 447 | } | ||
| 448 | 10759 | return str + 1; | |
| 449 | } | ||
| 450 | ✗ | return nullptr; | |
| 451 | } | ||
| 452 | |||
| 453 | 41996 | inline char * strqchr(char * str, char key) | |
| 454 | { | ||
| 455 | 252746 | while (*str != '\0') | |
| 456 | { | ||
| 457 | 245516 | if (*str == key) { return str; } | |
| 458 | 210750 | else if (*str == '"' || *str == '\'') | |
| 459 | { | ||
| 460 | // Special case hack for ''', which is currently our official way of handling the ' character. | ||
| 461 | // Even though it really stinks. | ||
| 462 | 816 | if (str[0] == '\'' && str[1] == '\'' && str[2] == '\'') { str += 2; } | |
| 463 | else | ||
| 464 | { | ||
| 465 | 798 | char delimiter = *str; | |
| 466 | |||
| 467 | do | ||
| 468 | { | ||
| 469 | 3996 | str++; | |
| 470 | |||
| 471 | // If we want to support backslash escapes, we'll have to add that right here. | ||
| 472 | 3996 | } while (*str != delimiter && *str != '\0'); | |
| 473 | |||
| 474 | // This feels like a superfluous check, but I can't really find a clean way to avoid it. | ||
| 475 | 798 | if (*str == '\0') { return nullptr; } | |
| 476 | } | ||
| 477 | } | ||
| 478 | |||
| 479 | 210750 | str++; | |
| 480 | } | ||
| 481 | |||
| 482 | 7230 | return nullptr; | |
| 483 | } | ||
| 484 | |||
| 485 | 41384 | inline const char * strqchr(const char * str, char key) { | |
| 486 | 41384 | return strqchr(const_cast<char*>(str), key); | |
| 487 | } | ||
| 488 | |||
| 489 | 2580 | inline string substr(const char * str, int len) | |
| 490 | { | ||
| 491 | 2580 | return string(str, len); | |
| 492 | } | ||
| 493 | |||
| 494 | |||
| 495 | 44808 | inline char *strip_whitespace(char *str) | |
| 496 | { | ||
| 497 | 44913 | while(is_space(*str)) str++; | |
| 498 | 44916 | for(int i = strlen(str) - 1; i >= 0; i--) | |
| 499 | { | ||
| 500 | 42304 | if(!is_space(str[i])) | |
| 501 | { | ||
| 502 | 42196 | str[i + 1] = 0; | |
| 503 | 42196 | return str; | |
| 504 | } | ||
| 505 | } | ||
| 506 | 2612 | return str; | |
| 507 | } | ||
| 508 | 266 | inline void strip_whitespace(string &str) | |
| 509 | { | ||
| 510 | 266 | str = string(strip_whitespace(str.temp_raw())); | |
| 511 | 266 | } | |
| 512 | |||
| 513 | 41324 | inline void grab_until_space(string& to, const char*& from) { | |
| 514 | // i tried to use strchr here as an optimization, but it wasn't even faster.... | ||
| 515 | 41324 | const char* params = strqchr(from, ' '); | |
| 516 | 41324 | if(params) { | |
| 517 | 34124 | to.assign(from, params - from); | |
| 518 | 34124 | from = params+1; | |
| 519 | } else { | ||
| 520 | 7200 | to.assign(from); | |
| 521 | 7200 | from += to.length(); // now from points at a null byte | |
| 522 | } | ||
| 523 | 41324 | } | |
| 524 | |||
| 525 | string &itrim(string &str, const char * left, const char * right); | ||
| 526 | |||
| 527 | 110327 | inline string &lower(string &old) | |
| 528 | { | ||
| 529 | 110327 | int length = old.length(); | |
| 530 | 958990 | for (int i=0;i<length;i++) old.raw()[i]=(char)to_lower(old.data()[i]); | |
| 531 | 110327 | return old; | |
| 532 | } | ||
| 533 | |||
| 534 |