1 /* 2 SuperCollider real time audio synthesis system 3 Copyright (c) 2002 James McCartney. All rights reserved. 4 http://www.audiosynth.com 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 /* 21 22 Primitives for String. 23 24 */ 25 26 #include "PyrPrimitive.h" 27 #include "PyrKernel.h" 28 #include "GC.h" 29 #include "Hash.h" 30 #include <string.h> 31 #include <stdlib.h> 32 #include <ctype.h> 33 #include <vector> 34 #include "PyrLexer.h" 35 #include "SC_Filesystem.hpp" 36 #include "SC_Codecvt.hpp" // path_to_utf8_str 37 #ifdef _WIN32 38 # include <direct.h> 39 # include "SC_Win32Utils.h" 40 #else 41 # include <sys/param.h> 42 #endif 43 44 #include <boost/regex.hpp> 45 #include <boost/intrusive/list.hpp> 46 #include <boost/intrusive/unordered_set.hpp> 47 #include <boost/filesystem/fstream.hpp> // ifstream 48 #include <boost/filesystem/path.hpp> // path 49 50 #include <yaml-cpp/yaml.h> 51 52 using namespace std; 53 namespace bfs = boost::filesystem; 54 55 int prStringAsSymbol(struct VMGlobals* g, int numArgsPushed); 56 int prStringAsSymbol(struct VMGlobals* g, int numArgsPushed) { 57 PyrSlot* a; 58 char str[1024], *strp = nullptr; 59 int len; 60 61 a = g->sp; 62 len = slotRawObject(a)->size; 63 strp = len > 1023 ? (char*)malloc(len + 1) : str; 64 65 memcpy(strp, slotRawString(a)->s, len); 66 strp[len] = 0; 67 68 SetSymbol(a, getsym(strp)); 69 70 if (len > 1023) 71 free(strp); 72 73 return errNone; 74 } 75 76 int prString_AsInteger(struct VMGlobals* g, int numArgsPushed); 77 int prString_AsInteger(struct VMGlobals* g, int numArgsPushed) { 78 PyrSlot* a = g->sp; 79 80 char str[256]; 81 int err = slotStrVal(a, str, 255); 82 if (err) 83 return err; 84 85 SetInt(a, atoi(str)); 86 87 return errNone; 88 } 89 90 int prString_AsFloat(struct VMGlobals* g, int numArgsPushed); 91 int prString_AsFloat(struct VMGlobals* g, int numArgsPushed) { 92 PyrSlot* a = g->sp; 93 94 char str[256]; 95 int err = slotStrVal(a, str, 255); 96 if (err) 97 return err; 98 99 SetFloat(a, atof(str)); 100 101 return errNone; 102 } 103 104 int prString_AsCompileString(struct VMGlobals* g, int numArgsPushed) { 105 PyrSlot* a = g->sp; 106 PyrString* scstr = slotRawString(a); 107 char* chars1 = scstr->s; 108 int newSize = scstr->size + 2; 109 for (int i = 0; i < scstr->size; ++i) { 110 if (chars1[i] == '"' || chars1[i] == '\\') 111 newSize++; 112 } 113 PyrString* newString = newPyrStringN(g->gc, newSize, 0, true); 114 char* chars2 = newString->s; 115 chars2[0] = '"'; 116 chars2[newSize - 1] = '"'; 117 int k = 1; 118 for (int i = 0; i < scstr->size; ++i) { 119 int c = chars1[i]; 120 if (c == '"' || c == '\\') 121 chars2[k++] = '\\'; 122 chars2[k++] = c; 123 } 124 SetObject(a, newString); 125 return errNone; 126 } 127 128 int prString_Format(struct VMGlobals* g, int numArgsPushed) { 129 PyrSlot* a = g->sp - 1; 130 PyrSlot* b = g->sp; 131 132 if (!isKindOfSlot(b, class_array)) 133 return errWrongType; 134 135 char* fmt = slotRawString(a)->s; 136 137 int asize = slotRawObject(a)->size; 138 int bsize = slotRawObject(b)->size; 139 int csize = asize; 140 141 PyrSlot* slots = slotRawObject(b)->slots; 142 for (int i = 0; i < bsize; ++i) { 143 PyrSlot* slot = slots + i; 144 if (!isKindOfSlot(slot, class_string)) 145 return errWrongType; 146 csize += slotRawString(slot)->size; 147 } 148 PyrString* newString = newPyrStringN(g->gc, csize, 0, true); 149 char* buf = newString->s; 150 151 int k = 0; 152 int index = 0; 153 for (int i = 0; i < asize;) { 154 char ch = fmt[i++]; 155 if (ch == '%') { 156 if (index < bsize) { 157 PyrString* bstring = slotRawString(&slots[index]); 158 memcpy(buf + k, bstring->s, bstring->size); 159 k += bstring->size; 160 index++; 161 } 162 } else if (ch == '\\') { 163 if (i >= asize) 164 break; 165 ch = fmt[i++]; 166 if (ch == '%') { 167 buf[k++] = '%'; 168 } else { 169 i--; 170 buf[k++] = '\\'; 171 } 172 } else { 173 buf[k++] = ch; 174 } 175 } 176 newString->size = k; 177 SetObject(a, newString); 178 return errNone; 179 }; 180 181 namespace detail { 182 183 namespace bin = boost::intrusive; 184 185 class regex_lru_cache { 186 int regex_flags; 187 188 struct regex_node : bin::list_base_hook<>, bin::unordered_set_base_hook<> { 189 public: 190 regex_node(const char* str, size_t size, int regex_flags): pattern(str, size, regex_flags) {} 191 192 boost::regex const& get(void) const { return pattern; } 193 194 private: 195 boost::regex pattern; 196 }; 197 198 struct regex_equal { 199 bool operator()(regex_node const& lhs, regex_node const& rhs) const { return lhs.get() == rhs.get(); } 200 201 bool operator()(const char* lhs, regex_node const& rhs) const { 202 return strcmp(lhs, rhs.get().str().c_str()) == 0; 203 } 204 }; 205 206 static inline std::size_t string_hash(const char* str) { 207 std::size_t ret = 0; 208 209 // sdbm hash 210 int c; 211 while ((c = *str++)) 212 ret = c + (ret << 6) + (ret << 16) - ret; 213 214 return ret; 215 } 216 217 struct regex_hash { 218 size_t operator()(regex_node const& arg) const { return string_hash(arg.get().str().c_str()); } 219 220 size_t operator()(const char* arg) const { return string_hash(arg); } 221 }; 222 223 typedef bin::unordered_set<regex_node, bin::equal<regex_equal>, bin::hash<regex_hash>, bin::power_2_buckets<true>, 224 bin::constant_time_size<false>> 225 re_set_t; 226 typedef re_set_t::bucket_type bucket_type; 227 typedef re_set_t::bucket_traits bucket_traits; 228 bucket_type buckets[128]; 229 re_set_t re_set; 230 231 bin::list<regex_node> re_list; 232 233 void pop_lru() { 234 regex_node& rlu = re_list.back(); 235 re_list.pop_back(); 236 re_set.erase(rlu); 237 delete &rlu; 238 } 239 240 public: 241 regex_lru_cache(int regex_flags = boost::regex_constants::ECMAScript): 242 re_set(bucket_traits(buckets, 128)), 243 re_list() {} 244 245 ~regex_lru_cache() { 246 while (!re_list.empty()) { 247 pop_lru(); 248 } 249 } 250 251 boost::regex const& get_regex(const char* str, size_t size) { 252 re_set_t::iterator re_in_cache = re_set.find(str, regex_hash(), regex_equal()); 253 if (re_in_cache != re_set.end()) { 254 regex_node& node = *re_in_cache; 255 bin::list<regex_node>::iterator re_in_list = bin::list<regex_node>::s_iterator_to(node); 256 257 re_list.splice(re_list.begin(), re_list, re_in_list); // move to the begin of the list 258 assert(&re_list.front() == &node); 259 return node.get(); 260 } 261 262 if (re_list.size() >= 64) 263 pop_lru(); 264 265 regex_node* new_node = new regex_node(str, size, regex_flags); 266 re_set.insert(*new_node); 267 re_list.push_front(*new_node); 268 return new_node->get(); 269 } 270 }; 271 272 } 273 274 int prString_Regexp(struct VMGlobals* g, int numArgsPushed) { 275 /* not reentrant */ 276 static detail::regex_lru_cache regex_lru_cache(boost::regex_constants::ECMAScript | boost::regex_constants::nosubs); 277 278 using namespace boost; 279 280 int start, end, len; 281 282 PyrSlot* a = g->sp - 3; 283 PyrSlot* b = g->sp - 2; 284 PyrSlot* c = g->sp - 1; 285 PyrSlot* d = g->sp; 286 287 if (!isKindOfSlot(b, class_string)) 288 return errWrongType; 289 if (NotInt(c) || (NotInt(d) && NotNil(d))) 290 return errWrongType; 291 start = slotRawInt(c); 292 293 len = slotRawObject(b)->size; // last char index instead of size 294 295 if (IsNil(d)) { 296 end = len; 297 } else { 298 end = slotRawInt(d); 299 } 300 301 if (end > len) 302 end = len; 303 304 if (end - start <= 0) { 305 SetFalse(a); 306 return errNone; 307 } 308 309 int stringlen = end - start; 310 311 try { 312 regex const& pattern = regex_lru_cache.get_regex(slotRawString(a)->s, slotRawObject(a)->size); 313 match_flag_type flags = match_nosubs | match_any; 314 315 const char* stringStart = slotRawString(b)->s + start; 316 const char* stringEnd = stringStart + stringlen; 317 bool res = regex_search(stringStart, stringEnd, pattern, flags); 318 319 if (res) 320 SetTrue(a); 321 else 322 SetFalse(a); 323 324 return errNone; 325 } catch (std::exception const& e) { 326 postfl("Warning: Exception in _String_Regexp - %s\n", e.what()); 327 return errFailed; 328 } 329 } 330 331 struct sc_regexp_match { 332 int pos; 333 int len; 334 }; 335 336 337 static int prString_FindRegexp(struct VMGlobals* g, int numArgsPushed) { 338 /* not reentrant */ 339 static detail::regex_lru_cache regex_lru_cache(boost::regex_constants::ECMAScript); 340 341 using namespace boost; 342 343 PyrSlot* a = g->sp - 2; // source string 344 PyrSlot* b = g->sp - 1; // pattern 345 PyrSlot* c = g->sp; // offset 346 347 if (!isKindOfSlot(b, class_string) || (NotInt(c))) { 348 SetNil(a); 349 return errWrongType; 350 } 351 352 int offset = slotRawInt(c); 353 int stringlen = std::max(slotRawObject(a)->size - offset, 0); 354 355 std::vector<sc_regexp_match> matches; 356 const char* const stringBegin = slotRawString(a)->s + offset; 357 try { 358 regex const& pattern = regex_lru_cache.get_regex(slotRawString(b)->s, slotRawObject(b)->size); 359 match_flag_type flags = match_default; 360 361 match_results<const char*> what; 362 const char* start = stringBegin; 363 const char* end = start + stringlen; 364 while (start <= end && regex_search(start, end, what, pattern, flags)) { 365 for (int i = 0; i < what.size(); ++i) { 366 sc_regexp_match match; 367 if (what[i].matched) { 368 match.pos = what[i].first - stringBegin; 369 match.len = what[i].second - what[i].first; 370 } else { 371 match.pos = 0; 372 match.len = 0; 373 } 374 matches.push_back(match); 375 } 376 start = what[0].second; 377 if (what[0].first == what[0].second) 378 ++start; 379 } 380 } catch (std::exception const& e) { 381 postfl("Warning: Exception in _String_FindRegexp - %s\n", e.what()); 382 SetNil(a); 383 return errFailed; 384 } 385 386 int match_count = matches.size(); 387 388 PyrObject* result_array = newPyrArray(g->gc, match_count, 0, true); 389 ++g->sp; // advance the stack to avoid overwriting receiver 390 SetObject(g->sp, result_array); // push result to make reachable 391 392 for (int i = 0; i < match_count; ++i) { 393 int pos = matches[i].pos; 394 int len = matches[i].len; 395 396 PyrObject* array = newPyrArray(g->gc, 2, 0, true); 397 SetObject(result_array->slots + i, array); 398 result_array->size++; 399 g->gc->GCWriteNew(result_array, array); // we know array is white so we can use GCWriteNew 400 401 PyrString* matched_string = newPyrStringN(g->gc, len, 0, true); 402 memcpy(matched_string->s, stringBegin + pos, len); 403 404 array->size = 2; 405 SetInt(array->slots, pos + offset); 406 SetObject(array->slots + 1, matched_string); 407 g->gc->GCWriteNew(array, matched_string); // we know matched_string is white so we can use GCWriteNew 408 }; 409 410 --g->sp; // pop the stack back to the receiver slot since we stored result_array there above 411 SetObject(a, result_array); // now we can set the result in a 412 413 return errNone; 414 } 415 416 static int prString_FindRegexpAt(struct VMGlobals* g, int numArgsPushed) { 417 /* not reentrant */ 418 static detail::regex_lru_cache regex_lru_cache(boost::regex_constants::ECMAScript); 419 420 using namespace boost; 421 422 PyrSlot* a = g->sp - 2; // source string 423 PyrSlot* b = g->sp - 1; // pattern 424 PyrSlot* c = g->sp; // offset 425 426 if (!isKindOfSlot(b, class_string) || (NotInt(c))) { 427 SetNil(a); 428 return errWrongType; 429 } 430 431 int offset = slotRawInt(c); 432 int stringlen = std::max(slotRawObject(a)->size - offset, 0); 433 434 int matched_len = 0; 435 436 const char* const stringBegin = slotRawString(a)->s + offset; 437 try { 438 regex const& pattern = regex_lru_cache.get_regex(slotRawString(b)->s, slotRawObject(b)->size); 439 440 // match_continuous: the match must begin at the offset start 441 match_flag_type flags = match_continuous; 442 443 match_results<const char*> what; 444 const char* start = stringBegin; 445 const char* end = start + stringlen; 446 if (regex_search(start, end, what, pattern, flags)) { 447 assert(what[0].first == stringBegin); 448 matched_len = what[0].second - what[0].first; 449 } else { 450 SetNil(a); 451 return errNone; 452 } 453 } catch (std::exception const& e) { 454 postfl("Warning: Exception in _String_FindRegexpAt - %s\n", e.what()); 455 return errFailed; 456 } 457 458 PyrObject* array = newPyrArray(g->gc, 2, 0, true); 459 ++g->sp; 460 SetObject(g->sp, array); // push on stack to make reachable 461 462 PyrString* matched_string = newPyrStringN(g->gc, matched_len, 0, true); 463 memcpy(matched_string->s, stringBegin, (size_t)matched_len); 464 465 array->size = 2; 466 SetInt(array->slots + 1, matched_len); 467 SetObject(array->slots, matched_string); 468 g->gc->GCWriteNew(array, matched_string); // we know matched_string is white so we can use GCWriteNew 469 --g->sp; // pop the stack back to the receiver slot since we stored array there above 470 SetObject(a, array); // now we can set the result in a 471 472 return errNone; 473 } 474 475 int memcmpi(char* a, char* b, int len) { 476 for (int i = 0; i < len; ++i) { 477 char aa = toupper(a[i]); 478 char bb = toupper(b[i]); 479 if (aa < bb) 480 return -1; 481 if (aa > bb) 482 return 1; 483 } 484 return 0; 485 } 486 487 int prStringCompare(struct VMGlobals* g, int numArgsPushed); 488 int prStringCompare(struct VMGlobals* g, int numArgsPushed) { 489 PyrSlot *a, *b, *c; 490 int cmp, length; 491 492 a = g->sp - 2; 493 b = g->sp - 1; 494 c = g->sp; 495 496 if (NotObj(b) || !isKindOf(slotRawObject(b), class_string)) 497 return errWrongType; 498 499 length = sc_min(slotRawObject(a)->size, slotRawObject(b)->size); 500 if (IsTrue(c)) 501 cmp = memcmpi(slotRawString(a)->s, slotRawString(b)->s, length); 502 else 503 cmp = memcmp(slotRawString(a)->s, slotRawString(b)->s, length); 504 if (cmp == 0) { 505 if (slotRawObject(a)->size < slotRawObject(b)->size) 506 cmp = -1; 507 else if (slotRawObject(a)->size > slotRawObject(b)->size) 508 cmp = 1; 509 } 510 SetInt(a, cmp); 511 return errNone; 512 } 513 514 int prStringHash(struct VMGlobals* g, int numArgsPushed); 515 int prStringHash(struct VMGlobals* g, int numArgsPushed) { 516 PyrSlot* a = g->sp; 517 int hash = Hash(slotRawString(a)->s, slotRawString(a)->size); 518 SetInt(a, hash); 519 return errNone; 520 } 521 522 int prString_PathMatch(struct VMGlobals* g, int numArgsPushed); 523 int prString_PathMatch(struct VMGlobals* g, int numArgsPushed) { 524 PyrSlot* a = g->sp; 525 char pattern[PATH_MAX]; 526 int err = slotStrVal(a, pattern, PATH_MAX - 1); 527 if (err) 528 return err; 529 530 SC_Filesystem::Glob* glob = SC_Filesystem::makeGlob(pattern); 531 532 // exit early with empty array if no matches found 533 if (!glob) { 534 SetObject(a, newPyrArray(g->gc, 0, 0, true)); 535 return errNone; 536 } 537 538 // read all paths into a vector 539 std::vector<bfs::path> paths; 540 while (true) { 541 const bfs::path& matched_path = SC_Filesystem::globNext(glob); 542 if (matched_path.empty()) 543 break; 544 else 545 paths.push_back(matched_path); 546 }; 547 548 // create array with appropriate reserved size 549 PyrObject* array = newPyrArray(g->gc, paths.size(), 0, true); 550 SetObject(a, array); // this is okay here as we don't use the receiver below 551 552 // convert paths and copy into sclang array. 553 for (int i = 0; i < paths.size(); ++i) { 554 const std::string& matched_path_utf8 = SC_Codecvt::path_to_utf8_str(paths[i]); 555 PyrObject* string = (PyrObject*)newPyrString(g->gc, matched_path_utf8.c_str(), 0, true); 556 SetObject(array->slots + i, string); 557 g->gc->GCWriteNew(array, string); // we know string is white so we can use GCWriteNew 558 array->size++; 559 } 560 561 SC_Filesystem::freeGlob(glob); 562 return errNone; 563 } 564 565 int prString_Getenv(struct VMGlobals* g, int numArgsPushed); 566 int prString_Getenv(struct VMGlobals* g, int /* numArgsPushed */) { 567 PyrSlot* arg = g->sp; 568 char key[256]; 569 char* value; 570 int err; 571 572 err = slotStrVal(arg, key, 256); 573 if (err) 574 return err; 575 576 #ifdef _WIN32 577 char buf[1024]; 578 DWORD size = GetEnvironmentVariable(key, buf, 1024); 579 if (size == 0 || size > 1024) 580 value = 0; 581 else 582 value = buf; 583 #else 584 value = getenv(key); 585 #endif 586 587 if (value) { 588 PyrString* pyrString = newPyrString(g->gc, value, 0, true); 589 if (!pyrString) 590 return errFailed; 591 SetObject(arg, pyrString); 592 } else { 593 SetNil(arg); 594 } 595 596 return errNone; 597 } 598 599 int prString_Setenv(struct VMGlobals* g, int numArgsPushed); 600 int prString_Setenv(struct VMGlobals* g, int /* numArgsPushed */) { 601 PyrSlot* args = g->sp - 1; 602 char key[256]; 603 int err; 604 605 err = slotStrVal(args + 0, key, 256); 606 if (err) 607 return err; 608 609 if (IsNil(args + 1)) { 610 #ifdef _WIN32 611 SetEnvironmentVariable(key, NULL); 612 #else 613 unsetenv(key); 614 #endif 615 } else { 616 char value[1024]; 617 err = slotStrVal(args + 1, value, 1024); 618 if (err) 619 return err; 620 #ifdef _WIN32 621 SetEnvironmentVariable(key, value); 622 #else 623 setenv(key, value, 1); 624 #endif 625 } 626 627 return errNone; 628 } 629 630 int prStripRtf(struct VMGlobals* g, int numArgsPushed); 631 int prStripRtf(struct VMGlobals* g, int numArgsPushed) { 632 PyrSlot* a = g->sp; 633 int len = slotRawObject(a)->size; 634 char* chars = (char*)malloc(len + 1); 635 memcpy(chars, slotRawString(a)->s, len); 636 chars[len] = 0; 637 rtf2txt(chars); 638 639 PyrString* string = newPyrString(g->gc, chars, 0, false); 640 SetObject(a, string); 641 free(chars); 642 643 return errNone; 644 } 645 646 int prStripHtml(struct VMGlobals* g, int numArgsPushed); 647 int prStripHtml(struct VMGlobals* g, int numArgsPushed) { 648 PyrSlot* a = g->sp; 649 int len = slotRawObject(a)->size; 650 char* chars = (char*)malloc(len + 1); 651 memcpy(chars, slotRawString(a)->s, len); 652 chars[len] = 0; 653 html2txt(chars); 654 655 PyrString* string = newPyrString(g->gc, chars, 0, false); 656 SetObject(a, string); 657 free(chars); 658 659 return errNone; 660 } 661 662 int prString_Find(struct VMGlobals* g, int numArgsPushed); 663 int prString_Find(struct VMGlobals* g, int numArgsPushed) { 664 PyrSlot* a = g->sp - 3; // source string 665 PyrSlot* b = g->sp - 2; // search string 666 PyrSlot* c = g->sp - 1; // ignoreCase 667 PyrSlot* d = g->sp; // offset 668 669 int offset; 670 int err = slotIntVal(d, &offset); 671 if (err) 672 return err; 673 674 int alength = slotRawObject(a)->size - offset; 675 if (alength <= 0) { 676 SetNil(a); 677 return errNone; 678 } 679 680 if (isKindOfSlot(b, class_string)) { 681 int blength = slotRawObject(b)->size; 682 683 if ((blength == 0) 684 // should also return nil if search string is longer than source 685 || (blength > alength)) { 686 SetNil(a); 687 return errNone; 688 } 689 690 int cmp = 1; // assume contains will be false 691 char* achar = slotRawString(a)->s + offset; 692 char* bchar = slotRawString(b)->s; 693 char bchar0 = bchar[0]; 694 int scanlength = alength - blength; 695 if (IsTrue(c)) { 696 bchar0 = toupper(bchar0); 697 for (int i = 0; i <= scanlength; ++i, ++achar) { 698 if (toupper(*achar) == bchar0) { 699 cmp = memcmpi(achar + 1, bchar + 1, blength - 1); 700 if (cmp == 0) 701 break; 702 } 703 } 704 } else { 705 for (int i = 0; i <= scanlength; ++i, ++achar) { 706 if (*achar == bchar0) { 707 cmp = memcmp(achar + 1, bchar + 1, blength - 1); 708 if (cmp == 0) 709 break; 710 } 711 } 712 } 713 if (cmp == 0) { 714 SetInt(a, achar - slotRawString(a)->s); 715 } else { 716 SetNil(a); 717 } 718 return errNone; 719 720 } else if (IsChar(b)) { 721 char* achar = slotRawString(a)->s + offset; 722 char bchar = slotRawChar(b); 723 int scanlength = alength - 1; 724 if (IsTrue(c)) { 725 bchar = toupper(bchar); 726 for (int i = 0; i <= scanlength; ++i, ++achar) { 727 if (toupper(*achar) == bchar) { 728 SetInt(a, achar - slotRawString(a)->s); 729 return errNone; 730 } 731 } 732 } else { 733 for (int i = 0; i <= scanlength; ++i, ++achar) { 734 if (*achar == bchar) { 735 SetInt(a, achar - slotRawString(a)->s); 736 return errNone; 737 } 738 } 739 } 740 SetNil(a); 741 return errNone; 742 } else 743 return errWrongType; 744 } 745 746 int prString_FindBackwards(struct VMGlobals* g, int numArgsPushed); 747 int prString_FindBackwards(struct VMGlobals* g, int numArgsPushed) { 748 PyrSlot* a = g->sp - 3; // source string 749 PyrSlot* b = g->sp - 2; // search string 750 PyrSlot* c = g->sp - 1; // ignoreCase 751 PyrSlot* d = g->sp; // offset 752 753 int offset; 754 int err = slotIntVal(d, &offset); 755 if (err) 756 return err; 757 758 int alength = sc_min(offset + 1, slotRawObject(a)->size); 759 if (alength <= 0) { 760 SetNil(a); 761 return errNone; 762 } 763 764 if (isKindOfSlot(b, class_string)) { 765 int blength = slotRawObject(b)->size; 766 767 if ((blength == 0) 768 // should also return nil if search string is longer than source 769 || (blength > alength)) { 770 SetNil(a); 771 return errNone; 772 } 773 774 int cmp = 1; // assume contains will be false 775 char* achar = slotRawString(a)->s + (alength - blength); 776 char* bchar = slotRawString(b)->s; 777 char bchar0 = bchar[0]; 778 int scanlength = alength - blength; 779 if (IsTrue(c)) { 780 bchar0 = toupper(bchar0); 781 for (int i = scanlength; i >= 0; --i, --achar) { 782 if (toupper(*achar) == bchar0) { 783 cmp = memcmpi(achar + 1, bchar + 1, blength - 1); 784 if (cmp == 0) 785 break; 786 } 787 } 788 } else { 789 for (int i = scanlength; i >= 0; --i, --achar) { 790 if (*achar == bchar0) { 791 cmp = memcmp(achar + 1, bchar + 1, blength - 1); 792 if (cmp == 0) 793 break; 794 } 795 } 796 } 797 if (cmp == 0) { 798 SetInt(a, achar - slotRawString(a)->s); 799 } else { 800 SetNil(a); 801 } 802 return errNone; 803 } else if (IsChar(b)) { 804 char* achar = slotRawString(a)->s + (alength - 1); 805 char bchar = slotRawChar(b); 806 int scanlength = alength - 1; 807 if (IsTrue(c)) { 808 bchar = toupper(bchar); 809 for (int i = scanlength; i >= 0; --i, --achar) { 810 if (toupper(*achar) == bchar) { 811 SetInt(a, achar - slotRawString(a)->s); 812 return errNone; 813 } 814 } 815 } else { 816 for (int i = scanlength; i >= 0; --i, --achar) { 817 if (*achar == bchar) { 818 SetInt(a, achar - slotRawString(a)->s); 819 return errNone; 820 } 821 } 822 } 823 SetNil(a); 824 return errNone; 825 } else 826 return errWrongType; 827 } 828 829 /** \brief Expand `~` to home directory and resolve aliases 830 * 831 * Prints an error message if alias resolution failed. 832 */ 833 int prString_StandardizePath(struct VMGlobals* g, int numArgsPushed); 834 int prString_StandardizePath(struct VMGlobals* g, int /* numArgsPushed */) { 835 PyrSlot* arg = g->sp; 836 char ipath[PATH_MAX]; 837 838 int err = slotStrVal(arg, ipath, PATH_MAX); 839 if (err != errNone) 840 return err; 841 842 bfs::path p = SC_Codecvt::utf8_str_to_path(ipath); 843 p = SC_Filesystem::instance().expandTilde(p); 844 bool isAlias; 845 p = SC_Filesystem::resolveIfAlias(p, isAlias); 846 847 // Don't consider alias resolution a failure condition, but print an error 848 if (isAlias && p.empty()) 849 error("standardizePath: symlink resolution failed for '%s'\n", ipath); 850 851 const std::string& utf8_str = SC_Codecvt::path_to_utf8_str(p); 852 PyrString* pyrString = newPyrString(g->gc, utf8_str.c_str(), 0, true); 853 SetObject(arg, pyrString); 854 855 return errNone; 856 } 857 858 int prString_EscapeChar(struct VMGlobals* g, int numArgsPushed) { 859 PyrSlot* arg = g->sp - 1; 860 PyrSlot* charToEscapeSlot = g->sp; 861 862 assert(isKindOfSlot(arg, class_string)); 863 864 if (!IsChar(charToEscapeSlot)) 865 return errWrongType; 866 867 char charToEscape = slotRawChar(charToEscapeSlot); 868 869 PyrString* argString = slotRawString(arg); 870 int length = argString->size; 871 PyrString* resultString = newPyrStringN(g->gc, length * 2 + 1, 0, 1); // pressimize 872 873 char* original = argString->s; 874 char* result = resultString->s; 875 876 int resultLength = length; 877 for (int i = 0; i != length; ++i) { 878 char current = *original++; 879 if (current == charToEscape) { 880 *result++ = '\\'; 881 resultLength += 1; 882 } 883 *result++ = current; 884 } 885 *result = 0; 886 887 resultString->size = resultLength; 888 889 SetRaw(arg, (PyrObject*)resultString); 890 891 return errNone; 892 } 893 894 static void yaml_traverse(struct VMGlobals* g, const YAML::Node& node, PyrObject* parent, PyrSlot* slot) { 895 YAML::NodeType::value type = node.Type(); 896 string out; 897 PyrObject* result = nullptr; 898 899 switch (type) { 900 case YAML::NodeType::Scalar: 901 out = node.as<string>(); 902 result = (PyrObject*)newPyrString(g->gc, out.c_str(), 0, true); 903 SetObject(slot, result); 904 if (parent) 905 g->gc->GCWriteNew(parent, result); // we know result is white so we can use GCWriteNew 906 break; 907 908 case YAML::NodeType::Sequence: 909 result = newPyrArray(g->gc, node.size(), 0, true); 910 SetObject(slot, result); 911 if (parent) 912 g->gc->GCWriteNew(parent, result); // we know result is white so we can use GCWriteNew 913 for (unsigned int i = 0; i < node.size(); i++) { 914 const YAML::Node& subnode = node[i]; 915 result->size++; 916 yaml_traverse(g, subnode, result, result->slots + i); 917 } 918 break; 919 920 case YAML::NodeType::Map: { 921 result = instantiateObject(g->gc, s_dictionary->u.classobj, 0, false, true); 922 SetObject(slot, result); 923 if (parent) 924 g->gc->GCWriteNew(parent, result); // we know result is white so we can use GCWriteNew 925 926 PyrObject* array = newPyrArray(g->gc, node.size() * 2, 0, true); 927 result->size = 2; 928 SetObject(result->slots, array); // array 929 SetInt(result->slots + 1, node.size()); // size 930 g->gc->GCWriteNew(result, array); // we know array is white so we can use GCWriteNew 931 932 int j = 0; 933 for (auto const& element : node) { 934 const YAML::Node& key = element.first; 935 const YAML::Node& value = element.second; 936 out = key.as<string>(); 937 PyrObject* pkey = (PyrObject*)newPyrString(g->gc, out.c_str(), 0, true); 938 SetObject(array->slots + j, pkey); 939 array->size++; 940 g->gc->GCWriteNew(array, pkey); // we know pkey is white so we can use GCWriteNew 941 942 array->size++; 943 yaml_traverse(g, value, array, array->slots + j + 1); 944 945 j += 2; 946 } 947 break; 948 } 949 950 case YAML::NodeType::Null: 951 SetNil(slot); 952 break; 953 954 default: 955 postfl("WARNING: yaml_traverse(): unknown/unsupported node type\n"); 956 SetNil(slot); 957 } 958 } 959 960 int prString_ParseYAML(struct VMGlobals* g, int numArgsPushed) { 961 PyrSlot* arg = g->sp; 962 963 if (!isKindOfSlot(arg, class_string)) 964 return errWrongType; 965 966 string str((const char*)slotRawString(arg)->s, slotRawString(arg)->size); 967 968 std::istringstream fin(str); 969 YAML::Node doc = YAML::Load(fin); 970 yaml_traverse(g, doc, nullptr, arg); 971 972 return errNone; 973 } 974 975 int prString_ParseYAMLFile(struct VMGlobals* g, int numArgsPushed) { 976 PyrSlot* arg = g->sp; 977 978 if (!isKindOfSlot(arg, class_string)) 979 return errWrongType; 980 981 string str((const char*)slotRawString(arg)->s, slotRawString(arg)->size); 982 983 const bfs::path& path = SC_Codecvt::utf8_str_to_path(str); 984 bfs::ifstream fin(path); 985 YAML::Node doc = YAML::Load(fin); 986 yaml_traverse(g, doc, nullptr, arg); 987 988 return errNone; 989 } 990 991 void initStringPrimitives(); 992 void initStringPrimitives() { 993 int base, index = 0; 994 995 base = nextPrimitiveIndex(); 996 997 definePrimitive(base, index++, "_StringCompare", prStringCompare, 3, 0); 998 definePrimitive(base, index++, "_StringHash", prStringHash, 1, 0); 999 definePrimitive(base, index++, "_StringPathMatch", prString_PathMatch, 1, 0); 1000 definePrimitive(base, index++, "_StringAsSymbol", prStringAsSymbol, 1, 0); 1001 definePrimitive(base, index++, "_String_AsInteger", prString_AsInteger, 1, 0); 1002 definePrimitive(base, index++, "_String_AsFloat", prString_AsFloat, 1, 0); 1003 definePrimitive(base, index++, "_String_AsCompileString", prString_AsCompileString, 1, 0); 1004 definePrimitive(base, index++, "_String_Getenv", prString_Getenv, 1, 0); 1005 definePrimitive(base, index++, "_String_Setenv", prString_Setenv, 2, 0); 1006 definePrimitive(base, index++, "_String_Find", prString_Find, 4, 0); 1007 definePrimitive(base, index++, "_String_FindBackwards", prString_FindBackwards, 4, 0); 1008 definePrimitive(base, index++, "_String_Format", prString_Format, 2, 0); 1009 definePrimitive(base, index++, "_String_Regexp", prString_Regexp, 4, 0); 1010 definePrimitive(base, index++, "_String_FindRegexp", prString_FindRegexp, 3, 0); 1011 definePrimitive(base, index++, "_String_FindRegexpAt", prString_FindRegexpAt, 3, 0); 1012 definePrimitive(base, index++, "_StripRtf", prStripRtf, 1, 0); 1013 definePrimitive(base, index++, "_StripHtml", prStripHtml, 1, 0); 1014 definePrimitive(base, index++, "_String_StandardizePath", prString_StandardizePath, 1, 0); 1015 definePrimitive(base, index++, "_String_EscapeChar", prString_EscapeChar, 2, 0); 1016 definePrimitive(base, index++, "_String_ParseYAML", prString_ParseYAML, 1, 0); 1017 definePrimitive(base, index++, "_String_ParseYAMLFile", prString_ParseYAMLFile, 1, 0); 1018 } 1019 1020 #if _SC_PLUGINS_ 1021 1022 1023 # include "SCPlugin.h" 1024 1025 // export the function that SC will call to load the plug in. 1026 # pragma export on 1027 extern "C" { 1028 SCPlugIn* loadPlugIn(void); 1029 } 1030 # pragma export off 1031 1032 1033 // define plug in object 1034 class APlugIn : public SCPlugIn { 1035 public: 1036 APlugIn(); 1037 virtual ~APlugIn(); 1038 1039 virtual void AboutToCompile(); 1040 }; 1041 1042 APlugIn::APlugIn() { 1043 // constructor for plug in 1044 } 1045 1046 APlugIn::~APlugIn() { 1047 // destructor for plug in 1048 } 1049 1050 void APlugIn::AboutToCompile() { 1051 // this is called each time the class library is compiled. 1052 initStringPrimitives(); 1053 } 1054 1055 // This function is called when the plug in is loaded into SC. 1056 // It returns an instance of APlugIn. 1057 SCPlugIn* loadPlugIn() { return new APlugIn(); } 1058 1059 #endif 1060