Showing the changes to core/util_string.cpp (1 of 129 files). Show all
Unified · Split
| @@ -0,0 +1,569 @@ | |||
| 1 | /*---------------------------------------------------------------------------- | ||
| 2 | ChucK Concurrent, On-the-fly Audio Programming Language | ||
| 3 | Compiler and Virtual Machine | ||
| 4 | |||
| 5 | Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved. | ||
| 6 | http://chuck.stanford.edu/ | ||
| 7 | http://chuck.cs.princeton.edu/ | ||
| 8 | |||
| 9 | This program is free software; you can redistribute it and/or modify | ||
| 10 | it under the terms of the GNU General Public License as published by | ||
| 11 | the Free Software Foundation; either version 2 of the License, or | ||
| 12 | (at your option) any later version. | ||
| 13 | |||
| 14 | This program is distributed in the hope that it will be useful, | ||
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | GNU General Public License for more details. | ||
| 18 | |||
| 19 | You should have received a copy of the GNU General Public License | ||
| 20 | along with this program; if not, write to the Free Software | ||
| 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
| 22 | U.S.A. | ||
| 23 | -----------------------------------------------------------------------------*/ | ||
| 24 | |||
| 25 | //----------------------------------------------------------------------------- | ||
| 26 | // name: util_string.cpp | ||
| 27 | // desc: string functions utility | ||
| 28 | // | ||
| 29 | // author: Ge Wang (ge@ccrma.stanford.edu | gewang@cs.princeton.edu) | ||
| 30 | // Spencer Salazar (spencer@ccrma.stanford.edu) | ||
| 31 | // date: Summer 2005 | ||
| 32 | //----------------------------------------------------------------------------- | ||
| 33 | #include "util_string.h" | ||
| 34 | #include "chuck_errmsg.h" | ||
| 35 | |||
| 36 | #ifdef __PLATFORM_WIN32__ | ||
| 37 | #include <Windows.h> | ||
| 38 | #endif // __PLATFORM_WIN32__ | ||
| 39 | |||
| 40 | #ifdef __PLATFORM_LINUX__ | ||
| 41 | #include <linux/limits.h> | ||
| 42 | #endif // __PLATFORM_LINUX__ | ||
| 43 | |||
| 44 | #include <stdio.h> | ||
| 45 | using namespace std; | ||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | //----------------------------------------------------------------------------- | ||
| 50 | // name: itoa() | ||
| 51 | // desc: ... | ||
| 52 | //----------------------------------------------------------------------------- | ||
| 53 | string itoa( t_CKINT val ) | ||
| 54 | { | ||
| 55 | char buffer[128]; | ||
| 56 | sprintf( buffer, "%li", val ); | ||
| 57 | return string(buffer); | ||
| 58 | } | ||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | //----------------------------------------------------------------------------- | ||
| 63 | // name: ftoa() | ||
| 64 | // desc: ... | ||
| 65 | //----------------------------------------------------------------------------- | ||
| 66 | string ftoa( t_CKFLOAT val, t_CKUINT precision ) | ||
| 67 | { | ||
| 68 | char str[32]; | ||
| 69 | char buffer[128]; | ||
| 70 | if( precision > 32 ) precision = 32; | ||
| 71 | sprintf( str, "%%.%lif", precision ); | ||
| 72 | sprintf( buffer, str, val ); | ||
| 73 | return string(buffer); | ||
| 74 | } | ||
| 75 | |||
| 76 | |||
| 77 | |||
| 78 | |||
| 79 | //----------------------------------------------------------------------------- | ||
| 80 | // name: tolower() | ||
| 81 | // desc: ... | ||
| 82 | //----------------------------------------------------------------------------- | ||
| 83 | string tolower( const string & str ) | ||
| 84 | { | ||
| 85 | string s = str; | ||
| 86 | t_CKUINT len = s.length(); | ||
| 87 | |||
| 88 | // loop | ||
| 89 | for( t_CKUINT i = 0; i < len; i++ ) | ||
| 90 | if( s[i] >= 'A' && s[i] <= 'Z' ) | ||
| 91 | s[i] += 32; | ||
| 92 | |||
| 93 | return s; | ||
| 94 | } | ||
| 95 | |||
| 96 | |||
| 97 | |||
| 98 | |||
| 99 | //----------------------------------------------------------------------------- | ||
| 100 | // name: toupper() | ||
| 101 | // desc: ... | ||
| 102 | //----------------------------------------------------------------------------- | ||
| 103 | string toupper( const string & str ) | ||
| 104 | { | ||
| 105 | string s = str; | ||
| 106 | t_CKUINT len = s.length(); | ||
| 107 | |||
| 108 | // loop | ||
| 109 | for( t_CKUINT i = 0; i < len; i++ ) | ||
| 110 | if( s[i] >= 'a' && s[i] <= 'z' ) | ||
| 111 | s[i] -= 32; | ||
| 112 | |||
| 113 | return s; | ||
| 114 | } | ||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | |||
| 119 | //----------------------------------------------------------------------------- | ||
| 120 | // name: trim() | ||
| 121 | // desc: ... | ||
| 122 | //----------------------------------------------------------------------------- | ||
| 123 | string trim( const string & val ) | ||
| 124 | { | ||
| 125 | // two ends | ||
| 126 | t_CKINT start = 0; | ||
| 127 | t_CKINT end = val.length() - 1; | ||
| 128 | |||
| 129 | // left trim | ||
| 130 | for( start = 0; start < end; start++ ) | ||
| 131 | { | ||
| 132 | // non-white space | ||
| 133 | if( val[start] != ' ' && val[start] != '\t' ) | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | |||
| 137 | // if start > end, then all white space | ||
| 138 | if( start > end ) return ""; | ||
| 139 | |||
| 140 | // right trim | ||
| 141 | for( ; end >= start; end-- ) | ||
| 142 | { | ||
| 143 | // non-white space | ||
| 144 | if( val[end] != ' ' && val[end] != '\t' ) | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | |||
| 148 | // cannot be | ||
| 149 | assert( end >= start ); | ||
| 150 | |||
| 151 | // return | ||
| 152 | return val.substr( start, end - start + 1 ); | ||
| 153 | } | ||
| 154 | |||
| 155 | |||
| 156 | |||
| 157 | //----------------------------------------------------------------------------- | ||
| 158 | // name: ltrim() | ||
| 159 | // desc: ... | ||
| 160 | //----------------------------------------------------------------------------- | ||
| 161 | string ltrim( const string & val ) | ||
| 162 | { | ||
| 163 | // two ends | ||
| 164 | t_CKINT start = 0; | ||
| 165 | t_CKINT end = val.length() - 1; | ||
| 166 | |||
| 167 | // left trim | ||
| 168 | for( start = 0; start < end; start++ ) | ||
| 169 | { | ||
| 170 | // non-white space | ||
| 171 | if( val[start] != ' ' && val[start] != '\t' ) | ||
| 172 | break; | ||
| 173 | } | ||
| 174 | |||
| 175 | // if start > end, then all white space | ||
| 176 | if( start > end ) return ""; | ||
| 177 | |||
| 178 | // return | ||
| 179 | return val.substr( start, end - start + 1 ); | ||
| 180 | } | ||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | //----------------------------------------------------------------------------- | ||
| 186 | // name: rtrim() | ||
| 187 | // desc: ... | ||
| 188 | //----------------------------------------------------------------------------- | ||
| 189 | string rtrim( const string & val ) | ||
| 190 | { | ||
| 191 | // two ends | ||
| 192 | t_CKINT start = 0; | ||
| 193 | t_CKINT end = val.length() - 1; | ||
| 194 | |||
| 195 | // right trim | ||
| 196 | for( ; end >= start; end-- ) | ||
| 197 | { | ||
| 198 | // non-white space | ||
| 199 | if( val[end] != ' ' && val[end] != '\t' ) | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | |||
| 203 | // if end < start, then all white space | ||
| 204 | if( end < start ) return ""; | ||
| 205 | |||
| 206 | // return | ||
| 207 | return val.substr( start, end - start + 1 ); | ||
| 208 | } | ||
| 209 | |||
| 210 | |||
| 211 | |||
| 212 | |||
| 213 | //----------------------------------------------------------------------------- | ||
| 214 | // name: extract_args() | ||
| 215 | // desc: extract argument from format filename:arg1:arg2:etc | ||
| 216 | //----------------------------------------------------------------------------- | ||
| 217 | t_CKBOOL extract_args( const string & token, | ||
| 218 | string & filename, vector<string> & args ) | ||
| 219 | { | ||
| 220 | // clear vector | ||
| 221 | args.clear(); | ||
| 222 | // clear filename | ||
| 223 | filename = ""; | ||
| 224 | |||
| 225 | // last : found pos | ||
| 226 | t_CKINT prev_pos = 0; | ||
| 227 | // curr : pos | ||
| 228 | t_CKINT i = 0; | ||
| 229 | |||
| 230 | string tmp; | ||
| 231 | |||
| 232 | // copy and trim | ||
| 233 | string s = trim( token ); | ||
| 234 | |||
| 235 | // ignore second character as arg separator if its : on Windows | ||
| 236 | t_CKBOOL ignoreSecond = FALSE; | ||
| 237 | #ifdef __PLATFORM_WIN32__ | ||
| 238 | ignoreSecond = TRUE; | ||
| 239 | #endif // __PLATFORM_WIN32__ | ||
| 240 | |||
| 241 | // detect | ||
| 242 | t_CKBOOL scan = FALSE; | ||
| 243 | t_CKBOOL ret = TRUE; | ||
| 244 | t_CKBOOL ignoreNext = FALSE; | ||
| 245 | char * mask = NULL; | ||
| 246 | for( i = 0; i < s.length(); i++ ) | ||
| 247 | if( s[i] == '\\' ) | ||
| 248 | { | ||
| 249 | scan = TRUE; | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | |||
| 253 | // mad... | ||
| 254 | if( scan ) | ||
| 255 | { | ||
| 256 | mask = new char[s.length()]; | ||
| 257 | // zero | ||
| 258 | memset(mask, 0, s.length()*sizeof(char)); | ||
| 259 | |||
| 260 | // loop through | ||
| 261 | for( i = 0; i < s.length(); i++ ) | ||
| 262 | { | ||
| 263 | // escape (tom and ge fixed this, which used to look for \\ then : and randomly incremented i) | ||
| 264 | // added '/' 1.3.1.1 | ||
| 265 | // if( s[i] == ':' && (i+1) < s.length() && ( s[i+1] == '\\' || s[i+1] == '/' ) ) | ||
| 266 | // { | ||
| 267 | // mask[i] = 1; | ||
| 268 | // break; // added 1.3.1.1 | ||
| 269 | // } | ||
| 270 | |||
| 271 | // 1.3.2.0: spencer and ge fixed this, requiring \ to escape : | ||
| 272 | if( s[i] == '\\' && (i+1) < s.length() ) | ||
| 273 | { | ||
| 274 | // check next char | ||
| 275 | if( s[i+1] == ':' || s[i+1] == '\\' ) | ||
| 276 | { | ||
| 277 | // add | ||
| 278 | mask[i] = 1; | ||
| 279 | // skip next character | ||
| 280 | i++; | ||
| 281 | } | ||
| 282 | else | ||
| 283 | { | ||
| 284 | // error: recognized escape target | ||
| 285 | CK_FPRINTF_STDERR( "[chuck]: unrecognized escape sequence: \\%c", s[i+1] ); | ||
| 286 | // return false | ||
| 287 | ret = FALSE; | ||
| 288 | // go clean | ||
| 289 | goto done; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | // loop through | ||
| 296 | for( i = 0; i < s.length(); i++ ) | ||
| 297 | { | ||
| 298 | // check mask | ||
| 299 | if( mask && mask[i] ) | ||
| 300 | { | ||
| 301 | //CK_FPRINTF_STDERR( "skipping %c\n", s[i] ); | ||
| 302 | // mark the next as false | ||
| 303 | ignoreNext = TRUE; | ||
| 304 | // skip this one | ||
| 305 | continue; | ||
| 306 | } | ||
| 307 | |||
| 308 | // look for : | ||
| 309 | if( !ignoreNext && s[i] == ':' && !(ignoreSecond && i == 1)) | ||
| 310 | { | ||
| 311 | // sanity | ||
| 312 | if( i == 0 ) | ||
| 313 | { | ||
| 314 | ret = FALSE; | ||
| 315 | goto done; | ||
| 316 | } | ||
| 317 | |||
| 318 | // copy | ||
| 319 | if( filename == "" ) | ||
| 320 | filename = tmp; | ||
| 321 | else | ||
| 322 | args.push_back( tmp ); | ||
| 323 | |||
| 324 | tmp = ""; | ||
| 325 | |||
| 326 | // update | ||
| 327 | prev_pos = i + 1; | ||
| 328 | } | ||
| 329 | else | ||
| 330 | { | ||
| 331 | tmp.append(1, s[i]); | ||
| 332 | } | ||
| 333 | |||
| 334 | // reset | ||
| 335 | ignoreNext = FALSE; | ||
| 336 | } | ||
| 337 | |||
| 338 | // get the remainder, if any | ||
| 339 | if( tmp.length() ) | ||
| 340 | { | ||
| 341 | // copy | ||
| 342 | if( filename == "" ) | ||
| 343 | filename = tmp; | ||
| 344 | else | ||
| 345 | args.push_back( tmp ); | ||
| 346 | } | ||
| 347 | |||
| 348 | // testing code - spencer 1.3.2.0 | ||
| 349 | // CK_FPRINTF_STDERR( "INPUT: %s\n", token.c_str() ); | ||
| 350 | // CK_FPRINTF_STDERR( "FILENAME: %s\n", filename.c_str() ); | ||
| 351 | // for(i = 0; i < args.size(); i++) | ||
| 352 | // CK_FPRINTF_STDERR( "ARG: %s\n", args[i].c_str() ); | ||
| 353 | |||
| 354 | done: | ||
| 355 | // reclaim | ||
| 356 | SAFE_DELETE_ARRAY( mask ); | ||
| 357 | |||
| 358 | return ret; | ||
| 359 | } | ||
| 360 | |||
| 361 | |||
| 362 | /* from http://developer.apple.com/library/mac/#qa/qa1549/_index.html */ | ||
| 363 | |||
| 364 | #ifndef __PLATFORM_WIN32__ | ||
| 365 | |||
| 366 | #include <glob.h> | ||
| 367 | |||
| 368 | char* CreatePathByExpandingTildePath(const char* path) | ||
| 369 | { | ||
| 370 | glob_t globbuf; | ||
| 371 | char **v; | ||
| 372 | char *expandedPath = NULL, *result = NULL; | ||
| 373 | |||
| 374 | assert(path != NULL); | ||
| 375 | |||
| 376 | if (glob(path, GLOB_TILDE, NULL, &globbuf) == 0) //success | ||
| 377 | { | ||
| 378 | v = globbuf.gl_pathv; //list of matched pathnames | ||
| 379 | expandedPath = v[0]; //number of matched pathnames, gl_pathc == 1 | ||
| 380 | |||
| 381 | result = (char*)calloc(1, strlen(expandedPath) + 1); //the extra char is for the null-termination | ||
| 382 | if(result) | ||
| 383 | strncpy(result, expandedPath, strlen(expandedPath) + 1); //copy the null-termination as well | ||
| 384 | |||
| 385 | globfree(&globbuf); | ||
| 386 | } | ||
| 387 | |||
| 388 | return result; | ||
| 389 | } | ||
| 390 | |||
| 391 | #endif // __PLATFORM_WIN32__ | ||
| 392 | |||
| 393 | |||
| 394 | // get full path to file | ||
| 395 | std::string get_full_path( const std::string & fp ) | ||
| 396 | { | ||
| 397 | #ifndef __PLATFORM_WIN32__ | ||
| 398 | |||
| 399 | char buf[PATH_MAX]; | ||
| 400 | |||
| 401 | char * result = realpath(fp.c_str(), buf); | ||
| 402 | |||
| 403 | // try with .ck extension | ||
| 404 | if(result == NULL && !str_endsin(fp.c_str(), ".ck")) | ||
| 405 | result = realpath((fp + ".ck").c_str(), buf); | ||
| 406 | |||
| 407 | if(result == NULL) | ||
| 408 | return fp; | ||
| 409 | else | ||
| 410 | return buf; | ||
| 411 | |||
| 412 | #else // | ||
| 413 | |||
| 414 | char buf[MAX_PATH]; | ||
| 415 | |||
| 416 | DWORD result = GetFullPathName(fp.c_str(), MAX_PATH, buf, NULL); | ||
| 417 | |||
| 418 | // try with .ck extension | ||
| 419 | if(result == 0 && !str_endsin(fp.c_str(), ".ck")) | ||
| 420 | result = GetFullPathName((fp + ".ck").c_str(), MAX_PATH, buf, NULL); | ||
| 421 | |||
| 422 | if(result == 0) | ||
| 423 | return fp; | ||
| 424 | else | ||
| 425 | return normalize_directory_separator(buf); | ||
| 426 | |||
| 427 | #endif // __PLATFORM_WIN32__ | ||
| 428 | } | ||
| 429 | |||
| 430 | |||
| 431 | std::string expand_filepath( std::string & fp ) | ||
| 432 | { | ||
| 433 | #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) | ||
| 434 | // no expansion in Windows systems | ||
| 435 | return fp; | ||
| 436 | #else | ||
| 437 | |||
| 438 | char * expanded_cstr = CreatePathByExpandingTildePath( fp.c_str() ); | ||
| 439 | |||
| 440 | if(expanded_cstr == NULL) | ||
| 441 | return fp; | ||
| 442 | |||
| 443 | std::string expanded_stdstr = expanded_cstr; | ||
| 444 | |||
| 445 | free(expanded_cstr); | ||
| 446 | |||
| 447 | return expanded_stdstr; | ||
| 448 | |||
| 449 | #endif | ||
| 450 | } | ||
| 451 | |||
| 452 | |||
| 453 | std::string extract_filepath_dir(std::string &filepath) | ||
| 454 | { | ||
| 455 | char path_separator = '/'; | ||
| 456 | |||
| 457 | //#ifdef __WINDOWS_DS__ | ||
| 458 | // path_separator = '\\'; | ||
| 459 | //#else | ||
| 460 | // path_separator = '/'; | ||
| 461 | //#endif | ||
| 462 | |||
| 463 | // if the last character is a slash, skip it | ||
| 464 | int i = filepath.rfind(path_separator); | ||
| 465 | |||
| 466 | if(i == std::string::npos) | ||
| 467 | return std::string(); | ||
| 468 | |||
| 469 | // skip any/all extra trailing slashes | ||
| 470 | while( i > 0 && filepath[i-1] == path_separator ) | ||
| 471 | i--; | ||
| 472 | |||
| 473 | // change spencer 2014-7-17: include trailing slash | ||
| 474 | return std::string(filepath, 0, i+1); | ||
| 475 | } | ||
| 476 | |||
| 477 | // added: ge 1.3.2.0 | ||
| 478 | string dir_go_up( const string & dir, t_CKINT numUp ) | ||
| 479 | { | ||
| 480 | // separator | ||
| 481 | char path_separator = '/'; | ||
| 482 | |||
| 483 | // sanity check | ||
| 484 | if( numUp < 0 ) numUp = -numUp; | ||
| 485 | |||
| 486 | // pos | ||
| 487 | size_t pos = dir.length(); | ||
| 488 | |||
| 489 | // skip any trailing slashes | ||
| 490 | while( pos > 0 && dir[pos-1] == path_separator ) | ||
| 491 | pos--; | ||
| 492 | |||
| 493 | // loop | ||
| 494 | while( numUp > 0 ) | ||
| 495 | { | ||
| 496 | // find the last slash | ||
| 497 | pos = dir.rfind( path_separator, pos-1 ); | ||
| 498 | // skip any extra slashes | ||
| 499 | while( pos > 0 && dir[pos-1] == path_separator ) | ||
| 500 | pos--; | ||
| 501 | |||
| 502 | // not there | ||
| 503 | if( pos == string::npos ) | ||
| 504 | return ""; | ||
| 505 | else if( pos == 0 ) | ||
| 506 | return "/"; | ||
| 507 | |||
| 508 | // TODO: what about windows? | ||
| 509 | // TODO: what about windows network: with two backslashes? "\\\\" | ||
| 510 | |||
| 511 | // decrement | ||
| 512 | numUp--; | ||
| 513 | } | ||
| 514 | |||
| 515 | |||
| 516 | // otherwise | ||
| 517 | // change spencer 2014-7-17: include trailing slash | ||
| 518 | string str = string( dir, 0, pos ); | ||
| 519 | if( str[str.length()-1] != '/' ) str = str + "/"; | ||
| 520 | return str; | ||
| 521 | } | ||
| 522 | |||
| 523 | //----------------------------------------------------------------------------- | ||
| 524 | // name: parse_path_list() | ||
| 525 | // desc: split "x:y:z"-style path list into {"x","y","z"} | ||
| 526 | //----------------------------------------------------------------------------- | ||
| 527 | void parse_path_list( std::string & str, std::list<std::string> & lst ) | ||
| 528 | { | ||
| 529 | #if defined(__PLATFORM_WIN32__) | ||
| 530 | const char separator = ';'; | ||
| 531 | #else | ||
| 532 | const char separator = ':'; | ||
| 533 | #endif | ||
| 534 | std::string::size_type i = 0, last = 0; | ||
| 535 | while( last < str.size() && | ||
| 536 | ( i = str.find( separator, last ) ) != std::string::npos ) | ||
| 537 | { | ||
| 538 | lst.push_back( str.substr( last, i - last ) ); | ||
| 539 | last = i + 1; | ||
| 540 | } | ||
| 541 | |||
| 542 | lst.push_back( str.substr( last, str.size() - last ) ); | ||
| 543 | } | ||
| 544 | |||
| 545 | |||
| 546 | |||
| 547 | std::string normalize_directory_separator(const std::string &filepath) | ||
| 548 | { | ||
| 549 | #ifdef __PLATFORM_WIN32__ | ||
| 550 | std::string new_filepath = filepath; | ||
| 551 | int len = new_filepath.size(); | ||
| 552 | for(int i = 0; i < len; i++) | ||
| 553 | { | ||
| 554 | if(new_filepath[i] == '\\') new_filepath[i] = '/'; | ||
| 555 | } | ||
| 556 | |||
| 557 | return new_filepath; | ||
| 558 | #else | ||
| 559 | return std::string(filepath); | ||
| 560 | #endif // __PLATFORM_WIN32__ | ||
| 561 | } | ||
| 562 | |||
| 563 | int str_endsin(const char *str, const char *end) | ||
| 564 | { | ||
| 565 | size_t len = strlen(str); | ||
| 566 | size_t endlen = strlen(end); | ||
| 567 | |||
| 568 | return strncmp(str+(len-endlen), end, endlen) == 0; | ||
| 569 | } | ||