Ruby 1.9.3p327(2012-11-10revision37606)
win32/file.c
Go to the documentation of this file.
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 20127 (us-ascii) */
00216     if (enc == rb_usascii_encoding() || enc == rb_ascii8bit_encoding()) {
00217         UINT code_page = 20127;
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     pos = *wfullpath + size - 1;
00280     while (!IS_DIR_SEPARATOR_P(*pos) && pos != *wfullpath) {
00281         if (!extension_len && *pos == L'.') {
00282             extension_len = path_len - 1;
00283         }
00284         if (path_len > max_short_name_size || extension_len > max_extension_size) {
00285             return size;
00286         }
00287         path_len++;
00288         pos--;
00289     }
00290 
00291     find_handle = FindFirstFileW(*wfullpath, &find_data);
00292     if (find_handle != INVALID_HANDLE_VALUE) {
00293         size_t trail_pos = wcslen(*wfullpath);
00294         size_t file_len = wcslen(find_data.cFileName);
00295 
00296         FindClose(find_handle);
00297         while (trail_pos > 0) {
00298             if (IS_DIR_SEPARATOR_P((*wfullpath)[trail_pos]))
00299                 break;
00300             trail_pos--;
00301         }
00302         size = trail_pos + 1 + file_len;
00303         if ((size + 1) > sizeof(*wfullpath) / sizeof((*wfullpath)[0])) {
00304             wchar_t *buf = (wchar_t *)xmalloc((size + 1) * sizeof(wchar_t));
00305             wcsncpy(buf, *wfullpath, trail_pos + 1);
00306             if (heap)
00307                 xfree(*wfullpath);
00308             *wfullpath = buf;
00309         }
00310         wcsncpy(*wfullpath + trail_pos + 1, find_data.cFileName, file_len + 1);
00311     }
00312     return size;
00313 }
00314 
00315 VALUE
00316 rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
00317 {
00318     size_t size = 0, wpath_len = 0, wdir_len = 0, whome_len = 0;
00319     size_t buffer_len = 0;
00320     char *fullpath = NULL;
00321     wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL, *wdir = NULL;
00322     wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL;
00323     UINT path_cp, cp;
00324     VALUE path = fname, dir = dname;
00325     wchar_t wfullpath_buffer[PATH_BUFFER_SIZE];
00326     wchar_t path_drive = L'\0', dir_drive = L'\0';
00327     int ignore_dir = 0;
00328     rb_encoding *path_encoding;
00329     int tainted = 0;
00330 
00331     /* tainted if path is tainted */
00332     tainted = OBJ_TAINTED(path);
00333 
00334     /* get path encoding */
00335     if (NIL_P(dir)) {
00336         path_encoding = rb_enc_get(path);
00337     }
00338     else {
00339         path_encoding = rb_enc_check(path, dir);
00340     }
00341 
00342     cp = path_cp = code_page(path_encoding);
00343 
00344     /* workaround invalid codepage */
00345     if (path_cp == INVALID_CODE_PAGE) {
00346         cp = CP_UTF8;
00347         if (!NIL_P(path)) {
00348             path = fix_string_encoding(path, path_encoding);
00349         }
00350     }
00351 
00352     /* convert char * to wchar_t */
00353     convert_mb_to_wchar(path, &wpath, &wpath_pos, &wpath_len, cp);
00354 
00355     /* determine if we need the user's home directory */
00356     /* expand '~' only if NOT rb_file_absolute_path() where `abs_mode` is 1 */
00357     if (abs_mode == 0 && wpath_len > 0 && wpath_pos[0] == L'~' &&
00358         (wpath_len == 1 || IS_DIR_SEPARATOR_P(wpath_pos[1]))) {
00359         /* tainted if expanding '~' */
00360         tainted = 1;
00361 
00362         whome = home_dir();
00363         if (whome == NULL) {
00364             xfree(wpath);
00365             rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
00366         }
00367         whome_len = wcslen(whome);
00368 
00369         if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) {
00370             xfree(wpath);
00371             rb_raise(rb_eArgError, "non-absolute home");
00372         }
00373 
00374         /* use filesystem encoding if expanding home dir */
00375         path_encoding = rb_filesystem_encoding();
00376         cp = path_cp = system_code_page();
00377 
00378         /* ignores dir since we are expading home */
00379         ignore_dir = 1;
00380 
00381         /* exclude ~ from the result */
00382         wpath_pos++;
00383         wpath_len--;
00384 
00385         /* exclude separator if present */
00386         if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
00387             wpath_pos++;
00388             wpath_len--;
00389         }
00390     }
00391     else if (wpath_len >= 2 && wpath_pos[1] == L':') {
00392         if (wpath_len >= 3 && IS_DIR_SEPARATOR_P(wpath_pos[2])) {
00393             /* ignore dir since path contains a drive letter and a root slash */
00394             ignore_dir = 1;
00395         }
00396         else {
00397             /* determine if we ignore dir or not later */
00398             path_drive = wpath_pos[0];
00399         }
00400     }
00401     else if (abs_mode == 0 && wpath_len >= 2 && wpath_pos[0] == L'~') {
00402         wchar_t *wuser = wpath_pos + 1;
00403         wchar_t *pos = wuser;
00404         char *user;
00405 
00406         /* tainted if expanding '~' */
00407         tainted = 1;
00408 
00409         while (!IS_DIR_SEPARATOR_P(*pos) && *pos != '\0')
00410             pos++;
00411 
00412         *pos = '\0';
00413         convert_wchar_to_mb(wuser, &user, &size, cp);
00414 
00415         /* convert to VALUE and set the path encoding */
00416         if (path_cp == INVALID_CODE_PAGE) {
00417             VALUE tmp = rb_enc_str_new(user, size, rb_utf8_encoding());
00418             result = rb_str_encode(tmp, rb_enc_from_encoding(path_encoding), 0, Qnil);
00419             rb_str_resize(tmp, 0);
00420         }
00421         else {
00422             result = rb_enc_str_new(user, size, path_encoding);
00423         }
00424 
00425         xfree(wpath);
00426         if (user)
00427             xfree(user);
00428 
00429         rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result));
00430     }
00431 
00432     /* convert dir */
00433     if (!ignore_dir && !NIL_P(dir)) {
00434         /* fix string encoding */
00435         if (path_cp == INVALID_CODE_PAGE) {
00436             dir = fix_string_encoding(dir, path_encoding);
00437         }
00438 
00439         /* convert char * to wchar_t */
00440         convert_mb_to_wchar(dir, &wdir, NULL, &wdir_len, cp);
00441 
00442         if (wdir_len >= 2 && wdir[1] == L':') {
00443             dir_drive = wdir[0];
00444             if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
00445                 wdir_len = 2;
00446             }
00447         }
00448         else if (wdir_len >= 2 && IS_DIR_UNC_P(wdir)) {
00449             /* UNC path */
00450             if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
00451                 /* cut the UNC path tail to '//host/share' */
00452                 size_t separators = 0;
00453                 size_t pos = 2;
00454                 while (pos < wdir_len && separators < 2) {
00455                     if (IS_DIR_SEPARATOR_P(wdir[pos])) {
00456                         separators++;
00457                     }
00458                     pos++;
00459                 }
00460                 if (separators == 2)
00461                     wdir_len = pos - 1;
00462             }
00463         }
00464     }
00465 
00466     /* determine if we ignore dir or not */
00467     if (!ignore_dir && path_drive && dir_drive) {
00468         if (towupper(path_drive) == towupper(dir_drive)) {
00469             /* exclude path drive letter to use dir */
00470             wpath_pos += 2;
00471             wpath_len -= 2;
00472         }
00473         else {
00474             /* ignore dir since path drive is different from dir drive */
00475             ignore_dir = 1;
00476             wdir_len = 0;
00477         }
00478     }
00479 
00480     if (!ignore_dir && wpath_len >= 2 && IS_DIR_UNC_P(wpath)) {
00481         /* ignore dir since path has UNC root */
00482         ignore_dir = 1;
00483         wdir_len = 0;
00484     }
00485     else if (!ignore_dir && wpath_len >= 1 && IS_DIR_SEPARATOR_P(wpath[0]) &&
00486              !dir_drive && !(wdir_len >= 2 && IS_DIR_UNC_P(wdir))) {
00487         /* ignore dir since path has root slash and dir doesn't have drive or UNC root */
00488         ignore_dir = 1;
00489         wdir_len = 0;
00490     }
00491 
00492     buffer_len = wpath_len + 1 + wdir_len + 1 + whome_len + 1;
00493 
00494     buffer = buffer_pos = (wchar_t *)xmalloc((buffer_len + 1) * sizeof(wchar_t));
00495 
00496     /* add home */
00497     if (whome_len) {
00498         wcsncpy(buffer_pos, whome, whome_len);
00499         buffer_pos += whome_len;
00500     }
00501 
00502     /* Add separator if required */
00503     if (whome_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) {
00504         buffer_pos[0] = L'\\';
00505         buffer_pos++;
00506     }
00507 
00508     if (wdir_len) {
00509         /* tainted if dir is used and dir is tainted */
00510         if (!tainted && OBJ_TAINTED(dir))
00511             tainted = 1;
00512 
00513         wcsncpy(buffer_pos, wdir, wdir_len);
00514         buffer_pos += wdir_len;
00515     }
00516 
00517     /* add separator if required */
00518     if (wdir_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) {
00519         buffer_pos[0] = L'\\';
00520         buffer_pos++;
00521     }
00522 
00523     /* now deal with path */
00524     if (wpath_len) {
00525         wcsncpy(buffer_pos, wpath_pos, wpath_len);
00526         buffer_pos += wpath_len;
00527     }
00528 
00529     /* GetFullPathNameW requires at least "." to determine current directory */
00530     if (wpath_len == 0) {
00531         buffer_pos[0] = L'.';
00532         buffer_pos++;
00533     }
00534 
00535     /* Ensure buffer is NULL terminated */
00536     buffer_pos[0] = L'\0';
00537 
00538     /* tainted if path is relative */
00539     if (!tainted && PathIsRelativeW(buffer) && !(buffer_len >= 2 && IS_DIR_UNC_P(buffer)))
00540         tainted = 1;
00541 
00542     /* FIXME: Make this more robust */
00543     /* Determine require buffer size */
00544     size = GetFullPathNameW(buffer, PATH_BUFFER_SIZE, wfullpath_buffer, NULL);
00545     if (size > PATH_BUFFER_SIZE) {
00546         /* allocate more memory than alloted originally by PATH_BUFFER_SIZE */
00547         wfullpath = (wchar_t *)xmalloc(size * sizeof(wchar_t));
00548         size = GetFullPathNameW(buffer, size, wfullpath, NULL);
00549     }
00550     else {
00551         wfullpath = wfullpath_buffer;
00552     }
00553 
00554     /* Remove any trailing slashes */
00555     if (IS_DIR_SEPARATOR_P(wfullpath[size - 1]) &&
00556         wfullpath[size - 2] != L':' &&
00557         !(size == 2 && IS_DIR_UNC_P(wfullpath))) {
00558         size -= 1;
00559         wfullpath[size] = L'\0';
00560     }
00561 
00562     /* Remove any trailing dot */
00563     if (wfullpath[size - 1] == L'.') {
00564         size -= 1;
00565         wfullpath[size] = L'\0';
00566     }
00567 
00568     /* removes trailing invalid ':$DATA' */
00569     size = remove_invalid_alternative_data(wfullpath, size);
00570 
00571     /* Replace the trailing path to long name */
00572     if (long_name)
00573         size = replace_to_long_name(&wfullpath, size, (wfullpath != wfullpath_buffer));
00574 
00575     /* sanitize backslashes with forwardslashes */
00576     replace_wchar(wfullpath, L'\\', L'/');
00577 
00578     /* convert to char * */
00579     size = WideCharToMultiByte(cp, 0, wfullpath, size, NULL, 0, NULL, NULL);
00580     if (size > (size_t)RSTRING_LEN(result)) {
00581         rb_str_modify(result);
00582         rb_str_resize(result, size);
00583     }
00584 
00585     WideCharToMultiByte(cp, 0, wfullpath, size, RSTRING_PTR(result), size, NULL, NULL);
00586     rb_str_set_len(result, size);
00587 
00588     /* convert to VALUE and set the path encoding */
00589     if (path_cp == INVALID_CODE_PAGE) {
00590         VALUE tmp;
00591         size_t len;
00592 
00593         rb_enc_associate(result, rb_utf8_encoding());
00594         ENC_CODERANGE_CLEAR(result);
00595         tmp = rb_str_encode(result, rb_enc_from_encoding(path_encoding), 0, Qnil);
00596         len = RSTRING_LEN(tmp);
00597         rb_str_modify(result);
00598         rb_str_resize(result, len);
00599         memcpy(RSTRING_PTR(result), RSTRING_PTR(tmp), len);
00600         rb_str_resize(tmp, 0);
00601     }
00602     rb_enc_associate(result, path_encoding);
00603     ENC_CODERANGE_CLEAR(result);
00604 
00605     /* makes the result object tainted if expanding tainted strings or returning modified path */
00606     if (tainted)
00607         OBJ_TAINT(result);
00608 
00609     /* TODO: better cleanup */
00610     if (buffer)
00611         xfree(buffer);
00612 
00613     if (wpath)
00614         xfree(wpath);
00615 
00616     if (wdir)
00617         xfree(wdir);
00618 
00619     if (whome)
00620         xfree(whome);
00621 
00622     if (wfullpath && wfullpath != wfullpath_buffer)
00623         xfree(wfullpath);
00624 
00625     if (fullpath)
00626         xfree(fullpath);
00627 
00628     return result;
00629 }
00630 
00631 int
00632 rb_file_load_ok(const char *path)
00633 {
00634     int ret = 1;
00635     DWORD attr = GetFileAttributes(path);
00636     if (attr == INVALID_FILE_ATTRIBUTES ||
00637         attr & FILE_ATTRIBUTE_DIRECTORY) {
00638         ret = 0;
00639     }
00640     else {
00641         HANDLE h = CreateFile(path, GENERIC_READ,
00642                               FILE_SHARE_READ | FILE_SHARE_WRITE,
00643                               NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
00644         if (h != INVALID_HANDLE_VALUE) {
00645             CloseHandle(h);
00646         }
00647         else {
00648             ret = 0;
00649         }
00650     }
00651     return ret;
00652 }
00653 
00654 void
00655 rb_w32_init_file(void)
00656 {
00657     rb_code_page = rb_hash_new();
00658 
00659     /* prevent GC removing rb_code_page */
00660     rb_gc_register_mark_object(rb_code_page);
00661 }
00662