ok() does not support '%S'. Store the Ansi version, convert to Unicode
[wine] / library / port.c
1 /*
2  * Misc. functions for systems that don't have them
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #ifdef __BEOS__
25 #include <be/kernel/fs_info.h>
26 #include <be/kernel/OS.h>
27 #endif
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <sys/types.h>
39 #ifdef HAVE_SYS_INTTYPES_H
40 # include <sys/inttypes.h>
41 #endif
42 #ifdef HAVE_SYS_TIME_h
43 # include <sys/time.h>
44 #endif
45 #include <sys/stat.h>
46 #ifdef HAVE_SYS_IOCTL_H
47 #include <sys/ioctl.h>
48 #endif
49 #include <errno.h>
50 #include <fcntl.h>
51 #ifdef HAVE_TERMIOS_H
52 #include <termios.h>
53 #endif
54 #ifdef HAVE_SYS_MMAN_H
55 #include <sys/mman.h>
56 #endif
57 #ifdef HAVE_LIBIO_H
58 # include <libio.h>
59 #endif
60 #ifdef HAVE_SYSCALL_H
61 # include <syscall.h>
62 #endif
63 #ifdef HAVE_STDINT_H
64 # include <stdint.h>
65 #endif
66
67 /***********************************************************************
68  *              usleep
69  */
70 #ifndef HAVE_USLEEP
71 int usleep (unsigned int useconds)
72 {
73 #if defined(__EMX__)
74     DosSleep(useconds);
75     return 0;
76 #elif defined(__BEOS__)
77     return snooze(useconds);
78 #elif defined(HAVE_SELECT)
79     struct timeval delay;
80
81     delay.tv_sec = useconds / 1000000;
82     delay.tv_usec = useconds % 1000000;
83
84     select( 0, 0, 0, 0, &delay );
85     return 0;
86 #else /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
87     errno = ENOSYS;
88     return -1;
89 #endif /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
90 }
91 #endif /* HAVE_USLEEP */
92
93 /***********************************************************************
94  *              memmove
95  */
96 #ifndef HAVE_MEMMOVE
97 void *memmove( void *dest, const void *src, size_t len )
98 {
99     register char *dst = dest;
100
101     /* Use memcpy if not overlapping */
102     if ((dst + len <= (char *)src) || ((char *)src + len <= dst))
103     {
104         memcpy( dst, src, len );
105     }
106     /* Otherwise do it the hard way (FIXME: could do better than this) */
107     else if (dst < (char *)src)
108     {
109         while (len--) *dst++ = *((char *)src)++;
110     }
111     else
112     {
113         dst += len - 1;
114         src = (char *)src + len - 1;
115         while (len--) *dst-- = *((char *)src)--;
116     }
117     return dest;
118 }
119 #endif  /* HAVE_MEMMOVE */
120
121 /***********************************************************************
122  *              strerror
123  */
124 #ifndef HAVE_STRERROR
125 const char *strerror( int err )
126 {
127     /* Let's hope we have sys_errlist then */
128     return sys_errlist[err];
129 }
130 #endif  /* HAVE_STRERROR */
131
132
133 /***********************************************************************
134  *              getpagesize
135  */
136 #ifndef HAVE_GETPAGESIZE
137 size_t getpagesize(void)
138 {
139 # ifdef __svr4__
140     return sysconf(_SC_PAGESIZE);
141 # elif defined(__i386__)
142     return 4096;
143 # else
144 #  error Cannot get the page size on this platform
145 # endif
146 }
147 #endif  /* HAVE_GETPAGESIZE */
148
149
150 /***********************************************************************
151  *              clone
152  */
153 #if !defined(HAVE_CLONE) && defined(__linux__)
154 int clone( int (*fn)(void *), void *stack, int flags, void *arg )
155 {
156 #ifdef __i386__
157     int ret;
158     void **stack_ptr = (void **)stack;
159     *--stack_ptr = arg;  /* Push argument on stack */
160     *--stack_ptr = fn;   /* Push function pointer (popped into ebx) */
161     __asm__ __volatile__( "pushl %%ebx\n\t"
162                           "movl %2,%%ebx\n\t"
163                           "int $0x80\n\t"
164                           "popl %%ebx\n\t"   /* Contains fn in the child */
165                           "testl %%eax,%%eax\n\t"
166                           "jnz 0f\n\t"
167                           "xorl %ebp,%ebp\n\t"    /* Terminate the stack frames */
168                           "call *%%ebx\n\t"       /* Should never return */
169                           "xorl %%eax,%%eax\n\t"  /* Just in case it does*/
170                           "0:"
171                           : "=a" (ret)
172                           : "0" (SYS_clone), "r" (flags), "c" (stack_ptr) );
173     assert( ret );  /* If ret is 0, we returned from the child function */
174     if (ret > 0) return ret;
175     errno = -ret;
176     return -1;
177 #else
178     errno = EINVAL;
179     return -1;
180 #endif  /* __i386__ */
181 }
182 #endif  /* !HAVE_CLONE && __linux__ */
183
184 /***********************************************************************
185  *              strcasecmp
186  */
187 #ifndef HAVE_STRCASECMP
188 int strcasecmp( const char *str1, const char *str2 )
189 {
190     const unsigned char *ustr1 = (const unsigned char *)str1;
191     const unsigned char *ustr2 = (const unsigned char *)str2;
192
193     while (*ustr1 && toupper(*ustr1) == toupper(*ustr2)) {
194         ustr1++;
195         ustr2++;
196     }
197     return toupper(*ustr1) - toupper(*ustr2);
198 }
199 #endif /* HAVE_STRCASECMP */
200
201 /***********************************************************************
202  *              strncasecmp
203  */
204 #ifndef HAVE_STRNCASECMP
205 int strncasecmp( const char *str1, const char *str2, size_t n )
206 {
207     const unsigned char *ustr1 = (const unsigned char *)str1;
208     const unsigned char *ustr2 = (const unsigned char *)str2;
209     int res;
210
211     if (!n) return 0;
212     while ((--n > 0) && *ustr1) {
213         if ((res = toupper(*ustr1) - toupper(*ustr2))) return res;
214         ustr1++;
215         ustr2++;
216     }
217     return toupper(*ustr1) - toupper(*ustr2);
218 }
219 #endif /* HAVE_STRNCASECMP */
220
221 /***********************************************************************
222  *              getsockopt
223  */
224 #ifndef HAVE_GETSOCKOPT
225 int getsockopt(int socket, int level, int option_name,
226                void *option_value, size_t *option_len)
227 {
228     errno = ENOSYS;
229     return -1;
230 }
231 #endif /* !defined(HAVE_GETSOCKOPT) */
232
233 /***********************************************************************
234  *              inet_network
235  */
236 #ifndef HAVE_INET_NETWORK
237 unsigned long inet_network(const char *cp)
238 {
239     errno = ENOSYS;
240     return 0;
241 }
242 #endif /* defined(HAVE_INET_NETWORK) */
243
244 /***********************************************************************
245  *              statfs
246  */
247 #ifndef HAVE_STATFS
248 int statfs(const char *name, struct statfs *info)
249 {
250 #ifdef __BEOS__
251     dev_t mydev;
252     fs_info fsinfo;
253
254     if(!info) {
255         errno = ENOSYS;
256         return -1;
257     }
258
259     if ((mydev = dev_for_path(name)) < 0) {
260         errno = ENOSYS;
261         return -1;
262     }
263
264     if (fs_stat_dev(mydev,&fsinfo) < 0) {
265         errno = ENOSYS;
266         return -1;
267     }
268
269     info->f_bsize = fsinfo.block_size;
270     info->f_blocks = fsinfo.total_blocks;
271     info->f_bfree = fsinfo.free_blocks;
272     return 0;
273 #else /* defined(__BEOS__) */
274     errno = ENOSYS;
275     return -1;
276 #endif /* defined(__BEOS__) */
277 }
278 #endif /* !defined(HAVE_STATFS) */
279
280
281 /***********************************************************************
282  *              lstat
283  */
284 #ifndef HAVE_LSTAT
285 int lstat(const char *file_name, struct stat *buf)
286 {
287     return stat( file_name, buf );
288 }
289 #endif /* HAVE_LSTAT */
290
291 /***********************************************************************
292  *              mkstemp
293  */
294 #ifndef HAVE_MKSTEMP
295 int mkstemp(char *tmpfn)
296 {
297     int tries;
298     char *xstart;
299
300     xstart = tmpfn+strlen(tmpfn)-1;
301     while ((xstart > tmpfn) && (*xstart == 'X'))
302         xstart--;
303     tries = 10;
304     while (tries--) {
305         char *newfn = mktemp(tmpfn);
306         int fd;
307         if (!newfn) /* something else broke horribly */
308             return -1;
309         fd = open(newfn,O_CREAT|O_RDWR|O_EXCL,0600);
310         if (fd!=-1)
311             return fd;
312         newfn = xstart;
313         /* fill up with X and try again ... */
314         while (*newfn) *newfn++ = 'X';
315     }
316     return -1;
317 }
318 #endif /* HAVE_MKSTEMP */
319
320
321 /***********************************************************************
322  *              pread
323  *
324  * FIXME: this is not thread-safe
325  */
326 #ifndef HAVE_PREAD
327 ssize_t pread( int fd, void *buf, size_t count, off_t offset )
328 {
329     ssize_t ret;
330     off_t old_pos;
331
332     if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1;
333     if (lseek( fd, offset, SEEK_SET ) == -1) return -1;
334     if ((ret = read( fd, buf, count )) == -1)
335     {
336         int err = errno;  /* save errno */
337         lseek( fd, old_pos, SEEK_SET );
338         errno = err;
339         return -1;
340     }
341     if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
342     return ret;
343 }
344 #endif /* HAVE_PREAD */
345
346
347 /***********************************************************************
348  *              pwrite
349  *
350  * FIXME: this is not thread-safe
351  */
352 #ifndef HAVE_PWRITE
353 ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset )
354 {
355     ssize_t ret;
356     off_t old_pos;
357
358     if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1;
359     if (lseek( fd, offset, SEEK_SET ) == -1) return -1;
360     if ((ret = write( fd, buf, count )) == -1)
361     {
362         int err = errno;  /* save errno */
363         lseek( fd, old_pos, SEEK_SET );
364         errno = err;
365         return -1;
366     }
367     if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
368     return ret;
369 }
370 #endif /* HAVE_PWRITE */
371
372
373 #if defined(__svr4__) || defined(__NetBSD__)
374 /***********************************************************************
375  *             try_mmap_fixed
376  *
377  * The purpose of this routine is to emulate the behaviour of
378  * the Linux mmap() routine if a non-NULL address is passed,
379  * but the MAP_FIXED flag is not set.  Linux in this case tries
380  * to place the mapping at the specified address, *unless* the
381  * range is already in use.  Solaris, however, completely ignores
382  * the address argument in this case.
383  *
384  * As Wine code occasionally relies on the Linux behaviour, e.g. to
385  * be able to map non-relocateable PE executables to their proper
386  * start addresses, or to map the DOS memory to 0, this routine
387  * emulates the Linux behaviour by checking whether the desired
388  * address range is still available, and placing the mapping there
389  * using MAP_FIXED if so.
390  */
391 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
392                            int fildes, off_t off)
393 {
394     char * volatile result = NULL;
395     int pagesize = getpagesize();
396     pid_t pid;
397
398     /* We only try to map to a fixed address if
399        addr is non-NULL and properly aligned,
400        and MAP_FIXED isn't already specified. */
401
402     if ( !addr )
403         return 0;
404     if ( (uintptr_t)addr & (pagesize-1) )
405         return 0;
406     if ( flags & MAP_FIXED )
407         return 0;
408
409     /* We use vfork() to freeze all threads of the
410        current process.  This allows us to check without
411        race condition whether the desired memory range is
412        already in use.  Note that because vfork() shares
413        the address spaces between parent and child, we
414        can actually perform the mapping in the child. */
415
416     if ( (pid = vfork()) == -1 )
417     {
418         perror("try_mmap_fixed: vfork");
419         exit(1);
420     }
421     if ( pid == 0 )
422     {
423         int i;
424         char vec;
425
426         /* We call mincore() for every page in the desired range.
427            If any of these calls succeeds, the page is already
428            mapped and we must fail. */
429         for ( i = 0; i < len; i += pagesize )
430             if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 )
431                _exit(1);
432
433         /* Perform the mapping with MAP_FIXED set.  This is safe
434            now, as none of the pages is currently in use. */
435         result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
436         if ( result == addr )
437             _exit(0);
438
439         if ( result != (void *) -1 ) /* This should never happen ... */
440             munmap( result, len );
441
442        _exit(1);
443     }
444
445     /* vfork() lets the parent continue only after the child
446        has exited.  Furthermore, Wine sets SIGCHLD to SIG_IGN,
447        so we don't need to wait for the child. */
448
449     return result == addr;
450 }
451 #endif
452
453 /***********************************************************************
454  *              wine_anon_mmap
455  *
456  * Portable wrapper for anonymous mmaps
457  */
458 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
459 {
460 #ifdef HAVE_MMAP
461     static int fdzero = -1;
462
463 #ifdef MAP_ANON
464     flags |= MAP_ANON;
465 #else
466     if (fdzero == -1)
467     {
468         if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
469         {
470             perror( "/dev/zero: open" );
471             exit(1);
472         }
473     }
474 #endif  /* MAP_ANON */
475
476 #ifdef MAP_SHARED
477     flags &= ~MAP_SHARED;
478 #endif
479
480     /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
481 #ifdef MAP_PRIVATE
482     flags |= MAP_PRIVATE;
483 #endif
484
485 #if defined(__svr4__) || defined(__NetBSD__)
486     if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
487         return start;
488 #endif
489
490     return mmap( start, size, prot, flags, fdzero, 0 );
491 #else
492     return (void *)-1;
493 #endif
494 }
495
496
497 /*
498  * These functions provide wrappers around dlopen() and associated
499  * functions.  They work around a bug in glibc 2.1.x where calling
500  * a dl*() function after a previous dl*() function has failed
501  * without a dlerror() call between the two will cause a crash.
502  * They all take a pointer to a buffer that
503  * will receive the error description (from dlerror()).  This
504  * parameter may be NULL if the error description is not required.
505  */
506
507 /***********************************************************************
508  *              wine_dlopen
509  */
510 void *wine_dlopen( const char *filename, int flag, char *error, int errorsize )
511 {
512 #ifdef HAVE_DLOPEN
513     void *ret;
514     const char *s;
515     dlerror(); dlerror();
516     ret = dlopen( filename, flag );
517     s = dlerror();
518     if (error)
519     {
520         strncpy( error, s ? s : "", errorsize );
521         error[errorsize - 1] = '\0';
522     }
523     dlerror();
524     return ret;
525 #else
526     if (error)
527     {
528         strncpy( error, "dlopen interface not detected by configure", errorsize );
529         error[errorsize - 1] = '\0';
530     }
531     return NULL;
532 #endif
533 }
534
535 /***********************************************************************
536  *              wine_dlsym
537  */
538 void *wine_dlsym( void *handle, const char *symbol, char *error, int errorsize )
539 {
540 #ifdef HAVE_DLOPEN
541     void *ret;
542     const char *s;
543     dlerror(); dlerror();
544     ret = dlsym( handle, symbol );
545     s = dlerror();
546     if (error)
547     {
548         strncpy( error, s ? s : "", errorsize );
549         error[errorsize - 1] = '\0';
550     }
551     dlerror();
552     return ret;
553 #else
554     if (error)
555     {
556         strncpy( error, "dlopen interface not detected by configure", errorsize );
557         error[errorsize - 1] = '\0';
558     }
559     return NULL;
560 #endif
561 }
562
563 /***********************************************************************
564  *              wine_dlclose
565  */
566 int wine_dlclose( void *handle, char *error, int errorsize )
567 {
568 #ifdef HAVE_DLOPEN
569     int ret;
570     const char *s;
571     dlerror(); dlerror();
572     ret = dlclose( handle );
573     s = dlerror();
574     if (error)
575     {
576         strncpy( error, s ? s : "", errorsize );
577         error[errorsize - 1] = '\0';
578     }
579     dlerror();
580     return ret;
581 #else
582     if (error)
583     {
584         strncpy( error, "dlopen interface not detected by configure", errorsize );
585         error[errorsize - 1] = '\0';
586     }
587     return 1;
588 #endif
589 }
590
591 /***********************************************************************
592  *              wine_rewrite_s4tos2
593  *
594  * Convert 4 byte Unicode strings to 2 byte Unicode strings in-place.
595  * This is only practical if literal strings are writable.
596  */
597 unsigned short* wine_rewrite_s4tos2(const wchar_t* str4 )
598 {
599     unsigned short *str2,*s2;
600
601     if (str4==NULL)
602       return NULL;
603
604     if ((*str4 & 0xffff0000) != 0) {
605         /* This string has already been converted. Return it as is */
606         return (unsigned short*)str4;
607     }
608
609     /* Note that we can also end up here if the string has a single
610      * character. In such a case we will convert the string over and
611      * over again. But this is harmless.
612      */
613     str2=s2=(unsigned short*)str4;
614     do {
615         *s2=(unsigned short)*str4;
616         s2++;
617     } while (*str4++ != L'\0');
618
619     return str2;
620 }
621
622 #ifndef HAVE_ECVT
623 /***********************************************************************
624  *              ecvt
625  */
626 char *ecvt (double number, int  ndigits,  int  *decpt,  int *sign)
627 {
628     static char buf[40]; /* ought to be enough */
629     char *dec;
630     sprintf(buf, "%.*e", ndigits /* FIXME wrong */, number);
631     *sign = (number < 0);
632     dec = strchr(buf, '.');
633     *decpt = (dec) ? (int)dec - (int)buf : -1;
634     return buf;
635 }
636 #endif /* HAVE_ECVT */
637
638 #ifndef HAVE_FCVT
639 /***********************************************************************
640  *              fcvt
641  */
642 char *fcvt (double number, int  ndigits,  int  *decpt,  int *sign)
643 {
644     static char buf[40]; /* ought to be enough */
645     char *dec;
646     sprintf(buf, "%.*e", ndigits, number);
647     *sign = (number < 0);
648     dec = strchr(buf, '.');
649     *decpt = (dec) ? (int)dec - (int)buf : -1;
650     return buf;
651 }
652 #endif /* HAVE_FCVT */
653
654 #ifndef HAVE_GCVT
655 /***********************************************************************
656  *              gcvt
657  *
658  * FIXME: uses both E and F.
659  */
660 char *gcvt (double number, size_t  ndigit,  char *buff)
661 {
662     sprintf(buff, "%.*E", (int)ndigit, number);
663     return buff;
664 }
665 #endif /* HAVE_GCVT */
666
667
668 #ifndef wine_memcpy_unaligned
669 /***********************************************************************
670  *              wine_memcpy_unaligned
671  *
672  * This is necessary to defeat optimizations of memcpy by gcc.
673  */
674 void *wine_memcpy_unaligned( void *dst, const void *src, size_t size )
675 {
676     return memcpy( dst, src, size );
677 }
678 #endif
679
680
681 /***********************************************************************
682  *              pthread functions
683  */
684
685 #ifndef HAVE_PTHREAD_GETSPECIFIC
686 void pthread_getspecific() { assert(0); }
687 #endif
688
689 #ifndef HAVE_PTHREAD_KEY_CREATE
690 void pthread_key_create() { assert(0); }
691 #endif
692
693 #ifndef HAVE_PTHREAD_MUTEX_LOCK
694 void pthread_mutex_lock() { assert(0); }
695 #endif
696
697 #ifndef HAVE_PTHREAD_MUTEX_UNLOCK
698 void pthread_mutex_unlock() { assert(0); }
699 #endif
700
701 #ifndef HAVE_PTHREAD_SETSPECIFIC
702 void pthread_setspecific() { assert(0); }
703 #endif
704
705 /***********************************************************************
706  *              interlocked functions
707  */
708 #ifdef __i386__
709
710 #ifdef __GNUC__
711
712 __ASM_GLOBAL_FUNC(interlocked_cmpxchg,
713                   "movl 12(%esp),%eax\n\t"
714                   "movl 8(%esp),%ecx\n\t"
715                   "movl 4(%esp),%edx\n\t"
716                   "lock; cmpxchgl %ecx,(%edx)\n\t"
717                   "ret");
718 __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr,
719                   "movl 12(%esp),%eax\n\t"
720                   "movl 8(%esp),%ecx\n\t"
721                   "movl 4(%esp),%edx\n\t"
722                   "lock; cmpxchgl %ecx,(%edx)\n\t"
723                   "ret");
724 __ASM_GLOBAL_FUNC(interlocked_xchg,
725                   "movl 8(%esp),%eax\n\t"
726                   "movl 4(%esp),%edx\n\t"
727                   "lock; xchgl %eax,(%edx)\n\t"
728                   "ret");
729 __ASM_GLOBAL_FUNC(interlocked_xchg_ptr,
730                   "movl 8(%esp),%eax\n\t"
731                   "movl 4(%esp),%edx\n\t"
732                   "lock; xchgl %eax,(%edx)\n\t"
733                   "ret");
734 __ASM_GLOBAL_FUNC(interlocked_xchg_add,
735                   "movl 8(%esp),%eax\n\t"
736                   "movl 4(%esp),%edx\n\t"
737                   "lock; xaddl %eax,(%edx)\n\t"
738                   "ret");
739
740 #elif defined(_MSC_VER)
741
742 __declspec(naked) long interlocked_cmpxchg( long *dest, long xchg, long compare )
743 {
744     __asm mov eax, 12[esp];
745     __asm mov ecx, 8[esp];
746     __asm mov edx, 4[esp];
747     __asm lock cmpxchg [edx], ecx;
748     __asm ret;
749 }
750
751 __declspec(naked) void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
752 {
753     __asm mov eax, 12[esp];
754     __asm mov ecx, 8[esp];
755     __asm mov edx, 4[esp];
756     __asm lock cmpxchg [edx], ecx;
757     __asm ret;
758 }
759
760 __declspec(naked) long interlocked_xchg( long *dest, long val )
761 {
762     __asm mov eax, 8[esp];
763     __asm mov edx, 4[esp];
764     __asm lock xchg [edx], eax;
765     __asm ret;
766 }
767
768 __declspec(naked) void *interlocked_xchg_ptr( void **dest, void *val )
769 {
770     __asm mov eax, 8[esp];
771     __asm mov edx, 4[esp];
772     __asm lock xchg [edx], eax;
773     __asm ret;
774 }
775
776 __declspec(naked) long interlocked_xchg_add( long *dest, long incr )
777 {
778     __asm mov eax, 8[esp];
779     __asm mov edx, 4[esp];
780     __asm lock xadd [edx], eax;
781     __asm ret;
782 }
783
784 #else
785 # error You must implement the interlocked* functions for your compiler
786 #endif
787
788 #elif defined(__powerpc__)
789 void* interlocked_cmpxchg_ptr( void **dest, void* xchg, void* compare)
790 {
791     long ret = 0;
792     long scratch;
793     __asm__ __volatile__(
794     "0:    lwarx %0,0,%2 ;"
795     "      xor. %1,%4,%0;"
796     "      bne 1f;"
797     "      stwcx. %3,0,%2;"
798     "      bne- 0b;"
799     "1:    "
800     : "=&r"(ret), "=&r"(scratch)
801     : "r"(dest), "r"(xchg), "r"(compare)
802     : "cr0","memory");
803     return (void*)ret;
804 }
805
806 long interlocked_cmpxchg( long *dest, long xchg, long compare)
807 {
808     long ret = 0;
809     long scratch;
810     __asm__ __volatile__(
811     "0:    lwarx %0,0,%2 ;"
812     "      xor. %1,%4,%0;"
813     "      bne 1f;"
814     "      stwcx. %3,0,%2;"
815     "      bne- 0b;"
816     "1:    "
817     : "=&r"(ret), "=&r"(scratch)
818     : "r"(dest), "r"(xchg), "r"(compare)
819     : "cr0","memory");
820     return ret;
821 }
822
823 long interlocked_xchg_add( long *dest, long incr )
824 {
825     long ret = 0;
826     long zero = 0;
827     __asm__ __volatile__(
828         "0:    lwarx %0, %3, %1;"
829         "      add %0, %2, %0;"
830         "      stwcx. %0, %3, %1;"
831         "      bne- 0b;"
832         : "=&r" (ret)
833         : "r"(dest), "r"(incr), "r"(zero)
834         : "cr0", "memory"
835     );
836     return ret-incr;
837 }
838
839 long interlocked_xchg( long* dest, long val )
840 {
841     long ret = 0;
842     __asm__ __volatile__(
843     "0:    lwarx %0,0,%1 ;"
844     "      stwcx. %2,0,%1;"
845     "      bne- 0b;"
846     : "=&r"(ret)
847     : "r"(dest), "r"(val)
848     : "cr0","memory");
849     return ret;
850 }
851
852 void* interlocked_xchg_ptr( void** dest, void* val )
853 {
854     void *ret = NULL;
855     __asm__ __volatile__(
856     "0:    lwarx %0,0,%1 ;"
857     "      stwcx. %2,0,%1;"
858     "      bne- 0b;"
859     : "=&r"(ret)
860     : "r"(dest), "r"(val)
861     : "cr0","memory");
862     return ret;
863 }
864
865 #elif defined(__sparc__) && defined(__sun__)
866
867 /*
868  * As the earlier Sparc processors lack necessary atomic instructions,
869  * I'm simply falling back to the library-provided _lwp_mutex routines
870  * to ensure mutual exclusion in a way appropriate for the current
871  * architecture.
872  *
873  * FIXME:  If we have the compare-and-swap instruction (Sparc v9 and above)
874  *         we could use this to speed up the Interlocked operations ...
875  */
876 #include <synch.h>
877 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
878
879 long interlocked_cmpxchg( long *dest, long xchg, long compare )
880 {
881     _lwp_mutex_lock( &interlocked_mutex );
882     if (*dest == compare) *dest = xchg;
883     else compare = *dest;
884     _lwp_mutex_unlock( &interlocked_mutex );
885     return compare;
886 }
887
888 void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
889 {
890     _lwp_mutex_lock( &interlocked_mutex );
891     if (*dest == compare) *dest = xchg;
892     else compare = *dest;
893     _lwp_mutex_unlock( &interlocked_mutex );
894     return compare;
895 }
896
897 long interlocked_xchg( long *dest, long val )
898 {
899     long retv;
900     _lwp_mutex_lock( &interlocked_mutex );
901     retv = *dest;
902     *dest = val;
903     _lwp_mutex_unlock( &interlocked_mutex );
904     return retv;
905 }
906
907 void *interlocked_xchg_ptr( void **dest, void *val )
908 {
909     long retv;
910     _lwp_mutex_lock( &interlocked_mutex );
911     retv = *dest;
912     *dest = val;
913     _lwp_mutex_unlock( &interlocked_mutex );
914     return retv;
915 }
916
917 long interlocked_xchg_add( long *dest, long incr )
918 {
919     long retv;
920     _lwp_mutex_lock( &interlocked_mutex );
921     retv = *dest;
922     *dest += incr;
923     _lwp_mutex_unlock( &interlocked_mutex );
924     return retv;
925 }
926 #else
927 # error You must implement the interlocked* functions for your CPU
928 #endif