/* * 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 "crtdll.h" #include #define __USE_ISOC9X 1 /* for isfinite */ #include #include #include #include "ntddk.h" #include "wingdi.h" #include "winuser.h" DEFAULT_DEBUG_CHANNEL(crtdll); 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 */ 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; const INT CRTDLL__sys_nerr = 43; /********************************************************************* * 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(); } 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; } } /********************************************************************* * _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; } /********************************************************************* * _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)); } /********************************************************************* * _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)); } /********************************************************************* * vswprintf (CRTDLL.501) */ INT __cdecl CRTDLL_vswprintf( LPWSTR buffer, LPCWSTR spec, va_list args ) { return wvsprintfW( buffer, spec, args ); } /********************************************************************* * longjmp (CRTDLL.426) */ VOID __cdecl CRTDLL_longjmp(jmp_buf env, int val) { FIXME("CRTDLL_longjmp semistup, expect crash\n"); longjmp(env, val); } /********************************************************************* * setlocale (CRTDLL.453) */ LPSTR __cdecl CRTDLL_setlocale(INT category,LPCSTR locale) { LPSTR categorystr; switch (category) { case CRTDLL_LC_ALL: categorystr="LC_ALL";break; case CRTDLL_LC_COLLATE: categorystr="LC_COLLATE";break; case CRTDLL_LC_CTYPE: categorystr="LC_CTYPE";break; case CRTDLL_LC_MONETARY: categorystr="LC_MONETARY";break; case CRTDLL_LC_NUMERIC: categorystr="LC_NUMERIC";break; case CRTDLL_LC_TIME: categorystr="LC_TIME";break; default: categorystr = "UNKNOWN?";break; } FIXME("(%s,%s),stub!\n",categorystr,locale); return "C"; } /********************************************************************* * _isctype (CRTDLL.138) */ BOOL __cdecl CRTDLL__isctype(CHAR x,CHAR type) { if ((type & CRTDLL_SPACE) && isspace(x)) return TRUE; if ((type & CRTDLL_PUNCT) && ispunct(x)) return TRUE; if ((type & CRTDLL_LOWER) && islower(x)) return TRUE; if ((type & CRTDLL_UPPER) && isupper(x)) return TRUE; if ((type & CRTDLL_ALPHA) && isalpha(x)) return TRUE; if ((type & CRTDLL_DIGIT) && isdigit(x)) return TRUE; if ((type & CRTDLL_CONTROL) && iscntrl(x)) return TRUE; /* check CRTDLL_LEADBYTE */ return FALSE; } /********************************************************************* * _fullpath (CRTDLL.114) */ LPSTR __cdecl CRTDLL__fullpath(LPSTR buf, LPCSTR name, INT size) { if (!buf) { size = 256; if(!(buf = CRTDLL_malloc(size))) return NULL; } if (!GetFullPathNameA( name, size, buf, NULL )) return NULL; TRACE("CRTDLL_fullpath got %s\n",buf); return buf; } /********************************************************************* * _splitpath (CRTDLL.279) */ VOID __cdecl CRTDLL__splitpath(LPCSTR path, LPSTR drive, LPSTR directory, LPSTR filename, LPSTR extension ) { /* drive includes : directory includes leading and trailing (forward and backward slashes) filename without dot and slashes extension with leading dot */ char * drivechar,*dirchar,*namechar; TRACE("CRTDLL__splitpath got %s\n",path); drivechar = strchr(path,':'); dirchar = strrchr(path,'/'); namechar = strrchr(path,'\\'); dirchar = max(dirchar,namechar); if (dirchar) namechar = strrchr(dirchar,'.'); else namechar = strrchr(path,'.'); if (drive) { *drive = 0x00; if (drivechar) { strncat(drive,path,drivechar-path+1); path = drivechar+1; } } if (directory) { *directory = 0x00; if (dirchar) { strncat(directory,path,dirchar-path+1); path = dirchar+1; } } if (filename) { *filename = 0x00; if (namechar) { strncat(filename,path,namechar-path); if (extension) { *extension = 0x00; strcat(extension,namechar); } } } TRACE("CRTDLL__splitpath found %s %s %s %s\n",drive,directory,filename,extension); } /********************************************************************* * _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; } /********************************************************************** * _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); CRTDLL_free(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; } /********************************************************************* * _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(c); } /********************************************************************* * __toascii (CRTDLL.035) * */ INT __cdecl CRTDLL___toascii(INT c) { return 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(LONG c) { return (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(LONG c) { return (isalpha(c) || c == '_'); } /********************************************************************* * _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 * comp_func [in] Pointer to comparason 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) * * Unimplemented. Obsolete. Give it up. Use controlfp(), if you must. * */ UINT __cdecl CRTDLL__control87(UINT x, UINT y) { /* Will never be supported, no possible targets have an 87/287 FP unit */ WARN(":Ignoring control87 call, dont trust any FP results!\n"); return 0; } /********************************************************************* * _controlfp (CRTDLL.061) * * Set the state of the floating point unit. * * PARAMS * FIXME: * * RETURNS * None * * BUGS * Unimplemented. */ UINT __cdecl CRTDLL__controlfp( UINT x, UINT y) { FIXME(":stub!\n"); return 0; } /********************************************************************* * _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 (isfinite(d)?1:0); /* See comment for CRTDLL__isnan() */ } /********************************************************************* * _fpreset (CRTDLL.107) * * Reset the state of the floating point processor. * * PARAMS * None. * * RETURNS * None. * * BUGS * Unimplemented. */ VOID __cdecl CRTDLL__fpreset(void) { FIXME(":stub!\n"); } /********************************************************************* * _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; }