/* * The C RunTime DLL * * Implements C run-time functionality as known from UNIX. * * Copyright 1996,1998 Marcus Meissner * Copyright 1996 Jukka Iivonen * Copyright 1997,2000 Uwe Bonnes * Copyright 2000 Jon Griffiths */ /* Unresolved issues Uwe Bonnes 970904: - tested with ftp://ftp.remcomp.com/pub/remcomp/lcc-win32.zip, a C-Compiler for Win32, based on lcc, from Jacob Navia UB 000416: - probably not thread safe */ /* NOTE: This file also implements the wcs* functions. They _ARE_ in * the newer Linux libcs, but use 4 byte wide characters, so are unusable, * since we need 2 byte wide characters. - Marcus Meissner, 981031 */ #include "config.h" #include "crtdll.h" #ifdef HAVE_IEEEFP_H #include #endif #include #define __USE_ISOC9X 1 #define __USE_ISOC99 1 #include #include #include #include "ntddk.h" #include "wingdi.h" #include "winuser.h" #ifndef HAVE_FINITE #ifndef finite /* Could be macro */ #ifdef isfinite #define finite(x) isfinite(x) #else #define finite(x) (!isnan(x)) /* At least catch some cases */ #endif #endif #endif #ifndef signbit #define signbit(x) 0 #endif DEFAULT_DEBUG_CHANNEL(crtdll); double CRTDLL_HUGE_dll; /* CRTDLL.20 */ UINT CRTDLL_argc_dll; /* CRTDLL.23 */ LPSTR *CRTDLL_argv_dll; /* CRTDLL.24 */ LPSTR CRTDLL_acmdln_dll; /* CRTDLL.38 */ UINT CRTDLL_basemajor_dll; /* CRTDLL.42 */ UINT CRTDLL_baseminor_dll; /* CRTDLL.43 */ UINT CRTDLL_baseversion_dll; /* CRTDLL.44 */ UINT CRTDLL_commode_dll; /* CRTDLL.59 */ LPSTR CRTDLL_environ_dll; /* CRTDLL.75 */ UINT CRTDLL_fmode_dll; /* CRTDLL.104 */ UINT CRTDLL_osmajor_dll; /* CRTDLL.241 */ UINT CRTDLL_osminor_dll; /* CRTDLL.242 */ UINT CRTDLL_osmode_dll; /* CRTDLL.243 */ UINT CRTDLL_osver_dll; /* CRTDLL.244 */ UINT CRTDLL_osversion_dll; /* CRTDLL.245 */ LONG CRTDLL_timezone_dll = 0; /* CRTDLL.304 */ UINT CRTDLL_winmajor_dll; /* CRTDLL.329 */ UINT CRTDLL_winminor_dll; /* CRTDLL.330 */ UINT CRTDLL_winver_dll; /* CRTDLL.331 */ INT CRTDLL_doserrno = 0; INT CRTDLL_errno = 0; INT CRTDLL__mb_cur_max_dll = 1; const INT CRTDLL__sys_nerr = 43; /* ASCII char classification flags - binary compatible */ #define _C_ CRTDLL_CONTROL #define _S_ CRTDLL_SPACE #define _P_ CRTDLL_PUNCT #define _D_ CRTDLL_DIGIT #define _H_ CRTDLL_HEX #define _U_ CRTDLL_UPPER #define _L_ CRTDLL_LOWER WORD CRTDLL_ctype [257] = { 0, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_C_, _S_|_C_, _S_|_C_, _S_|_C_, _S_|_C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|CRTDLL_BLANK, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _P_, _P_, _P_, _P_, _P_, _P_, _L_|_H_, _L_|_H_, _L_|_H_, _L_|_H_, _L_|_H_, _L_|_H_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _P_, _P_, _P_, _P_, _C_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Internal: Current ctype table for locale */ WORD __CRTDLL_current_ctype[257]; /* pctype is used by macros in the Win32 headers. It must point * To a table of flags exactly like ctype. To allow locale * changes to affect ctypes (i.e. isleadbyte), we use a second table * and update its flags whenever the current locale changes. */ WORD* CRTDLL_pctype_dll = __CRTDLL_current_ctype + 1; /********************************************************************* * CRTDLL_MainInit (CRTDLL.init) */ BOOL WINAPI CRTDLL_Init(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { TRACE("(0x%08x,%ld,%p)\n",hinstDLL,fdwReason,lpvReserved); if (fdwReason == DLL_PROCESS_ATTACH) { __CRTDLL__init_io(); __CRTDLL_init_console(); CRTDLL_setlocale( CRTDLL_LC_ALL, "C" ); CRTDLL_HUGE_dll = HUGE_VAL; } else if (fdwReason == DLL_PROCESS_DETACH) { CRTDLL__fcloseall(); __CRTDLL_free_console(); } return TRUE; } /* INTERNAL: Set the crt and dos errno's from the OS error given. */ void __CRTDLL__set_errno(ULONG err) { /* FIXME: not MT safe */ CRTDLL_doserrno = err; switch(err) { #define ERR_CASE(oserr) case oserr: #define ERR_MAPS(oserr,crterr) case oserr:CRTDLL_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_MAPS(ERROR_LOCK_VIOLATION, 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, ENOENT); ERR_MAPS(ERROR_IO_DEVICE, EIO); ERR_MAPS(ERROR_BAD_FORMAT, ENOEXEC); ERR_MAPS(ERROR_INVALID_HANDLE, EBADF); ERR_CASE(ERROR_OUTOFMEMORY) ERR_CASE(ERROR_INVALID_BLOCK) ERR_CASE(ERROR_NOT_ENOUGH_QUOTA); ERR_MAPS(ERROR_ARENA_TRASHED, ENOMEM); ERR_MAPS(ERROR_BUSY, EBUSY); ERR_CASE(ERROR_ALREADY_EXISTS) ERR_MAPS(ERROR_FILE_EXISTS, EEXIST); ERR_MAPS(ERROR_BAD_DEVICE, ENODEV); ERR_MAPS(ERROR_TOO_MANY_OPEN_FILES, EMFILE); ERR_MAPS(ERROR_DISK_FULL, ENOSPC); ERR_MAPS(ERROR_BROKEN_PIPE, EPIPE); ERR_MAPS(ERROR_POSSIBLE_DEADLOCK, EDEADLK); ERR_MAPS(ERROR_DIR_NOT_EMPTY, ENOTEMPTY); ERR_MAPS(ERROR_BAD_ENVIRONMENT, E2BIG); ERR_CASE(ERROR_WAIT_NO_CHILDREN) ERR_MAPS(ERROR_CHILD_NOT_COMPLETE, ECHILD); ERR_CASE(ERROR_NO_PROC_SLOTS) ERR_CASE(ERROR_MAX_THRDS_REACHED) ERR_MAPS(ERROR_NESTING_NOT_ALLOWED, EAGAIN); default: /* Remaining cases map to EINVAL */ /* FIXME: may be missing some errors above */ CRTDLL_errno = EINVAL; } } #if defined(__GNUC__) && defined(__i386__) #define FPU_DOUBLE(var) double var; \ __asm__ __volatile__( "fstpl %0;fwait" : "=m" (var) : ) #define FPU_DOUBLES(var1,var2) double var1,var2; \ __asm__ __volatile__( "fstpl %0;fwait" : "=m" (var2) : ); \ __asm__ __volatile__( "fstpl %0;fwait" : "=m" (var1) : ) #else #define FPU_DOUBLE(var) double var = sqrt(-1); \ FIXME(":not implemented\n"); #define FPU_DOUBLES(var1,var2) double var1,var2; \ var1=var2=sqrt(-1); FIXME(":not implemented\n") #endif /********************************************************************* * _CIacos (CRTDLL.004) */ double __cdecl CRTDLL__CIacos(void) { FPU_DOUBLE(x); if (x < -1.0 || x > 1.0 || !finite(x)) CRTDLL_errno = EDOM; return acos(x); } /********************************************************************* * _CIasin (CRTDLL.005) */ double __cdecl CRTDLL__CIasin(void) { FPU_DOUBLE(x); if (x < -1.0 || x > 1.0 || !finite(x)) CRTDLL_errno = EDOM; return asin(x); } /********************************************************************* * _CIatan (CRTDLL.006) */ double __cdecl CRTDLL__CIatan(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return atan(x); } /********************************************************************* * _CIatan2 (CRTDLL.007) */ double __cdecl CRTDLL__CIatan2(void) { FPU_DOUBLES(x,y); if (!finite(x)) CRTDLL_errno = EDOM; return atan2(x,y); } /********************************************************************* * _CIcos (CRTDLL.008) */ double __cdecl CRTDLL__CIcos(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return cos(x); } /********************************************************************* * _CIcosh (CRTDLL.009) */ double __cdecl CRTDLL__CIcosh(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return cosh(x); } /********************************************************************* * _CIexp (CRTDLL.010) */ double __cdecl CRTDLL__CIexp(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return exp(x); } /********************************************************************* * _CIfmod (CRTDLL.011) */ double __cdecl CRTDLL__CIfmod(void) { FPU_DOUBLES(x,y); if (!finite(x) || !finite(y)) CRTDLL_errno = EDOM; return fmod(x,y); } /********************************************************************* * _CIlog (CRTDLL.012) */ double __cdecl CRTDLL__CIlog(void) { FPU_DOUBLE(x); if (x < 0.0 || !finite(x)) CRTDLL_errno = EDOM; if (x == 0.0) CRTDLL_errno = ERANGE; return log(x); } /********************************************************************* * _CIlog10 (CRTDLL.013) */ double __cdecl CRTDLL__CIlog10(void) { FPU_DOUBLE(x); if (x < 0.0 || !finite(x)) CRTDLL_errno = EDOM; if (x == 0.0) CRTDLL_errno = ERANGE; return log10(x); } /********************************************************************* * _CIpow (CRTDLL.014) */ double __cdecl CRTDLL__CIpow(void) { double z; FPU_DOUBLES(x,y); /* FIXME: If x < 0 and y is not integral, set EDOM */ z = pow(x,y); if (!finite(z)) CRTDLL_errno = EDOM; return z; } /********************************************************************* * _CIsin (CRTDLL.015) */ double __cdecl CRTDLL__CIsin(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return sin(x); } /********************************************************************* * _CIsinh (CRTDLL.016) */ double __cdecl CRTDLL__CIsinh(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return sinh(x); } /********************************************************************* * _CIsqrt (CRTDLL.017) */ double __cdecl CRTDLL__CIsqrt(void) { FPU_DOUBLE(x); if (x < 0.0 || !finite(x)) CRTDLL_errno = EDOM; return sqrt(x); } /********************************************************************* * _CItan (CRTDLL.018) */ double __cdecl CRTDLL__CItan(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return tan(x); } /********************************************************************* * _CItanh (CRTDLL.019) */ double __cdecl CRTDLL__CItanh(void) { FPU_DOUBLE(x); if (!finite(x)) CRTDLL_errno = EDOM; return tanh(x); } /********************************************************************* * _GetMainArgs (CRTDLL.022) */ LPSTR * __cdecl CRTDLL__GetMainArgs(LPDWORD argc,LPSTR **argv, LPSTR *environ,DWORD flag) { char *cmdline; char **xargv; int xargc,end,last_arg,afterlastspace; DWORD version; TRACE("(%p,%p,%p,%ld).\n", argc,argv,environ,flag ); if (CRTDLL_acmdln_dll != NULL) HeapFree(GetProcessHeap(), 0, CRTDLL_acmdln_dll); CRTDLL_acmdln_dll = cmdline = CRTDLL__strdup( GetCommandLineA() ); TRACE("got '%s'\n", cmdline); version = GetVersion(); CRTDLL_osver_dll = version >> 16; CRTDLL_winminor_dll = version & 0xFF; CRTDLL_winmajor_dll = (version>>8) & 0xFF; CRTDLL_baseversion_dll = version >> 16; CRTDLL_winver_dll = ((version >> 8) & 0xFF) + ((version & 0xFF) << 8); CRTDLL_baseminor_dll = (version >> 16) & 0xFF; CRTDLL_basemajor_dll = (version >> 24) & 0xFF; CRTDLL_osversion_dll = version & 0xFFFF; CRTDLL_osminor_dll = version & 0xFF; CRTDLL_osmajor_dll = (version>>8) & 0xFF; /* missing threading init */ end=0;last_arg=0;xargv=NULL;xargc=0;afterlastspace=0; while (1) { if ((cmdline[end]==' ') || (cmdline[end]=='\0')) { if (cmdline[end]=='\0') last_arg=1; else cmdline[end]='\0'; /* alloc xargc + NULL entry */ xargv=(char**)HeapReAlloc( GetProcessHeap(), 0, xargv, sizeof(char*)*(xargc+1)); if (strlen(cmdline+afterlastspace)) { xargv[xargc] = CRTDLL__strdup(cmdline+afterlastspace); xargc++; if (!last_arg) /* need to seek to the next arg ? */ { end++; while (cmdline[end]==' ') end++; } afterlastspace=end; } else { xargv[xargc] = NULL; /* the last entry is NULL */ break; } } else end++; } CRTDLL_argc_dll = xargc; *argc = xargc; CRTDLL_argv_dll = xargv; *argv = xargv; TRACE("found %d arguments\n", CRTDLL_argc_dll); CRTDLL_environ_dll = *environ = GetEnvironmentStringsA(); return environ; } /********************************************************************* * _clearfp (CRTDLL.056) * * Clear and return the previous FP status. */ UINT __cdecl CRTDLL__clearfp( VOID ) { UINT retVal = CRTDLL__statusfp(); #if defined(__GNUC__) && defined(__i386__) __asm__ __volatile__( "fnclex" ); #else FIXME(":Not Implemented!\n"); #endif return retVal; } /********************************************************************* * _fpclass (CRTDLL.105) * * Return the FP classification of d. */ INT __cdecl CRTDLL__fpclass(double d) { #if defined(HAVE_FPCLASS) || defined(fpclass) switch (fpclass( d )) { case FP_SNAN: return _FPCLASS_SNAN; case FP_QNAN: return _FPCLASS_QNAN; case FP_NINF: return _FPCLASS_NINF; case FP_PINF: return _FPCLASS_PINF; case FP_NDENORM: return _FPCLASS_ND; case FP_PDENORM: return _FPCLASS_PD; case FP_NZERO: return _FPCLASS_NZ; case FP_PZERO: return _FPCLASS_PZ; case FP_NNORM: return _FPCLASS_NN; } return _FPCLASS_PN; #elif defined (fpclassify) switch (fpclassify( d )) { case FP_NAN: return _FPCLASS_QNAN; case FP_INFINITE: return signbit(d) ? _FPCLASS_NINF : _FPCLASS_PINF; case FP_SUBNORMAL: return signbit(d) ?_FPCLASS_ND : _FPCLASS_PD; case FP_ZERO: return signbit(d) ? _FPCLASS_NZ : _FPCLASS_PZ; } return signbit(d) ? _FPCLASS_NN : _FPCLASS_PN; #else if (!finite(d)) return _FPCLASS_QNAN; return d == 0.0 ? _FPCLASS_PZ : (d < 0 ? _FPCLASS_NN : _FPCLASS_PN); #endif } /********************************************************************* * _initterm (CRTDLL.135) */ DWORD __cdecl CRTDLL__initterm(_INITTERMFUN *start,_INITTERMFUN *end) { _INITTERMFUN *current; TRACE("(%p,%p)\n",start,end); current=start; while (current> (32-shift)); } /********************************************************************* * _logb (CRTDLL.174) */ double __cdecl CRTDLL__logb(double x) { if (!finite(x)) CRTDLL_errno = EDOM; return logb(x); } /********************************************************************* * _lrotl (CRTDLL.175) */ DWORD __cdecl CRTDLL__lrotl(DWORD x,INT shift) { shift &= 31; return (x << shift) | (x >> (32-shift)); } /********************************************************************* * _lrotr (CRTDLL.176) */ DWORD __cdecl CRTDLL__lrotr(DWORD x,INT shift) { shift &= 0x1f; return (x >> shift) | (x << (32-shift)); } /********************************************************************* * _rotr (CRTDLL.258) */ DWORD __cdecl CRTDLL__rotr(UINT x,INT shift) { shift &= 0x1f; return (x >> shift) | (x << (32-shift)); } /********************************************************************* * _scalb (CRTDLL.259) * * Return x*2^y. */ double __cdecl CRTDLL__scalb(double x, LONG y) { /* Note - Can't forward directly as libc expects y as double */ double y2 = (double)y; if (!finite(x)) CRTDLL_errno = EDOM; return scalb( x, y2 ); } /********************************************************************* * longjmp (CRTDLL.426) */ VOID __cdecl CRTDLL_longjmp(jmp_buf env, int val) { FIXME("CRTDLL_longjmp semistup, expect crash\n"); longjmp(env, val); } /********************************************************************* * _isctype (CRTDLL.138) */ INT __cdecl CRTDLL__isctype(INT c, UINT type) { if (c >= -1 && c <= 255) return CRTDLL_pctype_dll[c] & type; if (CRTDLL__mb_cur_max_dll != 1 && c > 0) { /* FIXME: Is there a faster way to do this? */ WORD typeInfo; char convert[3], *pconv = convert; if (CRTDLL_pctype_dll[(UINT)c >> 8] & CRTDLL_LEADBYTE) *pconv++ = (UINT)c >> 8; *pconv++ = c & 0xff; *pconv = 0; /* FIXME: Use ctype LCID */ if (GetStringTypeExA(__CRTDLL_current_lc_all_lcid, CT_CTYPE1, convert, convert[1] ? 2 : 1, &typeInfo)) return typeInfo & type; } return 0; } /* 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 * * Convert a partial path into a complete, normalised path. */ LPSTR __cdecl CRTDLL__fullpath(LPSTR absPath, LPCSTR relPath, 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 CRTDLL__getcwd(absPath, size); if (size < 4) { CRTDLL_errno = ERANGE; return NULL; } TRACE(":resolving relative path '%s'\n",relPath); CRTDLL__splitpath(relPath, drive, dir, file, ext); /* Get Directory and drive into 'res' */ if (!dir[0] || (dir[0] != '/' && dir[0] != '\\')) { /* Relative or no directory given */ CRTDLL__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 CRTDLL__strdup(res); strcpy(absPath,res); return absPath; } /********************************************************************* * _splitpath (CRTDLL.279) * * Split a path string into components. * * PARAMETERS * inpath [in] Path to split * drive [out] "x:" or "" * directory [out] "\dir", "\dir\", "/dir", "/dir/", "./" etc * filename [out] filename, without dot or slashes * extension [out] ".ext" or "" */ VOID __cdecl CRTDLL__splitpath(LPCSTR inpath, LPSTR drv, LPSTR dir, LPSTR fname, LPSTR 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); } } } /********************************************************************* * _matherr (CRTDLL.181) * * Default handler for math errors. */ INT __cdecl CRTDLL__matherr(struct _exception *e) { /* FIXME: Supposedly this can be user overridden, but * currently it will never be called anyway. User will * need to use .spec ignore directive to override. */ FIXME(":Unhandled math error!\n"); return e == NULL ? 0 : 0; } /********************************************************************* * _makepath (CRTDLL.182) */ VOID __cdecl CRTDLL__makepath(LPSTR path, LPCSTR drive, LPCSTR directory, LPCSTR filename, LPCSTR extension ) { char ch; TRACE("CRTDLL__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("CRTDLL__makepath returns %s\n",path); } /********************************************************************* * _errno (CRTDLL.52) * Return the address of the CRT errno (Not the libc errno). * * BUGS * Not MT safe. */ LPINT __cdecl CRTDLL__errno( VOID ) { return &CRTDLL_errno; } /********************************************************************* * __doserrno (CRTDLL.26) * * Return the address of the DOS errno (holding the last OS error). * * BUGS * Not MT safe. */ LPINT __cdecl CRTDLL___doserrno( VOID ) { return &CRTDLL_doserrno; } /********************************************************************** * _statusfp (CRTDLL.279) * * Return the status of the FP control word. */ UINT __cdecl CRTDLL__statusfp( VOID ) { UINT retVal = 0; #if defined(__GNUC__) && defined(__i386__) UINT fpword; __asm__ __volatile__( "fstsw %0" : "=m" (fpword) : ); if (fpword & 0x1) retVal |= _SW_INVALID; if (fpword & 0x2) retVal |= _SW_DENORMAL; if (fpword & 0x4) retVal |= _SW_ZERODIVIDE; if (fpword & 0x8) retVal |= _SW_OVERFLOW; if (fpword & 0x10) retVal |= _SW_UNDERFLOW; if (fpword & 0x20) retVal |= _SW_INEXACT; #else FIXME(":Not implemented!\n"); #endif return retVal; } /********************************************************************** * _strerror (CRTDLL.284) * * Return a formatted system error message. * * NOTES * The caller does not own the string returned. */ extern int sprintf(char *str, const char *format, ...); LPSTR __cdecl CRTDLL__strerror (LPCSTR err) { static char strerrbuff[256]; sprintf(strerrbuff,"%s: %s\n",err,CRTDLL_strerror(CRTDLL_errno)); return strerrbuff; } /********************************************************************* * perror (CRTDLL.435) * * Print a formatted system error message to stderr. */ VOID __cdecl CRTDLL_perror (LPCSTR err) { char *err_str = CRTDLL_strerror(CRTDLL_errno); CRTDLL_fprintf(CRTDLL_stderr,"%s: %s\n",err,err_str); } /********************************************************************* * strerror (CRTDLL.465) * * Return the text of an error. * * NOTES * The caller does not own the string returned. */ extern char *strerror(int errnum); LPSTR __cdecl CRTDLL_strerror (INT err) { return strerror(err); } /********************************************************************* * signal (CRTDLL.455) */ LPVOID __cdecl CRTDLL_signal(INT sig, sig_handler_type ptr) { FIXME("(%d %p):stub.\n", sig, ptr); return (void*)-1; } /********************************************************************* * _sleep (CRTDLL.267) */ VOID __cdecl CRTDLL__sleep(ULONG timeout) { TRACE("CRTDLL__sleep for %ld milliseconds\n",timeout); Sleep((timeout)?timeout:1); } /********************************************************************* * getenv (CRTDLL.437) */ LPSTR __cdecl CRTDLL_getenv(LPCSTR name) { LPSTR environ = GetEnvironmentStringsA(); LPSTR pp,pos = NULL; unsigned int length; for (pp = environ; (*pp); pp = pp + strlen(pp) +1) { pos =strchr(pp,'='); if (pos) length = pos -pp; else length = strlen(pp); if (!strncmp(pp,name,length)) break; } if ((pp)&& (pos)) { pp = pos+1; TRACE("got %s\n",pp); } FreeEnvironmentStringsA( environ ); return pp; } /********************************************************************* * isalnum (CRTDLL.442) */ INT __cdecl CRTDLL_isalnum(INT c) { return CRTDLL__isctype( c,CRTDLL_ALPHA | CRTDLL_DIGIT ); } /********************************************************************* * isalpha (CRTDLL.443) */ INT __cdecl CRTDLL_isalpha(INT c) { return CRTDLL__isctype( c, CRTDLL_ALPHA ); } /********************************************************************* * iscntrl (CRTDLL.444) */ INT __cdecl CRTDLL_iscntrl(INT c) { return CRTDLL__isctype( c, CRTDLL_CONTROL ); } /********************************************************************* * isdigit (CRTDLL.445) */ INT __cdecl CRTDLL_isdigit(INT c) { return CRTDLL__isctype( c, CRTDLL_DIGIT ); } /********************************************************************* * isgraph (CRTDLL.446) */ INT __cdecl CRTDLL_isgraph(INT c) { return CRTDLL__isctype( c, CRTDLL_ALPHA | CRTDLL_DIGIT | CRTDLL_PUNCT ); } /********************************************************************* * isleadbyte (CRTDLL.447) */ INT __cdecl CRTDLL_isleadbyte(UCHAR c) { return CRTDLL__isctype( c, CRTDLL_LEADBYTE ); } /********************************************************************* * islower (CRTDLL.447) */ INT __cdecl CRTDLL_islower(INT c) { return CRTDLL__isctype( c, CRTDLL_LOWER ); } /********************************************************************* * isprint (CRTDLL.448) */ INT __cdecl CRTDLL_isprint(INT c) { return CRTDLL__isctype( c, CRTDLL_ALPHA | CRTDLL_DIGIT | CRTDLL_BLANK | CRTDLL_PUNCT ); } /********************************************************************* * ispunct (CRTDLL.449) */ INT __cdecl CRTDLL_ispunct(INT c) { return CRTDLL__isctype( c, CRTDLL_PUNCT ); } /********************************************************************* * isspace (CRTDLL.450) */ INT __cdecl CRTDLL_isspace(INT c) { return CRTDLL__isctype( c, CRTDLL_SPACE ); } /********************************************************************* * isupper (CRTDLL.451) */ INT __cdecl CRTDLL_isupper(INT c) { return CRTDLL__isctype( c, CRTDLL_UPPER ); } /********************************************************************* * isxdigit (CRTDLL.452) */ INT __cdecl CRTDLL_isxdigit(INT c) { return CRTDLL__isctype( c, CRTDLL_HEX ); } /********************************************************************* * ldexp (CRTDLL.454) */ double __cdecl CRTDLL_ldexp(double x, LONG y) { double z = ldexp(x,y); if (!finite(z)) CRTDLL_errno = ERANGE; else if (z == 0 && signbit(z)) z = 0.0; /* Convert -0 -> +0 */ return z; } /********************************************************************* * _except_handler2 (CRTDLL.78) */ INT __cdecl CRTDLL__except_handler2 ( PEXCEPTION_RECORD rec, PEXCEPTION_FRAME frame, PCONTEXT context, PEXCEPTION_FRAME *dispatcher) { FIXME ("exception %lx flags=%lx at %p handler=%p %p %p stub\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame->Handler, context, dispatcher); return ExceptionContinueSearch; } /********************************************************************* * __isascii (CRTDLL.028) * */ INT __cdecl CRTDLL___isascii(INT c) { return isascii((unsigned)c); } /********************************************************************* * __toascii (CRTDLL.035) * */ INT __cdecl CRTDLL___toascii(INT c) { return (unsigned)c & 0x7f; } /********************************************************************* * iswascii (CRTDLL.404) * */ INT __cdecl CRTDLL_iswascii(LONG c) { return ((unsigned)c < 0x80); } /********************************************************************* * __iscsym (CRTDLL.029) * * Is a character valid in a C identifier (a-Z,0-9,_). * * PARAMS * c [I]: Character to check * * RETURNS * Non zero if c is valid as t a C identifier. */ INT __cdecl CRTDLL___iscsym(UCHAR c) { return (c < 127 && (isalnum(c) || c == '_')); } /********************************************************************* * __iscsymf (CRTDLL.030) * * Is a character valid as the first letter in a C identifier (a-Z,_). * * PARAMS * c [in] Character to check * * RETURNS * Non zero if c is valid as the first letter in a C identifier. */ INT __cdecl CRTDLL___iscsymf(UCHAR c) { return (c < 127 && (isalpha(c) || c == '_')); } /********************************************************************* * _lfind (CRTDLL.170) * * Perform a linear search of an array for an element. */ LPVOID __cdecl CRTDLL__lfind(LPCVOID match, LPCVOID start, LPUINT array_size, UINT elem_size, comp_func cf) { UINT size = *array_size; if (size) do { if (cf(match, start) == 0) return (LPVOID)start; /* found */ start += elem_size; } while (--size); return NULL; } /********************************************************************* * _loaddll (CRTDLL.171) * * Get a handle to a DLL in memory. The DLL is loaded if it is not already. * * PARAMS * dll [in] Name of DLL to load. * * RETURNS * Success: A handle to the loaded DLL. * * Failure: FIXME. */ INT __cdecl CRTDLL__loaddll(LPSTR dllname) { return LoadLibraryA(dllname); } /********************************************************************* * _unloaddll (CRTDLL.313) * * Free reference to a DLL handle from loaddll(). * * PARAMS * dll [in] Handle to free. * * RETURNS * Success: 0. * * Failure: Error number. */ INT __cdecl CRTDLL__unloaddll(HANDLE dll) { INT err; if (FreeLibrary(dll)) return 0; err = GetLastError(); __CRTDLL__set_errno(err); return err; } /********************************************************************* * _lsearch (CRTDLL.177) * * Linear search of an array of elements. Adds the item to the array if * not found. * * PARAMS * match [in] Pointer to element to match * start [in] Pointer to start of search memory * array_size [in] Length of search array (element count) * elem_size [in] Size of each element in memory * cf [in] Pointer to comparison function (like qsort()). * * RETURNS * Pointer to the location where element was found or added. */ LPVOID __cdecl CRTDLL__lsearch(LPVOID match,LPVOID start, LPUINT array_size, UINT elem_size, comp_func cf) { UINT size = *array_size; if (size) do { if (cf(match, start) == 0) return start; /* found */ start += elem_size; } while (--size); /* not found, add to end */ memcpy(start, match, elem_size); array_size[0]++; return start; } /********************************************************************* * _itow (CRTDLL.164) * * Convert an integer to a wide char string. */ extern LPSTR __cdecl _itoa( long , LPSTR , INT); /* ntdll */ /********************************************************************/ WCHAR* __cdecl CRTDLL__itow(INT value,WCHAR* out,INT base) { char buff[64]; /* FIXME: Whats the maximum buffer size for INT_MAX? */ _itoa(value, buff, base); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buff, -1, out, 64); return out; } /********************************************************************* * _ltow (CRTDLL.??) * * Convert a long to a wide char string. */ extern LPSTR __cdecl _ltoa( long , LPSTR , INT); /* ntdll */ /********************************************************************/ WCHAR* __cdecl CRTDLL__ltow(LONG value,WCHAR* out,INT base) { char buff[64]; /* FIXME: Whats the maximum buffer size for LONG_MAX? */ _ltoa(value, buff, base); MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, buff, -1, out, 64); return out; } /********************************************************************* * _ultow (CRTDLL.??) * * Convert an unsigned long to a wide char string. */ extern LPSTR __cdecl _ultoa( long , LPSTR , INT); /* ntdll */ /********************************************************************/ WCHAR* __cdecl CRTDLL__ultow(ULONG value,WCHAR* out,INT base) { char buff[64]; /* FIXME: Whats the maximum buffer size for ULONG_MAX? */ _ultoa(value, buff, base); MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, buff, -1, out, 64); return out; } /********************************************************************* * _toupper (CRTDLL.489) */ CHAR __cdecl CRTDLL__toupper(CHAR c) { return toupper(c); } /********************************************************************* * _tolower (CRTDLL.490) */ CHAR __cdecl CRTDLL__tolower(CHAR c) { return tolower(c); } /* FP functions */ /********************************************************************* * _cabs (CRTDLL.048) * * Return the absolue value of a complex number. * * PARAMS * c [in] Structure containing real and imaginary parts of complex number. * * RETURNS * Absolute value of complex number (always a positive real number). */ double __cdecl CRTDLL__cabs(struct complex c) { return sqrt(c.real * c.real + c.imaginary * c.imaginary); } /********************************************************************* * _chgsign (CRTDLL.053) * * Change the sign of an IEEE double. * * PARAMS * d [in] Number to invert. * * RETURNS * Number with sign inverted. */ double __cdecl CRTDLL__chgsign(double d) { /* FIXME: +-infinity,Nan not tested */ return -d; } /********************************************************************* * _control87 (CRTDLL.060) * * X86 implementation of _controlfp. * */ UINT __cdecl CRTDLL__control87(UINT newVal, UINT mask) { #if defined(__GNUC__) && defined(__i386__) UINT fpword, flags = 0; /* Get fp control word */ __asm__ __volatile__( "fstsw %0" : "=m" (fpword) : ); /* Convert into mask constants */ if (fpword & 0x1) flags |= _EM_INVALID; if (fpword & 0x2) flags |= _EM_DENORMAL; if (fpword & 0x4) flags |= _EM_ZERODIVIDE; if (fpword & 0x8) flags |= _EM_OVERFLOW; if (fpword & 0x10) flags |= _EM_UNDERFLOW; if (fpword & 0x20) flags |= _EM_INEXACT; switch(fpword & 0xC00) { case 0xC00: flags |= _RC_UP|_RC_DOWN; break; case 0x800: flags |= _RC_UP; break; case 0x400: flags |= _RC_DOWN; break; } switch(fpword & 0x300) { case 0x0: flags |= _PC_24; break; case 0x200: flags |= _PC_53; break; case 0x300: flags |= _PC_64; break; } if (fpword & 0x1000) flags |= _IC_AFFINE; /* Mask with parameters */ flags = (flags & ~mask) | (newVal & mask); /* Convert (masked) value back to fp word */ fpword = 0; if (flags & _EM_INVALID) fpword |= 0x1; if (flags & _EM_DENORMAL) fpword |= 0x2; if (flags & _EM_ZERODIVIDE) fpword |= 0x4; if (flags & _EM_OVERFLOW) fpword |= 0x8; if (flags & _EM_UNDERFLOW) fpword |= 0x10; if (flags & _EM_INEXACT) fpword |= 0x20; switch(flags & (_RC_UP | _RC_DOWN)) { case _RC_UP|_RC_DOWN: fpword |= 0xC00; break; case _RC_UP: fpword |= 0x800; break; case _RC_DOWN: fpword |= 0x400; break; } switch (flags & (_PC_24 | _PC_53)) { case _PC_64: fpword |= 0x300; break; case _PC_53: fpword |= 0x200; break; case _PC_24: fpword |= 0x0; break; } if (!(flags & _IC_AFFINE)) fpword |= 0x1000; /* Put fp control word */ __asm__ __volatile__( "fldcw %0" : : "m" (fpword) ); return fpword; #else return CRTDLL__controlfp( newVal, mask ); #endif } /********************************************************************* * _controlfp (CRTDLL.061) * * Set the state of the floating point unit. */ UINT __cdecl CRTDLL__controlfp( UINT newVal, UINT mask) { #if defined(__GNUC__) && defined(__i386__) return CRTDLL__control87( newVal, mask ); #else FIXME(":Not Implemented!\n"); return 0; #endif } /********************************************************************* * _copysign (CRTDLL.062) * * Return the number x with the sign of y. */ double __cdecl CRTDLL__copysign(double x, double y) { /* FIXME: Behaviour for Nan/Inf etc? */ if (y < 0.0) return x < 0.0 ? x : -x; return x < 0.0 ? -x : x; } /********************************************************************* * _finite (CRTDLL.101) * * Determine if an IEEE double is finite (i.e. not +/- Infinity). * * PARAMS * d [in] Number to check. * * RETURNS * Non zero if number is finite. */ INT __cdecl CRTDLL__finite(double d) { return (finite(d)?1:0); /* See comment for CRTDLL__isnan() */ } /********************************************************************* * _fpreset (CRTDLL.107) * * Reset the state of the floating point processor. */ VOID __cdecl CRTDLL__fpreset(void) { #if defined(__GNUC__) && defined(__i386__) __asm__ __volatile__( "fninit" ); #else FIXME(":Not Implemented!\n"); #endif } /********************************************************************* * _isnan (CRTDLL.164) * * Determine if an IEEE double is unrepresentable (NaN). * * PARAMS * d [in] Number to check. * * RETURNS * Non zero if number is NaN. */ INT __cdecl CRTDLL__isnan(double d) { /* some implementations return -1 for true(glibc), crtdll returns 1. * Do the same, as the result may be used in calculations. */ return isnan(d)?1:0; } /********************************************************************* * _purecall (CRTDLL.249) * * Abort program after pure virtual function call. */ VOID __cdecl CRTDLL__purecall(VOID) { CRTDLL__amsg_exit( 25 ); } /********************************************************************* * div (CRTDLL.358) * * Return the quotient and remainder of long integer division. * * VERSION * [i386] Windows binary compatible - returns the struct in eax/edx. */ #ifdef __i386__ LONGLONG __cdecl CRTDLL_div(INT x, INT y) { LONGLONG retVal; div_t dt = div(x,y); retVal = ((LONGLONG)dt.rem << 32) | dt.quot; return retVal; } #endif /* !defined(__i386__) */ /********************************************************************* * div (CRTDLL.358) * * Return the quotient and remainder of long integer division. * * VERSION * [!i386] Non-x86 can't run win32 apps so we don't need binary compatibility */ #ifndef __i386__ div_t __cdecl CRTDLL_div(INT x, INT y) { return div(x,y); } #endif /* !defined(__i386__) */ /********************************************************************* * ldiv (CRTDLL.249) * * Return the quotient and remainder of long integer division. * VERSION * [i386] Windows binary compatible - returns the struct in eax/edx. */ #ifdef __i386__ ULONGLONG __cdecl CRTDLL_ldiv(LONG x, LONG y) { ULONGLONG retVal; ldiv_t ldt = ldiv(x,y); retVal = ((ULONGLONG)ldt.rem << 32) | (ULONG)ldt.quot; return retVal; } #endif /* defined(__i386__) */ /********************************************************************* * ldiv (CRTDLL.249) * * Return the quotient and remainder of long integer division. * * VERSION * [!i386] Non-x86 can't run win32 apps so we don't need binary compatibility */ #ifndef __i386__ ldiv_t __cdecl CRTDLL_ldiv(LONG x, LONG y) { return ldiv(x,y); } #endif /* !defined(__i386__) */ /********************************************************************* * _y0 (CRTDLL.332) * */ double __cdecl CRTDLL__y0(double x) { double retVal; if (!finite(x)) CRTDLL_errno = EDOM; retVal = y0(x); if (CRTDLL__fpclass(retVal) == _FPCLASS_NINF) { CRTDLL_errno = EDOM; retVal = sqrt(-1); } return retVal; } /********************************************************************* * _y1 (CRTDLL.333) * */ double __cdecl CRTDLL__y1(double x) { double retVal; if (!finite(x)) CRTDLL_errno = EDOM; retVal = y1(x); if (CRTDLL__fpclass(retVal) == _FPCLASS_NINF) { CRTDLL_errno = EDOM; retVal = sqrt(-1); } return retVal; } /********************************************************************* * _yn (CRTDLL.334) * */ double __cdecl CRTDLL__yn(INT x, double y) { double retVal; if (!finite(y)) CRTDLL_errno = EDOM; retVal = yn(x,y); if (CRTDLL__fpclass(retVal) == _FPCLASS_NINF) { CRTDLL_errno = EDOM; retVal = sqrt(-1); } return retVal; } /********************************************************************* * _nextafter (CRTDLL.235) * */ double __cdecl CRTDLL__nextafter(double x, double y) { double retVal; if (!finite(x) || !finite(y)) CRTDLL_errno = EDOM; retVal = nextafter(x,y); return retVal; } /********************************************************************* * _searchenv (CRTDLL.260) * * Search CWD and each directory of an environment variable for * location of a file. */ VOID __cdecl CRTDLL__searchenv(LPCSTR file, LPCSTR env, LPSTR buff) { LPSTR envVal, penv; char curPath[MAX_PATH]; *buff = '\0'; /* Try CWD first */ if (GetFileAttributesA( file ) != 0xFFFFFFFF) { GetFullPathNameA( file, MAX_PATH, buff, NULL ); /* Sigh. This error is *always* set, regardless of sucess */ __CRTDLL__set_errno(ERROR_FILE_NOT_FOUND); return; } /* Search given environment variable */ envVal = CRTDLL_getenv(env); if (!envVal) { __CRTDLL__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) { __CRTDLL__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(buff, curPath); __CRTDLL__set_errno(ERROR_FILE_NOT_FOUND); return; /* Found */ } penv = *end ? end + 1 : end; } while(1); }