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