/********************************************************************* * strncpy_s (MSVCRT.@) */ int CDECL strncpy_s(char *dest, MSVCRT_size_t numberOfElements, const char *src, MSVCRT_size_t count) { MSVCRT_size_t i, end; TRACE("(%s %lu %s %lu)\n", dest, numberOfElements, src, count); if(!count) return 0; if(!dest || !src || !numberOfElements) { MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0); *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } if(count!=MSVCRT__TRUNCATE && count<numberOfElements) end = count; else end = numberOfElements-1; for(i=0; i<end && src[i]; i++) dest[i] = src[i]; if(!src[i] || end==count || count==MSVCRT__TRUNCATE) { dest[i] = '\0'; return 0; } MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0); dest[0] = '\0'; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; }
/********************************************************************* * _get_tzname (MSVCRT.@) */ int CDECL MSVCRT__get_tzname(MSVCRT_size_t *ret, char *buf, MSVCRT_size_t bufsize, int index) { char *timezone; switch(index) { case 0: timezone = tzname_std; break; case 1: timezone = tzname_dst; break; default: *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } if(!ret || (!buf && bufsize > 0) || (buf && !bufsize)) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } *ret = strlen(timezone)+1; if(!buf && !bufsize) return 0; strcpy(buf, timezone); return 0; }
/********************************************************************* * _wgetdcwd (MSVCRT.@) * * Unicode version of _wgetdcwd. */ MSVCRT_wchar_t* CDECL MSVCRT__wgetdcwd(int drive, MSVCRT_wchar_t * buf, int size) { static MSVCRT_wchar_t* 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 { MSVCRT_wchar_t dir[MAX_PATH]; MSVCRT_wchar_t drivespec[4] = {'A', ':', '\\', 0}; int dir_len; drivespec[0] += drive - 1; if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE) { *MSVCRT__errno() = MSVCRT_EACCES; return NULL; } dir_len = GetFullPathNameW(drivespec,MAX_PATH,dir,&dummy); if (dir_len >= size || dir_len < 1) { *MSVCRT__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; }
/********************************************************************* * _localtime64_s (MSVCRT.@) */ int CDECL _localtime64_s(struct MSVCRT_tm *time, const MSVCRT___time64_t *secs) { struct tm *tm; time_t seconds; if (!time || !secs || *secs < 0 || *secs > _MAX__TIME64_T) { if (time) write_invalid_msvcrt_tm(time); *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } seconds = *secs; _mlock(_TIME_LOCK); if (!(tm = localtime(&seconds))) { _munlock(_TIME_LOCK); *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } unix_tm_to_msvcrt(time, tm); _munlock(_TIME_LOCK); return 0; }
/********************************************************************* * _strlwr_s_l (MSVCRT.@) */ int CDECL _strlwr_s_l(char *str, MSVCRT_size_t len, MSVCRT__locale_t locale) { char *ptr = str; if (!str || !len) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } while (len && *ptr) { len--; ptr++; } if (!len) { str[0] = '\0'; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } while (*str) { *str = MSVCRT__tolower_l((unsigned char)*str, locale); str++; } return 0; }
/****************************************************************** * _wcsupr_s_l (MSVCRT.@) */ int CDECL MSVCRT__wcsupr_s_l( MSVCRT_wchar_t* str, MSVCRT_size_t n, MSVCRT__locale_t locale ) { MSVCRT_wchar_t* ptr = str; if (!str || !n) { if (str) *str = '\0'; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } while (n--) { if (!*ptr) return 0; /* FIXME: add locale support */ *ptr = toupperW(*ptr); ptr++; } /* MSDN claims that the function should return and set errno to * ERANGE, which doesn't seem to be true based on the tests. */ *str = '\0'; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; }
/********************************************************************* * memmove_s (MSVCRT.@) */ int CDECL memmove_s(void *dest, MSVCRT_size_t numberOfElements, const void *src, MSVCRT_size_t count) { TRACE("(%p %lu %p %lu)\n", dest, numberOfElements, src, count); if(!count) return 0; if(!dest || !src) { if(dest) memset(dest, 0, numberOfElements); *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } if(count > numberOfElements) { memset(dest, 0, numberOfElements); *MSVCRT__errno() = MSVCRT_ERANGE; return MSVCRT_ERANGE; } memmove(dest, src, count); return 0; }
/********************************************************************** * __wcserror_s (MSVCRT.@) */ int CDECL MSVCRT___wcserror_s(MSVCRT_wchar_t* buffer, MSVCRT_size_t nc, const MSVCRT_wchar_t* str) { int err; static const WCHAR colonW[] = {':', ' ', '\0'}; static const WCHAR nlW[] = {'\n', '\0'}; size_t len; err = *MSVCRT__errno(); if (err < 0 || err > MSVCRT__sys_nerr) err = MSVCRT__sys_nerr; len = MultiByteToWideChar(CP_ACP, 0, MSVCRT__sys_errlist[err], -1, NULL, 0) + 1 /* \n */; if (str && *str) len += lstrlenW(str) + 2 /* ': ' */; if (len > nc) { MSVCRT_INVALID_PMT("buffer[nc] is too small", MSVCRT_ERANGE); return MSVCRT_ERANGE; } if (str && *str) { lstrcpyW(buffer, str); lstrcatW(buffer, colonW); } else buffer[0] = '\0'; len = lstrlenW(buffer); MultiByteToWideChar(CP_ACP, 0, MSVCRT__sys_errlist[err], -1, buffer + len, 256 - len); lstrcatW(buffer, nlW); return 0; }
/********************************************************************* * _cwait (MSVCRT.@) */ MSVCRT_intptr_t CDECL _cwait(int *status, MSVCRT_intptr_t pid, int action) { HANDLE hPid = (HANDLE)pid; int doserrno; action = action; /* Remove warning */ if (!WaitForSingleObject(hPid, INFINITE)) { if (status) { DWORD stat; GetExitCodeProcess(hPid, &stat); *status = (int)stat; } return pid; } doserrno = GetLastError(); if (doserrno == ERROR_INVALID_HANDLE) { *MSVCRT__errno() = MSVCRT_ECHILD; *MSVCRT___doserrno() = doserrno; } else msvcrt_set_errno(doserrno); return status ? *status = -1 : -1; }
/********************************************************************* * malloc (MSVCRT.@) */ void* CDECL MSVCRT_malloc(MSVCRT_size_t size) { void *ret = msvcrt_heap_alloc(0, size); if (!ret) *MSVCRT__errno() = MSVCRT_ENOMEM; return ret; }
/********************************************************************* * malloc (MSVCRT.@) */ void* CDECL MSVCRT_malloc(MSVCRT_size_t size) { void *ret = HeapAlloc(GetProcessHeap(),0,size); if (!ret) *MSVCRT__errno() = MSVCRT_ENOMEM; return ret; }
/* INTERNAL: Set the crt and dos errno's from the OS error given. */ void msvcrt_set_errno(int err) { int *errno = MSVCRT__errno(); MSVCRT_ulong *doserrno = MSVCRT___doserrno(); *doserrno = err; switch(err) { #define ERR_CASE(oserr) case oserr: #define ERR_MAPS(oserr, crterr) case oserr: *errno = crterr; break ERR_CASE(ERROR_ACCESS_DENIED) ERR_CASE(ERROR_NETWORK_ACCESS_DENIED) ERR_CASE(ERROR_CANNOT_MAKE) ERR_CASE(ERROR_SEEK_ON_DEVICE) ERR_CASE(ERROR_LOCK_FAILED) ERR_CASE(ERROR_FAIL_I24) ERR_CASE(ERROR_CURRENT_DIRECTORY) ERR_CASE(ERROR_DRIVE_LOCKED) ERR_CASE(ERROR_NOT_LOCKED) ERR_CASE(ERROR_INVALID_ACCESS) ERR_CASE(ERROR_SHARING_VIOLATION) ERR_MAPS(ERROR_LOCK_VIOLATION, MSVCRT_EACCES); ERR_CASE(ERROR_FILE_NOT_FOUND) ERR_CASE(ERROR_NO_MORE_FILES) ERR_CASE(ERROR_BAD_PATHNAME) ERR_CASE(ERROR_BAD_NETPATH) ERR_CASE(ERROR_INVALID_DRIVE) ERR_CASE(ERROR_BAD_NET_NAME) ERR_CASE(ERROR_FILENAME_EXCED_RANGE) ERR_MAPS(ERROR_PATH_NOT_FOUND, MSVCRT_ENOENT); ERR_MAPS(ERROR_IO_DEVICE, MSVCRT_EIO); ERR_MAPS(ERROR_BAD_FORMAT, MSVCRT_ENOEXEC); ERR_MAPS(ERROR_INVALID_HANDLE, MSVCRT_EBADF); ERR_CASE(ERROR_OUTOFMEMORY) ERR_CASE(ERROR_INVALID_BLOCK) ERR_CASE(ERROR_NOT_ENOUGH_QUOTA); ERR_MAPS(ERROR_ARENA_TRASHED, MSVCRT_ENOMEM); ERR_MAPS(ERROR_BUSY, MSVCRT_EBUSY); ERR_CASE(ERROR_ALREADY_EXISTS) ERR_MAPS(ERROR_FILE_EXISTS, MSVCRT_EEXIST); ERR_MAPS(ERROR_BAD_DEVICE, MSVCRT_ENODEV); ERR_MAPS(ERROR_TOO_MANY_OPEN_FILES, MSVCRT_EMFILE); ERR_MAPS(ERROR_DISK_FULL, MSVCRT_ENOSPC); ERR_MAPS(ERROR_BROKEN_PIPE, MSVCRT_EPIPE); ERR_MAPS(ERROR_POSSIBLE_DEADLOCK, MSVCRT_EDEADLK); ERR_MAPS(ERROR_DIR_NOT_EMPTY, MSVCRT_ENOTEMPTY); ERR_MAPS(ERROR_BAD_ENVIRONMENT, MSVCRT_E2BIG); ERR_CASE(ERROR_WAIT_NO_CHILDREN) ERR_MAPS(ERROR_CHILD_NOT_COMPLETE, MSVCRT_ECHILD); ERR_CASE(ERROR_NO_PROC_SLOTS) ERR_CASE(ERROR_MAX_THRDS_REACHED) ERR_MAPS(ERROR_NESTING_NOT_ALLOWED, MSVCRT_EAGAIN); default: /* Remaining cases map to EINVAL */ /* FIXME: may be missing some errors above */ *errno = MSVCRT_EINVAL; } }
/********************************************************************* * _get_errno (MSVCRT.@) */ int CDECL _get_errno(int *pValue) { if (!pValue) return MSVCRT_EINVAL; *pValue = *MSVCRT__errno(); return 0; }
/********************************************************************* * _wstrtime_s (MSVCRT.@) */ int CDECL _wstrtime_s(MSVCRT_wchar_t* time, MSVCRT_size_t size) { if(time && size) time[0] = '\0'; if(!time) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } if(size < 9) { *MSVCRT__errno() = MSVCRT_ERANGE; return MSVCRT_ERANGE; } _wstrtime(time); return 0; }
/********************************************************************** * _wstrdate_s (MSVCRT.@) */ int CDECL _wstrdate_s(MSVCRT_wchar_t* date, MSVCRT_size_t size) { if(date && size) date[0] = '\0'; if(!date) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } if(size < 9) { *MSVCRT__errno() = MSVCRT_ERANGE; return MSVCRT_ERANGE; } _wstrdate(date); return 0; }
/********************************************************************* * rand_s (MSVCRT.@) */ int CDECL MSVCRT_rand_s(unsigned int *pval) { if (!pval || !RtlGenRandom(pval, sizeof(*pval))) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } return 0; }
/********************************************************************* * _ctime32_s (MSVCRT.@) */ int CDECL MSVCRT__ctime32_s(char *res, MSVCRT_size_t len, const MSVCRT___time32_t *time) { struct MSVCRT_tm *t; if( !MSVCRT_CHECK_PMT( res != NULL ) || !MSVCRT_CHECK_PMT( len >= 26 ) ) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } res[0] = '\0'; if( !MSVCRT_CHECK_PMT( time != NULL ) || !MSVCRT_CHECK_PMT( *time > 0 ) ) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } t = MSVCRT__localtime32( time ); strcpy( res, MSVCRT_asctime( t ) ); return 0; }
/********************************************************************* * MSVCRT_wcsrtombs_s_l (INTERNAL) */ static int MSVCRT_wcsrtombs_s_l(MSVCRT_size_t *ret, char *mbstr, MSVCRT_size_t size, const MSVCRT_wchar_t **wcstr, MSVCRT_size_t count, MSVCRT__locale_t locale) { MSVCRT_size_t conv; if(!mbstr && !size && wcstr) { conv = MSVCRT_wcsrtombs_l(NULL, wcstr, 0, locale); if(ret) *ret = conv+1; return 0; } if (!MSVCRT_CHECK_PMT(wcstr != NULL) || !MSVCRT_CHECK_PMT(*wcstr != NULL) || !MSVCRT_CHECK_PMT(mbstr != NULL)) { if(mbstr && size) mbstr[0] = '\0'; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } if(count==MSVCRT__TRUNCATE || size<count) conv = size; else conv = count; conv = MSVCRT_wcsrtombs_l(mbstr, wcstr, conv, locale); if(conv<size) mbstr[conv++] = '\0'; else if(conv==size && (count==MSVCRT__TRUNCATE || mbstr[conv-1]=='\0')) mbstr[conv-1] = '\0'; else { MSVCRT_INVALID_PMT("mbstr[size] is too small"); if(size) mbstr[0] = '\0'; *MSVCRT__errno() = MSVCRT_ERANGE; return MSVCRT_ERANGE; } if(ret) *ret = conv; return 0; }
/********************************************************************* * _ftime32_s (MSVCRT.@) */ int CDECL MSVCRT__ftime32_s(struct MSVCRT___timeb32 *buf) { if( !MSVCRT_CHECK_PMT( buf != NULL ) ) { *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } MSVCRT__ftime32(buf); return 0; }
/********************************************************************* * calloc (MSVCRT.@) */ void* CDECL DECLSPEC_HOTPATCH MSVCRT_calloc(MSVCRT_size_t count, MSVCRT_size_t size) { MSVCRT_size_t bytes = count*size; if (size && bytes / size != count) { *MSVCRT__errno() = MSVCRT_ENOMEM; return NULL; } return msvcrt_heap_alloc(HEAP_ZERO_MEMORY, bytes); }
/********************************************************************* * perror (MSVCRT.@) */ void CDECL MSVCRT_perror(const char* str) { int err = *MSVCRT__errno(); if (err < 0 || err > MSVCRT__sys_nerr) err = MSVCRT__sys_nerr; if (str && *str) { MSVCRT__write( 2, str, strlen(str) ); MSVCRT__write( 2, ": ", 2 ); } MSVCRT__write( 2, MSVCRT__sys_errlist[err], strlen(MSVCRT__sys_errlist[err]) ); MSVCRT__write( 2, "\n", 1 ); }
/********************************************************************* * _findnexti64 (MSVCRT.@) * * 64-bit version of _findnext. */ int CDECL MSVCRT__findnexti64(MSVCRT_intptr_t hand, struct MSVCRT__finddatai64_t * ft) { WIN32_FIND_DATAA find_data; if (!FindNextFileA((HANDLE)hand, &find_data)) { *MSVCRT__errno() = MSVCRT_ENOENT; return -1; } msvcrt_fttofdi64(&find_data,ft); return 0; }
/********************************************************************* * _wfindnexti64 (MSVCRT.@) * * Unicode version of _findnexti64. */ int CDECL MSVCRT__wfindnexti64(long hand, struct MSVCRT__wfinddatai64_t * ft) { WIN32_FIND_DATAW find_data; if (!FindNextFileW((HANDLE)hand, &find_data)) { *MSVCRT__errno() = MSVCRT_ENOENT; return -1; } msvcrt_wfttofdi64(&find_data,ft); return 0; }
/********************************************************************* * _aligned_offset_malloc (MSVCRT.@) */ void * CDECL _aligned_offset_malloc(MSVCRT_size_t size, MSVCRT_size_t alignment, MSVCRT_size_t offset) { void *memblock, *temp, **saved; TRACE("(%lu, %lu, %lu)\n", size, alignment, offset); /* alignment must be a power of 2 */ if ((alignment & (alignment - 1)) != 0) { *MSVCRT__errno() = MSVCRT_EINVAL; return NULL; } /* offset must be less than size */ if (offset >= size) { *MSVCRT__errno() = MSVCRT_EINVAL; return NULL; } /* don't align to less than void pointer size */ if (alignment < sizeof(void *)) alignment = sizeof(void *); /* allocate enough space for void pointer and alignment */ temp = MSVCRT_malloc(size + alignment + sizeof(void *)); if (!temp) return NULL; /* adjust pointer for proper alignment and offset */ memblock = ALIGN_PTR(temp, alignment, offset); /* Save the real allocation address below returned address */ /* so it can be found later to free. */ saved = SAVED_PTR(memblock); *saved = temp; return memblock; }
/********************************************************************* * _wfindnext64i32 (MSVCRT.@) * * Unicode version of _findnext64i32. */ int CDECL MSVCRT__wfindnext64i32(MSVCRT_intptr_t hand, struct MSVCRT__wfinddata64i32_t * ft) { WIN32_FIND_DATAW find_data; if (!FindNextFileW((HANDLE)hand, &find_data)) { *MSVCRT__errno() = MSVCRT_ENOENT; return -1; } msvcrt_wfttofd64i32(&find_data,ft); return 0; }
static MSVCRT_intptr_t msvcrt_spawn(int flags, const MSVCRT_wchar_t* exe, MSVCRT_wchar_t* cmdline, MSVCRT_wchar_t* env, int use_path) { STARTUPINFOW si; PROCESS_INFORMATION pi; MSVCRT_wchar_t fullname[MAX_PATH]; TRACE("%x %s %s %s %d\n", flags, debugstr_w(exe), debugstr_w(cmdline), debugstr_w(env), use_path); if ((unsigned)flags > MSVCRT__P_DETACH) { *MSVCRT__errno() = MSVCRT_EINVAL; return -1; } msvcrt_search_executable(exe, fullname, use_path); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); msvcrt_create_io_inherit_block(&si.cbReserved2, &si.lpReserved2); if (!CreateProcessW(fullname, cmdline, NULL, NULL, TRUE, flags == MSVCRT__P_DETACH ? DETACHED_PROCESS : 0, env, NULL, &si, &pi)) { msvcrt_set_errno(GetLastError()); MSVCRT_free(si.lpReserved2); return -1; } MSVCRT_free(si.lpReserved2); switch(flags) { case MSVCRT__P_WAIT: WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess,&pi.dwProcessId); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return pi.dwProcessId; case MSVCRT__P_DETACH: CloseHandle(pi.hProcess); pi.hProcess = 0; /* fall through */ case MSVCRT__P_NOWAIT: case MSVCRT__P_NOWAITO: CloseHandle(pi.hThread); return (MSVCRT_intptr_t)pi.hProcess; case MSVCRT__P_OVERLAY: MSVCRT__exit(0); } return -1; /* can't reach here */ }
/********************************************************************* * _chdrive (MSVCRT.@) * * Change the current drive. * * PARAMS * newdrive [I] Drive number to change to (1 = 'A', 2 = 'B', ...) * * RETURNS * Success: 0. The current drive is set to newdrive. * Failure: -1. errno indicates the error. * * NOTES * See SetCurrentDirectoryA. */ int CDECL MSVCRT__chdrive(int newdrive) { WCHAR buffer[] = {'A', ':', 0}; buffer[0] += newdrive - 1; if (!SetCurrentDirectoryW( buffer )) { msvcrt_set_errno(GetLastError()); if (newdrive <= 0) *MSVCRT__errno() = MSVCRT_EACCES; return -1; } return 0; }
/********************************************************************* * _beginthread (MSVCRT.@) */ MSVCRT_uintptr_t CDECL _beginthread( MSVCRT__beginthread_start_routine_t start_address, /* [in] Start address of routine that begins execution of new thread */ unsigned int stack_size, /* [in] Stack size for new thread or 0 */ void *arglist) /* [in] Argument list to be passed to new thread or NULL */ { _beginthread_trampoline_t* trampoline; HANDLE thread; TRACE("(%p, %d, %p)\n", start_address, stack_size, arglist); trampoline = MSVCRT_malloc(sizeof(*trampoline)); if(!trampoline) { *MSVCRT__errno() = MSVCRT_EAGAIN; return -1; } thread = CreateThread(NULL, stack_size, _beginthread_trampoline, trampoline, CREATE_SUSPENDED, NULL); if(!thread) { MSVCRT_free(trampoline); *MSVCRT__errno() = MSVCRT_EAGAIN; return -1; } trampoline->thread = thread; trampoline->start_address = start_address; trampoline->arglist = arglist; if(ResumeThread(thread) == -1) { MSVCRT_free(trampoline); *MSVCRT__errno() = MSVCRT_EAGAIN; return -1; } return (MSVCRT_uintptr_t)thread; }
/****************************************************************** * _wcslwr_s (MSVCRT.@) */ int CDECL MSVCRT__wcslwr_s( MSVCRT_wchar_t* str, MSVCRT_size_t n ) { MSVCRT_wchar_t* ptr = str; if (!str || !n) { if (str) *str = '\0'; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } while (n--) { if (!*ptr) return 0; *ptr = tolowerW(*ptr); ptr++; } /* MSDN claims that the function should return and set errno to * ERANGE, which doesn't seem to be true based on the tests. */ *str = '\0'; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; }
/********************************************************************* * _getdcwd (MSVCRT.@) * * Get the current working directory on a given disk. * * PARAMS * drive [I] Drive letter to get the current working directory from. * buf [O] Destination for the current working directory. * size [I] Length of drive in characters. * * RETURNS * Success: If drive is NULL, returns an allocated string containing the path. * Otherwise populates drive with the path and returns it. * Failure: NULL. errno indicates the error. */ 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[] = {'A', ':', 0}; int dir_len; drivespec[0] += drive - 1; if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE) { *MSVCRT__errno() = MSVCRT_EACCES; return NULL; } dir_len = GetFullPathNameA(drivespec,MAX_PATH,dir,&dummy); if (dir_len >= size || dir_len < 1) { *MSVCRT__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; }