win32/file.c

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

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7