Ruby  2.0.0p247(2013-06-27revision41674)
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 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