1#include "ruby/ruby.h" 2#include "ruby/encoding.h" 3#include <winbase.h> 4#include <wchar.h> 5#include <shlwapi.h> 6 7#ifndef INVALID_FILE_ATTRIBUTES 8# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 9#endif 10 11/* cache 'encoding name' => 'code page' into a hash */ 12static VALUE rb_code_page; 13 14#define IS_DIR_SEPARATOR_P(c) (c == L'\\' || c == L'/') 15#define IS_DIR_UNC_P(c) (IS_DIR_SEPARATOR_P(c[0]) && IS_DIR_SEPARATOR_P(c[1])) 16 17/* MultiByteToWideChar() doesn't work with code page 51932 */ 18#define INVALID_CODE_PAGE 51932 19#define PATH_BUFFER_SIZE MAX_PATH * 2 20 21#define insecure_obj_p(obj, level) ((level) >= 4 || ((level) > 0 && OBJ_TAINTED(obj))) 22 23static inline void 24replace_wchar(wchar_t *s, int find, int replace) 25{ 26 while (*s != 0) { 27 if (*s == find) 28 *s = replace; 29 s++; 30 } 31} 32 33/* Convert str from multibyte char to wchar with specified code page */ 34static inline void 35convert_mb_to_wchar(VALUE str, wchar_t **wstr, wchar_t **wstr_pos, size_t *wstr_len, UINT code_page) 36{ 37 size_t len; 38 39 if (NIL_P(str)) 40 return; 41 42 len = MultiByteToWideChar(code_page, 0, RSTRING_PTR(str), -1, NULL, 0) + 1; 43 *wstr = (wchar_t *)xmalloc(len * sizeof(wchar_t)); 44 if (wstr_pos) 45 *wstr_pos = *wstr; 46 47 MultiByteToWideChar(code_page, 0, RSTRING_PTR(str), -1, *wstr, len); 48 *wstr_len = len - 2; 49} 50 51static inline void 52convert_wchar_to_mb(const wchar_t *wstr, char **str, size_t *str_len, UINT code_page) 53{ 54 size_t len; 55 56 len = WideCharToMultiByte(code_page, 0, wstr, -1, NULL, 0, NULL, NULL); 57 *str = (char *)xmalloc(len * sizeof(char)); 58 WideCharToMultiByte(code_page, 0, wstr, -1, *str, len, NULL, NULL); 59 60 /* do not count terminator as part of the string length */ 61 *str_len = len - 1; 62} 63 64/* 65 Return user's home directory using environment variables combinations. 66 Memory allocated by this function should be manually freed afterwards. 67 68 Try: 69 HOME, HOMEDRIVE + HOMEPATH and USERPROFILE environment variables 70 TODO: Special Folders - Profile and Personal 71*/ 72static wchar_t * 73home_dir(void) 74{ 75 wchar_t *buffer = NULL; 76 size_t buffer_len = 0, len = 0; 77 size_t home_env = 0; 78 79 /* 80 GetEnvironmentVariableW when used with NULL will return the required 81 buffer size and its terminating character. 82 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx 83 */ 84 85 if (len = GetEnvironmentVariableW(L"HOME", NULL, 0)) { 86 buffer_len = len; 87 home_env = 1; 88 } 89 else if (len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) { 90 buffer_len = len; 91 if (len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) { 92 buffer_len += len; 93 home_env = 2; 94 } 95 else { 96 buffer_len = 0; 97 } 98 } 99 else if (len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) { 100 buffer_len = len; 101 home_env = 3; 102 } 103 104 /* allocate buffer */ 105 if (home_env) 106 buffer = (wchar_t *)xmalloc(buffer_len * sizeof(wchar_t)); 107 108 switch (home_env) { 109 case 1: 110 /* HOME */ 111 GetEnvironmentVariableW(L"HOME", buffer, buffer_len); 112 break; 113 case 2: 114 /* HOMEDRIVE + HOMEPATH */ 115 len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len); 116 GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len); 117 break; 118 case 3: 119 /* USERPROFILE */ 120 GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len); 121 break; 122 default: 123 break; 124 } 125 126 if (home_env) { 127 /* sanitize backslashes with forwardslashes */ 128 replace_wchar(buffer, L'\\', L'/'); 129 130 return buffer; 131 } 132 133 return NULL; 134} 135 136/* Remove trailing invalid ':$DATA' of the path. */ 137static inline size_t 138remove_invalid_alternative_data(wchar_t *wfullpath, size_t size) 139{ 140 static const wchar_t prime[] = L":$DATA"; 141 enum { prime_len = (sizeof(prime) / sizeof(wchar_t)) -1 }; 142 143 if (size <= prime_len || _wcsnicmp(wfullpath + size - prime_len, prime, prime_len) != 0) 144 return size; 145 146 /* alias of stream */ 147 /* get rid of a bug of x64 VC++ */ 148 if (wfullpath[size - (prime_len + 1)] == ':') { 149 /* remove trailing '::$DATA' */ 150 size -= prime_len + 1; /* prime */ 151 wfullpath[size] = L'\0'; 152 } 153 else { 154 /* remove trailing ':$DATA' of paths like '/aa:a:$DATA' */ 155 wchar_t *pos = wfullpath + size - (prime_len + 1); 156 while (!IS_DIR_SEPARATOR_P(*pos) && pos != wfullpath) { 157 if (*pos == L':') { 158 size -= prime_len; /* alternative */ 159 wfullpath[size] = L'\0'; 160 break; 161 } 162 pos--; 163 } 164 } 165 return size; 166} 167 168/* Return system code page. */ 169static inline UINT 170system_code_page(void) 171{ 172 return AreFileApisANSI() ? CP_ACP : CP_OEMCP; 173} 174 175/* 176 Return code page number of the encoding. 177 Cache code page into a hash for performance since finding the code page in 178 Encoding#names is slow. 179*/ 180static UINT 181code_page(rb_encoding *enc) 182{ 183 VALUE code_page_value, name_key; 184 VALUE encoding, names_ary = Qundef, name; 185 char *enc_name; 186 struct RString fake_str; 187 ID names; 188 long i; 189 190 if (!enc) 191 return system_code_page(); 192 193 enc_name = (char *)rb_enc_name(enc); 194 195 fake_str.basic.flags = T_STRING|RSTRING_NOEMBED; 196 fake_str.basic.klass = rb_cString; 197 fake_str.as.heap.len = strlen(enc_name); 198 fake_str.as.heap.ptr = enc_name; 199 fake_str.as.heap.aux.capa = fake_str.as.heap.len; 200 name_key = (VALUE)&fake_str; 201 ENCODING_CODERANGE_SET(name_key, rb_usascii_encindex(), ENC_CODERANGE_7BIT); 202 203 code_page_value = rb_hash_lookup(rb_code_page, name_key); 204 if (code_page_value != Qnil) 205 return (UINT)FIX2INT(code_page_value); 206 207 name_key = rb_usascii_str_new2(enc_name); 208 209 encoding = rb_enc_from_encoding(enc); 210 if (!NIL_P(encoding)) { 211 CONST_ID(names, "names"); 212 names_ary = rb_funcall(encoding, names, 0); 213 } 214 215 /* map US-ASCII and ASCII-8bit as code page 1252 (us-ascii) */ 216 if (enc == rb_usascii_encoding() || enc == rb_ascii8bit_encoding()) { 217 UINT code_page = 1252; 218 rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page)); 219 return code_page; 220 } 221 222 if (names_ary != Qundef) { 223 for (i = 0; i < RARRAY_LEN(names_ary); i++) { 224 name = RARRAY_PTR(names_ary)[i]; 225 if (strncmp("CP", RSTRING_PTR(name), 2) == 0) { 226 int code_page = atoi(RSTRING_PTR(name) + 2); 227 if (code_page != 0) { 228 rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page)); 229 return (UINT)code_page; 230 } 231 } 232 } 233 } 234 235 rb_hash_aset(rb_code_page, name_key, INT2FIX(INVALID_CODE_PAGE)); 236 return INVALID_CODE_PAGE; 237} 238 239static inline VALUE 240fix_string_encoding(VALUE str, rb_encoding *encoding) 241{ 242 VALUE result, tmp; 243 244 tmp = rb_enc_str_new(RSTRING_PTR(str), RSTRING_LEN(str), encoding); 245 result = rb_str_encode(tmp, rb_enc_from_encoding(rb_utf8_encoding()), 0, Qnil); 246 247 return result; 248} 249 250/* 251 Replace the last part of the path to long name. 252 We try to avoid to call FindFirstFileW() since it takes long time. 253*/ 254static inline size_t 255replace_to_long_name(wchar_t **wfullpath, size_t size, int heap) 256{ 257 WIN32_FIND_DATAW find_data; 258 HANDLE find_handle; 259 260 /* 261 Skip long name conversion if the path is already long name. 262 Short name is 8.3 format. 263 http://en.wikipedia.org/wiki/8.3_filename 264 This check can be skipped for directory components that have file 265 extensions longer than 3 characters, or total lengths longer than 266 12 characters. 267 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx 268 */ 269 size_t const max_short_name_size = 8 + 1 + 3; 270 size_t const max_extension_size = 3; 271 size_t path_len = 1, extension_len = 0; 272 wchar_t *pos = *wfullpath; 273 274 if (size == 3 && pos[1] == L':' && pos[2] == L'\\' && pos[3] == L'\0') { 275 /* root path doesn't need short name expansion */ 276 return size; 277 } 278 279 /* skip long name conversion if path contains wildcard characters */ 280 if (wcspbrk(pos, L"*?")) { 281 return size; 282 } 283 284 pos = *wfullpath + size - 1; 285 while (!IS_DIR_SEPARATOR_P(*pos) && pos != *wfullpath) { 286 if (!extension_len && *pos == L'.') { 287 extension_len = path_len - 1; 288 } 289 if (path_len > max_short_name_size || extension_len > max_extension_size) { 290 return size; 291 } 292 path_len++; 293 pos--; 294 } 295 296 find_handle = FindFirstFileW(*wfullpath, &find_data); 297 if (find_handle != INVALID_HANDLE_VALUE) { 298 size_t trail_pos = wcslen(*wfullpath); 299 size_t file_len = wcslen(find_data.cFileName); 300 301 FindClose(find_handle); 302 while (trail_pos > 0) { 303 if (IS_DIR_SEPARATOR_P((*wfullpath)[trail_pos])) 304 break; 305 trail_pos--; 306 } 307 size = trail_pos + 1 + file_len; 308 if ((size + 1) > sizeof(*wfullpath) / sizeof((*wfullpath)[0])) { 309 wchar_t *buf = (wchar_t *)xmalloc((size + 1) * sizeof(wchar_t)); 310 wcsncpy(buf, *wfullpath, trail_pos + 1); 311 if (heap) 312 xfree(*wfullpath); 313 *wfullpath = buf; 314 } 315 wcsncpy(*wfullpath + trail_pos + 1, find_data.cFileName, file_len + 1); 316 } 317 return size; 318} 319 320static inline VALUE 321get_user_from_path(wchar_t **wpath, int offset, UINT cp, UINT path_cp, rb_encoding *path_encoding) 322{ 323 VALUE result, tmp; 324 wchar_t *wuser = *wpath + offset; 325 wchar_t *pos = wuser; 326 char *user; 327 size_t size; 328 329 while (!IS_DIR_SEPARATOR_P(*pos) && *pos != '\0') 330 pos++; 331 332 *pos = '\0'; 333 convert_wchar_to_mb(wuser, &user, &size, cp); 334 335 /* convert to VALUE and set the path encoding */ 336 if (path_cp == INVALID_CODE_PAGE) { 337 tmp = rb_enc_str_new(user, size, rb_utf8_encoding()); 338 result = rb_str_encode(tmp, rb_enc_from_encoding(path_encoding), 0, Qnil); 339 rb_str_resize(tmp, 0); 340 } 341 else { 342 result = rb_enc_str_new(user, size, path_encoding); 343 } 344 345 if (user) 346 xfree(user); 347 348 return result; 349} 350 351VALUE 352rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result) 353{ 354 size_t size = 0, wpath_len = 0, wdir_len = 0, whome_len = 0; 355 size_t buffer_len = 0; 356 char *fullpath = NULL; 357 wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL; 358 wchar_t *wdir = NULL, *wdir_pos = NULL; 359 wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL; 360 UINT path_cp, cp; 361 VALUE path = fname, dir = dname; 362 wchar_t wfullpath_buffer[PATH_BUFFER_SIZE]; 363 wchar_t path_drive = L'\0', dir_drive = L'\0'; 364 int ignore_dir = 0; 365 rb_encoding *path_encoding; 366 int tainted = 0; 367 368 /* tainted if path is tainted */ 369 tainted = OBJ_TAINTED(path); 370 371 /* get path encoding */ 372 if (NIL_P(dir)) { 373 path_encoding = rb_enc_get(path); 374 } 375 else { 376 path_encoding = rb_enc_check(path, dir); 377 } 378 379 cp = path_cp = code_page(path_encoding); 380 381 /* workaround invalid codepage */ 382 if (path_cp == INVALID_CODE_PAGE) { 383 cp = CP_UTF8; 384 if (!NIL_P(path)) { 385 path = fix_string_encoding(path, path_encoding); 386 } 387 } 388 389 /* convert char * to wchar_t */ 390 convert_mb_to_wchar(path, &wpath, &wpath_pos, &wpath_len, cp); 391 392 /* determine if we need the user's home directory */ 393 /* expand '~' only if NOT rb_file_absolute_path() where `abs_mode` is 1 */ 394 if (abs_mode == 0 && wpath_len > 0 && wpath_pos[0] == L'~' && 395 (wpath_len == 1 || IS_DIR_SEPARATOR_P(wpath_pos[1]))) { 396 /* tainted if expanding '~' */ 397 tainted = 1; 398 399 whome = home_dir(); 400 if (whome == NULL) { 401 xfree(wpath); 402 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'"); 403 } 404 whome_len = wcslen(whome); 405 406 if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) { 407 xfree(wpath); 408 xfree(whome); 409 rb_raise(rb_eArgError, "non-absolute home"); 410 } 411 412 /* use filesystem encoding if expanding home dir */ 413 path_encoding = rb_filesystem_encoding(); 414 cp = path_cp = system_code_page(); 415 416 /* ignores dir since we are expading home */ 417 ignore_dir = 1; 418 419 /* exclude ~ from the result */ 420 wpath_pos++; 421 wpath_len--; 422 423 /* exclude separator if present */ 424 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { 425 wpath_pos++; 426 wpath_len--; 427 } 428 } 429 else if (wpath_len >= 2 && wpath_pos[1] == L':') { 430 if (wpath_len >= 3 && IS_DIR_SEPARATOR_P(wpath_pos[2])) { 431 /* ignore dir since path contains a drive letter and a root slash */ 432 ignore_dir = 1; 433 } 434 else { 435 /* determine if we ignore dir or not later */ 436 path_drive = wpath_pos[0]; 437 } 438 } 439 else if (abs_mode == 0 && wpath_len >= 2 && wpath_pos[0] == L'~') { 440 result = get_user_from_path(&wpath_pos, 1, cp, path_cp, path_encoding); 441 442 if (wpath) 443 xfree(wpath); 444 445 rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result)); 446 } 447 448 /* convert dir */ 449 if (!ignore_dir && !NIL_P(dir)) { 450 /* fix string encoding */ 451 if (path_cp == INVALID_CODE_PAGE) { 452 dir = fix_string_encoding(dir, path_encoding); 453 } 454 455 /* convert char * to wchar_t */ 456 convert_mb_to_wchar(dir, &wdir, &wdir_pos, &wdir_len, cp); 457 458 if (abs_mode == 0 && wdir_len > 0 && wdir_pos[0] == L'~' && 459 (wdir_len == 1 || IS_DIR_SEPARATOR_P(wdir_pos[1]))) { 460 /* tainted if expanding '~' */ 461 tainted = 1; 462 463 whome = home_dir(); 464 if (whome == NULL) { 465 xfree(wpath); 466 xfree(wdir); 467 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'"); 468 } 469 whome_len = wcslen(whome); 470 471 if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) { 472 xfree(wpath); 473 xfree(wdir); 474 xfree(whome); 475 rb_raise(rb_eArgError, "non-absolute home"); 476 } 477 478 /* exclude ~ from the result */ 479 wdir_pos++; 480 wdir_len--; 481 482 /* exclude separator if present */ 483 if (wdir_len && IS_DIR_SEPARATOR_P(wdir_pos[0])) { 484 wdir_pos++; 485 wdir_len--; 486 } 487 } 488 else if (wdir_len >= 2 && wdir[1] == L':') { 489 dir_drive = wdir[0]; 490 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { 491 wdir_len = 2; 492 } 493 } 494 else if (wdir_len >= 2 && IS_DIR_UNC_P(wdir)) { 495 /* UNC path */ 496 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { 497 /* cut the UNC path tail to '//host/share' */ 498 size_t separators = 0; 499 size_t pos = 2; 500 while (pos < wdir_len && separators < 2) { 501 if (IS_DIR_SEPARATOR_P(wdir[pos])) { 502 separators++; 503 } 504 pos++; 505 } 506 if (separators == 2) 507 wdir_len = pos - 1; 508 } 509 } 510 else if (abs_mode == 0 && wdir_len >= 2 && wdir_pos[0] == L'~') { 511 result = get_user_from_path(&wdir_pos, 1, cp, path_cp, path_encoding); 512 if (wpath) 513 xfree(wpath); 514 515 if (wdir) 516 xfree(wdir); 517 518 rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result)); 519 } 520 } 521 522 /* determine if we ignore dir or not */ 523 if (!ignore_dir && path_drive && dir_drive) { 524 if (towupper(path_drive) == towupper(dir_drive)) { 525 /* exclude path drive letter to use dir */ 526 wpath_pos += 2; 527 wpath_len -= 2; 528 } 529 else { 530 /* ignore dir since path drive is different from dir drive */ 531 ignore_dir = 1; 532 wdir_len = 0; 533 } 534 } 535 536 if (!ignore_dir && wpath_len >= 2 && IS_DIR_UNC_P(wpath)) { 537 /* ignore dir since path has UNC root */ 538 ignore_dir = 1; 539 wdir_len = 0; 540 } 541 else if (!ignore_dir && wpath_len >= 1 && IS_DIR_SEPARATOR_P(wpath[0]) && 542 !dir_drive && !(wdir_len >= 2 && IS_DIR_UNC_P(wdir))) { 543 /* ignore dir since path has root slash and dir doesn't have drive or UNC root */ 544 ignore_dir = 1; 545 wdir_len = 0; 546 } 547 548 buffer_len = wpath_len + 1 + wdir_len + 1 + whome_len + 1; 549 550 buffer = buffer_pos = (wchar_t *)xmalloc((buffer_len + 1) * sizeof(wchar_t)); 551 552 /* add home */ 553 if (whome_len) { 554 wcsncpy(buffer_pos, whome, whome_len); 555 buffer_pos += whome_len; 556 } 557 558 /* Add separator if required */ 559 if (whome_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) { 560 buffer_pos[0] = L'\\'; 561 buffer_pos++; 562 } 563 564 if (wdir_len) { 565 /* tainted if dir is used and dir is tainted */ 566 if (!tainted && OBJ_TAINTED(dir)) 567 tainted = 1; 568 569 wcsncpy(buffer_pos, wdir_pos, wdir_len); 570 buffer_pos += wdir_len; 571 } 572 573 /* add separator if required */ 574 if (wdir_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) { 575 buffer_pos[0] = L'\\'; 576 buffer_pos++; 577 } 578 579 /* now deal with path */ 580 if (wpath_len) { 581 wcsncpy(buffer_pos, wpath_pos, wpath_len); 582 buffer_pos += wpath_len; 583 } 584 585 /* GetFullPathNameW requires at least "." to determine current directory */ 586 if (wpath_len == 0) { 587 buffer_pos[0] = L'.'; 588 buffer_pos++; 589 } 590 591 /* Ensure buffer is NULL terminated */ 592 buffer_pos[0] = L'\0'; 593 594 /* tainted if path is relative */ 595 if (!tainted && PathIsRelativeW(buffer) && !(buffer_len >= 2 && IS_DIR_UNC_P(buffer))) 596 tainted = 1; 597 598 /* FIXME: Make this more robust */ 599 /* Determine require buffer size */ 600 size = GetFullPathNameW(buffer, PATH_BUFFER_SIZE, wfullpath_buffer, NULL); 601 if (size > PATH_BUFFER_SIZE) { 602 /* allocate more memory than alloted originally by PATH_BUFFER_SIZE */ 603 wfullpath = (wchar_t *)xmalloc(size * sizeof(wchar_t)); 604 size = GetFullPathNameW(buffer, size, wfullpath, NULL); 605 } 606 else { 607 wfullpath = wfullpath_buffer; 608 } 609 610 /* Remove any trailing slashes */ 611 if (IS_DIR_SEPARATOR_P(wfullpath[size - 1]) && 612 wfullpath[size - 2] != L':' && 613 !(size == 2 && IS_DIR_UNC_P(wfullpath))) { 614 size -= 1; 615 wfullpath[size] = L'\0'; 616 } 617 618 /* Remove any trailing dot */ 619 if (wfullpath[size - 1] == L'.') { 620 size -= 1; 621 wfullpath[size] = L'\0'; 622 } 623 624 /* removes trailing invalid ':$DATA' */ 625 size = remove_invalid_alternative_data(wfullpath, size); 626 627 /* Replace the trailing path to long name */ 628 if (long_name) 629 size = replace_to_long_name(&wfullpath, size, (wfullpath != wfullpath_buffer)); 630 631 /* sanitize backslashes with forwardslashes */ 632 replace_wchar(wfullpath, L'\\', L'/'); 633 634 /* convert to char * */ 635 size = WideCharToMultiByte(cp, 0, wfullpath, size, NULL, 0, NULL, NULL); 636 if (size > (size_t)RSTRING_LEN(result)) { 637 rb_str_modify(result); 638 rb_str_resize(result, size); 639 } 640 641 WideCharToMultiByte(cp, 0, wfullpath, size, RSTRING_PTR(result), size, NULL, NULL); 642 rb_str_set_len(result, size); 643 644 /* convert to VALUE and set the path encoding */ 645 if (path_cp == INVALID_CODE_PAGE) { 646 VALUE tmp; 647 size_t len; 648 649 rb_enc_associate(result, rb_utf8_encoding()); 650 ENC_CODERANGE_CLEAR(result); 651 tmp = rb_str_encode(result, rb_enc_from_encoding(path_encoding), 0, Qnil); 652 len = RSTRING_LEN(tmp); 653 rb_str_modify(result); 654 rb_str_resize(result, len); 655 memcpy(RSTRING_PTR(result), RSTRING_PTR(tmp), len); 656 rb_str_resize(tmp, 0); 657 } 658 rb_enc_associate(result, path_encoding); 659 ENC_CODERANGE_CLEAR(result); 660 661 /* makes the result object tainted if expanding tainted strings or returning modified path */ 662 if (tainted) 663 OBJ_TAINT(result); 664 665 /* TODO: better cleanup */ 666 if (buffer) 667 xfree(buffer); 668 669 if (wpath) 670 xfree(wpath); 671 672 if (wdir) 673 xfree(wdir); 674 675 if (whome) 676 xfree(whome); 677 678 if (wfullpath && wfullpath != wfullpath_buffer) 679 xfree(wfullpath); 680 681 if (fullpath) 682 xfree(fullpath); 683 684 return result; 685} 686 687int 688rb_file_load_ok(const char *path) 689{ 690 int ret = 1; 691 DWORD attr = GetFileAttributes(path); 692 if (attr == INVALID_FILE_ATTRIBUTES || 693 attr & FILE_ATTRIBUTE_DIRECTORY) { 694 ret = 0; 695 } 696 else { 697 HANDLE h = CreateFile(path, GENERIC_READ, 698 FILE_SHARE_READ | FILE_SHARE_WRITE, 699 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 700 if (h != INVALID_HANDLE_VALUE) { 701 CloseHandle(h); 702 } 703 else { 704 ret = 0; 705 } 706 } 707 return ret; 708} 709 710void 711rb_w32_init_file(void) 712{ 713 rb_code_page = rb_hash_new(); 714 715 /* prevent GC removing rb_code_page */ 716 rb_gc_register_mark_object(rb_code_page); 717} 718