/* * Misc. functions for systems that don't have them * * Copyright 1996 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "wine/port.h" #ifdef __BEOS__ #include #include #endif #include #include #include #include #include #include #include #ifdef HAVE_SYS_INTTYPES_H # include #endif #ifdef HAVE_SYS_TIME_h # include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #include #include #ifdef HAVE_TERMIOS_H #include #endif #ifdef HAVE_SYS_MMAN_H #include #endif #ifdef HAVE_LIBIO_H # include #endif #ifdef HAVE_SYSCALL_H # include #endif #ifdef HAVE_STDINT_H # include #endif /*********************************************************************** * usleep */ #ifndef HAVE_USLEEP int usleep (unsigned int useconds) { #if defined(__EMX__) DosSleep(useconds); return 0; #elif defined(__BEOS__) return snooze(useconds); #elif defined(HAVE_SELECT) struct timeval delay; delay.tv_sec = useconds / 1000000; delay.tv_usec = useconds % 1000000; select( 0, 0, 0, 0, &delay ); return 0; #else /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */ errno = ENOSYS; return -1; #endif /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */ } #endif /* HAVE_USLEEP */ /*********************************************************************** * memmove */ #ifndef HAVE_MEMMOVE void *memmove( void *dest, const void *src, unsigned int len ) { register char *dst = dest; /* Use memcpy if not overlapping */ if ((dst + len <= (char *)src) || ((char *)src + len <= dst)) { memcpy( dst, src, len ); } /* Otherwise do it the hard way (FIXME: could do better than this) */ else if (dst < src) { while (len--) *dst++ = *((char *)src)++; } else { dst += len - 1; src = (char *)src + len - 1; while (len--) *dst-- = *((char *)src)--; } return dest; } #endif /* HAVE_MEMMOVE */ /*********************************************************************** * strerror */ #ifndef HAVE_STRERROR const char *strerror( int err ) { /* Let's hope we have sys_errlist then */ return sys_errlist[err]; } #endif /* HAVE_STRERROR */ /*********************************************************************** * getpagesize */ #ifndef HAVE_GETPAGESIZE size_t getpagesize(void) { # ifdef __svr4__ return sysconf(_SC_PAGESIZE); # elif defined(__i386__) return 4096; # else # error Cannot get the page size on this platform # endif } #endif /* HAVE_GETPAGESIZE */ /*********************************************************************** * clone */ #if !defined(HAVE_CLONE) && defined(__linux__) int clone( int (*fn)(void *), void *stack, int flags, void *arg ) { #ifdef __i386__ int ret; void **stack_ptr = (void **)stack; *--stack_ptr = arg; /* Push argument on stack */ *--stack_ptr = fn; /* Push function pointer (popped into ebx) */ __asm__ __volatile__( "pushl %%ebx\n\t" "movl %2,%%ebx\n\t" "int $0x80\n\t" "popl %%ebx\n\t" /* Contains fn in the child */ "testl %%eax,%%eax\n\t" "jnz 0f\n\t" "xorl %ebp,%ebp\n\t" /* Terminate the stack frames */ "call *%%ebx\n\t" /* Should never return */ "xorl %%eax,%%eax\n\t" /* Just in case it does*/ "0:" : "=a" (ret) : "0" (SYS_clone), "r" (flags), "c" (stack_ptr) ); assert( ret ); /* If ret is 0, we returned from the child function */ if (ret > 0) return ret; errno = -ret; return -1; #else errno = EINVAL; return -1; #endif /* __i386__ */ } #endif /* !HAVE_CLONE && __linux__ */ /*********************************************************************** * strcasecmp */ #ifndef HAVE_STRCASECMP int strcasecmp( const char *str1, const char *str2 ) { const unsigned char *ustr1 = (const unsigned char *)str1; const unsigned char *ustr2 = (const unsigned char *)str2; while (*ustr1 && toupper(*ustr1) == toupper(*ustr2)) { ustr1++; ustr2++; } return toupper(*ustr1) - toupper(*ustr2); } #endif /* HAVE_STRCASECMP */ /*********************************************************************** * strncasecmp */ #ifndef HAVE_STRNCASECMP int strncasecmp( const char *str1, const char *str2, size_t n ) { const unsigned char *ustr1 = (const unsigned char *)str1; const unsigned char *ustr2 = (const unsigned char *)str2; int res; if (!n) return 0; while ((--n > 0) && *ustr1) { if ((res = toupper(*ustr1) - toupper(*ustr2))) return res; ustr1++; ustr2++; } return toupper(*ustr1) - toupper(*ustr2); } #endif /* HAVE_STRNCASECMP */ /*********************************************************************** * getnetbyaddr */ #ifndef HAVE_GETNETBYADDR struct netent *getnetbyaddr(unsigned long net, int type) { errno = ENOSYS; return NULL; } #endif /* defined(HAVE_GETNETBYNAME) */ /*********************************************************************** * getnetbyname */ #ifndef HAVE_GETNETBYNAME struct netent *getnetbyname(const char *name) { errno = ENOSYS; return NULL; } #endif /* defined(HAVE_GETNETBYNAME) */ /*********************************************************************** * getprotobyname */ #ifndef HAVE_GETPROTOBYNAME struct protoent *getprotobyname(const char *name) { errno = ENOSYS; return NULL; } #endif /* !defined(HAVE_GETPROTOBYNAME) */ /*********************************************************************** * getprotobynumber */ #ifndef HAVE_GETPROTOBYNUMBER struct protoent *getprotobynumber(int proto) { errno = ENOSYS; return NULL; } #endif /* !defined(HAVE_GETPROTOBYNUMBER) */ /*********************************************************************** * getservbyport */ #ifndef HAVE_GETSERVBYPORT struct servent *getservbyport(int port, const char *proto) { errno = ENOSYS; return NULL; } #endif /* !defined(HAVE_GETSERVBYPORT) */ /*********************************************************************** * getsockopt */ #ifndef HAVE_GETSOCKOPT int getsockopt(int socket, int level, int option_name, void *option_value, size_t *option_len) { errno = ENOSYS; return -1; } #endif /* !defined(HAVE_GETSOCKOPT) */ /*********************************************************************** * inet_network */ #ifndef HAVE_INET_NETWORK unsigned long inet_network(const char *cp) { errno = ENOSYS; return 0; } #endif /* defined(HAVE_INET_NETWORK) */ /*********************************************************************** * statfs */ #ifndef HAVE_STATFS int statfs(const char *name, struct statfs *info) { #ifdef __BEOS__ dev_t mydev; fs_info fsinfo; if(!info) { errno = ENOSYS; return -1; } if ((mydev = dev_for_path(name)) < 0) { errno = ENOSYS; return -1; } if (fs_stat_dev(mydev,&fsinfo) < 0) { errno = ENOSYS; return -1; } info->f_bsize = fsinfo.block_size; info->f_blocks = fsinfo.total_blocks; info->f_bfree = fsinfo.free_blocks; return 0; #else /* defined(__BEOS__) */ errno = ENOSYS; return -1; #endif /* defined(__BEOS__) */ } #endif /* !defined(HAVE_STATFS) */ /*********************************************************************** * lstat */ #ifndef HAVE_LSTAT int lstat(const char *file_name, struct stat *buf) { return stat( file_name, buf ); } #endif /* HAVE_LSTAT */ /*********************************************************************** * pread * * FIXME: this is not thread-safe */ #ifndef HAVE_PREAD ssize_t pread( int fd, void *buf, size_t count, off_t offset ) { ssize_t ret; off_t old_pos; if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1; if (lseek( fd, offset, SEEK_SET ) == -1) return -1; if ((ret = read( fd, buf, count )) == -1) { int err = errno; /* save errno */ lseek( fd, old_pos, SEEK_SET ); errno = err; return -1; } if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1; return ret; } #endif /* HAVE_PREAD */ /*********************************************************************** * pwrite * * FIXME: this is not thread-safe */ #ifndef HAVE_PWRITE ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset ) { ssize_t ret; off_t old_pos; if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1; if (lseek( fd, offset, SEEK_SET ) == -1) return -1; if ((ret = write( fd, buf, count )) == -1) { int err = errno; /* save errno */ lseek( fd, old_pos, SEEK_SET ); errno = err; return -1; } if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1; return ret; } #endif /* HAVE_PWRITE */ #if defined(__svr4__) || defined(__NetBSD__) /*********************************************************************** * try_mmap_fixed * * The purpose of this routine is to emulate the behaviour of * the Linux mmap() routine if a non-NULL address is passed, * but the MAP_FIXED flag is not set. Linux in this case tries * to place the mapping at the specified address, *unless* the * range is already in use. Solaris, however, completely ignores * the address argument in this case. * * As Wine code occasionally relies on the Linux behaviour, e.g. to * be able to map non-relocateable PE executables to their proper * start addresses, or to map the DOS memory to 0, this routine * emulates the Linux behaviour by checking whether the desired * address range is still available, and placing the mapping there * using MAP_FIXED if so. */ static int try_mmap_fixed (void *addr, size_t len, int prot, int flags, int fildes, off_t off) { char * volatile result = NULL; int pagesize = getpagesize(); pid_t pid; /* We only try to map to a fixed address if addr is non-NULL and properly aligned, and MAP_FIXED isn't already specified. */ if ( !addr ) return 0; if ( (uintptr_t)addr & (pagesize-1) ) return 0; if ( flags & MAP_FIXED ) return 0; /* We use vfork() to freeze all threads of the current process. This allows us to check without race condition whether the desired memory range is already in use. Note that because vfork() shares the address spaces between parent and child, we can actually perform the mapping in the child. */ if ( (pid = vfork()) == -1 ) { perror("try_mmap_fixed: vfork"); exit(1); } if ( pid == 0 ) { int i; char vec; /* We call mincore() for every page in the desired range. If any of these calls succeeds, the page is already mapped and we must fail. */ for ( i = 0; i < len; i += pagesize ) if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 ) _exit(1); /* Perform the mapping with MAP_FIXED set. This is safe now, as none of the pages is currently in use. */ result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off ); if ( result == addr ) _exit(0); if ( result != (void *) -1 ) /* This should never happen ... */ munmap( result, len ); _exit(1); } /* vfork() lets the parent continue only after the child has exited. Furthermore, Wine sets SIGCHLD to SIG_IGN, so we don't need to wait for the child. */ return result == addr; } #endif /*********************************************************************** * wine_anon_mmap * * Portable wrapper for anonymous mmaps */ void *wine_anon_mmap( void *start, size_t size, int prot, int flags ) { #ifdef HAVE_MMAP static int fdzero = -1; #ifdef MAP_ANON flags |= MAP_ANON; #else if (fdzero == -1) { if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1) { perror( "/dev/zero: open" ); exit(1); } } #endif /* MAP_ANON */ #ifdef MAP_SHARED flags &= ~MAP_SHARED; #endif /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */ #ifdef MAP_PRIVATE flags |= MAP_PRIVATE; #endif #if defined(__svr4__) || defined(__NetBSD__) if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) ) return start; #endif return mmap( start, size, prot, flags, fdzero, 0 ); #else return (void *)-1; #endif } /* * These functions provide wrappers around dlopen() and associated * functions. They work around a bug in glibc 2.1.x where calling * a dl*() function after a previous dl*() function has failed * without a dlerror() call between the two will cause a crash. * They all take a pointer to a buffer that * will receive the error description (from dlerror()). This * parameter may be NULL if the error description is not required. */ /*********************************************************************** * wine_dlopen */ void *wine_dlopen( const char *filename, int flag, char *error, int errorsize ) { #ifdef HAVE_DLOPEN void *ret; const char *s; dlerror(); dlerror(); ret = dlopen( filename, flag ); s = dlerror(); if (error) { strncpy( error, s ? s : "", errorsize ); error[errorsize - 1] = '\0'; } dlerror(); return ret; #else if (error) { strncpy( error, "dlopen interface not detected by configure", errorsize ); error[errorsize - 1] = '\0'; } return NULL; #endif } /*********************************************************************** * wine_dlsym */ void *wine_dlsym( void *handle, const char *symbol, char *error, int errorsize ) { #ifdef HAVE_DLOPEN void *ret; const char *s; dlerror(); dlerror(); ret = dlsym( handle, symbol ); s = dlerror(); if (error) { strncpy( error, s ? s : "", errorsize ); error[errorsize - 1] = '\0'; } dlerror(); return ret; #else if (error) { strncpy( error, "dlopen interface not detected by configure", errorsize ); error[errorsize - 1] = '\0'; } return NULL; #endif } /*********************************************************************** * wine_dlclose */ int wine_dlclose( void *handle, char *error, int errorsize ) { #ifdef HAVE_DLOPEN int ret; const char *s; dlerror(); dlerror(); ret = dlclose( handle ); s = dlerror(); if (error) { strncpy( error, s ? s : "", errorsize ); error[errorsize - 1] = '\0'; } dlerror(); return ret; #else if (error) { strncpy( error, "dlopen interface not detected by configure", errorsize ); error[errorsize - 1] = '\0'; } return 1; #endif } /*********************************************************************** * wine_rewrite_s4tos2 * * Convert 4 byte Unicode strings to 2 byte Unicode strings in-place. * This is only practical if literal strings are writable. */ unsigned short* wine_rewrite_s4tos2(const wchar_t* str4 ) { unsigned short *str2,*s2; if (str4==NULL) return NULL; if ((*str4 & 0xffff0000) != 0) { /* This string has already been converted. Return it as is */ return (unsigned short*)str4; } /* Note that we can also end up here if the string has a single * character. In such a case we will convert the string over and * over again. But this is harmless. */ str2=s2=(unsigned short*)str4; do { *s2=(unsigned short)*str4; s2++; } while (*str4++ != L'\0'); return str2; } #ifndef HAVE_ECVT /* * NetBSD 1.5 doesn't have ecvt, fcvt, gcvt. We just check for ecvt, though. * Fix/verify these implementations ! */ /*********************************************************************** * ecvt */ char *ecvt (double number, int ndigits, int *decpt, int *sign) { static char buf[40]; /* ought to be enough */ char *dec; sprintf(buf, "%.*e", ndigits /* FIXME wrong */, number); *sign = (number < 0); dec = strchr(buf, '.'); *decpt = (dec) ? (int)dec - (int)buf : -1; return buf; } /*********************************************************************** * fcvt */ char *fcvt (double number, int ndigits, int *decpt, int *sign) { static char buf[40]; /* ought to be enough */ char *dec; sprintf(buf, "%.*e", ndigits, number); *sign = (number < 0); dec = strchr(buf, '.'); *decpt = (dec) ? (int)dec - (int)buf : -1; return buf; } /*********************************************************************** * gcvt * * FIXME: uses both E and F. */ char *gcvt (double number, size_t ndigit, char *buff) { sprintf(buff, "%.*E", (int)ndigit, number); return buff; } #endif /* HAVE_ECVT */ /*********************************************************************** * interlocked functions */ #ifdef __i386__ __ASM_GLOBAL_FUNC(interlocked_cmpxchg, "movl 12(%esp),%eax\n\t" "movl 8(%esp),%ecx\n\t" "movl 4(%esp),%edx\n\t" "lock; cmpxchgl %ecx,(%edx)\n\t" "ret"); __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr, "movl 12(%esp),%eax\n\t" "movl 8(%esp),%ecx\n\t" "movl 4(%esp),%edx\n\t" "lock; cmpxchgl %ecx,(%edx)\n\t" "ret"); __ASM_GLOBAL_FUNC(interlocked_xchg, "movl 8(%esp),%eax\n\t" "movl 4(%esp),%edx\n\t" "lock; xchgl %eax,(%edx)\n\t" "ret"); __ASM_GLOBAL_FUNC(interlocked_xchg_ptr, "movl 8(%esp),%eax\n\t" "movl 4(%esp),%edx\n\t" "lock; xchgl %eax,(%edx)\n\t" "ret"); __ASM_GLOBAL_FUNC(interlocked_xchg_add, "movl 8(%esp),%eax\n\t" "movl 4(%esp),%edx\n\t" "lock; xaddl %eax,(%edx)\n\t" "ret"); #elif defined(__sparc__) && defined(__sun__) /* * As the earlier Sparc processors lack necessary atomic instructions, * I'm simply falling back to the library-provided _lwp_mutex routines * to ensure mutual exclusion in a way appropriate for the current * architecture. * * FIXME: If we have the compare-and-swap instruction (Sparc v9 and above) * we could use this to speed up the Interlocked operations ... */ #include static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX; long interlocked_cmpxchg( long *dest, long xchg, long compare ) { _lwp_mutex_lock( &interlocked_mutex ); if (*dest == compare) *dest = xchg; else compare = *dest; _lwp_mutex_unlock( &interlocked_mutex ); return compare; } void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare ) { _lwp_mutex_lock( &interlocked_mutex ); if (*dest == compare) *dest = xchg; else compare = *dest; _lwp_mutex_unlock( &interlocked_mutex ); return compare; } long interlocked_xchg( long *dest, long val ) { long retv; _lwp_mutex_lock( &interlocked_mutex ); retv = *dest; *dest = val; _lwp_mutex_unlock( &interlocked_mutex ); return retv; } void *interlocked_xchg_ptr( void **dest, void *val ) { long retv; _lwp_mutex_lock( &interlocked_mutex ); retv = *dest; *dest = val; _lwp_mutex_unlock( &interlocked_mutex ); return retv; } long interlocked_xchg_add( long *dest, long incr ) { long retv; _lwp_mutex_lock( &interlocked_mutex ); retv = *dest; *dest += incr; _lwp_mutex_unlock( &interlocked_mutex ); return retv; } #else # error You must implement the interlocked* functions for your CPU #endif