Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 #include "ruby/ruby.h" 00002 #include "ruby/encoding.h" 00003 #include <winbase.h> 00004 #include <wchar.h> 00005 #include <shlwapi.h> 00006 00007 #ifndef INVALID_FILE_ATTRIBUTES 00008 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 00009 #endif 00010 00011 /* cache 'encoding name' => 'code page' into a hash */ 00012 static VALUE rb_code_page; 00013 00014 #define IS_DIR_SEPARATOR_P(c) (c == L'\\' || c == L'/') 00015 #define IS_DIR_UNC_P(c) (IS_DIR_SEPARATOR_P(c[0]) && IS_DIR_SEPARATOR_P(c[1])) 00016 00017 /* MultiByteToWideChar() doesn't work with code page 51932 */ 00018 #define INVALID_CODE_PAGE 51932 00019 #define PATH_BUFFER_SIZE MAX_PATH * 2 00020 00021 #define insecure_obj_p(obj, level) ((level) >= 4 || ((level) > 0 && OBJ_TAINTED(obj))) 00022 00023 static inline void 00024 replace_wchar(wchar_t *s, int find, int replace) 00025 { 00026 while (*s != 0) { 00027 if (*s == find) 00028 *s = replace; 00029 s++; 00030 } 00031 } 00032 00033 /* Convert str from multibyte char to wchar with specified code page */ 00034 static inline void 00035 convert_mb_to_wchar(VALUE str, wchar_t **wstr, wchar_t **wstr_pos, size_t *wstr_len, UINT code_page) 00036 { 00037 size_t len; 00038 00039 if (NIL_P(str)) 00040 return; 00041 00042 len = MultiByteToWideChar(code_page, 0, RSTRING_PTR(str), -1, NULL, 0) + 1; 00043 *wstr = (wchar_t *)xmalloc(len * sizeof(wchar_t)); 00044 if (wstr_pos) 00045 *wstr_pos = *wstr; 00046 00047 MultiByteToWideChar(code_page, 0, RSTRING_PTR(str), -1, *wstr, len); 00048 *wstr_len = len - 2; 00049 } 00050 00051 static inline void 00052 convert_wchar_to_mb(const wchar_t *wstr, char **str, size_t *str_len, UINT code_page) 00053 { 00054 size_t len; 00055 00056 len = WideCharToMultiByte(code_page, 0, wstr, -1, NULL, 0, NULL, NULL); 00057 *str = (char *)xmalloc(len * sizeof(char)); 00058 WideCharToMultiByte(code_page, 0, wstr, -1, *str, len, NULL, NULL); 00059 00060 /* do not count terminator as part of the string length */ 00061 *str_len = len - 1; 00062 } 00063 00064 /* 00065 Return user's home directory using environment variables combinations. 00066 Memory allocated by this function should be manually freed afterwards. 00067 00068 Try: 00069 HOME, HOMEDRIVE + HOMEPATH and USERPROFILE environment variables 00070 TODO: Special Folders - Profile and Personal 00071 */ 00072 static wchar_t * 00073 home_dir(void) 00074 { 00075 wchar_t *buffer = NULL; 00076 size_t buffer_len = 0, len = 0; 00077 size_t home_env = 0; 00078 00079 /* 00080 GetEnvironmentVariableW when used with NULL will return the required 00081 buffer size and its terminating character. 00082 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx 00083 */ 00084 00085 if (len = GetEnvironmentVariableW(L"HOME", NULL, 0)) { 00086 buffer_len = len; 00087 home_env = 1; 00088 } 00089 else if (len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) { 00090 buffer_len = len; 00091 if (len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) { 00092 buffer_len += len; 00093 home_env = 2; 00094 } 00095 else { 00096 buffer_len = 0; 00097 } 00098 } 00099 else if (len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) { 00100 buffer_len = len; 00101 home_env = 3; 00102 } 00103 00104 /* allocate buffer */ 00105 if (home_env) 00106 buffer = (wchar_t *)xmalloc(buffer_len * sizeof(wchar_t)); 00107 00108 switch (home_env) { 00109 case 1: 00110 /* HOME */ 00111 GetEnvironmentVariableW(L"HOME", buffer, buffer_len); 00112 break; 00113 case 2: 00114 /* HOMEDRIVE + HOMEPATH */ 00115 len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len); 00116 GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len); 00117 break; 00118 case 3: 00119 /* USERPROFILE */ 00120 GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len); 00121 break; 00122 default: 00123 break; 00124 } 00125 00126 if (home_env) { 00127 /* sanitize backslashes with forwardslashes */ 00128 replace_wchar(buffer, L'\\', L'/'); 00129 00130 return buffer; 00131 } 00132 00133 return NULL; 00134 } 00135 00136 /* Remove trailing invalid ':$DATA' of the path. */ 00137 static inline size_t 00138 remove_invalid_alternative_data(wchar_t *wfullpath, size_t size) 00139 { 00140 static const wchar_t prime[] = L":$DATA"; 00141 enum { prime_len = (sizeof(prime) / sizeof(wchar_t)) -1 }; 00142 00143 if (size <= prime_len || _wcsnicmp(wfullpath + size - prime_len, prime, prime_len) != 0) 00144 return size; 00145 00146 /* alias of stream */ 00147 /* get rid of a bug of x64 VC++ */ 00148 if (wfullpath[size - (prime_len + 1)] == ':') { 00149 /* remove trailing '::$DATA' */ 00150 size -= prime_len + 1; /* prime */ 00151 wfullpath[size] = L'\0'; 00152 } 00153 else { 00154 /* remove trailing ':$DATA' of paths like '/aa:a:$DATA' */ 00155 wchar_t *pos = wfullpath + size - (prime_len + 1); 00156 while (!IS_DIR_SEPARATOR_P(*pos) && pos != wfullpath) { 00157 if (*pos == L':') { 00158 size -= prime_len; /* alternative */ 00159 wfullpath[size] = L'\0'; 00160 break; 00161 } 00162 pos--; 00163 } 00164 } 00165 return size; 00166 } 00167 00168 /* Return system code page. */ 00169 static inline UINT 00170 system_code_page(void) 00171 { 00172 return AreFileApisANSI() ? CP_ACP : CP_OEMCP; 00173 } 00174 00175 /* 00176 Return code page number of the encoding. 00177 Cache code page into a hash for performance since finding the code page in 00178 Encoding#names is slow. 00179 */ 00180 static UINT 00181 code_page(rb_encoding *enc) 00182 { 00183 VALUE code_page_value, name_key; 00184 VALUE encoding, names_ary = Qundef, name; 00185 char *enc_name; 00186 struct RString fake_str; 00187 ID names; 00188 long i; 00189 00190 if (!enc) 00191 return system_code_page(); 00192 00193 enc_name = (char *)rb_enc_name(enc); 00194 00195 fake_str.basic.flags = T_STRING|RSTRING_NOEMBED; 00196 fake_str.basic.klass = rb_cString; 00197 fake_str.as.heap.len = strlen(enc_name); 00198 fake_str.as.heap.ptr = enc_name; 00199 fake_str.as.heap.aux.capa = fake_str.as.heap.len; 00200 name_key = (VALUE)&fake_str; 00201 ENCODING_CODERANGE_SET(name_key, rb_usascii_encindex(), ENC_CODERANGE_7BIT); 00202 00203 code_page_value = rb_hash_lookup(rb_code_page, name_key); 00204 if (code_page_value != Qnil) 00205 return (UINT)FIX2INT(code_page_value); 00206 00207 name_key = rb_usascii_str_new2(enc_name); 00208 00209 encoding = rb_enc_from_encoding(enc); 00210 if (!NIL_P(encoding)) { 00211 CONST_ID(names, "names"); 00212 names_ary = rb_funcall(encoding, names, 0); 00213 } 00214 00215 /* map US-ASCII and ASCII-8bit as code page 1252 (us-ascii) */ 00216 if (enc == rb_usascii_encoding() || enc == rb_ascii8bit_encoding()) { 00217 UINT code_page = 1252; 00218 rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page)); 00219 return code_page; 00220 } 00221 00222 if (names_ary != Qundef) { 00223 for (i = 0; i < RARRAY_LEN(names_ary); i++) { 00224 name = RARRAY_PTR(names_ary)[i]; 00225 if (strncmp("CP", RSTRING_PTR(name), 2) == 0) { 00226 int code_page = atoi(RSTRING_PTR(name) + 2); 00227 if (code_page != 0) { 00228 rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page)); 00229 return (UINT)code_page; 00230 } 00231 } 00232 } 00233 } 00234 00235 rb_hash_aset(rb_code_page, name_key, INT2FIX(INVALID_CODE_PAGE)); 00236 return INVALID_CODE_PAGE; 00237 } 00238 00239 static inline VALUE 00240 fix_string_encoding(VALUE str, rb_encoding *encoding) 00241 { 00242 VALUE result, tmp; 00243 00244 tmp = rb_enc_str_new(RSTRING_PTR(str), RSTRING_LEN(str), encoding); 00245 result = rb_str_encode(tmp, rb_enc_from_encoding(rb_utf8_encoding()), 0, Qnil); 00246 00247 return result; 00248 } 00249 00250 /* 00251 Replace the last part of the path to long name. 00252 We try to avoid to call FindFirstFileW() since it takes long time. 00253 */ 00254 static inline size_t 00255 replace_to_long_name(wchar_t **wfullpath, size_t size, int heap) 00256 { 00257 WIN32_FIND_DATAW find_data; 00258 HANDLE find_handle; 00259 00260 /* 00261 Skip long name conversion if the path is already long name. 00262 Short name is 8.3 format. 00263 http://en.wikipedia.org/wiki/8.3_filename 00264 This check can be skipped for directory components that have file 00265 extensions longer than 3 characters, or total lengths longer than 00266 12 characters. 00267 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx 00268 */ 00269 size_t const max_short_name_size = 8 + 1 + 3; 00270 size_t const max_extension_size = 3; 00271 size_t path_len = 1, extension_len = 0; 00272 wchar_t *pos = *wfullpath; 00273 00274 if (size == 3 && pos[1] == L':' && pos[2] == L'\\' && pos[3] == L'\0') { 00275 /* root path doesn't need short name expansion */ 00276 return size; 00277 } 00278 00279 /* skip long name conversion if path contains wildcard characters */ 00280 if (wcspbrk(pos, L"*?")) { 00281 return size; 00282 } 00283 00284 pos = *wfullpath + size - 1; 00285 while (!IS_DIR_SEPARATOR_P(*pos) && pos != *wfullpath) { 00286 if (!extension_len && *pos == L'.') { 00287 extension_len = path_len - 1; 00288 } 00289 if (path_len > max_short_name_size || extension_len > max_extension_size) { 00290 return size; 00291 } 00292 path_len++; 00293 pos--; 00294 } 00295 00296 find_handle = FindFirstFileW(*wfullpath, &find_data); 00297 if (find_handle != INVALID_HANDLE_VALUE) { 00298 size_t trail_pos = wcslen(*wfullpath); 00299 size_t file_len = wcslen(find_data.cFileName); 00300 00301 FindClose(find_handle); 00302 while (trail_pos > 0) { 00303 if (IS_DIR_SEPARATOR_P((*wfullpath)[trail_pos])) 00304 break; 00305 trail_pos--; 00306 } 00307 size = trail_pos + 1 + file_len; 00308 if ((size + 1) > sizeof(*wfullpath) / sizeof((*wfullpath)[0])) { 00309 wchar_t *buf = (wchar_t *)xmalloc((size + 1) * sizeof(wchar_t)); 00310 wcsncpy(buf, *wfullpath, trail_pos + 1); 00311 if (heap) 00312 xfree(*wfullpath); 00313 *wfullpath = buf; 00314 } 00315 wcsncpy(*wfullpath + trail_pos + 1, find_data.cFileName, file_len + 1); 00316 } 00317 return size; 00318 } 00319 00320 static inline VALUE 00321 get_user_from_path(wchar_t **wpath, int offset, UINT cp, UINT path_cp, rb_encoding *path_encoding) 00322 { 00323 VALUE result, tmp; 00324 wchar_t *wuser = *wpath + offset; 00325 wchar_t *pos = wuser; 00326 char *user; 00327 size_t size; 00328 00329 while (!IS_DIR_SEPARATOR_P(*pos) && *pos != '\0') 00330 pos++; 00331 00332 *pos = '\0'; 00333 convert_wchar_to_mb(wuser, &user, &size, cp); 00334 00335 /* convert to VALUE and set the path encoding */ 00336 if (path_cp == INVALID_CODE_PAGE) { 00337 tmp = rb_enc_str_new(user, size, rb_utf8_encoding()); 00338 result = rb_str_encode(tmp, rb_enc_from_encoding(path_encoding), 0, Qnil); 00339 rb_str_resize(tmp, 0); 00340 } 00341 else { 00342 result = rb_enc_str_new(user, size, path_encoding); 00343 } 00344 00345 if (user) 00346 xfree(user); 00347 00348 return result; 00349 } 00350 00351 VALUE 00352 rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result) 00353 { 00354 size_t size = 0, wpath_len = 0, wdir_len = 0, whome_len = 0; 00355 size_t buffer_len = 0; 00356 char *fullpath = NULL; 00357 wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL; 00358 wchar_t *wdir = NULL, *wdir_pos = NULL; 00359 wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL; 00360 UINT path_cp, cp; 00361 VALUE path = fname, dir = dname; 00362 wchar_t wfullpath_buffer[PATH_BUFFER_SIZE]; 00363 wchar_t path_drive = L'\0', dir_drive = L'\0'; 00364 int ignore_dir = 0; 00365 rb_encoding *path_encoding; 00366 int tainted = 0; 00367 00368 /* tainted if path is tainted */ 00369 tainted = OBJ_TAINTED(path); 00370 00371 /* get path encoding */ 00372 if (NIL_P(dir)) { 00373 path_encoding = rb_enc_get(path); 00374 } 00375 else { 00376 path_encoding = rb_enc_check(path, dir); 00377 } 00378 00379 cp = path_cp = code_page(path_encoding); 00380 00381 /* workaround invalid codepage */ 00382 if (path_cp == INVALID_CODE_PAGE) { 00383 cp = CP_UTF8; 00384 if (!NIL_P(path)) { 00385 path = fix_string_encoding(path, path_encoding); 00386 } 00387 } 00388 00389 /* convert char * to wchar_t */ 00390 convert_mb_to_wchar(path, &wpath, &wpath_pos, &wpath_len, cp); 00391 00392 /* determine if we need the user's home directory */ 00393 /* expand '~' only if NOT rb_file_absolute_path() where `abs_mode` is 1 */ 00394 if (abs_mode == 0 && wpath_len > 0 && wpath_pos[0] == L'~' && 00395 (wpath_len == 1 || IS_DIR_SEPARATOR_P(wpath_pos[1]))) { 00396 /* tainted if expanding '~' */ 00397 tainted = 1; 00398 00399 whome = home_dir(); 00400 if (whome == NULL) { 00401 xfree(wpath); 00402 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'"); 00403 } 00404 whome_len = wcslen(whome); 00405 00406 if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) { 00407 xfree(wpath); 00408 rb_raise(rb_eArgError, "non-absolute home"); 00409 } 00410 00411 /* use filesystem encoding if expanding home dir */ 00412 path_encoding = rb_filesystem_encoding(); 00413 cp = path_cp = system_code_page(); 00414 00415 /* ignores dir since we are expading home */ 00416 ignore_dir = 1; 00417 00418 /* exclude ~ from the result */ 00419 wpath_pos++; 00420 wpath_len--; 00421 00422 /* exclude separator if present */ 00423 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { 00424 wpath_pos++; 00425 wpath_len--; 00426 } 00427 } 00428 else if (wpath_len >= 2 && wpath_pos[1] == L':') { 00429 if (wpath_len >= 3 && IS_DIR_SEPARATOR_P(wpath_pos[2])) { 00430 /* ignore dir since path contains a drive letter and a root slash */ 00431 ignore_dir = 1; 00432 } 00433 else { 00434 /* determine if we ignore dir or not later */ 00435 path_drive = wpath_pos[0]; 00436 } 00437 } 00438 else if (abs_mode == 0 && wpath_len >= 2 && wpath_pos[0] == L'~') { 00439 result = get_user_from_path(&wpath_pos, 1, cp, path_cp, path_encoding); 00440 00441 if (wpath) 00442 xfree(wpath); 00443 00444 rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result)); 00445 } 00446 00447 /* convert dir */ 00448 if (!ignore_dir && !NIL_P(dir)) { 00449 /* fix string encoding */ 00450 if (path_cp == INVALID_CODE_PAGE) { 00451 dir = fix_string_encoding(dir, path_encoding); 00452 } 00453 00454 /* convert char * to wchar_t */ 00455 convert_mb_to_wchar(dir, &wdir, &wdir_pos, &wdir_len, cp); 00456 00457 if (abs_mode == 0 && wdir_len > 0 && wdir_pos[0] == L'~' && 00458 (wdir_len == 1 || IS_DIR_SEPARATOR_P(wdir_pos[1]))) { 00459 /* tainted if expanding '~' */ 00460 tainted = 1; 00461 00462 whome = home_dir(); 00463 if (whome == NULL) { 00464 xfree(wpath); 00465 xfree(wdir); 00466 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'"); 00467 } 00468 whome_len = wcslen(whome); 00469 00470 if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) { 00471 xfree(wpath); 00472 xfree(wdir); 00473 rb_raise(rb_eArgError, "non-absolute home"); 00474 } 00475 00476 /* exclude ~ from the result */ 00477 wdir_pos++; 00478 wdir_len--; 00479 00480 /* exclude separator if present */ 00481 if (wdir_len && IS_DIR_SEPARATOR_P(wdir_pos[0])) { 00482 wdir_pos++; 00483 wdir_len--; 00484 } 00485 } 00486 else if (wdir_len >= 2 && wdir[1] == L':') { 00487 dir_drive = wdir[0]; 00488 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { 00489 wdir_len = 2; 00490 } 00491 } 00492 else if (wdir_len >= 2 && IS_DIR_UNC_P(wdir)) { 00493 /* UNC path */ 00494 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { 00495 /* cut the UNC path tail to '//host/share' */ 00496 size_t separators = 0; 00497 size_t pos = 2; 00498 while (pos < wdir_len && separators < 2) { 00499 if (IS_DIR_SEPARATOR_P(wdir[pos])) { 00500 separators++; 00501 } 00502 pos++; 00503 } 00504 if (separators == 2) 00505 wdir_len = pos - 1; 00506 } 00507 } 00508 else if (abs_mode == 0 && wdir_len >= 2 && wdir_pos[0] == L'~') { 00509 result = get_user_from_path(&wdir_pos, 1, cp, path_cp, path_encoding); 00510 if (wpath) 00511 xfree(wpath); 00512 00513 if (wdir) 00514 xfree(wdir); 00515 00516 rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result)); 00517 } 00518 } 00519 00520 /* determine if we ignore dir or not */ 00521 if (!ignore_dir && path_drive && dir_drive) { 00522 if (towupper(path_drive) == towupper(dir_drive)) { 00523 /* exclude path drive letter to use dir */ 00524 wpath_pos += 2; 00525 wpath_len -= 2; 00526 } 00527 else { 00528 /* ignore dir since path drive is different from dir drive */ 00529 ignore_dir = 1; 00530 wdir_len = 0; 00531 } 00532 } 00533 00534 if (!ignore_dir && wpath_len >= 2 && IS_DIR_UNC_P(wpath)) { 00535 /* ignore dir since path has UNC root */ 00536 ignore_dir = 1; 00537 wdir_len = 0; 00538 } 00539 else if (!ignore_dir && wpath_len >= 1 && IS_DIR_SEPARATOR_P(wpath[0]) && 00540 !dir_drive && !(wdir_len >= 2 && IS_DIR_UNC_P(wdir))) { 00541 /* ignore dir since path has root slash and dir doesn't have drive or UNC root */ 00542 ignore_dir = 1; 00543 wdir_len = 0; 00544 } 00545 00546 buffer_len = wpath_len + 1 + wdir_len + 1 + whome_len + 1; 00547 00548 buffer = buffer_pos = (wchar_t *)xmalloc((buffer_len + 1) * sizeof(wchar_t)); 00549 00550 /* add home */ 00551 if (whome_len) { 00552 wcsncpy(buffer_pos, whome, whome_len); 00553 buffer_pos += whome_len; 00554 } 00555 00556 /* Add separator if required */ 00557 if (whome_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) { 00558 buffer_pos[0] = L'\\'; 00559 buffer_pos++; 00560 } 00561 00562 if (wdir_len) { 00563 /* tainted if dir is used and dir is tainted */ 00564 if (!tainted && OBJ_TAINTED(dir)) 00565 tainted = 1; 00566 00567 wcsncpy(buffer_pos, wdir_pos, wdir_len); 00568 buffer_pos += wdir_len; 00569 } 00570 00571 /* add separator if required */ 00572 if (wdir_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) { 00573 buffer_pos[0] = L'\\'; 00574 buffer_pos++; 00575 } 00576 00577 /* now deal with path */ 00578 if (wpath_len) { 00579 wcsncpy(buffer_pos, wpath_pos, wpath_len); 00580 buffer_pos += wpath_len; 00581 } 00582 00583 /* GetFullPathNameW requires at least "." to determine current directory */ 00584 if (wpath_len == 0) { 00585 buffer_pos[0] = L'.'; 00586 buffer_pos++; 00587 } 00588 00589 /* Ensure buffer is NULL terminated */ 00590 buffer_pos[0] = L'\0'; 00591 00592 /* tainted if path is relative */ 00593 if (!tainted && PathIsRelativeW(buffer) && !(buffer_len >= 2 && IS_DIR_UNC_P(buffer))) 00594 tainted = 1; 00595 00596 /* FIXME: Make this more robust */ 00597 /* Determine require buffer size */ 00598 size = GetFullPathNameW(buffer, PATH_BUFFER_SIZE, wfullpath_buffer, NULL); 00599 if (size > PATH_BUFFER_SIZE) { 00600 /* allocate more memory than alloted originally by PATH_BUFFER_SIZE */ 00601 wfullpath = (wchar_t *)xmalloc(size * sizeof(wchar_t)); 00602 size = GetFullPathNameW(buffer, size, wfullpath, NULL); 00603 } 00604 else { 00605 wfullpath = wfullpath_buffer; 00606 } 00607 00608 /* Remove any trailing slashes */ 00609 if (IS_DIR_SEPARATOR_P(wfullpath[size - 1]) && 00610 wfullpath[size - 2] != L':' && 00611 !(size == 2 && IS_DIR_UNC_P(wfullpath))) { 00612 size -= 1; 00613 wfullpath[size] = L'\0'; 00614 } 00615 00616 /* Remove any trailing dot */ 00617 if (wfullpath[size - 1] == L'.') { 00618 size -= 1; 00619 wfullpath[size] = L'\0'; 00620 } 00621 00622 /* removes trailing invalid ':$DATA' */ 00623 size = remove_invalid_alternative_data(wfullpath, size); 00624 00625 /* Replace the trailing path to long name */ 00626 if (long_name) 00627 size = replace_to_long_name(&wfullpath, size, (wfullpath != wfullpath_buffer)); 00628 00629 /* sanitize backslashes with forwardslashes */ 00630 replace_wchar(wfullpath, L'\\', L'/'); 00631 00632 /* convert to char * */ 00633 size = WideCharToMultiByte(cp, 0, wfullpath, size, NULL, 0, NULL, NULL); 00634 if (size > (size_t)RSTRING_LEN(result)) { 00635 rb_str_modify(result); 00636 rb_str_resize(result, size); 00637 } 00638 00639 WideCharToMultiByte(cp, 0, wfullpath, size, RSTRING_PTR(result), size, NULL, NULL); 00640 rb_str_set_len(result, size); 00641 00642 /* convert to VALUE and set the path encoding */ 00643 if (path_cp == INVALID_CODE_PAGE) { 00644 VALUE tmp; 00645 size_t len; 00646 00647 rb_enc_associate(result, rb_utf8_encoding()); 00648 ENC_CODERANGE_CLEAR(result); 00649 tmp = rb_str_encode(result, rb_enc_from_encoding(path_encoding), 0, Qnil); 00650 len = RSTRING_LEN(tmp); 00651 rb_str_modify(result); 00652 rb_str_resize(result, len); 00653 memcpy(RSTRING_PTR(result), RSTRING_PTR(tmp), len); 00654 rb_str_resize(tmp, 0); 00655 } 00656 rb_enc_associate(result, path_encoding); 00657 ENC_CODERANGE_CLEAR(result); 00658 00659 /* makes the result object tainted if expanding tainted strings or returning modified path */ 00660 if (tainted) 00661 OBJ_TAINT(result); 00662 00663 /* TODO: better cleanup */ 00664 if (buffer) 00665 xfree(buffer); 00666 00667 if (wpath) 00668 xfree(wpath); 00669 00670 if (wdir) 00671 xfree(wdir); 00672 00673 if (whome) 00674 xfree(whome); 00675 00676 if (wfullpath && wfullpath != wfullpath_buffer) 00677 xfree(wfullpath); 00678 00679 if (fullpath) 00680 xfree(fullpath); 00681 00682 return result; 00683 } 00684 00685 int 00686 rb_file_load_ok(const char *path) 00687 { 00688 int ret = 1; 00689 DWORD attr = GetFileAttributes(path); 00690 if (attr == INVALID_FILE_ATTRIBUTES || 00691 attr & FILE_ATTRIBUTE_DIRECTORY) { 00692 ret = 0; 00693 } 00694 else { 00695 HANDLE h = CreateFile(path, GENERIC_READ, 00696 FILE_SHARE_READ | FILE_SHARE_WRITE, 00697 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 00698 if (h != INVALID_HANDLE_VALUE) { 00699 CloseHandle(h); 00700 } 00701 else { 00702 ret = 0; 00703 } 00704 } 00705 return ret; 00706 } 00707 00708 void 00709 rb_w32_init_file(void) 00710 { 00711 rb_code_page = rb_hash_new(); 00712 00713 /* prevent GC removing rb_code_page */ 00714 rb_gc_register_mark_object(rb_code_page); 00715 } 00716