- Migrate CRTDLL to MSVCRT.
- Many fixes and a load of new functions.
diff --git a/dlls/msvcrt/dir.c b/dlls/msvcrt/dir.c
new file mode 100644
index 0000000..84cdb62
--- /dev/null
+++ b/dlls/msvcrt/dir.c
@@ -0,0 +1,780 @@
+/*
+ * msvcrt.dll drive/directory functions
+ *
+ * Copyright 1996,1998 Marcus Meissner
+ * Copyright 1996 Jukka Iivonen
+ * Copyright 1997,2000 Uwe Bonnes
+ * Copyright 2000 Jon Griffiths
+ */
+
+#include <time.h>
+#include "ntddk.h"
+#include "wine/unicode.h"
+#include "msvcrt.h"
+#include "ms_errno.h"
+
+DEFAULT_DEBUG_CHANNEL(msvcrt);
+
+typedef struct MSVCRT_finddata_t
+{
+ unsigned attrib;
+ time_t time_create; /* -1 when N/A */
+ time_t time_access; /* -1 when N/A */
+ time_t time_write;
+ unsigned long size; /* FIXME: 64 bit ??*/
+ char name[MAX_PATH];
+} MSVCRT_finddata_t;
+
+typedef struct MSVCRT_wfinddata_t
+{
+ unsigned attrib;
+ time_t time_create; /* -1 when N/A */
+ time_t time_access; /* -1 when N/A */
+ time_t time_write;
+ unsigned long size; /* FIXME: 64 bit ??*/
+ WCHAR name[MAX_PATH];
+} MSVCRT_wfinddata_t;
+
+typedef struct __MSVCRT_diskfree_t {
+ unsigned num_clusters;
+ unsigned available;
+ unsigned cluster_sectors;
+ unsigned sector_bytes;
+} MSVCRT_diskfree_t;
+
+/* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
+static void MSVCRT__fttofd(LPWIN32_FIND_DATAA fd, MSVCRT_finddata_t* ft)
+{
+ DWORD dw;
+
+ if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
+ ft->attrib = 0;
+ else
+ ft->attrib = fd->dwFileAttributes;
+
+ RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
+ ft->time_create = dw;
+ RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
+ ft->time_access = dw;
+ RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
+ ft->time_write = dw;
+ ft->size = fd->nFileSizeLow;
+ strcpy(ft->name, fd->cFileName);
+}
+
+/* INTERNAL: Translate wfinddata_t to PWIN32_FIND_DATAA */
+static void MSVCRT__wfttofd(LPWIN32_FIND_DATAW fd, MSVCRT_wfinddata_t* ft)
+{
+ DWORD dw;
+
+ if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
+ ft->attrib = 0;
+ else
+ ft->attrib = fd->dwFileAttributes;
+
+ RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
+ ft->time_create = dw;
+ RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
+ ft->time_access = dw;
+ RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
+ ft->time_write = dw;
+ ft->size = fd->nFileSizeLow;
+ strcpyW(ft->name, fd->cFileName);
+}
+
+char * MSVCRT__strndup(const char *, unsigned int);
+LPWSTR __cdecl MSVCRT__wcsdup( LPCWSTR );
+LPWSTR __cdecl MSVCRT__wstrndup( LPCWSTR , unsigned int );
+char *__cdecl MSVCRT_getenv(const char *);
+
+/*********************************************************************
+ * _chdir (MSVCRT.@)
+ */
+int __cdecl MSVCRT__chdir(const char * newdir)
+{
+ if (!SetCurrentDirectoryA(newdir))
+ {
+ MSVCRT__set_errno(newdir?GetLastError():0);
+ return -1;
+ }
+ return 0;
+}
+
+/*********************************************************************
+ * _wchdir (MSVCRT.@)
+ */
+int __cdecl MSVCRT__wchdir(const WCHAR * newdir)
+{
+ if (!SetCurrentDirectoryW(newdir))
+ {
+ MSVCRT__set_errno(newdir?GetLastError():0);
+ return -1;
+ }
+ return 0;
+}
+
+/*********************************************************************
+ * _chdrive (MSVCRT.@)
+ */
+int __cdecl MSVCRT__chdrive(int newdrive)
+{
+ char buffer[3] = "A:";
+ buffer[0] += newdrive - 1;
+ if (!SetCurrentDirectoryA( buffer ))
+ {
+ MSVCRT__set_errno(GetLastError());
+ if (newdrive <= 0)
+ SET_THREAD_VAR(errno,MSVCRT_EACCES);
+ return -1;
+ }
+ return 0;
+}
+
+/*********************************************************************
+ * _findclose (MSVCRT.@)
+ */
+int __cdecl MSVCRT__findclose(DWORD hand)
+{
+ TRACE(":handle %ld\n",hand);
+ if (!FindClose((HANDLE)hand))
+ {
+ MSVCRT__set_errno(GetLastError());
+ return -1;
+ }
+ return 0;
+}
+
+/*********************************************************************
+ * _findfirst (MSVCRT.@)
+ */
+DWORD __cdecl MSVCRT__findfirst(const char * fspec, MSVCRT_finddata_t* ft)
+{
+ WIN32_FIND_DATAA find_data;
+ HANDLE hfind;
+
+ hfind = FindFirstFileA(fspec, &find_data);
+ if (hfind == INVALID_HANDLE_VALUE)
+ {
+ MSVCRT__set_errno(GetLastError());
+ return -1;
+ }
+ MSVCRT__fttofd(&find_data,ft);
+ TRACE(":got handle %d\n",hfind);
+ return hfind;
+}
+
+/*********************************************************************
+ * _wfindfirst (MSVCRT.@)
+ */
+DWORD __cdecl MSVCRT__wfindfirst(const WCHAR * fspec, MSVCRT_wfinddata_t* ft)
+{
+ WIN32_FIND_DATAW find_data;
+ HANDLE hfind;
+
+ hfind = FindFirstFileW(fspec, &find_data);
+ if (hfind == INVALID_HANDLE_VALUE)
+ {
+ MSVCRT__set_errno(GetLastError());
+ return -1;
+ }
+ MSVCRT__wfttofd(&find_data,ft);
+ TRACE(":got handle %d\n",hfind);
+ return hfind;
+}
+
+/*********************************************************************
+ * _findnext (MSVCRT.@)
+ */
+int __cdecl MSVCRT__findnext(DWORD hand, MSVCRT_finddata_t * ft)
+{
+ WIN32_FIND_DATAA find_data;
+
+ if (!FindNextFileA(hand, &find_data))
+ {
+ SET_THREAD_VAR(errno,MSVCRT_ENOENT);
+ return -1;
+ }
+
+ MSVCRT__fttofd(&find_data,ft);
+ return 0;
+}
+
+/*********************************************************************
+ * _wfindnext (MSVCRT.@)
+ */
+int __cdecl MSVCRT__wfindnext(DWORD hand, MSVCRT_wfinddata_t * ft)
+{
+ WIN32_FIND_DATAW find_data;
+
+ if (!FindNextFileW(hand, &find_data))
+ {
+ SET_THREAD_VAR(errno,MSVCRT_ENOENT);
+ return -1;
+ }
+
+ MSVCRT__wfttofd(&find_data,ft);
+ return 0;
+}
+
+/*********************************************************************
+ * _getcwd (MSVCRT.@)
+ */
+char* __cdecl MSVCRT__getcwd(char * buf, int size)
+{
+ char dir[_MAX_PATH];
+ int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
+
+ if (dir_len < 1)
+ return NULL; /* FIXME: Real return value untested */
+
+ if (!buf)
+ {
+ if (size < 0)
+ return MSVCRT__strdup(dir);
+ return MSVCRT__strndup(dir,size);
+ }
+ if (dir_len >= size)
+ {
+ SET_THREAD_VAR(errno,MSVCRT_ERANGE);
+ return NULL; /* buf too small */
+ }
+ strcpy(buf,dir);
+ return buf;
+}
+
+/*********************************************************************
+ * _wgetcwd (MSVCRT.@)
+ */
+WCHAR* __cdecl MSVCRT__wgetcwd(WCHAR * buf, int size)
+{
+ WCHAR dir[_MAX_PATH];
+ int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
+
+ if (dir_len < 1)
+ return NULL; /* FIXME: Real return value untested */
+
+ if (!buf)
+ {
+ if (size < 0)
+ return MSVCRT__wcsdup(dir);
+ return MSVCRT__wstrndup(dir,size);
+ }
+ if (dir_len >= size)
+ {
+ SET_THREAD_VAR(errno,MSVCRT_ERANGE);
+ return NULL; /* buf too small */
+ }
+ strcpyW(buf,dir);
+ return buf;
+}
+
+/*********************************************************************
+ * _getdrive (MSVCRT.@)
+ */
+int __cdecl MSVCRT__getdrive(void)
+{
+ char buffer[MAX_PATH];
+ if (!GetCurrentDirectoryA( sizeof(buffer), buffer )) return 0;
+ if (buffer[1] != ':') return 0;
+ return toupper(buffer[0]) - 'A' + 1;
+}
+
+/*********************************************************************
+ * _getdcwd (MSVCRT.@)
+ */
+char* __cdecl MSVCRT__getdcwd(int drive, char * buf, int size)
+{
+ static char* dummy;
+
+ TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
+
+ if (!drive || drive == MSVCRT__getdrive())
+ return MSVCRT__getcwd(buf,size); /* current */
+ else
+ {
+ char dir[_MAX_PATH];
+ char drivespec[4] = {'A', ':', '\\', 0};
+ int dir_len;
+
+ drivespec[0] += drive - 1;
+ if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
+ {
+ SET_THREAD_VAR(errno,MSVCRT_EACCES);
+ return NULL;
+ }
+
+ dir_len = GetFullPathNameA(drivespec,_MAX_PATH,dir,&dummy);
+ if (dir_len >= size || dir_len < 1)
+ {
+ SET_THREAD_VAR(errno,MSVCRT_ERANGE);
+ return NULL; /* buf too small */
+ }
+
+ TRACE(":returning '%s'\n", dir);
+ if (!buf)
+ return MSVCRT__strdup(dir); /* allocate */
+
+ strcpy(buf,dir);
+ }
+ return buf;
+}
+
+/*********************************************************************
+ * _wgetdcwd (MSVCRT.@)
+ */
+WCHAR* __cdecl MSVCRT__wgetdcwd(int drive, WCHAR * buf, int size)
+{
+ static WCHAR* dummy;
+
+ TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
+
+ if (!drive || drive == MSVCRT__getdrive())
+ return MSVCRT__wgetcwd(buf,size); /* current */
+ else
+ {
+ WCHAR dir[_MAX_PATH];
+ WCHAR drivespec[4] = {'A', ':', '\\', 0};
+ int dir_len;
+
+ drivespec[0] += drive - 1;
+ if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
+ {
+ SET_THREAD_VAR(errno,MSVCRT_EACCES);
+ return NULL;
+ }
+
+ dir_len = GetFullPathNameW(drivespec,_MAX_PATH,dir,&dummy);
+ if (dir_len >= size || dir_len < 1)
+ {
+ SET_THREAD_VAR(errno,MSVCRT_ERANGE);
+ return NULL; /* buf too small */
+ }
+
+ TRACE(":returning '%s'\n", debugstr_w(dir));
+ if (!buf)
+ return MSVCRT__wcsdup(dir); /* allocate */
+ strcpyW(buf,dir);
+ }
+ return buf;
+}
+
+/*********************************************************************
+ * _getdiskfree (MSVCRT.@)
+ */
+unsigned int __cdecl MSVCRT__getdiskfree(unsigned int disk, MSVCRT_diskfree_t* d)
+{
+ char drivespec[4] = {'@', ':', '\\', 0};
+ DWORD ret[4];
+ unsigned int err;
+
+ if (disk > 26)
+ return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
+
+ drivespec[0] += disk; /* make a drive letter */
+
+ if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
+ {
+ d->cluster_sectors = (unsigned)ret[0];
+ d->sector_bytes = (unsigned)ret[1];
+ d->available = (unsigned)ret[2];
+ d->num_clusters = (unsigned)ret[3];
+ return 0;
+ }
+ err = GetLastError();
+ MSVCRT__set_errno(err);
+ return err;
+}
+
+/*********************************************************************
+ * _mkdir (MSVCRT.@)
+ */
+int __cdecl MSVCRT__mkdir(const char * newdir)
+{
+ if (CreateDirectoryA(newdir,NULL))
+ return 0;
+ MSVCRT__set_errno(GetLastError());
+ return -1;
+}
+
+/*********************************************************************
+ * _wmkdir (MSVCRT.@)
+ */
+int __cdecl MSVCRT__wmkdir(const WCHAR* newdir)
+{
+ if (CreateDirectoryW(newdir,NULL))
+ return 0;
+ MSVCRT__set_errno(GetLastError());
+ return -1;
+}
+
+/*********************************************************************
+ * _rmdir (MSVCRT.@)
+ */
+int __cdecl MSVCRT__rmdir(const char * dir)
+{
+ if (RemoveDirectoryA(dir))
+ return 0;
+ MSVCRT__set_errno(GetLastError());
+ return -1;
+}
+
+/*********************************************************************
+ * _wrmdir (MSVCRT.@)
+ */
+int __cdecl MSVCRT__wrmdir(const WCHAR * dir)
+{
+ if (RemoveDirectoryW(dir))
+ return 0;
+ MSVCRT__set_errno(GetLastError());
+ return -1;
+}
+
+/*********************************************************************
+ * _splitpath (MSVCRT.@)
+ */
+void __cdecl MSVCRT__splitpath(const char* inpath, char * drv, char * dir,
+ char* fname, char * ext )
+{
+ /* Modified PD code from 'snippets' collection. */
+ char ch, *ptr, *p;
+ char pathbuff[MAX_PATH],*path=pathbuff;
+
+ TRACE(":splitting path '%s'\n",path);
+ strcpy(pathbuff, inpath);
+
+ /* convert slashes to backslashes for searching */
+ for (ptr = (char*)path; *ptr; ++ptr)
+ if ('/' == *ptr)
+ *ptr = '\\';
+
+ /* look for drive spec */
+ if ('\0' != (ptr = strchr(path, ':')))
+ {
+ ++ptr;
+ if (drv)
+ {
+ strncpy(drv, path, ptr - path);
+ drv[ptr - path] = '\0';
+ }
+ path = ptr;
+ }
+ else if (drv)
+ *drv = '\0';
+
+ /* find rightmost backslash or leftmost colon */
+ if (NULL == (ptr = strrchr(path, '\\')))
+ ptr = (strchr(path, ':'));
+
+ if (!ptr)
+ {
+ ptr = (char *)path; /* no path */
+ if (dir)
+ *dir = '\0';
+ }
+ else
+ {
+ ++ptr; /* skip the delimiter */
+ if (dir)
+ {
+ ch = *ptr;
+ *ptr = '\0';
+ strcpy(dir, path);
+ *ptr = ch;
+ }
+ }
+
+ if (NULL == (p = strrchr(ptr, '.')))
+ {
+ if (fname)
+ strcpy(fname, ptr);
+ if (ext)
+ *ext = '\0';
+ }
+ else
+ {
+ *p = '\0';
+ if (fname)
+ strcpy(fname, ptr);
+ *p = '.';
+ if (ext)
+ strcpy(ext, p);
+ }
+
+ /* Fix pathological case - Win returns ':' as part of the
+ * directory when no drive letter is given.
+ */
+ if (drv && drv[0] == ':')
+ {
+ *drv = '\0';
+ if (dir)
+ {
+ pathbuff[0] = ':';
+ pathbuff[1] = '\0';
+ strcat(pathbuff,dir);
+ strcpy(dir,pathbuff);
+ }
+ }
+}
+
+/* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
+static void fln_fix(char *path)
+{
+ int dir_flag = 0, root_flag = 0;
+ char *r, *p, *q, *s;
+
+ /* Skip drive */
+ if (NULL == (r = strrchr(path, ':')))
+ r = path;
+ else
+ ++r;
+
+ /* Ignore leading slashes */
+ while ('\\' == *r)
+ if ('\\' == r[1])
+ strcpy(r, &r[1]);
+ else
+ {
+ root_flag = 1;
+ ++r;
+ }
+
+ p = r; /* Change "\\" to "\" */
+ while (NULL != (p = strchr(p, '\\')))
+ if ('\\' == p[1])
+ strcpy(p, &p[1]);
+ else
+ ++p;
+
+ while ('.' == *r) /* Scrunch leading ".\" */
+ {
+ if ('.' == r[1])
+ {
+ /* Ignore leading ".." */
+ for (p = (r += 2); *p && (*p != '\\'); ++p)
+ ;
+ }
+ else
+ {
+ for (p = r + 1 ;*p && (*p != '\\'); ++p)
+ ;
+ }
+ strcpy(r, p + ((*p) ? 1 : 0));
+ }
+
+ while ('\\' == path[strlen(path)-1]) /* Strip last '\\' */
+ {
+ dir_flag = 1;
+ path[strlen(path)-1] = '\0';
+ }
+
+ s = r;
+
+ /* Look for "\." in path */
+
+ while (NULL != (p = strstr(s, "\\.")))
+ {
+ if ('.' == p[2])
+ {
+ /* Execute this section if ".." found */
+ q = p - 1;
+ while (q > r) /* Backup one level */
+ {
+ if (*q == '\\')
+ break;
+ --q;
+ }
+ if (q > r)
+ {
+ strcpy(q, p + 3);
+ s = q;
+ }
+ else if ('.' != *q)
+ {
+ strcpy(q + ((*q == '\\') ? 1 : 0),
+ p + 3 + ((*(p + 3)) ? 1 : 0));
+ s = q;
+ }
+ else s = ++p;
+ }
+ else
+ {
+ /* Execute this section if "." found */
+ q = p + 2;
+ for ( ;*q && (*q != '\\'); ++q)
+ ;
+ strcpy (p, q);
+ }
+ }
+
+ if (root_flag) /* Embedded ".." could have bubbled up to root */
+ {
+ for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
+ ;
+ if (r != p)
+ strcpy(r, p);
+ }
+
+ if (dir_flag)
+ strcat(path, "\\");
+}
+
+/*********************************************************************
+ * _fullpath (MSVCRT.@)
+ */
+LPSTR __cdecl MSVCRT__fullpath(char * absPath, const char* relPath, unsigned int size)
+{
+ char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
+ char res[MAX_PATH];
+ size_t len;
+
+ res[0] = '\0';
+
+ if (!relPath || !*relPath)
+ return MSVCRT__getcwd(absPath, size);
+
+ if (size < 4)
+ {
+ SET_THREAD_VAR(errno,MSVCRT_ERANGE);
+ return NULL;
+ }
+
+ TRACE(":resolving relative path '%s'\n",relPath);
+
+ MSVCRT__splitpath(relPath, drive, dir, file, ext);
+
+ /* Get Directory and drive into 'res' */
+ if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
+ {
+ /* Relative or no directory given */
+ MSVCRT__getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 : 0, res, MAX_PATH);
+ strcat(res,"\\");
+ if (dir[0])
+ strcat(res,dir);
+ if (drive[0])
+ res[0] = drive[0]; /* If given a drive, preserve the letter case */
+ }
+ else
+ {
+ strcpy(res,drive);
+ strcat(res,dir);
+ }
+
+ strcat(res,"\\");
+ strcat(res, file);
+ strcat(res, ext);
+ fln_fix(res);
+
+ len = strlen(res);
+ if (len >= MAX_PATH || len >= (size_t)size)
+ return NULL; /* FIXME: errno? */
+
+ if (!absPath)
+ return MSVCRT__strdup(res);
+ strcpy(absPath,res);
+ return absPath;
+}
+
+/*********************************************************************
+ * _makepath (MSVCRT.@)
+ */
+VOID __cdecl MSVCRT__makepath(char * path, const char * drive,
+ const char *directory, const char * filename,
+ const char * extension )
+{
+ char ch;
+ TRACE("MSVCRT__makepath got %s %s %s %s\n", drive, directory,
+ filename, extension);
+
+ if ( !path )
+ return;
+
+ path[0] = 0;
+ if (drive && drive[0])
+ {
+ path[0] = drive[0];
+ path[1] = ':';
+ path[2] = 0;
+ }
+ if (directory && directory[0])
+ {
+ strcat(path, directory);
+ ch = path[strlen(path)-1];
+ if (ch != '/' && ch != '\\')
+ strcat(path,"\\");
+ }
+ if (filename && filename[0])
+ {
+ strcat(path, filename);
+ if (extension && extension[0])
+ {
+ if ( extension[0] != '.' )
+ strcat(path,".");
+ strcat(path,extension);
+ }
+ }
+
+ TRACE("MSVCRT__makepath returns %s\n",path);
+}
+
+
+/*********************************************************************
+ * _searchenv (MSVCRT.@)
+ */
+void __cdecl MSVCRT__searchenv(const char* file, const char* env, char *buf)
+{
+ char*envVal, *penv;
+ char curPath[MAX_PATH];
+
+ *buf = '\0';
+
+ /* Try CWD first */
+ if (GetFileAttributesA( file ) != 0xFFFFFFFF)
+ {
+ GetFullPathNameA( file, MAX_PATH, buf, NULL );
+ /* Sigh. This error is *always* set, regardless of success */
+ MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
+ return;
+ }
+
+ /* Search given environment variable */
+ envVal = MSVCRT_getenv(env);
+ if (!envVal)
+ {
+ MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
+ return;
+ }
+
+ penv = envVal;
+ TRACE(":searching for %s in paths %s\n", file, envVal);
+
+ do
+ {
+ LPSTR end = penv;
+
+ while(*end && *end != ';') end++; /* Find end of next path */
+ if (penv == end || !*penv)
+ {
+ MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
+ return;
+ }
+ strncpy(curPath, penv, end - penv);
+ if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
+ {
+ curPath[end - penv] = '\\';
+ curPath[end - penv + 1] = '\0';
+ }
+ else
+ curPath[end - penv] = '\0';
+
+ strcat(curPath, file);
+ TRACE("Checking for file %s\n", curPath);
+ if (GetFileAttributesA( curPath ) != 0xFFFFFFFF)
+ {
+ strcpy(buf, curPath);
+ MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
+ return; /* Found */
+ }
+ penv = *end ? end + 1 : end;
+ } while(1);
+}
+