Release 950727
Sat Jul 22 22:39:09 IDT 1995 Michael Veksler <e1678223@tochnapc2.technion.ac.il>
* [ipc/*]
New directory. This directory contains the new inter-wine
communications support. It enables DDE protocols between two wine
instances. Currently it is limited to DDE, but can be enhanced to
support OLE between 2 different wine instances. This is very
important for libwine.a DDE/OLE support.
* [tools/ipcl]
A script to delete garbage IPC handles (shared memory, semaphores
and message queues). The current inter-wine communication is not
perfect, and sometimes leaves garbage behind.
* [if1632/relay.c] [include/atom.h] [include/global.h]
[loader/selector.c] [loader/task.c] [loader/module.c]
[loader/signal.c] [memory/global.c] [misc/atom.c]
[windows/class.c] [windows/message.c] [windows/win.c]
[Imakefile]
Hooks for inter-wine DDE support, current Global.*Atom functions
renamed to Local.*Atom since Global.*Atom are used for Inter-Wine
DDE communication. (The first call to these functions sets up the
IPC structures - which otherwise cause unneeded overhead.
Mon Jul 17 19:55:21 1995 Alexandre Julliard <julliard@sunsite.unc.edu>
* [controls/menu.c]
Don't crash if a NULL string is passed to menu functions.
* [memory/selector.c]
We now use a bit in ldt_flags_copy to indicate free LDT entries.
Fixed a bug in SELECTOR_ReallocBlock that could cause it to
overwrite valid LDT entries when growing a block.
* [miscemu/instr.c]
Emulate int xx instruction by storing the interrupt vector in
CS:IP and returning directly. This allows a program to install an
interrupt vector.
* [windows/win.c]
Added function WIN_GetTopParent to get the top-level parent of a
window.
Sun Jul 16 18:17:17 1995 Gregory Trubetskoy <grisha@mira.com>
* [loader/resource.c]
Added LoadIconHandler. It doesn't do anything yet, but now you
can use borland help files with winhelp.exe.
Sun Jul 16 11:58:45 1995 Anand Kumria <akumria@ozemail.com.au>
* [misc/main.c]
Fixed to return 386 Enhanced mode correctly. Also return the same
type of CPU, for both Enhanced and Standard mode, namely a 386.
Sun Jul 16 00:02:04 1995 Martin von Loewis <loewis@informatik.hu-berlin.de>
* [Configure] [include/options.h] [include/wineopts.h]
[misc/main.c][misc/spy.c]
Removed support of spy file. Redirected spy messages to stddeb.
Removed -spy option. Added -debugmsg +spy option.
* [debugger/dbg.y][debugger/debug.l]
Enabled segmented addresses (seg:offs) for break and x commands.
* [if1632/gdi.spec] [objects/region.c] [windows/graphics.c]
[include/region.h]
FrameRgn, REGION_FrameRgn: New functions
* [if1632/kernel.spec]
IsWinOldApTask: Return false
* [if1632/mouse.spec]
CplApplet: Removed
* [if1632/user.spec] [windows/win.c]
ShowOwnedPopups: New function
* [if1632/winsock.spec] [misc/winsocket.c]
inet_addr, select: New prototypes in relay code
Fixed memory layout for netdb functions (getXbyY).
WINSOCK_ioctlsocket: Translated FIONREAD, FIONBIO, and FIOASYNC
* [objects/clipping.c]
RectVisible: Fixed call to LPToDP
* [rc/winerc.c]
main: Removed extra argument to getopt for Linux.
Tue Jul 11 00:14:41 1995 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* [controls/listbox.c]
Yet another fix for ListBoxDirectory().
* [loader/module.c] [if1632/kernel.spec]
Make GetModuleHandle() accept instance handles as parameter.
* [if1632/relay.c] [loader/task.c]
Put a magic cookie at the bottom of the 32 bit stack, and check on
each return from a 32 bit function whether it's still there. Complain
if it's not.
* [if1632/user.spec]
Wrong entry for CloseDriver().
* [misc/dos_fs.c] [loader/task.c] [include/dos_fs.h] [misc/file.c]
[miscemu/int21.c]
Large parts of dos_fs.c simplified. Changed it to use one
current drive/directory per task, which is set to the module path on
task creation.
Prevent CorelPaint from closing stdin.
open() with O_CREAT set must be passed three parameters.
DOS FindFirst()/FindNext() could crash when FA_LABEL was set. Fixed,
it's in DOS_readdir() now.
* [misc/profile.c]
Some badly written software (Lotus Freelance Graphics) passes a bogus
size parameter that caused Wine to write off the end of a segment.
Fixed. (It's probably too paranoid now.)
* [multimedia/mmsystem.c] [multimedia/time.c] [multimedia/joystick.c]
[multimedia/Imakefile] [if1632/winprocs.spec]
16 bit entry point for MMSysTimeCallback.
Split off time.c and joystick.c from mmsystem.c.
* [objects/dib.c]
GetDIBits(): call XGetImage() via CallTo32_LargeStack.
* [windows/cursor.c]
DestroyCursor(): do nothing for builtin cursors.
* [windows/mdi.c]
Half of WM_MDISETMENU implemented.
* [windows/win.c]
EnumWindows() and EnumTaskWindows() never enumerated any windows.
Fixed.
* [windows/*.c]
Fixed GetParent() to return correct values for owned windows.
* [windows/message.c]
Don't try to activate disabled top-level windows.
* [windows/nonclient.c]
Work around a bug in gcc-2.7.0.
* [tools/build.c] [include/stackframe.h] [memory/global.c]
[loader/task.c] [memory/selector.c]
Some Visual Basic programs (and possibly others, too) expect ES to be
preserved by a call to an API function, so we have to save it.
In GlobalFree() and FreeSelector(), we must clear CURRENT_STACK16->es
to prevent segfaults if ES contained the selector to be freed.
Sun Jul 9 20:21:20 1995 Jon Tombs <jon@gtex02.us.es>
* [*/*]
Added missing prototypes to header files and relevant includes
to reduce compile time warnings.
Sun Jul 9 18:32:56 1995 Michael Patra <micky@marie.physik.tu-berlin.de>
* [configure.in] [include/config.h] [*/Makefile.in]
New configuration scheme based on autoconf.
Sat Jul 8 14:12:45 1995 Morten Welinder <terra+@cs.cmu.edu>
* [miscemu/ioports.c]
Revamp to have only one in- and one out- variant, both really
implemented.
* [miscemu/instr.c]
INSTR_EmulateInstruction: Use new ioport interface. Implement
string io. Correct instruction pointer for 32-bit code.
* [include/miscemu.h]
Update port function prototypes.
* [include/registers.h]
Defined FS and GS.
Sat Jul 8 13:38:54 1995 Hans de Graaff <graaff@twi72.twi.tudelft.nl>
* [misc/dos_fs.c]
ChopOffSlash(): A path consisting off a single slash is left
intact, and multiple slashes are all removed.
diff --git a/ipc/Imakefile b/ipc/Imakefile
new file mode 100644
index 0000000..cf5d6df
--- /dev/null
+++ b/ipc/Imakefile
@@ -0,0 +1,33 @@
+#include "../Wine.tmpl"
+
+MODULE = ipc
+
+TEST_SRCS = \
+ shm_fragment_test.c \
+ bit_array_test.c\
+ dde_proc_test.c \
+ dde_atom_test.c \
+ shm_semaph_test.c \
+ wine_test_stub.c \
+ hash_test.c \
+ dde_mem_test.c
+
+SRCS = bit_array.c \
+ dde_atom.c \
+ dde_mem.c \
+ dde_proc.c \
+ generic_hash.c \
+ shm_block.c \
+ shm_fragment.c \
+ shm_main_blk.c \
+ shm_semaph.c
+
+OBJS = $(SRCS:.c=.o)
+TEST_OBJS = $(TEST_SRCS:.c=.o)
+
+WineRelocatableTarget($(MODULE),,$(OBJS))
+DependTarget()
+
+includes::
+
+install::
diff --git a/ipc/Makefile.in b/ipc/Makefile.in
new file mode 100644
index 0000000..fea89fe
--- /dev/null
+++ b/ipc/Makefile.in
@@ -0,0 +1,57 @@
+CC = @CC@
+CFLAGS = @CFLAGS@
+XINCL = @x_includes@
+TOPSRC = @top_srcdir@
+DIVINCL = -I$(TOPSRC)/include
+LD = @LD@
+LDCOMBINEFLAGS = @LDCOMBINEFLAGS@
+
+
+MODULE = ipc
+
+SRCS = bit_array.c \
+ dde_atom.c \
+ dde_mem.c \
+ dde_proc.c \
+ generic_hash.c \
+ shm_block.c \
+ shm_fragment.c \
+ shm_main_blk.c \
+ shm_semaph.c
+
+OBJS = $(SRCS:.c=.o)
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(XINCL) $(DIVINCL) -o $*.o $<
+
+all: $(MODULE).o
+
+$(MODULE).o: $(OBJS)
+ $(LD) $(LDCOMBINEFLAGS) $(OBJS) -o $(MODULE).o
+
+depend:
+ sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+ $(CC) $(DIVINCL) $(XINCL) -MM *.c >> tmp_make
+ cp tmp_make Makefile
+ rm tmp_make
+
+clean:
+ rm -f *.o \#*\# *~ tmp_make
+
+distclean: clean
+ rm Makefile
+
+countryclean:
+
+NAMES = $(SRCS:.c=)
+
+winelibclean:
+ for i in $(NAMES); do \
+ if test `grep -c WINELIB $$i.c` -ne 0; then \
+ rm $$i.o; \
+ fi; \
+ done
+
+dummy:
+
+### Dependencies:
diff --git a/ipc/README b/ipc/README
new file mode 100644
index 0000000..6903f53
--- /dev/null
+++ b/ipc/README
@@ -0,0 +1,8 @@
+This is a pre-alpha code, it does not work for 100%,
+but it does not break anything (I hope).
+Proper documentation (LaTeX) will be ready for the next release.
+
+You can use "ipcl" perl script to remove junk IPC stuff.
+You can use "ipcs" system program to find junk IPC stuff.
+
+Michael
diff --git a/ipc/TEST_FRAGMENT.std b/ipc/TEST_FRAGMENT.std
new file mode 100644
index 0000000..968a440
--- /dev/null
+++ b/ipc/TEST_FRAGMENT.std
@@ -0,0 +1,42 @@
+After shm_FragmentInit
+{0x0020,0xffe0} [total free=ffe0]
+0: After shm_FragmentAlloc(block, 0x010000) == NULL
+{0x0020,0xffe0} [total free=ffe0]
+1: After shm_FragmentAlloc(block, 0x003fdc) == 0x00c024
+{0x0020,0xc000} [total free=c000]
+2: After shm_FragmentAlloc(block, 0x003ffc) == 0x008024
+{0x0020,0x8000} [total free=8000]
+3: After shm_FragmentAlloc(block, 0x003ffc) == 0x004024
+{0x0020,0x4000} [total free=4000]
+4: After shm_FragmentAlloc(block, 0x003ffd) == NULL
+{0x0020,0x4000} [total free=4000]
+5: After shm_FragmentAlloc(block, 0x003ffc) == 0x000024
+no free fragments [total free=0000]
+6: Doing shm_FragmentFree(block, 0x000024)
+{0x0020,0x4000} [total free=4000]
+7: After shm_FragmentAlloc(block, 0x001bfc) == 0x002424
+{0x0020,0x2400} [total free=2400]
+8: After shm_FragmentAlloc(block, 0x0013fc) == 0x001024
+{0x0020,0x1000} [total free=1000]
+9: After shm_FragmentAlloc(block, 0x000ffc) == 0x000024
+no free fragments [total free=0000]
+10: Doing shm_FragmentFree(block, 0x000024)
+{0x0020,0x1000} [total free=1000]
+11: Doing shm_FragmentFree(block, 0x004024)
+{0x0020,0x1000} {0x4020,0x4000} [total free=5000]
+12: Doing shm_FragmentFree(block, 0x00c024)
+{0x0020,0x1000} {0x4020,0x4000} {0xc020,0x3fe0} [total free=8fe0]
+13: After shm_FragmentAlloc(block, 0x000ffc) == 0x000024
+{0x4020,0x4000} {0xc020,0x3fe0} [total free=7fe0]
+14: Doing shm_FragmentFree(block, 0x000024)
+{0x0020,0x1000} {0x4020,0x4000} {0xc020,0x3fe0} [total free=8fe0]
+15: After shm_FragmentAlloc(block, 0x000ffd) == 0x007014
+{0x0020,0x1000} {0x4020,0x2ff0} {0xc020,0x3fe0} [total free=7fd0]
+16: Doing shm_FragmentFree(block, 0x008024)
+{0x0020,0x1000} {0x4020,0x2ff0} {0x8020,0x7fe0} [total free=bfd0]
+17: Doing shm_FragmentFree(block, 0x001024)
+{0x0020,0x2400} {0x4020,0x2ff0} {0x8020,0x7fe0} [total free=d3d0]
+18: Doing shm_FragmentFree(block, 0x002424)
+{0x0020,0x6ff0} {0x8020,0x7fe0} [total free=efd0]
+19: Doing shm_FragmentFree(block, 0x007014)
+{0x0020,0xffe0} [total free=ffe0]
diff --git a/ipc/bit_array.c b/ipc/bit_array.c
new file mode 100644
index 0000000..d4cede8
--- /dev/null
+++ b/ipc/bit_array.c
@@ -0,0 +1,276 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: bit_array.c
+ * Purpose : manipulate array of bits
+ * Portability: This is not completely portable, non CISC arcitectures
+ * Might not have atomic Clear/Set/Toggle bit. On those
+ * architectures semaphores should be used.
+ * Big Endian Concerns: This code is big endian compatible,
+ * but the byte order will be different (i.e. bit 0 will be
+ * located in byte 3).
+ ***************************************************************************
+ */
+
+/*
+** uncoment the following line to disable assertions,
+** this may boost performance by up to 50%
+*/
+/* #define NDEBUG */
+
+#ifndef NO_ASM
+#define HAS_BITOPS
+#endif
+
+#include <stdio.h>
+
+#include <assert.h>
+
+#include "bit_array.h"
+#if defined(HAS_BITOPS)
+# include <asm/bitops.h>
+#else
+static __inline__ int clear_bit(int bit, int *mem);
+static __inline__ int set_bit(int bit, int *mem);
+#endif /* HAS_BITOPS */
+
+
+#define INT_NR(bit_nr) ((bit_nr) >> INT_LOG2)
+#define INT_COUNT(bit_count) INT_NR( bit_count + BITS_PER_INT - 1 )
+#define BIT_IN_INT(bit_nr) ((bit_nr) & (BITS_PER_INT - 1))
+
+#if !defined(HAS_BITOPS)
+
+/* first_zero maps bytes value to the index of first zero bit */
+static char first_zero[256];
+static int arrays_initialized=0;
+
+
+/*
+** initialize static arrays used for bit operations speedup.
+** Currently initialized: first_zero[256]
+** set "arrays_initialized" to inidate that arrays where initialized
+*/
+
+static void initialize_arrays()
+{
+ int i;
+ int bit;
+
+ for (i=0 ; i<256 ; i++) {
+ /* find the first zero bit in `i' */
+ for (bit=0 ; bit < BITS_PER_BYTE ; bit++)
+ /* break if the bit is zero */
+ if ( ( (1 << bit) & i )
+ == 0)
+ break;
+ first_zero[i]= bit;
+ }
+ arrays_initialized=1;
+}
+
+/*
+** Find first zero bit in the integer.
+** Assume there is at least one zero.
+*/
+static __inline__ int find_zbit_in_integer(unsigned int integer)
+{
+ int i;
+
+ /* find the zero bit */
+ for (i=0 ; i < sizeof(int) ; i++, integer>>=8) {
+ int byte= integer & 0xff;
+
+ if (byte != 0xff)
+ return ( first_zero[ byte ]
+ + (i << BYTE_LOG2) );
+ }
+ assert(0); /* never reached */
+ return 0;
+}
+
+/* return -1 on failure */
+static __inline__ int find_first_zero_bit(unsigned *array, int bits)
+{
+ unsigned int integer;
+ int i;
+ int bytes=INT_COUNT(bits);
+
+ if (!arrays_initialized)
+ initialize_arrays();
+
+ for ( i=bytes ; i ; i--, array++) {
+ integer= *array;
+
+ /* test if integer contains a zero bit */
+ if (integer != ~0U)
+ return ( find_zbit_in_integer(integer)
+ + ((bytes-i) << INT_LOG2) );
+ }
+
+ /* indicate failure */
+ return -1;
+}
+
+static __inline__ int test_bit(int pos, unsigned *array)
+{
+ unsigned int integer;
+ int bit = BIT_IN_INT(pos);
+
+ integer= array[ pos >> INT_LOG2 ];
+
+ return ( (integer & (1 << bit)) != 0
+ ? 1
+ : 0 ) ;
+}
+
+/*
+** The following two functions are x86 specific ,
+** other processors will need porting
+*/
+
+/* inputs: bit number and memory address (32 bit) */
+/* output: Value of the bit before modification */
+static __inline__ int clear_bit(int bit, int *mem)
+{
+ int ret;
+
+ __asm__("xor %1,%1
+ btrl %2,%0
+ adcl %1,%1"
+ :"=m" (*mem), "=&r" (ret)
+ :"r" (bit));
+ return (ret);
+}
+
+static __inline__ int set_bit(int bit, int *mem)
+{
+ int ret;
+ __asm__("xor %1,%1
+ btsl %2,%0
+ adcl %1,%1"
+ :"=m" (*mem), "=&r" (ret)
+ :"r" (bit));
+ return (ret);
+}
+
+#endif /* !deined(HAS_BITOPS) */
+
+
+/* AssembleArray: assemble an array object using existing data */
+bit_array *AssembleArray(bit_array *new_array, unsigned int *buff, int bits)
+{
+ assert(new_array!=NULL);
+ assert(buff!=NULL);
+ assert(bits>0);
+ assert((1 << INT_LOG2) == BITS_PER_INT); /* if fails, redefine INT_LOG2 */
+
+ new_array->bits=bits;
+ new_array->array=buff;
+ return new_array;
+}
+
+/* ResetArray: reset the bit array to zeros */
+int ResetArray(bit_array *bits)
+{
+ int i;
+ int *p;
+
+ assert(bits!=NULL);
+ assert(bits->array!=NULL);
+
+ for(i= INT_COUNT(bits->bits), p=bits->array; i ; p++, i--)
+ *p=0;
+ return 1;
+}
+
+
+/* VacantBit: find a vacant (zero) bit in the array,
+ * Return: Bit index on success, -1 on failure.
+ */
+int VacantBit(bit_array *bits)
+{
+ int bit;
+
+ assert(bits!=NULL);
+ assert(bits->array!=NULL);
+
+ bit= find_first_zero_bit(bits->array, bits->bits);
+
+ if (bit >= bits->bits) /* failed? */
+ return -1;
+
+ return bit;
+}
+
+int SampleBit(bit_array *bits, int i)
+{
+ assert(bits != NULL);
+ assert(bits->array != NULL);
+ assert(i >= 0 && i < bits->bits);
+
+ return ( test_bit(i,bits->array) != 0
+ ? 1
+ : 0
+ );
+}
+
+
+/*
+** Use "compare and exchange" mechanism to make sure
+** that bits are not modified while "integer" value
+** is calculated.
+**
+** This may be the slowest technique, but it is the most portable
+** (Since most architectures have compare and exchange command)
+*/
+int AssignBit(bit_array *bits, int bit_nr, int val)
+{
+ int ret;
+
+ assert(bits != NULL);
+ assert(bits->array != NULL);
+ assert(val==0 || val==1);
+ assert(bit_nr >= 0 && bit_nr < bits->bits);
+
+ if (val==0)
+ ret= clear_bit(BIT_IN_INT(bit_nr), &bits->array[ INT_NR(bit_nr) ]);
+ else
+ ret= set_bit(BIT_IN_INT(bit_nr), &bits->array[ INT_NR(bit_nr) ]);
+
+ return ( (ret!=0) ? 1 : 0);
+}
+
+/*
+** Allocate a free bit (==0) and make it used (==1).
+** This operation is guaranteed to resemble an atomic instruction.
+**
+** Return: allocated bit index, or -1 on failure.
+**
+** There is a crack between locating free bit, and allocating it.
+** We assign 1 to the bit, test it was not '1' before the assignment.
+** If it was, restart the seek and assign cycle.
+**
+*/
+
+int AllocateBit(bit_array *bits)
+{
+ int bit_nr;
+ int orig_bit;
+
+ assert(bits != NULL);
+ assert(bits->array != NULL);
+
+ do {
+ bit_nr= VacantBit(bits);
+
+ if (bit_nr == -1) /* No vacant bit ? */
+ return -1;
+
+ orig_bit = AssignBit(bits, bit_nr, 1);
+ } while (orig_bit != 0); /* it got assigned before we tried */
+
+ return bit_nr;
+}
diff --git a/ipc/bit_array_test.c b/ipc/bit_array_test.c
new file mode 100644
index 0000000..14ecb34
--- /dev/null
+++ b/ipc/bit_array_test.c
@@ -0,0 +1,93 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "bit_array.h"
+#define SIZE (8*sizeof(int)*3)
+
+static bit_array array;
+static int simple_array[SIZE];
+static int bits;
+
+int are_equal()
+{
+ int i;
+ for (i=0 ; i < SIZE ; i++)
+ if (SampleBit(&array,i) != simple_array[i]){
+ printf("failed bit %d (packed=%d, simple=%d)\n", i,
+ SampleBit(&array,i), simple_array[i]);
+ return 0;
+ }
+ return 1;
+}
+
+int is_same_vacant()
+{
+ int vacant;
+ for (vacant =0 ; simple_array[vacant]!=0 ; vacant++)
+ if ( vacant >= SIZE) {
+ vacant=-1;
+ break;
+ }
+
+
+ if ( VacantBit(&array) == vacant )
+ return 1;
+ else
+ return 0;
+}
+void assign_both(int bit_nr, int bit_val)
+{
+ int old_bit= simple_array[bit_nr];
+
+ simple_array[bit_nr]= bit_val;
+
+ bits+=bit_val - old_bit;
+
+ assert(AssignBit(&array, bit_nr, bit_val) == old_bit);
+ assert(are_equal());
+ assert(is_same_vacant());
+}
+
+
+int main()
+{
+ unsigned int integers[SIZE >> 5];
+ int i,j;
+
+ assert( AssembleArray(&array, integers, SIZE)
+ == &array);
+ ResetArray(&array);
+ for (i=0 ; i<SIZE ; i++)
+ simple_array[i]=0;
+
+ for (j=5 ; j ; j--) {
+ printf("\rleft %d\r",j);
+
+ for (i=0 ; VacantBit(&array) != -1 ; i++ ) {
+ if (i % 256 == 0) {
+ printf("left %d ",j);
+ printf("%3d up \r", bits);
+ fflush(stdout);
+ }
+ assign_both(rand() % SIZE,
+ (rand()% SIZE > bits ) ? 0 : 1 );
+ }
+
+ assign_both(rand() % SIZE, 1);
+
+ for (i=0 ; bits ; i++ ) {
+ if (i % 256 == 0) {
+ printf("left %d ",j);
+ printf("%3d down\r", bits);
+ fflush(stdout);
+ }
+ assign_both(rand() % SIZE,
+ (rand()% SIZE <= (SIZE-bits) ) ? 0 : 1 );
+ }
+
+ assign_both(rand() % SIZE, 0);
+ }
+
+ putchar('\n');
+ return 0;
+}
diff --git a/ipc/dde_atom.c b/ipc/dde_atom.c
new file mode 100644
index 0000000..c6ca2aa
--- /dev/null
+++ b/ipc/dde_atom.c
@@ -0,0 +1,273 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: dde_atom.c
+ * Purpose : atom functionality for DDE
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "dde_atom.h"
+#include "shm_main_blk.h"
+#include "shm_fragment.h"
+#include "stddebug.h"
+#include "debug.h"
+
+typedef struct
+{
+ WORD count;
+ BYTE str[1];
+} AtomData, *AtomData_ptr;
+
+#define EMPTY 0 /* empty hash entry */
+#define DELETED -1 /* deleted hash entry */
+#define MIN_STR_ATOM 0xfc00
+
+/* OFS2AtomData_ptr: extract AtomData_ptr from ofs */
+#define OFS2AtomData_ptr(ofs) ((AtomData*)((int)&main_block->block+(ofs)))
+
+/* OFS2AtomStr: find the string of the atom */
+#define OFS2AtomStr(ofs) (OFS2AtomData_ptr(atom_ofs)->str)
+
+/* offset of an atom according to index */
+#define ATOM_OFS(idx) (main_block->atoms[idx])
+
+/* rot_left: rotate (with wrap-around) */
+static __inline__ int rot_left(unsigned var,int count)
+{
+ return (var<<count) | (var>> (sizeof(var)-count));
+}
+/* find the entry in the atom table for this string */
+static int FindHash(LPCSTR str) /* ignore str case */
+{
+ int i,j;
+ unsigned hash1,hash2;
+ int deleted=-1; /* hash for deleted entry */
+ int atom_ofs;
+
+ /* get basic hash parameters */
+ for (i= hash1= hash2= 0; str[i] ; i++) {
+ hash1= rot_left(hash1,5) ^ toupper(str[i]);
+ hash2= rot_left(hash2,4) ^ toupper(str[i]);
+ }
+
+ hash1%= DDE_ATOMS;
+ atom_ofs=ATOM_OFS(hash1);
+ switch (atom_ofs) {
+ case EMPTY: /* empty atom entry */
+ return hash1;
+ case DELETED: /* deleted atom entry */
+ deleted=hash1;
+ break;
+ default : /* non empty atom entry */
+ if ( strcasecmp( OFS2AtomStr(atom_ofs) , str) == 0)
+ return hash1; /* found string in atom table */
+ }
+ hash2%= DDE_ATOMS-1 ; /* hash2=0..(DDE_ATOMS-2) */
+ hash2++; /* hash2=1..(DDE_ATOMS-1) */
+
+ /* make jumps in the hash table by hash2 steps */
+ for (i=hash1+hash2 ; ; i+=hash2) {
+ /* i wraps around into j */
+ j=i-DDE_ATOMS;
+ if (j >= 0)
+ i=j; /* i wraps around */
+
+ if (i==hash1)
+ /* here if covered all hash locations, and got back to beginning */
+ return deleted; /* return first empty entry - if any */
+ atom_ofs=ATOM_OFS(i);
+ switch (atom_ofs) {
+ case EMPTY: /* empty atom entry */
+ return i;
+ case DELETED: /* deleted atom entry */
+ if (deleted < 0)
+ /* consider only the first deleted entry */
+ deleted= i;
+ break;
+ default : /* nonempty atom entry */
+ if ( strcasecmp( OFS2AtomStr(atom_ofs) , str) == 0)
+ return i; /* found string in atom table */
+ }
+ }
+}
+
+void ATOM_GlobalInit(void)
+{
+ int i;
+
+ for (i=0 ; i < DDE_ATOMS ; i++)
+ ATOM_OFS(i)=EMPTY;
+}
+
+/***********************************************************************
+ * GlobalAddAtom (USER.268)
+ */
+
+/* important! don't forget to unlock semaphores before return */
+ATOM GlobalAddAtom( LPCSTR str )
+{
+ int atom_idx;
+ int atom_ofs;
+ AtomData_ptr ptr;
+ ATOM atom;
+
+ dprintf_atom(stddeb,"GlobalAddAtom(%p)\n", str);
+ if ((unsigned) str < MIN_STR_ATOM) /* MS-windows convention */
+ return (ATOM) (unsigned) str;
+ if (str[0] == '#') { /* wine convention */
+ atom= (ATOM) atoi(&str[1]);
+ return (atom<MIN_STR_ATOM) ? atom : 0;
+ }
+ dprintf_atom(stddeb,"GlobalAddAtom(\"%s\")\n",str);
+
+ DDE_IPC_init(); /* will initialize only if needed */
+
+ shm_write_wait(main_block->sem);
+
+ atom_idx=FindHash(str);
+ atom=(ATOM)0;
+
+ /* use "return" only at the end so semaphore handling is done only once */
+ if (atom_idx>=0) {
+ /* unless table full and item not found */
+ switch (atom_ofs= ATOM_OFS(atom_idx)) {
+ case DELETED:
+ case EMPTY: /* need to allocate new atom */
+ atom_ofs= shm_FragmentAlloc(&main_block->block,
+ strlen(str)+sizeof(AtomData));
+ if (atom_ofs==NIL)
+ break; /* no more memory (atom==0) */
+ ATOM_OFS(atom_idx)=atom_ofs;
+ ptr=OFS2AtomData_ptr(atom_ofs);
+ strcpy(ptr->str,str);
+ ptr->count=1;
+ atom=(ATOM)(atom_idx+MIN_STR_ATOM);
+ break;
+ default : /* has to update existing atom */
+ OFS2AtomData_ptr(atom_ofs)->count++;
+ atom=(ATOM)(atom_idx+MIN_STR_ATOM);
+ } /* end of switch */
+ } /* end of if */
+ shm_write_signal(main_block->sem);
+ return atom;
+}
+
+/***********************************************************************
+ * GlobalDeleteAtom (USER.269)
+ */
+
+ATOM GlobalDeleteAtom( ATOM atom )
+{
+ int atom_idx;
+ int atom_ofs;
+ AtomData_ptr atom_ptr;
+ ATOM retval=(ATOM) 0;
+
+ dprintf_atom(stddeb,"GlobalDeleteAtom(\"%d\")\n",(int)atom);
+ atom_idx=(int)atom - MIN_STR_ATOM;
+
+ if (atom_idx < 0 )
+ return 0;
+
+ DDE_IPC_init(); /* will initialize only if needed */
+
+ shm_write_wait(main_block->sem);
+ /* return used only once from here on -- for semaphore simplicity */
+ switch (atom_ofs=ATOM_OFS(atom_idx)) {
+ case DELETED:
+ case EMPTY:
+ fprintf(stderr,"trying to free unallocated atom %d\n", atom);
+ retval=atom;
+ break;
+ default :
+ atom_ptr=OFS2AtomData_ptr(atom_ofs);
+ if ( --atom_ptr->count == 0) {
+ shm_FragmentFree(&main_block->block,atom_ofs);
+ ATOM_OFS(atom_idx)=DELETED;
+ }
+ }
+
+ shm_write_signal(main_block->sem);
+ return retval;
+}
+
+/***********************************************************************
+ * GlobalFindAtom (USER.270)
+ */
+ATOM GlobalFindAtom( LPCSTR str )
+{
+ int atom_idx;
+ int atom_ofs;
+
+ dprintf_atom(stddeb,"GlobalFindAtom(%p)\n", str );
+ if ((unsigned) str < MIN_STR_ATOM) /* MS-windows convention */
+ return (ATOM) (unsigned) str;
+ if (str[0] == '#') { /* wine convention */
+ ATOM atom= (ATOM) atoi(&str[1]);
+ return (atom<MIN_STR_ATOM) ? atom : 0;
+ }
+ dprintf_atom(stddeb,"GlobalFindAtom(\"%s\")\n",str);
+
+ DDE_IPC_init(); /* will initialize only if needed */
+
+ shm_read_wait(main_block->sem);
+ atom_idx=FindHash(str);
+ if (atom_idx>=0)
+ atom_ofs=ATOM_OFS(atom_idx); /* is it free ? */
+ else
+ atom_ofs=EMPTY;
+ shm_read_signal(main_block->sem);
+
+ if (atom_ofs==EMPTY || atom_ofs==DELETED)
+ return 0;
+ else
+ return (ATOM)(atom_idx+MIN_STR_ATOM);
+}
+
+/***********************************************************************
+ * GlobalGetAtomName (USER.271)
+ */
+WORD GlobalGetAtomName( ATOM atom, LPSTR buffer, short count )
+{
+ int atom_idx, atom_ofs;
+ int size;
+ /* temporary buffer to hold maximum "#65535\0" */
+ char str_num[7];
+
+ if (count<2) /* no sense to go on */
+ return 0;
+ atom_idx=(int)atom - MIN_STR_ATOM;
+
+ if (atom_idx < 0) { /* word atom */
+ /* use wine convention... */
+ sprintf(str_num,"#%d%n",(int)atom,&size);
+ if (size+1>count) { /* overflow ? */
+ /* truncate the string */
+ size=count-1;
+ str_num[size]='\0';
+ }
+ strcpy(buffer,str_num);
+ return size;
+ }
+
+ DDE_IPC_init(); /* will initialize only if needed */
+
+ /* string atom */
+ shm_read_wait(main_block->sem);
+ atom_ofs=ATOM_OFS(atom_idx);
+ if (atom_ofs==EMPTY || atom_ofs==DELETED) {
+ fprintf(stderr,"GlobalGetAtomName: illegal atom=%d\n",(int)atom);
+ size=0;
+ } else { /* non empty entry */
+ /* string length will be at most count-1, find actual size */
+ sprintf(buffer,"%.*s%n",count-1, OFS2AtomStr(atom_ofs), &size);
+ }
+ shm_read_signal(main_block->sem);
+ return size;
+}
+
diff --git a/ipc/dde_atom_test.c b/ipc/dde_atom_test.c
new file mode 100644
index 0000000..dd587c4
--- /dev/null
+++ b/ipc/dde_atom_test.c
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: dde_atom_test.c
+ * Purpose : tests for dde_atom object
+ ***************************************************************************
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <win.h>
+#include "dde_atom.h"
+#include "shm_main_blk.h"
+#include <stddebug.h>
+#include <debug.h>
+#define TOGETHER (DDE_ATOMS/5)
+
+
+/* run random sequences */
+int main()
+{
+ ATOM atom_list[TOGETHER];
+ char str[TOGETHER][80];
+ int i,j,atom_n;
+ int atom_len[TOGETHER];
+
+ debugging_shm=1;
+ debugging_atom=0;
+ debugging_sem=0;
+
+ for (i=0 ; i<=10000/TOGETHER ; i++) {
+ for (atom_n=0 ; atom_n<TOGETHER ; atom_n++) {
+ atom_len[atom_n]=rand()%64+1;
+ for (j=atom_len[atom_n]-1; j>=0; j--)
+ do {
+ str[atom_n][j]=(char)(rand()%255+1);
+ } while (j==0 && str[atom_n][j]=='#');
+
+ str[atom_n][ atom_len[atom_n] ]='\0';
+
+ atom_list[atom_n]=GlobalAddAtom(str[atom_n]);
+
+ if (atom_list[atom_n]==0) {
+ fprintf(stderr,"failed i=%d, atom_n=%d\n",i,atom_n);
+ return 1;
+ }
+ if (atom_list[atom_n]!=GlobalAddAtom(str[atom_n])) {
+ fprintf(stderr,
+ "wrong second GlobalAddAtom(\"%s\")\n", str[atom_n]);
+ return 1;
+ }
+ } /* for */
+ for (atom_n=0 ; atom_n<TOGETHER ; atom_n++) {
+ char buf[80];
+ int len;
+
+ len=GlobalGetAtomName( atom_list[atom_n], buf, 79);
+ if (atom_len[atom_n] != len) {
+ fprintf(stderr, "i=%d, atom_n=%d; ", i, atom_n);
+ fprintf(stderr,
+ "wrong length of GlobalGetAtomName(\"%s\")\n",
+ str[atom_n]);
+
+ return 1;
+ }
+
+ }
+ for (atom_n=0 ; atom_n<TOGETHER ; atom_n++) {
+ GlobalDeleteAtom(atom_list[atom_n]);
+ if (atom_list[atom_n]!=GlobalAddAtom(str[atom_n])) {
+ fprintf(stderr, "i=%d, atom_n=%d; ", i, atom_n);
+ fprintf(stderr,
+ "wrong third GlobalAddAtom(\"%s\")\n", str[atom_n]);
+ return 1;
+ }
+ GlobalDeleteAtom(atom_list[atom_n]);
+ GlobalDeleteAtom(atom_list[atom_n]);
+
+ atom_list[atom_n]=GlobalAddAtom(str[atom_n]);
+ if (atom_list[atom_n]!=GlobalAddAtom(str[atom_n])) {
+ fprintf(stderr,
+ "i=%d, atom_n=%d wrong fifth GlobalAddAtom(\"%s\")\n",
+ i, atom_n,
+ str[atom_n]);
+ return 1;
+ }
+ GlobalDeleteAtom(atom_list[atom_n]);
+ if (atom_list[atom_n]!=GlobalFindAtom(str[atom_n])) {
+ fprintf(stderr,
+ "i=%d, atom_n=%d wrong GlobalFindAtom(\"%s\")\n",
+ i, atom_n,
+ str[atom_n]);
+ return 1;
+ }
+ GlobalDeleteAtom(atom_list[atom_n]);
+ }
+ }
+ return 0;
+}
diff --git a/ipc/dde_mem.c b/ipc/dde_mem.c
new file mode 100644
index 0000000..f19b013
--- /dev/null
+++ b/ipc/dde_mem.c
@@ -0,0 +1,282 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: dde_mem.c
+ * Purpose : shared DDE memory functionality for DDE
+ ***************************************************************************
+ */
+#include <stdio.h>
+#include <stddebug.h>
+#include <debug.h>
+#include <assert.h>
+#include "ldt.h"
+#include "shm_main_blk.h"
+#include "shm_fragment.h"
+#include "shm_semaph.h"
+#include "dde_mem.h"
+#include "bit_array.h"
+
+#define SEGPTR2HANDLE_INFO(sptr) ( (struct handle_info*)PTR_SEG_TO_LIN(sptr) )
+
+#define HINFO2DATAPTR(h_info_ptr) ( (void*) ( (char*)h_info_ptr + \
+ sizeof(struct handle_info) ) )
+#define DDE_MEM_IDX(handle) ((handle)& 0x7fff)
+#define DDE_MEM_HANDLE(idx) ((idx) | 0x8000)
+#define DDE_MEM_INFO(handle) (main_block->handles[ DDE_MEM_IDX(handle) ])
+/* List of shared handles.
+ * This entry resides on the shared memory, the data comes right
+ * after the `handle_info'.
+ * The entry is on the same block as the actual data.
+ * The `next' field gives relative reference (relative to the start of
+ * the blcok.
+ */
+struct handle_info {
+ WORD lock_count;
+ WORD flags;
+ int size; /* size of the data (net)*/
+};
+
+static bit_array free_handles;
+int debug_last_handle_size= 0; /* for debugging purpose only */
+
+
+/* locate_handle:
+ * locate a shared memory handle.
+ * Application:
+ * The handle is first searched for in attached blocks.
+ * At the beginning, only blocks owned by this process are
+ * attached.
+ * If a handle is not found, new blocks are attached.
+ * Arguments:
+ * h - the handle.
+ * RETURN: pointer to handle info.
+ */
+static struct handle_info *locate_handle(HGLOBAL h, struct local_shm_map *map)
+{
+ struct shm_block *block;
+
+ dprintf_global(stddeb,"shm:locate_handle(0x%04x)\n", h);
+
+
+ if (SampleBit( &free_handles, DDE_MEM_IDX(h)) == 0) {
+ dprintf_global(stddeb, "shm:locate_handle: return NULL\n");
+ return NULL; /* free!!! */
+ }
+
+ block= shm_locate_block(DDE_MEM_INFO(h).shmid, map);
+ if (block == NULL) {
+ /* nothing found */
+ dprintf_global(stddeb, "shm:locate_handle: return NULL\n");
+ return NULL;
+ }
+
+ return (struct handle_info *) REL2PTR(block, DDE_MEM_INFO(h).rel);
+
+}
+
+/* dde_alloc_handle: allocate shared DDE handle */
+static HGLOBAL dde_alloc_handle()
+{
+ int bit_nr;
+
+ bit_nr= AllocateBit( &free_handles);
+
+ if (bit_nr != -1)
+ return DDE_MEM_HANDLE(bit_nr);
+
+ dprintf_global(stddeb,"dde_alloc_handle: no free DDE handle found\n");
+ return 0;
+}
+/**********************************************************************
+ * DDE_malloc
+ */
+void *
+DDE_malloc(unsigned int flags, unsigned long size, SHMDATA *shmdata)
+{
+ int shmid;
+ struct shm_block *block;
+ struct handle_info *h_info;
+ struct local_shm_map *curr;
+ HGLOBAL handle;
+
+ dprintf_global(stddeb,"DDE_malloc flags %4X, size %ld\n", flags, size);
+ DDE_IPC_init(); /* make sure main shm block allocated */
+
+ shm_write_wait(main_block->proc[curr_proc_idx].sem);
+
+ /* Try to find fragment big enough for `size' */
+ /* iterate through all local shm blocks, and try to allocate
+ the fragment */
+
+ h_info= NULL;
+ for (curr= shm_map ; curr != NULL ; curr= curr->next) {
+ if (curr->proc_idx == curr_proc_idx) {
+ h_info= (struct handle_info *)
+ shm_FragPtrAlloc(curr->ptr, size+sizeof(struct handle_info));
+ if (h_info!=NULL) {
+ shmid= curr->shm_id;
+ break;
+ }
+ }
+ }
+
+ if (h_info == NULL) {
+
+ block= shm_create_block(0, size+sizeof(struct handle_info), &shmid);
+ if (block==NULL) {
+ shm_write_signal(main_block->proc[curr_proc_idx].sem);
+ return 0;
+ }
+ /* put the new block in the linked list */
+ block->next_shm_id= main_block->proc[curr_proc_idx].shmid;
+ main_block->proc[curr_proc_idx].shmid= shmid;
+ h_info= (struct handle_info *)
+ shm_FragPtrAlloc(block, size+sizeof(struct handle_info));
+ if (h_info==NULL) {
+ fprintf(stderr,"DDE_malloc: BUG! unallocated fragment\n");
+ shm_write_signal(main_block->proc[curr_proc_idx].sem);
+ return 0;
+ }
+ } else {
+ block= curr->ptr;
+ }
+
+ /* Here we have an allocated fragment */
+ h_info->flags= flags;
+ h_info->lock_count= 0;
+ h_info->size= size;
+ handle= dde_alloc_handle();
+
+ if (handle) {
+ dprintf_global(stddeb,
+ "DDE_malloc returning handle=0x%4x, ptr=0x%08lx\n",
+ (int)handle, (long) HINFO2DATAPTR(h_info));
+ DDE_MEM_INFO(handle).rel= PTR2REL(block, h_info);
+ DDE_MEM_INFO(handle).shmid= shmid;
+ }
+ else
+ dprintf_global(stddeb,"DDE_malloc failed\n");
+
+ shm_write_signal(main_block->proc[curr_proc_idx].sem);
+
+ shmdata->handle= handle;
+ return (char *)HINFO2DATAPTR(h_info);
+}
+
+HGLOBAL DDE_GlobalFree(HGLOBAL h)
+{
+ struct handle_info *h_info;
+ int handle_index= h & 0x7fff;
+ struct local_shm_map map;
+
+ dprintf_global(stddeb,"DDE_GlobalFree(0x%04x)\n",h);
+
+ if (h==0)
+ return 0;
+
+ h_info= locate_handle(h, &map);
+ if (h_info == NULL)
+ return h;
+
+ shm_write_wait(main_block->proc[map.proc_idx].sem);
+
+ shm_FragPtrFree(map.ptr, (struct shm_fragment *) h_info);
+
+ AssignBit( &free_handles, handle_index, 0);
+
+ /* FIXME: must free the shm block some day. */
+ shm_write_signal(main_block->proc[map.proc_idx].sem);
+ return 0;
+}
+
+WORD DDE_SyncHandle(HGLOBAL handle, WORD sel)
+
+{
+ struct handle_info *h_info;
+ void *local_ptr;
+ ldt_entry entry;
+
+ h_info= locate_handle(handle, NULL);
+ local_ptr= (void *)GET_SEL_BASE(sel);
+
+
+ if (h_info == NULL)
+ return 0;
+
+ if (local_ptr == (void *) HINFO2DATAPTR(h_info))
+ return sel;
+
+ /* need syncronization ! */
+ LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
+ entry.base= (unsigned long) HINFO2DATAPTR(h_info);
+ LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
+
+ return sel;
+}
+
+/*
+ * DDE_AttachHandle:
+ * Attach shm memory (The data must not be already attached).
+ * Parameters:
+ * handle - the memory to attach.
+ * segptr - in not null, return SEGPTR to the same block.
+ * return value:
+ * 32 bit pointer to the memory.
+ */
+
+void *DDE_AttachHandle(HGLOBAL handle, SEGPTR *segptr)
+{
+ struct handle_info *h_info;
+ SHMDATA shmdata;
+ void *ptr;
+ HGLOBAL hOwner = GetCurrentPDB();
+
+ assert(is_dde_handle(handle));
+ if (segptr != NULL)
+ *segptr=0;
+
+ dprintf_global(stddeb,"DDE_AttachHandle(%04x)\n",handle);
+ h_info=locate_handle(handle, NULL);
+
+ if (h_info == NULL)
+ return NULL;
+
+ if ( !(h_info->flags & GMEM_DDESHARE) ) {
+ fprintf(stderr,"DDE_AttachHandle: Corrupted memory handle info\n");
+ return NULL;
+ }
+
+ dprintf_global(stddeb,"DDE_AttachHandle: h_info=%06lx\n",(long)h_info);
+
+ shmdata.handle= handle;
+ shmdata.shmid= DDE_MEM_INFO(handle).shmid;
+
+ ptr= HINFO2DATAPTR(h_info);
+ /* Allocate the selector(s) */
+ if (! GLOBAL_CreateBlock( h_info->flags, ptr, h_info->size, hOwner,
+ FALSE, FALSE, FALSE, &shmdata))
+ return NULL;
+
+ if (segptr != NULL)
+ *segptr= (SEGPTR)MAKELONG( 0, shmdata.sel);
+
+ if (debugging_dde)
+ debug_last_handle_size= h_info->size;
+
+ dprintf_global(stddeb,"DDE_AttachHandle returns ptr=0x%08lx\n", (long)ptr);
+
+ return (LPSTR)ptr;
+
+}
+
+void DDE_mem_init()
+{
+ int nr_of_bits;
+
+ shm_init();
+
+ nr_of_bits= BITS_PER_BYTE * sizeof(main_block->free_handles);
+ AssembleArray( &free_handles, main_block->free_handles, nr_of_bits);
+}
diff --git a/ipc/dde_mem_test.c b/ipc/dde_mem_test.c
new file mode 100644
index 0000000..2fe42a5
--- /dev/null
+++ b/ipc/dde_mem_test.c
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: dde_mem_test.c
+ * Purpose : test shared DDE memory functionality for DDE
+ * Usage: Look for assertion failures
+ ***************************************************************************
+ */
+#include <stdio.h>
+#include <assert.h>
+#include <win.h>
+#include "dde_mem.h"
+/* stub */
+
+void ATOM_GlobalInit()
+{
+ printf("ATOM_GlobalInit\n");
+}
+
+
+int main()
+{
+ HWND h1,h2,h3;
+ int ret;
+ void *p1,*p2,*p3,*p;
+ SHMDATA shmdata;
+
+ /* alloc h1, h2, h3 */
+
+ setbuf(stdout,NULL);
+ p1=DDE_malloc(GMEM_DDESHARE, 0x6000, &shmdata);
+ h1= shmdata.handle;
+ assert(p1 != NULL);
+ assert(h1 != 0);
+ p2=DDE_malloc(GMEM_DDESHARE, 0xff00, &shmdata);
+ h2= shmdata.handle;
+ assert(p2 != NULL);
+ assert(h2 != 0);
+ p3=DDE_malloc(GMEM_DDESHARE, 0x6000, &shmdata);
+ h3= shmdata.handle;
+ assert(p3 != 0);
+ assert(h3 != 0);
+
+ /* lock h1, h2, h3 */
+ p=DDE_AttachHandle(h1,NULL);
+ assert(p1==p);
+ p=DDE_AttachHandle(h2,NULL);
+ assert(p2==p);
+ p=DDE_AttachHandle(h3,NULL);
+ assert(p3==p);
+
+
+
+ ret=DDE_GlobalFree(h1);
+ assert(ret==0);
+ /* do some implementation dependant tests */
+ p=DDE_malloc(GMEM_DDESHARE, 0x6000, &shmdata);
+ assert(p!=NULL);
+ assert(shmdata.handle==h1);
+ p=DDE_AttachHandle(h1,NULL);
+ assert(p1==p);
+
+ /* check freeing */
+ ret=DDE_GlobalFree(h1);
+ assert(ret==0);
+ ret=DDE_GlobalFree(h2);
+ assert(ret==0);
+ ret=DDE_GlobalFree(h3);
+ assert(ret==0);
+ return 0;
+}
diff --git a/ipc/dde_proc.c b/ipc/dde_proc.c
new file mode 100644
index 0000000..9f5f59e
--- /dev/null
+++ b/ipc/dde_proc.c
@@ -0,0 +1,718 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: dde_proc.c
+ * Purpose : DDE signals and processes functionality for DDE
+ ***************************************************************************
+ */
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/msg.h>
+#include "wintypes.h"
+#include "win.h"
+#include "shm_semaph.h"
+#include "shm_main_blk.h"
+#include "dde_proc.h"
+#include "dde_mem.h"
+#include "dde.h"
+#include "stddebug.h"
+#include "debug.h"
+
+int curr_proc_idx= -1;
+
+enum stop_wait_op stop_wait_op=CONT;
+int had_SIGUSR2 = 0;
+sigjmp_buf env_get_ack;
+sigjmp_buf env_wait_x;
+
+#define IDX_TO_HWND(idx) (0xfffe - (idx))
+#define HWND_TO_IDX(wnd) (0xfffe - (wnd))
+#define DDE_WIN_INFO(win) ( main_block->windows[HWND_TO_IDX(win)] )
+#define DDE_WIN2PROC(win) ( DDE_WIN_INFO(win).proc_idx )
+#define DDE_IsRemoteWindow(win) ( (win)<0xffff && (win)>=(0xffff-DDE_PROCS))
+#define DDE_SEND 1
+#define DDE_POST 2
+#define DDE_ACK 3
+#define DDE_MSG_SIZE sizeof(MSG)
+#define FREE_WND (WORD)(-2)
+#define DELETED_WND (WORD)(-3)
+#if defined(DEBUG_MSG) || defined(DEBUG_RUNTIME)
+static char *msg_type[4]={"********", "DDE_SEND", "DDE_POST", "DDE_ACK"};
+#endif
+
+struct msg_dat {
+ struct msgbuf dat;
+ char filler[DDE_MSG_SIZE];
+} ;
+
+typedef struct fifo_element {
+ int value;
+ struct fifo_element *next;
+} fifo_element;
+
+struct fifo {
+ fifo_element *first; /* first element in the fifo or NULL */
+ fifo_element *last; /* last element in the fifo or NULL */
+};
+static struct fifo fifo = {NULL,NULL};
+
+void dde_proc_delete(int proc_idx);
+
+void dde_proc_add_fifo(int val)
+{
+ fifo_element *created;
+
+ created= (fifo_element*) malloc( sizeof(fifo_element) );
+ created->value = val;
+ created->next = NULL;
+
+ if (fifo.first==NULL)
+ fifo.first= created;
+ else
+ fifo.last->next= created;
+ fifo.last = created;
+}
+
+/* get an item from the fifo, and return it.
+ * If fifo is empty, return -1
+ */
+int dde_proc_shift_fifo()
+{
+ int val;
+ fifo_element *deleted;
+
+ if (fifo.first == NULL)
+ return -1;
+
+ deleted= fifo.first;
+ val= deleted->value;
+ fifo.first= deleted->next;
+ if (fifo.first == NULL)
+ fifo.last= NULL;
+
+ free(deleted);
+ return val;
+}
+
+static void print_dde_message(char *desc, MSG *msg);
+
+/* This should be run only when main_block is first allocated. */
+void dde_proc_init(dde_proc proc)
+{
+ int proc_num;
+
+ for (proc_num=0 ; proc_num<DDE_PROCS ; proc_num++, proc++) {
+ proc->msg=-1;
+ proc->sem=-1;
+ proc->shmid=-1;
+ proc->pid=-1;
+ }
+}
+
+/* add current process to the list of processes */
+void dde_proc_add(dde_proc procs)
+{
+ dde_proc proc;
+ int proc_idx;
+ dprintf_dde(stddeb,"dde_proc_add(..)\n");
+ shm_write_wait(main_block->sem);
+
+ /* find free proc_idx and allocate it */
+ for (proc_idx=0, proc=procs ; proc_idx<DDE_PROCS ; proc_idx++, proc++)
+ if (proc->pid==-1)
+ break; /* found! */
+
+ if (proc_idx<DDE_PROCS) { /* got here beacuse a free was found ? */
+ dde_msg_setup(&proc->msg);
+ proc->pid=getpid();
+ curr_proc_idx=proc_idx;
+ shm_sem_init(&proc->sem);
+ }
+ else {
+ fflush(stdout);
+ fprintf(stderr,"dde_proc_add: Can't allocate process\n");
+ }
+ shm_write_signal(main_block->sem);
+}
+
+/* wait for dde - acknowledge message - or timout */
+static BOOL get_ack()
+{
+ struct timeval timeout;
+ int size;
+ struct msg_dat ack_buff;
+
+ /* timeout after exactly one seconf */
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ sigsetjmp(env_get_ack, 1);
+ /* get here after normal execution, or after siglongjmp */
+
+ do { /* loop to wait for DDE_ACK */
+ had_SIGUSR2=0;
+ stop_wait_op=CONT; /* sensitive code: disallow siglongjmp */
+ size= msgrcv( main_block->proc[curr_proc_idx].msg , &ack_buff.dat,
+ 1, DDE_ACK, IPC_NOWAIT);
+ if (size>=0) {
+ dprintf_msg(stddeb,"get_ack: received DDE_ACK message\n");
+ return TRUE;
+ }
+ if (DDE_GetRemoteMessage()) {
+ had_SIGUSR2=1; /* might have recieved SIGUSR2 */
+ }
+ stop_wait_op=STOP_WAIT_ACK; /* allow siglongjmp */
+
+ } while (had_SIGUSR2); /* loop if SIGUSR2 was recieved */
+
+ /* siglongjmp should be enabled at this moment */
+ select( 0, NULL, NULL, NULL, &timeout );
+ stop_wait_op=CONT; /* disallow further siglongjmp */
+
+ /* timeout !! (otherwise there would have been a siglongjmp) */
+ return FALSE;
+}
+
+/* Transfer one message to a given process */
+static BOOL DDE_DoOneMessage (int proc_idx, int size, struct msgbuf *msgbuf)
+{
+ dde_proc proc= &main_block->proc[ proc_idx ];
+
+
+ if (proc_idx == curr_proc_idx)
+ return FALSE;
+
+ if (kill(proc->pid,0) < 0) {
+ /* pid does not exist, or not our */
+ dde_proc_delete(proc_idx);
+ return FALSE;
+ }
+
+ if (debugging_dde) {
+ MSG *msg=(MSG*) &msgbuf->mtext;
+ char *title;
+ if (msgbuf->mtype==DDE_SEND)
+ title="sending dde:";
+ else if (msgbuf->mtype==DDE_POST)
+ title="posting dde:";
+ else
+ title=NULL;
+ if (title)
+ print_dde_message(title, msg);
+ else
+ fprintf(stddeb,"Unknown message type=0x%lx\n",msgbuf->mtype);
+ }
+ dprintf_msg(stddeb,
+ "DDE_DoOneMessage: to proc_idx=%d (pid=%d), queue=%u\n",
+ proc_idx, proc->pid, (unsigned)proc->msg);
+ if ( proc->msg != -1) {
+ dprintf_msg(stddeb, "DDE_DoOneMessage: doing...(type=%s)\n",
+ msg_type[msgbuf->mtype]);
+ size=msgsnd (proc->msg, msgbuf, size, 0);
+
+ if (size<0) {
+ fflush(stdout);
+ perror("msgsnd");
+ }
+ kill(proc->pid,SIGUSR2); /* tell the process there is a message */
+
+ dprintf_msg(stddeb,"DDE_DoOneMessage: "
+ "Trying to get acknowledgment from msg queue=%d\n",
+ proc->msg);
+ Yield(); /* force task switch, and */
+ /* acknowledgment sending */
+ if (get_ack()) {
+ return TRUE;
+ } else {
+ fflush(stdout);
+ fprintf(stderr,"get_ack: DDE_DoOneMessage: timeout\n");
+ return FALSE;
+ }
+ }
+ else {
+ dprintf_msg(stddeb,"DDE_DoOneMessage: message not sent, "
+ "target has no message queue\n");
+ return FALSE;
+ }
+}
+
+/* Do some sort of premitive hash table */
+static HWND HWND_Local2Remote(HWND orig)
+{
+ int dde_wnd_idx;
+ int deleted_idx= -1;
+ WND_DATA *tested;
+ WND_DATA *deleted= NULL;
+ int i;
+
+ dde_wnd_idx= orig % DDE_WINDOWS;
+ for ( i=0 ; i < DDE_WINDOWS ; i++, dde_wnd_idx++) {
+ if (dde_wnd_idx >= DDE_WINDOWS)
+ dde_wnd_idx -= DDE_WINDOWS; /* wrap-around */
+
+ tested= &main_block->windows[ dde_wnd_idx ];
+ if (tested->proc_idx == FREE_WND)
+ break;
+
+ if (deleted == NULL && tested->proc_idx == DELETED_WND) {
+ deleted= tested;
+ deleted_idx= dde_wnd_idx;
+ } else if (tested->wnd == orig && tested->proc_idx == curr_proc_idx) {
+ return IDX_TO_HWND(dde_wnd_idx);
+ }
+ }
+ if (deleted != NULL) { /* deleted is preferable */
+ /* free item, allocate it */
+ deleted->proc_idx= curr_proc_idx;
+ deleted->wnd = orig;
+ return IDX_TO_HWND(deleted_idx);
+ }
+ if (tested->proc_idx == FREE_WND) {
+ tested->proc_idx= curr_proc_idx;
+ tested->wnd = orig;
+ return IDX_TO_HWND(dde_wnd_idx);
+ }
+
+ fprintf(stderr,
+ "HWND_Local2Remote: Can't map any more windows to DDE windows\n");
+ return 0;
+}
+
+static BOOL DDE_DoMessage( MSG *msg, int type )
+{
+ int proc_idx;
+
+ MSG *remote_message;
+ struct msg_dat msg_dat;
+ BOOL success;
+
+ if (msg->wParam == 0)
+ return FALSE;
+
+ if (main_block==NULL) {
+ if (msg->message >= WM_DDE_FIRST && msg->message <= WM_DDE_LAST)
+ DDE_IPC_init();
+ else
+ return FALSE;
+ }
+
+
+ if (msg->wParam == (HWND)-1)
+ return FALSE;
+
+ if ( ! DDE_IsRemoteWindow(msg->hwnd) && msg->hwnd!= (HWND)-1)
+ return FALSE;
+
+ dprintf_msg(stddeb, "%s: DDE_DoMessage(hwnd=0x%x,msg=0x%x,..)\n",
+ msg_type[type], (int)msg->hwnd,(int)msg->message);
+
+
+ dprintf_msg(stddeb,
+ "DDE_DoMessage(hwnd=0x%x,msg=0x%x,..) // HWND_BROADCAST !\n",
+ (int)msg->hwnd,(int)msg->message);
+ remote_message=(void*)&msg_dat.dat.mtext;
+
+ memcpy(remote_message, msg, sizeof(*msg));
+ remote_message->wParam= HWND_Local2Remote(msg->wParam);
+ if (remote_message->wParam == 0)
+ return FALSE;
+
+ msg_dat.dat.mtype=type;
+
+ if (msg->hwnd == (HWND)-1) {
+ success= FALSE;
+ for ( proc_idx=0; proc_idx < DDE_PROCS ; proc_idx++) {
+ if (proc_idx == curr_proc_idx)
+ continue;
+ if (main_block->proc[ proc_idx ].msg != -1)
+ success|=DDE_DoOneMessage(proc_idx, DDE_MSG_SIZE, &msg_dat.dat);
+ }
+ return success;
+ } else {
+ return DDE_DoOneMessage(DDE_WIN2PROC(msg->hwnd), DDE_MSG_SIZE,
+ &msg_dat.dat);
+ }
+}
+
+BOOL DDE_SendMessage( MSG *msg)
+{
+ return DDE_DoMessage(msg, DDE_SEND);
+}
+
+BOOL DDE_PostMessage( MSG *msg)
+{
+ return DDE_DoMessage(msg, DDE_POST);
+}
+
+
+void dde_proc_send_ack(HWND wnd, BOOL val) {
+ int proc,msg;
+
+ static struct msgbuf msg_ack={DDE_ACK,{'0'}};
+
+ proc=DDE_WIN2PROC(wnd);
+ msg=main_block->proc[proc].msg;
+ dprintf_msg(stddeb,"DDE_GetRemoteMessage: sending ACK "
+ "to wnd=%4x, proc=%d,msg=%d, pid=%d\n",wnd,proc,msg,
+ main_block->proc[proc].pid
+ );
+
+ msg_ack.mtext[0]=val;
+ msgsnd (msg, &msg_ack, 1, 0);
+ kill(main_block->proc[proc].pid, SIGUSR2);
+}
+
+/* return true (non zero) if had a remote message */
+#undef DDE_GetRemoteMessage
+
+int DDE_GetRemoteMessage()
+{
+ static int nesting=0; /* to avoid infinite recursion */
+
+ MSG *remote_message;
+ int size;
+ struct msg_dat msg_dat;
+ BOOL was_sent; /* sent/received */
+ BOOL passed;
+ HWND hwnd;
+ WND *window;
+
+ if (curr_proc_idx==-1) /* do we have DDE initialized ? */
+ return 0;
+
+ if (nesting>10) {
+ fflush(stdout);
+ fprintf(stderr,"DDE_GetRemoteMessage: suspecting infinite recursion, exiting");
+ return 0;
+ }
+
+ remote_message=(void*)&msg_dat.dat.mtext;
+
+ /* test for SendMessage */
+ size= msgrcv( main_block->proc[curr_proc_idx].msg , &msg_dat.dat,
+ DDE_MSG_SIZE, DDE_SEND, IPC_NOWAIT);
+
+ if (size==DDE_MSG_SIZE) { /* is this a correct message (if any) ?*/
+ was_sent=TRUE;
+ dprintf_msg(stddeb,
+ "DDE:receive sent message. msg=%04x wPar=%04x"
+ " lPar=%08lx\n",
+ remote_message->message, remote_message->wParam,
+ remote_message->lParam);
+ } else {
+ size= msgrcv( main_block->proc[curr_proc_idx].msg , &msg_dat.dat,
+ DDE_MSG_SIZE, DDE_POST, IPC_NOWAIT);
+
+ if (size==DDE_MSG_SIZE) { /* is this a correct message (if any) ?*/
+ was_sent=FALSE;
+ dprintf_msg(stddeb,
+ "DDE:receive posted message. "
+ "msg=%04x wPar=%04x lPar=%08lx\n",
+ remote_message->message, remote_message->wParam,
+ remote_message->lParam);
+ }
+ else
+ return 0; /* no DDE message found */
+ }
+
+ /* At this point we are sure that there is a DDE message,
+ * was_sent is TRUE is the message was sent, and false if it was posted
+ */
+
+ nesting++;
+
+ if (debugging_dde) {
+ char *title;
+ if (was_sent)
+ title="receive sent dde:";
+ else
+ title="receive posted dde:";
+ print_dde_message(title, remote_message);
+ }
+
+ if (remote_message->hwnd != (HWND) -1 ) {
+ HWND dde_window= DDE_WIN_INFO(remote_message->hwnd).wnd;
+ /* we should know exactly where to send the message (locally)*/
+ if (was_sent) {
+ dprintf_dde(stddeb,
+ "SendMessage(wnd=0x%04x, msg=0x%04x, wPar=0x%04x,"
+ "lPar=0x%08x\n",
+ dde_window, remote_message->message,
+ remote_message->wParam, (int)remote_message->lParam);
+
+ /* execute the recieved message */
+ passed= SendMessage(dde_window, remote_message->message,
+ remote_message->wParam, remote_message->lParam);
+
+ /* Tell the sended, that the message is here */
+ dde_proc_send_ack(remote_message->wParam, passed);
+ }
+ else {
+ passed= PostMessage(dde_window, remote_message->message,
+ remote_message->wParam, remote_message->lParam);
+ if (passed == FALSE) {
+ /* Tell the sender, that the message is here, and failed */
+ dde_proc_send_ack(remote_message->wParam, FALSE);
+ }
+ else {
+ /* ack will be sent later, at the first peek/get message */
+ dde_proc_add_fifo(remote_message->wParam);
+ }
+ }
+ nesting--;
+ return 1;
+ }
+
+ /* iterate through all the windows */
+ for (hwnd = GetTopWindow(GetDesktopWindow());
+ hwnd && (window = WIN_FindWndPtr(hwnd))!=NULL ;
+ hwnd = window->hwndNext) {
+ if (window->dwStyle & WS_POPUP || window->dwStyle & WS_CAPTION) {
+ if (was_sent)
+ SendMessage( hwnd, remote_message->message,
+ remote_message->wParam, remote_message->lParam );
+ else
+ PostMessage( hwnd, remote_message->message,
+ remote_message->wParam, remote_message->lParam );
+ } /* if */
+ } /* for */
+
+ /* replay with DDE_ACK after broadcasting in DDE_GetRemoteMessage */
+ dde_proc_send_ack(remote_message->wParam, TRUE);
+
+ nesting--;
+ return 1;
+}
+
+int dde_reschedule()
+{
+ int ack_wnd;
+
+ ack_wnd= dde_proc_shift_fifo();
+ if (ack_wnd != -1) {
+ dde_proc_send_ack(ack_wnd, TRUE);
+ usleep(10000); /* force unix task switch */
+ return 1;
+ }
+ return 0;
+}
+void dde_msg_setup(int *msg_ptr)
+{
+ *msg_ptr= msgget (IPC_PRIVATE, IPC_CREAT | 0700);
+ if (*msg_ptr==-1)
+ perror("dde_msg_setup fails to get message queue");
+}
+
+/* do we have dde handling in the window ?
+ * If we have, atom usage will make this instance of wine set up
+ * it's IPC stuff.
+ */
+void DDE_TestDDE(HWND hwnd)
+{
+
+ if (main_block != NULL)
+ return;
+ dprintf_msg(stddeb,"DDE_TestDDE(0x%04x)\n", hwnd);
+ if (hwnd==0)
+ hwnd=-1;
+ /* just send a message to see how things are going */
+ SendMessage( hwnd, WM_DDE_INITIATE, 0, 0);
+}
+
+void dde_proc_delete(int proc_idx)
+{
+ dde_proc_done(&main_block->proc[proc_idx]);
+}
+void stop_wait(int a)
+{
+
+ had_SIGUSR2=1;
+ switch(stop_wait_op) {
+ case STOP_WAIT_ACK:
+ siglongjmp(env_get_ack,1);
+ break; /* never reached */
+ case STOP_WAIT_X:
+ siglongjmp(env_wait_x,1);
+ break; /* never reached */
+ case CONT:
+ /* do nothing */
+ }
+}
+
+static void print_dde_message(char *desc, MSG *msg)
+{
+ extern const char *MessageTypeNames[];
+ extern int debug_last_handle_size;
+ WORD wStatus,hWord;
+ void *ptr;
+ DDEACK *ddeack;
+ DDEADVISE *ddeadvise;
+ DDEDATA *ddedata;
+ DDEPOKE *ddepoke;
+
+ if (is_dde_handle(msg->lParam & 0xffff) )
+ ptr=DDE_AttachHandle(msg->lParam&0xffff, NULL);
+ else
+ ptr =NULL;
+ wStatus=LOWORD(msg->lParam);
+ hWord=HIWORD(msg->lParam);
+
+ fprintf(stddeb,"%s", desc);
+ fprintf(stddeb,"%04x %04x==%s %04x %08lx ",
+ msg->hwnd, msg->message,MessageTypeNames[msg->message],
+ msg->wParam, msg->lParam);
+ switch(msg->message) {
+ case WM_DDE_INITIATE:
+ case WM_DDE_REQUEST:
+ case WM_DDE_EXECUTE:
+ case WM_DDE_TERMINATE:
+ /* nothing to do */
+ break;
+ case WM_DDE_ADVISE:
+ /* DDEADVISE: hOptions in WM_DDE_ADVISE message */
+ if (ptr) {
+ ddeadvise=ptr;
+ fprintf(stddeb,"fDeferUpd=%d,fAckReq=%d,cfFormat=0x%x",
+ ddeadvise->fDeferUpd, ddeadvise->fAckReq,
+ ddeadvise->cfFormat);
+ } else
+ fprintf(stddeb,"NO-DATA");
+ fprintf(stddeb," atom=0x%x",hWord);
+ break;
+
+ case WM_DDE_UNADVISE:
+ fprintf(stddeb,"format=0x%x, atom=0x%x",wStatus,hWord);
+ break;
+ case WM_DDE_ACK:
+ ddeack=(DDEACK*)&wStatus;
+ fprintf(stddeb,"bAppReturnCode=%d,fBusy=%d,fAck=%d",
+ ddeack->bAppReturnCode, ddeack->fBusy, ddeack->fAck);
+ if (ddeack->fAck)
+ fprintf(stddeb,"(True)");
+ else
+ fprintf(stddeb,"(False)");
+ break;
+
+ case WM_DDE_DATA:
+ if (ptr) {
+ ddedata=ptr;
+ fprintf(stddeb,"fResponse=%d,fRelease=%d,"
+ "fAckReq=%d,cfFormat=0x%x,value=\"%.*s\"",
+ ddedata->fResponse, ddedata->fRelease,
+ ddedata->fAckReq, ddedata->cfFormat,
+ debug_last_handle_size- (int)sizeof(*ddedata)+1,
+ ddedata->Value);
+ } else
+ fprintf(stddeb,"NO-DATA");
+ fprintf(stddeb," atom=0x%04x",hWord);
+ break;
+
+ case WM_DDE_POKE:
+ if (ptr) {
+ ddepoke=ptr;
+ fprintf(stddeb,"fRelease=%d,cfFormat=0x%x,value[0]='%c'",
+ ddepoke->fRelease, ddepoke->cfFormat, ddepoke->Value[0]);
+ } else
+ fprintf(stddeb,"NO-DATA");
+ fprintf(stddeb," atom=0x%04x",hWord);
+ break;
+ }
+ fprintf(stddeb,"\n");
+}
+
+void dde_proc_done(dde_proc proc)
+{
+ if (proc->msg != -1)
+ msgctl(proc->msg, IPC_RMID, NULL);
+ proc->msg=-1;
+ proc->pid=-1;
+ shm_delete_chain(&proc->shmid);
+ shm_sem_done(&proc->sem);
+}
+
+/* delete entry, if old junk */
+void dde_proc_refresh(dde_proc proc)
+{
+ if (proc->pid == -1)
+ return;
+
+ if (kill(proc->pid, 0) != -1)
+ return;
+
+ /* get here if entry non empty, and the process does not exist */
+ dde_proc_done(proc);
+}
+
+void dde_wnd_setup()
+{
+ int i;
+
+ for (i=0 ; i < DDE_WINDOWS ; i++)
+ main_block->windows[i].proc_idx = FREE_WND;
+}
+
+static BOOL DDE_ProcHasWindows(int proc_idx)
+{
+ WND_DATA *tested;
+ int i;
+
+ for ( i=0 ; i < DDE_WINDOWS ; i++) {
+ tested= &main_block->windows[ i ];
+
+ if (tested->proc_idx == proc_idx)
+ return TRUE;
+ }
+ return FALSE;
+}
+/* Look for hwnd in the hash table of DDE windows,
+ * Delete it from there. If there are no more windows for this
+ * process, remove the process from the DDE data-structure.
+ * If there are no more processes - delete the whole DDE struff.
+ *
+ * This is inefficient, but who cares for the efficiency of this rare
+ * operation...
+ */
+void DDE_DestroyWindow(HWND hwnd)
+{
+ int dde_wnd_idx;
+ WND_DATA *tested;
+ int i;
+
+ if (main_block == NULL)
+ return;
+
+ dde_wnd_idx= hwnd % DDE_WINDOWS;
+
+ for ( i=0 ; i < DDE_WINDOWS ; i++, dde_wnd_idx++) {
+ if (dde_wnd_idx >= DDE_WINDOWS)
+ dde_wnd_idx -= DDE_WINDOWS; /* wrap-around */
+
+ tested= &main_block->windows[ dde_wnd_idx ];
+ if (tested->proc_idx == FREE_WND)
+ return; /* No window will get deleted here */
+
+ if (tested->wnd == hwnd && tested->proc_idx == curr_proc_idx) {
+ dde_reschedule();
+ tested->proc_idx= DELETED_WND;
+ if (DDE_ProcHasWindows( curr_proc_idx ))
+ return;
+ while (dde_reschedule()) /* make sure there are no other */
+ /* processes waiting for acknowledgment */
+ ;
+ dde_proc_delete( curr_proc_idx );
+ if (DDE_no_of_attached() == 1)
+ shm_delete_all(-1);
+ else {
+ shmdt( (void *) main_block);
+ main_block= NULL;
+ }
+ return;
+ }
+ }
+}
+
diff --git a/ipc/dde_proc_test.c b/ipc/dde_proc_test.c
new file mode 100644
index 0000000..1291fd8
--- /dev/null
+++ b/ipc/dde_proc_test.c
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: dde_proc.c
+ * Purpose : test DDE signals and processes functionality for DDE
+ * Usage: run two independant processes, one with an argument another
+ * without (with the argument is the server).
+ ***************************************************************************
+ */
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+#include <sys/syscall.h>
+#include <sys/param.h>
+#else
+#include <syscall.h>
+#endif
+#include <stdio.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <win.h>
+#include "dde.h"
+#include "dde_proc.h"
+#include "shm_main_blk.h"
+
+#if !defined(BSD4_4) || defined(linux) || defined(__FreeBSD__)
+char * cstack[4096];
+#endif
+#ifdef linux
+extern void ___sig_restore();
+extern void ___masksig_restore();
+
+/* Similar to the sigaction function in libc, except it leaves alone the
+ restorer field */
+
+static int
+wine_sigaction(int sig,struct sigaction * new, struct sigaction * old)
+{
+ __asm__("int $0x80":"=a" (sig)
+ :"0" (SYS_sigaction),"b" (sig),"c" (new),"d" (old));
+ if (sig>=0)
+ return 0;
+ errno = -sig;
+ return -1;
+}
+#endif
+
+struct sigaction usr2_act;
+
+
+void init_signals()
+{
+#ifdef linux
+ usr2_act.sa_handler = (__sighandler_t) stop_wait;
+ usr2_act.sa_flags = 0;
+ usr2_act.sa_restorer =
+ (void (*)()) (((unsigned int)(cstack) + sizeof(cstack) - 4) & ~3);
+ wine_sigaction(SIGUSR2,&usr2_act,NULL);
+#endif
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+ usr2_act.sa_hadnler = (void (*)) stop_wait;
+ usr2_act.sa_flags = SA_ONSTACK;
+ usr2_act.sa_mask = sig_mask;
+ usr2_act.sa_restorer =
+ (void (*)()) (((unsigned int)(cstack) + sizeof(cstack) - 4) & ~3);
+ if (sigaction(SIGUSR2,&usr2_act,NULL) <0) {
+ perror("sigaction: SIGUSR2");
+ exit(1);
+ }
+#endif
+}
+void ATOM_GlobalInit()
+{
+ printf("ATOM_GlobalInit\n");
+}
+
+
+void idle_loop()
+{
+ int timeout;
+ for(timeout=500; timeout ; timeout--) {
+ if (DDE_GetRemoteMessage())
+ exit(0); ;
+ usleep(1000);
+ }
+ exit(-1);
+}
+
+void client()
+{
+ MSG msg;
+ msg.hwnd=(HWND)-1;
+ msg.message= WM_DDE_INITIATE;
+ msg.wParam= 3;
+ msg.lParam= 4;
+ if (!DDE_SendMessage(&msg))
+ exit(-1);
+ idle_loop();
+}
+void server()
+{
+ DDE_IPC_init();
+ idle_loop();
+}
+
+int main(int argc, char *argv[])
+{
+ printf("Kill when done one message\n");
+ init_signals();
+ if (argc>1)
+ server();
+ else
+ client();
+ return 0;
+}
diff --git a/ipc/generic_hash.c b/ipc/generic_hash.c
new file mode 100644
index 0000000..6cee5d3
--- /dev/null
+++ b/ipc/generic_hash.c
@@ -0,0 +1,678 @@
+/***************************************************************************
+ * Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
+ ***************************************************************************
+ * File: generic_hash.c
+ * Purpose : dynamically growing hash, may use shared or local memory.
+ ***************************************************************************
+ */
+#include <stdlib.h>
+#include <assert.h>
+#include "generic_hash.h"
+
+#define ROUND_UP4(num) (( (num)+3) & ~3)
+
+#define FREE_ENTRY 0
+#define DELETED_ENTRY ((DWORD)-1)
+
+#define NO_OF_PRIMES 512
+#define GET_ITEM(items,size,i)\
+ (*(HASH_ITEM*) \
+ ( ((char *)(items))+ \
+ (i)*(size)) )
+
+static HASH_ITEM *locate_entry(HASH_CONTAINER* hash, DWORD key,
+ HASH_VAL *seeked_data, BOOL skip_deleted);
+
+static void copy_hash_items(HASH_CONTAINER *hash, HASH_ITEM *old_items,
+ int old_n_items);
+
+static BOOL arrays_initialized = FALSE;
+static int primes[NO_OF_PRIMES];
+static int best_primes[NO_OF_PRIMES];
+static int no_of_primes;
+static int no_of_best_primes;
+static int max_num;
+
+/* binary search for `num' in the `primes' array */
+static BOOL prime_binary_search_found(int num)
+{
+ int min_idx, max_idx, idx;
+
+ min_idx=0;
+ max_idx=no_of_primes-1;
+
+ while (min_idx <= max_idx) {
+ idx = (max_idx + min_idx) >> 1;
+ if (num == primes[idx])
+ return TRUE;
+ if (num < primes[idx])
+ max_idx = idx-1;
+ else
+ min_idx = idx+1;
+ }
+ return FALSE;
+}
+
+static BOOL is_prime(int num)
+{
+ int i;
+ if ((num & 0x1) == 0) /* can be divided by 2 */
+ if (num == 2)
+ return TRUE;
+ else
+ return FALSE;
+
+ if (num <= primes[no_of_primes-1])
+ return prime_binary_search_found(num);
+
+ for (i=0 ; i < no_of_primes ; i++) {
+ if (num % primes[i] == 0)
+ return FALSE;
+ if (num < primes[i] * primes[i])
+ return TRUE;
+ }
+ return TRUE;
+}
+
+static void setup_primes()
+{
+ int num;
+
+ primes[0]=2;
+ primes[1]=3;
+ no_of_primes=2;
+
+ /* count in modulo 6 to avoid numbers that divide by 2 or 3 */
+ for (num=5 ; ; num+=6) {
+ if (is_prime(num)) {
+ primes[no_of_primes++]=num;
+ if (no_of_primes >= NO_OF_PRIMES)
+ break;
+ }
+ if (is_prime(num+2)) {
+ primes[no_of_primes++]=num+2;
+ if (no_of_primes >= NO_OF_PRIMES)
+ break;
+ }
+ }
+ max_num= primes[no_of_primes-1] * primes[no_of_primes-1];
+}
+
+
+/* Find primes which are far "enough" from powers of two */
+
+void setup_best_primes()
+{
+ int i;
+ int num;
+ int pow2before, pow2after;
+ int min_range, max_range;
+
+ min_range=3;
+ max_range=3;
+ pow2before= 2;
+ pow2after= 4;
+
+ no_of_best_primes= 0;
+ for (i=0 ; i < no_of_primes ; i++){
+ num= primes[i];
+
+ if (num > pow2after) {
+ pow2before= pow2after;
+ pow2after <<=1;
+ min_range= pow2before+ (pow2before >> 3);
+ max_range= pow2after- (pow2before >> 2);
+ }
+ if (num > min_range && num < max_range)
+ best_primes[no_of_best_primes++]=num;
+ }
+}
+
+/* binary search for `num' in the `best_primes' array,
+ * Return smallest best_prime >= num.
+ */
+static int best_prime_binary_search(int num)
+{
+ int min_idx, max_idx, idx;
+
+ min_idx=0;
+ max_idx=no_of_best_primes-1;
+
+ while (1) {
+ idx = (max_idx + min_idx) >> 1;
+ if (num == best_primes[idx])
+ return num;
+ if (num < best_primes[idx]) {
+ max_idx = idx-1;
+ if (max_idx <= min_idx)
+ return best_primes[idx];
+ }
+ else {
+ min_idx = idx+1;
+ if (min_idx >= max_idx)
+ return best_primes[max_idx];
+ }
+ }
+
+}
+
+/* Find the best prime, near `num' (which can be any number) */
+static int best_prime(int num)
+{
+ int log2;
+ int pow2less, pow2more;
+ int min_range, max_range;
+
+ if (num < 11)
+ return 11;
+
+ if (num <= best_primes[no_of_best_primes-1])
+ return best_prime_binary_search(num);
+
+ assert( num < max_num );
+
+ for (log2=0 ; num >> log2 ; log2++)
+ ;
+
+ pow2less= 1 << log2;
+ pow2more= 1 << (log2+1);
+ min_range= pow2less + (pow2less >> 3);
+ max_range= pow2more - (pow2more >> 3);
+
+ if (num < min_range)
+ num= min_range;
+
+ num |= 1; /* make sure num can't be divided by 2 */
+
+ while (1) {
+ if (num >= max_range) {
+ pow2less<<= 1;
+ pow2more<<= 1;
+ min_range= pow2less + (pow2less >> 3);
+ max_range= pow2more - (pow2more >> 3);
+ num= min_range | 1; /* make sure num can't be divided by 2 */
+ }
+ /* num should be here in the range: (min_range, max_range) */
+ if (is_prime(num))
+ return num;
+ num+=2;
+ }
+}
+
+/* FIXME: This can be done before compiling. (uning a script)*/
+static void setup_arrays()
+{
+ setup_primes();
+ setup_best_primes();
+}
+
+/* Discard all DELETED_ENTRYs moving the data to it's correct location.
+ * Done without a temporary buffer.
+ * May require some efficiency improvements ( currently it's o(N^2)
+ * or is it o(N^3) in the worst case ? In the avarege it seems to be
+ * something like o(N log (N)))
+ */
+static void static_collect_garbge(HASH_CONTAINER *hash)
+{
+ int i;
+ BOOL change;
+ HASH_ITEM *items;
+ HASH_ITEM *located;
+ HASH_ITEM *item;
+ int key;
+
+ items= hash->items;
+
+ do {
+ change= FALSE;
+ for (i=hash->shared->total_items-1 ; i >= 0 ; i--) {
+ item= &GET_ITEM(items,hash->bytes_per_item,i);
+ key= item->key;
+ if (key != DELETED_ENTRY && key != FREE_ENTRY) {
+ /* try to place the entry in a deleted location */
+ located= locate_entry(hash, key, &item->data,
+ 0 /* no skip_deleted */);
+ if (located->key == DELETED_ENTRY) {
+ change= TRUE;
+ memcpy(&located, &item,
+ hash->bytes_per_item);
+ item->key= DELETED_ENTRY;
+ }
+ }
+ }
+ } while (change);
+
+ /* No change means that there is no need to go through a DELETED_ENTRY
+ * in order to reach an item, so DELETED_ENTRY looses it's special
+ * meaning, and it is the same as FREE_ENTRY.
+ */
+ for (i=hash->shared->total_items-1 ; i >= 0 ; i--)
+ if (GET_ITEM(items,hash->bytes_per_item,i).key == DELETED_ENTRY)
+ GET_ITEM(items,hash->bytes_per_item,i).key = FREE_ENTRY;
+ hash->shared->deleted_items=0;
+}
+
+static void collect_garbge(HASH_CONTAINER *hash)
+{
+ HASH_SHARED *shared= hash->shared;
+ HASH_ITEM *temp_items;
+ int size;
+
+ size= shared->total_items * hash->bytes_per_item;
+ temp_items= (HASH_ITEM*)malloc(size);
+ if (temp_items==NULL) {
+ static_collect_garbge(hash);
+ } else {
+ memcpy(temp_items, hash->items, size);
+ copy_hash_items(hash, temp_items, shared->total_items);
+ }
+}
+
+
+static void copy_hash_items(HASH_CONTAINER *hash, HASH_ITEM *old_items,
+ int old_n_items)
+{
+ HASH_SHARED *shared= hash->shared;
+ HASH_ITEM *item;
+ int i;
+
+ shared->deleted_items = 0;
+ shared->free_items= shared->total_items;
+
+ /* make all items free */
+ for (i= shared->total_items-1 ; i>=0 ; i--)
+ GET_ITEM(hash->items, hash->bytes_per_item, i).key = FREE_ENTRY;
+
+ /* copy items */
+ for (i=0 ; i <= old_n_items; i++) {
+ item= &GET_ITEM(old_items, hash->bytes_per_item,i);
+ if (item->key != FREE_ENTRY && item->key != DELETED_ENTRY)
+ hash_add_item(hash, item->key, &item->data);
+ }
+}
+
+
+static void reorder_hash(HASH_CONTAINER *hash)
+{
+ HASH_SHARED *shared= hash->shared;
+ HASH_ITEM *items, *old_items;
+ HASH_PTR shared_items, old_shared_items;
+ int n_items, old_n_items;
+ int size;
+
+ if (shared->deleted_items > hash->min_free_items) {
+ collect_garbge(hash);
+ return;
+ }
+ n_items= best_prime(shared->total_items * HASH_REALLOC_JUMPS);
+
+ size= n_items *
+ (sizeof(items[0]) - sizeof(items[0].data) + hash->bytes_per_item);
+
+ shared_items= hash->allocate_mem(size);
+ items= hash->access_mem(shared_items);
+
+ if (items == NULL) {
+ collect_garbge(hash);
+ return;
+ }
+ old_shared_items = shared->items;
+ old_n_items= shared->total_items;
+ old_items= hash->items;
+
+ /* setup a new clean hash based on the parameters of the original one */
+ hash->items= items;
+ shared->total_items = n_items;
+ shared->items= shared_items;
+ set_hash_parameters(hash, hash->maximum_load);
+ copy_hash_items(hash, old_items, old_n_items);
+
+ hash->free_mem(old_shared_items);
+ hash->last_ptr_update= ++shared->ptr_updates;
+}
+
+/* low level: attach hash existing hash items, no checks are performed
+ * No complex calculations done.
+ */
+static HASH_CONTAINER *attach_no_check(HASH_ITEM *items, int bytes_per_datum)
+{
+ HASH_CONTAINER *hash;
+ int bytes_per_item;
+ HASH_ITEM dummy_item;
+
+ hash= (HASH_CONTAINER*) malloc(sizeof(HASH_CONTAINER) );
+ if (hash == NULL)
+ return NULL;
+
+ bytes_per_item= bytes_per_datum+
+ sizeof(dummy_item)-sizeof(dummy_item.data);
+ hash->bytes_per_item= ROUND_UP4(bytes_per_item);
+ hash->items= items;
+ hash->is_correct_item= NULL;
+ hash->allocate_mem= HASH_MEM_ALLOC;
+ hash->access_mem= HASH_MEM_ACCESS;
+ hash->free_mem= HASH_MEM_FREE;
+ set_hash_parameters(hash, HASH_LOAD);
+
+
+ return hash;
+}
+
+
+/* Attach existing & running remote (i.e. shared) hash.
+ * Attach the items using the data stored in "shared"
+ */
+HASH_CONTAINER *attach_remote_hash(HASH_SHARED *shared, int bytes_per_datum,
+ HASH_ITEM *(*access_mem)(HASH_PTR))
+{
+ HASH_CONTAINER *hash;
+ HASH_ITEM *items;
+
+ assert(access_mem != NULL);
+ if (! arrays_initialized)
+ setup_arrays();
+
+ items=access_mem(shared->items);
+ hash= attach_no_check(items, bytes_per_datum);
+ if (hash == NULL)
+ return NULL;
+
+ hash->shared_was_malloced = FALSE;
+ hash->shared= shared;
+ return (hash);
+}
+
+
+HASH_CONTAINER *create_remote_hash(HASH_SHARED *shared,
+ int bytes_per_datum,
+ int total_items,
+ HASH_PTR (*allocate_mem)(int size),
+ HASH_ITEM *(*access_mem)(HASH_PTR))
+{
+ HASH_CONTAINER *hash;
+ int size;
+ int i;
+
+ assert(total_items >= 1);
+ assert(bytes_per_datum >=1);
+ assert(access_mem != NULL);
+ assert(allocate_mem != NULL);
+ assert(shared != NULL);
+ if (! arrays_initialized)
+ setup_arrays();
+
+ if (total_items < MIN_HASH)
+ total_items= MIN_HASH;
+ else
+ total_items= best_prime(total_items);
+
+ hash= attach_no_check(NULL, bytes_per_datum);
+
+ if (hash==NULL) {
+ free(hash);
+ return NULL;
+ }
+
+ shared->total_items= total_items;
+ hash->shared= shared;
+ hash->shared_was_malloced = FALSE;
+
+ size= total_items * hash->bytes_per_item;
+
+ shared->items = allocate_mem(size);
+ hash->items= access_mem(shared->items);
+
+ if (hash->items == NULL ) {
+ free(hash);
+ return NULL;
+ }
+ shared->items.ptr= hash->items;
+
+ /* make all items free */
+ for (i=0 ; i < total_items ; i++)
+ GET_ITEM(hash->items,hash->bytes_per_item,i).key = FREE_ENTRY;
+
+ shared->deleted_items= 0;
+ shared->free_items= total_items;
+ shared->ptr_updates= 0;
+ return hash;
+
+}
+
+/* hash constructor: create brand new hash */
+HASH_CONTAINER *create_hash(int bytes_per_datum, int total_items)
+{
+ HASH_CONTAINER *hash;
+ HASH_SHARED *shared;
+
+
+ shared= (HASH_SHARED*)malloc(sizeof(HASH_SHARED));
+ if (shared == NULL)
+ return NULL;
+
+ hash= create_remote_hash(shared, bytes_per_datum, total_items,
+ HASH_MEM_ALLOC, HASH_MEM_ACCESS);
+
+ if (hash == NULL) {
+ free(shared);
+ return NULL;
+ }
+
+ hash->shared_was_malloced = TRUE;
+ return hash;
+}
+
+/* set the extra handlers to non default values */
+void set_hash_handlers(HASH_CONTAINER *hash,
+ HASH_ITEM_TEST *is_correct_item,
+ HASH_PTR (*allocate_mem)(int size),
+ void (*free_mem)(HASH_PTR),
+ HASH_ITEM *(*access_mem)(HASH_PTR))
+{
+ assert(hash);
+ assert(allocate_mem);
+ assert(free_mem);
+
+ hash->free_mem = free_mem;
+ hash->allocate_mem = allocate_mem;
+ hash->access_mem = access_mem;
+ hash->is_correct_item = is_correct_item;
+}
+
+
+/* set extra parameters */
+void set_hash_parameters(HASH_CONTAINER *hash, int load)
+{
+ assert(hash);
+ assert(load>30); /* no sence to realloc with less than */
+ /* 50% load, limiting to 30% to be on */
+ /* the safe size */
+ assert(load<=100);
+
+ hash->maximum_load= load;
+ hash->min_free_items= (1.0 - load/100.0) * hash->shared->total_items + 1 ;
+}
+
+/* hash destructor: destroy anything related to the hash */
+void destroy_hash(HASH_CONTAINER *hash)
+{
+ assert(hash);
+ hash->free_mem(hash->shared->items);
+ if (hash->shared_was_malloced)
+ free(hash->shared);
+ free(hash);
+}
+
+/* hash destructor: just detach hash, without destroing it (makes */
+/* sence in shared memory environment) */
+void detach_hash(HASH_CONTAINER *hash)
+{
+ assert(hash);
+ free(hash);
+}
+
+
+/********** Hash usage *************/
+static __inline__ BOOL
+correct_entry(HASH_ITEM *item, int key, HASH_VAL *seeked_data,
+ HASH_ITEM_TEST *is_correct_item, BOOL skip_deleted)
+{
+ switch(item->key) {
+ case FREE_ENTRY:
+ return TRUE;
+
+ case DELETED_ENTRY:
+ return skip_deleted ? FALSE : TRUE;
+
+ default:
+ if (item->key != key)
+ return FALSE;
+ if (is_correct_item != NULL)
+ return is_correct_item(&item->data, seeked_data);
+ else
+ return TRUE;
+ }
+
+}
+
+/* The algorithm of the hash (one of the 2 standard hash implementations):
+ * Iterate through the hash table until
+ * 1. The entry has been found.
+ * 2. A FREE entry has been found.
+ * 3. For insert operations only- A DELETED entry has been found.
+ * The difference between DELETED and FREE entires is that
+ * DELETED entry was one occupied, while FREE was never allocated.
+ * The idea behind this structure to keep other entries reachable.
+ */
+
+static HASH_ITEM *locate_entry(HASH_CONTAINER* hash, DWORD key,
+ HASH_VAL *seeked_data, BOOL skip_deleted)
+{
+ DWORD hash_idx, hash_leaps;
+ HASH_ITEM *item;
+ int i;
+ int total_items;
+
+ assert(hash);
+
+ total_items= hash->shared->total_items;
+ hash_idx= key % total_items;
+
+ item= &GET_ITEM(hash->items, hash->bytes_per_item, hash_idx);
+
+ if ( correct_entry( item, key, seeked_data,
+ hash->is_correct_item, skip_deleted) )
+ return item;
+
+ /* get the WORDs in different order in this DWORD to avoid clustering */
+ hash_leaps=((DWORD)MAKELONG(HIWORD(key), LOWORD(key))
+ % (total_items-1)) +1;
+
+ /* interate through the hash table using hash_leaps */
+ for (i= total_items ; i ; i--) {
+ hash_idx+= hash_leaps;
+ if (hash_idx > total_items)
+ hash_idx -= total_items;
+
+ item= &GET_ITEM(hash->items,hash->bytes_per_item, hash_idx);
+ if ( correct_entry( item, key, seeked_data,
+ hash->is_correct_item, skip_deleted) )
+ return item;
+ }
+ return NULL;
+
+}
+
+static __inline__ void sync_shared_hash(HASH_CONTAINER *hash)
+{
+ HASH_SHARED *shared= hash->shared;
+
+ if (shared->ptr_updates == hash->last_ptr_update)
+ return;
+
+ assert(shared->ptr_updates >= hash->last_ptr_update);
+ hash->last_ptr_update= shared->ptr_updates;
+ hash->min_free_items= (1.0 - hash->maximum_load/100.0) *
+ shared->total_items + 1 ;
+
+ hash->items= hash->access_mem(shared->items);
+}
+
+HASH_VAL *hash_locate_item(HASH_CONTAINER* hash,
+ int key, HASH_VAL *seeked_data)
+{
+ HASH_ITEM *item;
+
+ assert(hash != NULL);
+ sync_shared_hash(hash);
+
+ item= locate_entry(hash, key, seeked_data, 1 /* skip_deleted */);
+ if (item == NULL)
+ return NULL;
+ if (item->key == FREE_ENTRY )
+ return NULL;
+
+ return &item->data;
+}
+
+
+BOOL hash_add_item(HASH_CONTAINER* hash, int key, HASH_VAL *data)
+{
+ HASH_SHARED *shared;
+ HASH_ITEM *item;
+
+ assert(hash != NULL);
+
+ sync_shared_hash(hash);
+ shared= hash->shared;
+
+ item=locate_entry(hash, key, data, 0 /* no skip_deleted */);
+ assert(item != NULL);
+ if (item->key == key)
+ return FALSE;
+ if (item->key == FREE_ENTRY)
+ shared->free_items--;
+ else
+ shared->deleted_items--;
+
+ item->key= key;
+ memcpy(&item->data, data, hash->bytes_per_item-sizeof(key));
+
+ if (shared->free_items < hash->min_free_items ||
+ shared->deleted_items > hash->min_free_items)
+ reorder_hash(hash);
+
+ return TRUE;
+}
+
+
+BOOL hash_delete_item(HASH_CONTAINER* hash, int key, HASH_VAL *seeked_data)
+{
+ HASH_ITEM *item;
+
+ assert(hash != NULL);
+ sync_shared_hash(hash);
+
+ item=locate_entry(hash, key, seeked_data, 1 /* skip_deleted */);
+ if (item == NULL)
+ return FALSE;
+
+ if (item->key == FREE_ENTRY)
+ return FALSE;
+
+ item->key = DELETED_ENTRY;
+ hash->shared->deleted_items++;
+
+ return TRUE;
+}
+
+void *ret_null()
+{
+ return NULL;
+}
+
+
+HASH_ITEM *access_local_hash(HASH_PTR ptr)
+{
+ return ptr.ptr;
+}
diff --git a/ipc/generic_hash.h b/ipc/generic_hash.h
new file mode 100644
index 0000000..5def7e6
--- /dev/null
+++ b/ipc/generic_hash.h
@@ -0,0 +1,141 @@
+/***************************************************************************
+ * Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
+ ***************************************************************************
+ * File: generic_hash.h
+ * Purpose : dynamically growing hash, may use shared or local memory.
+ ***************************************************************************
+ */
+#ifndef _GENERIC_HASH_H_
+#define _GENERIC_HASH_H_
+
+#include "wintypes.h"
+#include "shm_block.h"
+/* default hash values */
+#define HASH_LOAD 70
+#define HASH_MEM_ALLOC (HASH_PTR (*)(int size)) malloc
+#define HASH_MEM_FREE (void (*)(HASH_PTR)) free
+#define HASH_MEM_ACCESS access_local_hash
+#define HASH_REALLOC_JUMPS 1.5 /* Relative size of the new memory */
+#define MIN_HASH 13
+
+typedef union {
+ char string[1];
+ WORD words[1];
+ DWORD dwords[1];
+ char *ptr;
+ SEGPTR segptr;
+} HASH_VAL;
+
+typedef struct hash_item_struct {
+ DWORD key;
+ HASH_VAL data;
+} HASH_ITEM;
+
+/* point to the hash structure */
+typedef union {
+ HASH_ITEM* ptr; /* Local pointer */
+ REL_PTR rel; /* IPC relative address */
+ SEGPTR segptr; /* Universal (can be IPC or local) */
+} HASH_PTR;
+
+typedef struct hash_share_struct {
+ int total_items; /* total number of items (array size) */
+ int free_items; /* number of free items (excluding deleted) */
+ int deleted_items; /* number of deleted items */
+ int ptr_updates; /* Number of updates to `items' pointer */
+ /* (of items) - used for intecepting */
+ /* changes to the pointer. */
+ HASH_PTR items; /* pointer to the items */
+} HASH_SHARED;
+typedef BOOL HASH_ITEM_TEST(HASH_VAL *value, HASH_VAL *seeked_data);
+
+/* NOTE:
+ * 1. Keys 0 and -1 are reserved.
+ * 2. none of these items should be accessed directly, use existing
+ * functions. If they are not enough, add a new function.
+ */
+typedef struct hash_container_struct {
+ int bytes_per_item;
+ int maximum_load; /* in percents (0..100) default is 70 */
+ int min_free_items; /* minimum free items before reallocating
+ (Function of maximum_load) */
+
+ int last_ptr_update; /* to be compared with shared.ptr_updates */
+ BOOL shared_was_malloced; /* Need that to know how to destroy hash */
+
+ /* This is an optional handler.
+ * If not NULL, this function is used for distinguishing between
+ * different data with the same key (key field holds integer and
+ * is too short for long keys like strings).
+ */
+ HASH_ITEM_TEST *is_correct_item;
+
+ /* Handlers used for reallocating memory
+ * [by allocating new data and then freeing old data]
+ */
+ HASH_PTR (*allocate_mem)(int size);
+ void (*free_mem)(HASH_PTR);
+
+ /* Translator from HASH_PTR construct to a regular pointer.
+ use HASH_MEM_ACCESS, if no translation is needed */
+ HASH_ITEM *(*access_mem)(HASH_PTR);
+
+ HASH_ITEM *items;
+ HASH_SHARED *shared; /* Things to be on shared memory. */
+} HASH_CONTAINER;
+
+
+/********** Hash maintenance functions ***********/
+
+
+
+/* Attach existing & running remote (i.e. shared) hash.
+ * Attach the items using the data stored in "shared"
+ */
+HASH_CONTAINER *attach_remote_hash(HASH_SHARED *shared, int bytes_per_datum,
+ HASH_ITEM *(*access_mem)(HASH_PTR));
+
+
+HASH_CONTAINER *create_remote_hash(HASH_SHARED *shared,
+ int bytes_per_datum,
+ int total_items,
+ HASH_PTR (*allocate_mem)(int size),
+ HASH_ITEM *(*access_mem)(HASH_PTR));
+/* hash constructor: create brand new hash (not on shared memory) */
+HASH_CONTAINER *create_hash(int bytes_per_datum, int total_items);
+
+/* set the extra handlers to non default values */
+void set_hash_handlers(HASH_CONTAINER *hash,
+ HASH_ITEM_TEST *is_correct_item,
+ HASH_PTR (*allocate_mem)(int size),
+ void (*free_mem)(HASH_PTR),
+ HASH_ITEM *(*access_mem)(HASH_PTR));
+
+/* set extra parameters */
+void set_hash_parameters(HASH_CONTAINER *hash, int load);
+
+/* hash destructors */
+void destroy_hash(HASH_CONTAINER *hash);
+void detach_hash(HASH_CONTAINER *hash);
+
+
+/********** Hash usage *************/
+
+/* All following functions have the same format:
+ * hash- the hash structure to use
+ * key- used as primary means to get to the entry.
+ * data- 1. a secondary key (used only if `is_correct_item' is set).
+ * 2. data to store. (for hash_add_item).
+ */
+HASH_VAL *hash_locate_item(HASH_CONTAINER* hash,int key, HASH_VAL* seeked_data);
+BOOL hash_add_item(HASH_CONTAINER* hash, int key, HASH_VAL* data);
+BOOL hash_delete_item(HASH_CONTAINER* hash, int key, HASH_VAL* seeked_data);
+
+
+void *ret_null(); /* function returning null (used for */
+ /* disabling memory reallocation) */
+
+/* access function used by local (non IPC) memory */
+HASH_ITEM *access_local_hash(HASH_PTR ptr);
+
+#endif /* _GENERIC_HASH_H_ */
diff --git a/ipc/hash_test.c b/ipc/hash_test.c
new file mode 100644
index 0000000..dec32c4
--- /dev/null
+++ b/ipc/hash_test.c
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
+ ***************************************************************************
+ * File: hash_test.c
+ * Purpose : test generic_hash correctness.
+ * NOTE:
+ * This code covers only about 80% of generic_hash code.
+ * There might be bugs in the remaining 20% - although most
+ * of the functionality is tested with wine linckage.
+ * For complete testing a little more work should be done.
+ ***************************************************************************
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "generic_hash.h"
+
+#define SIZE 200
+typedef struct { int a,b;} DATA ;
+DATA data[SIZE];
+int keys[SIZE];
+int peeks=0;
+
+HASH_CONTAINER *hash1;
+HASH_CONTAINER *hash2; /* actual data is shared with hash1 */
+
+/* test insertion using keys[] and data[] inserting using hash1 and */
+/* hash2 periodically, test hash after every 2 insertions */
+void test_insert()
+{
+ int i,j;
+ HASH_VAL *item;
+
+ printf("testing insertion \n");
+ for (i=0 ; i < SIZE-1 ; i+=2) {
+ assert(hash_add_item(hash1, keys[i], (HASH_VAL *)&data[i]));
+ assert(hash_add_item(hash2, keys[i+1], (HASH_VAL *)&data[i+1]));
+ for (j=0 ; j <= i+1 ; j++) {
+ item= hash_locate_item(hash1, keys[j], (HASH_VAL *)&data[j]);
+ if (item == NULL) {
+ printf("NULL item: i=%d,j=%d\n",i,j);
+ continue;
+ }
+ peeks++;
+ if (memcmp(item,&data[j],sizeof(DATA))!=0) {
+ printf("i=%d,j=%d\n",i,j);
+ printf("saved=(%d,%d), orig=(%d,%d)\n",
+ ((DATA*)item)->a, ((DATA*)item)->b,
+ data[j].a, data[j].b);
+ }
+ }
+ }
+}
+
+/* test deletion using keys[] and data[] deleting using hash1 and */
+/* hash2 periodicly, test hash after every 2 deletions */
+void test_delete()
+{
+ int i,j;
+ HASH_VAL *item;
+
+ printf("testing deletion\n");
+ for (i=0 ; i < SIZE-1 ; i+=2) {
+ assert(hash_delete_item(hash2, keys[i], NULL));
+ assert(hash_delete_item(hash1, keys[i+1], NULL));
+ for (j=0 ; j < SIZE ; j++) {
+ item= hash_locate_item(hash2, keys[j], (HASH_VAL *)&data[j]);
+ if (item == NULL) {
+ if ( j > i+1)
+ printf("NULL item: i=%d,j=%d\n",i,j);
+ continue;
+ }
+ if (item != NULL && j <= i+1) {
+ printf("Non NULL item: i=%d,j=%d\n",i,j);
+ continue;
+ }
+ if (memcmp(item,&data[j],sizeof(DATA))!=0) {
+ printf("i=%d,j=%d\n",i,j);
+ printf("saved=(%d,%d), orig=(%d,%d)\n",
+ ((DATA*)item)->a, ((DATA*)item)->b,
+ data[j].a, data[j].b);
+ }
+ }
+ }
+
+}
+
+
+int main()
+{
+ int i;
+
+ hash1= create_hash(sizeof(DATA), 1);
+ assert(hash1);
+ hash2= attach_remote_hash(hash1->shared, sizeof(DATA), HASH_MEM_ACCESS);
+ assert(hash2);
+
+ for (i=0 ; i< SIZE ; i++) {
+ data[i].a= rand();
+ data[i].b= rand();
+ keys[i]= rand();
+ }
+
+ test_insert();
+ detach_hash(hash1);
+ free(hash1);
+ hash1= attach_remote_hash(hash2->shared, sizeof(DATA), HASH_MEM_ACCESS);
+
+ test_delete();
+ test_insert();
+
+ detach_hash(hash1);
+ destroy_hash(hash2);
+ printf("peeks=%d\n", peeks);
+ return 0;
+}
diff --git a/ipc/run_tests b/ipc/run_tests
new file mode 100644
index 0000000..638fb18
--- /dev/null
+++ b/ipc/run_tests
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+bit_array_test
+bit_array=$?
+
+dde_mem_test
+mem=$?
+
+hash_test
+hash=$?
+
+shm_semaph_test
+semaph=$?
+
+dde_atom_test
+atom=$?
+
+dde_proc_test 1 > proc_server &
+sleep 1
+dde_proc_test > proc_client
+fgrep "DDE:receive sent message. msg=03e0 wPar=fffb lPar=00000004" proc_server &&
+fgrep "DDE_GetRemoteMessage: sending ACK to wnd=fffb, proc=1" proc_server &&
+fgrep "get_ack: received DDE_ACK message" proc_client
+proc=$?
+rm proc_client proc_server
+
+shm_fragment_test | diff TEST_FRAGMENT.std -
+fragment=$?
+
+echo ====================================================================
+echo Test results:
+
+echo -n "bit_array "
+if [ $bit_array -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
+
+echo -n "dde_mem "
+if [ $mem -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
+
+echo -n "hash "
+if [ $hash -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
+
+echo -n "shm_semaph "
+if [ $semaph -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
+
+echo -n "dde_proc "
+if [ $proc -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
+
+echo -n "shm_fragment "
+if [ $fragment -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
diff --git a/ipc/shm_block.c b/ipc/shm_block.c
new file mode 100644
index 0000000..057c981
--- /dev/null
+++ b/ipc/shm_block.c
@@ -0,0 +1,191 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: shm_block.c
+ * Purpose: Treat a shared memory block.
+ ***************************************************************************
+ */
+
+#define inline __inline__
+#include <sys/sem.h>
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddebug.h>
+#include <debug.h>
+#include <global.h>
+#include "selectors.h"
+#include "shm_fragment.h"
+#include "shm_block.h"
+#include "shm_semaph.h"
+#include "dde_proc.h"
+
+/* How each shmid is maped to local pointer */
+/* Only attached shm blocks are in this construct */
+struct local_shm_map *shm_map=NULL;
+
+/* setup a new shm block (construct a shm block object).
+ * block: The pointer to the memory block (local mapping)
+ * first: The first data byte (excluding header stuff),
+ * if 0 (zero) Use the default.
+ * size: The size of the memory block.
+ */
+void shm_setup_block(struct shm_block *block, int first, int size)
+{
+ dprintf_shm(stddeb,"Setting up shm block at 0x%08x\n",(int )block);
+ /* setup block internal data structure */
+ if (first <= 0) {
+ first=sizeof(*block);
+ /* round up - so everything starts on cache line boundary
+ * (assume cache line=32 bytes, may be bigger/smaller for
+ * different processors and different L2 caches .)
+ */
+ first=(first+0x1f) & ~0x1f;
+ }
+ block->free=size-first;
+ block->next_shm_id=-1; /* IPC shm ID (for initial linking) */
+ block->proc_idx= curr_proc_idx;
+ /* block->size is initialized in shm_FragmentInit */
+ shm_FragmentInit(block, first, size); /* first item in the free list */
+
+ dprintf_shm(stddeb,
+ "block was set up at 0x%08x, size=0x%04xKB, 1st usable=%02x\n",
+ (int )block,size/1024,first);
+}
+
+/* shm_attach_block: attach existing shm block, setup selectors
+ * shm_id - id of the block to attach.
+ * proc_idx - if not -1, puts this data into local mapping
+ * map - localy mapped info about this block.
+ */
+/* NOTE: there is no check if this block is already attached.
+ * Attaching the same block more than once - is possible
+ * In case of doubt use shm_locate_block.
+ */
+struct shm_block *shm_attach_block(int shm_id, int proc_idx,
+ struct local_shm_map *map)
+{
+ struct shm_block *block;
+ struct shmid_ds ds;
+ struct local_shm_map *this;
+
+ shmctl(shm_id, IPC_STAT, &ds );
+
+ block=(struct shm_block*)shmat(shm_id, NULL, 0);
+ if (block==NULL) return NULL;
+
+ this=(struct local_shm_map *)malloc(sizeof(*this));
+ this->next= shm_map;
+ shm_map = this;
+ this->shm_id= shm_id;
+ this->ptr = block;
+
+ if (proc_idx < 0)
+ this->proc_idx=block->proc_idx;
+ else
+ this->proc_idx=proc_idx;
+
+ if (map != NULL) {
+ memcpy(map, this, sizeof(map));
+ map->next= NULL; /* don't pass private info */
+ }
+
+ return block;
+}
+
+struct shm_block *shm_create_block(int first, int size, int *shm_id)
+{
+ struct shm_block *block;
+
+ if (size==0)
+ size=SHM_MINBLOCK;
+ else
+ /* round up size to a multiple of SHM_MINBLOCK */
+ size= (size+SHM_MINBLOCK-1) & ~(SHM_MINBLOCK-1);
+ *shm_id= shmget ( IPC_PRIVATE, size ,0700);
+ if (*shm_id==-1)
+ return NULL;
+ block=shm_attach_block(*shm_id, curr_proc_idx, NULL);
+ if (block!=NULL)
+ shm_setup_block(block, first, size);
+
+ return block;
+}
+
+/*
+** Locate attached block. (return it, or NULL on failure)
+** shm_id is the block we look for.
+** *map - will get all the info related to this local map + proc_idx
+** (may be NULL)
+** *seg - will get the segment this block is attached to.
+*/
+struct shm_block *shm_locate_attached_block(int shm_id,
+ struct local_shm_map *map)
+{
+ struct local_shm_map *curr;
+
+ for (curr= shm_map ; curr != NULL ; curr= curr->next) {
+ if (curr->shm_id == shm_id) {
+ if (map) {
+ memcpy(map, curr, sizeof(*curr) );
+ map->next = NULL; /* this is private info ! */
+ }
+ return curr->ptr;
+ }
+ }
+
+ /* block not found ! */
+ return 0;
+}
+
+/* shm_locate_block: see shm_attach_block.
+ In addition to shm_attach_block, make sure this
+ block is not already attached.
+ */
+struct shm_block *shm_locate_block(int shm_id, struct local_shm_map *map)
+{
+
+ struct shm_block *ret;
+ ret= shm_locate_attached_block(shm_id, map);
+ if (ret!=NULL)
+ return ret;
+ /* block not found ! , try to attach */
+ return shm_attach_block(shm_id, -1, map);
+}
+
+static void forget_attached(int shmid)
+{
+ struct local_shm_map *curr, **point_to_curr;
+
+ for (curr= shm_map, point_to_curr= &shm_map ;
+ curr != NULL ;
+ curr= curr->next, point_to_curr= &curr->next ) {
+ if (curr->shm_id == shmid) {
+ *point_to_curr= curr->next;
+ return;
+ }
+ }
+}
+
+/* delete chain of shm blocks (pointing to each other)
+ * Do it in reverse order. (This is what the recursion is for)
+ */
+void shm_delete_chain(int *shmid)
+{
+ struct shm_block *block;
+
+ if (*shmid == -1)
+ return;
+
+ block= shm_locate_block(*shmid, NULL);
+ forget_attached( *shmid );
+ if (block == NULL)
+ return;
+ shm_delete_chain(&block->next_shm_id);
+ shmctl(*shmid, IPC_RMID, NULL);
+ *shmid=-1;
+ shmdt((char *)block);
+}
diff --git a/ipc/shm_fragment.c b/ipc/shm_fragment.c
new file mode 100644
index 0000000..098d6cc
--- /dev/null
+++ b/ipc/shm_fragment.c
@@ -0,0 +1,178 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: shm_fragment.c
+ * Purpose: Data fragments and free list items. Allocate and free blocks.
+ ***************************************************************************
+ */
+#include <stdio.h> /* for debugging only */
+#include <stddebug.h>
+#include <debug.h> /* for "stddeb" */
+
+#include "shm_fragment.h"
+#include "shm_block.h"
+
+/******************************************************************************
+ *
+ * Free list: all fragments are ordered according to memory location.
+ * new fragments are inserted in this way.
+ *
+ ******************************************************************************
+ */
+
+#define FRAG_PTR(block,ofs) ((struct shm_fragment *) ((char *) block + ofs) )
+#define NEXT_FRAG(block,ofs) ( FRAG_PTR(block,ofs)->info.next )
+
+/* setup first item in the free list */
+void shm_FragmentInit(struct shm_block *block,int first, int size)
+{
+ struct shm_fragment *fragment;
+
+ /* round up to nearest 16 byte boundary */
+ first=(first+15)& ~15;
+ block->free_list=first;
+
+ /* make all the block (exluding the header) free */
+ fragment= FRAG_PTR(block, first);
+ block->free= fragment->size= size-first;
+ fragment->info.next=0;
+}
+
+void shm_FragPtrFree(struct shm_block *block, void *ptr)
+{
+ /* ptr points to fragment->info.data, find pointer to fragment,
+ * find the offset of this pointer in block.
+ */
+ if (ptr)
+ shm_FragmentFree(block, PTR2REL(block, ptr));
+}
+void shm_FragmentFree(struct shm_block *block, int fragment_ofs)
+{
+ struct shm_fragment *fragment=NULL;
+ int prev;
+ int next;
+
+ fragment_ofs-=(int )&fragment->info.data;
+ fragment= FRAG_PTR(block, fragment_ofs);
+
+ block->free+=fragment->size;
+ /* scan free list to find candidates for merging with fragment */
+ for (prev=0, next=block->free_list;
+ (next!=0) && (fragment_ofs > next) ;
+ prev=next, next=NEXT_FRAG(block,next) )
+ ;
+
+ /* insert fragment between, prev and next
+ * prev==0: fragment will be the first item in free list
+ * next==0: fragment will be the last item in free list
+ */
+
+
+ /* update fragment (point to next, or merge with next) */
+
+ if ( fragment_ofs+fragment->size == next ) {
+ /* merge with the next free block */
+ fragment->size+= FRAG_PTR(block,next)->size;
+ fragment->info.next=FRAG_PTR(block,next)->info.next;
+ } else
+ /* fragment should be inserted before the next fragment or end of */
+ /* list. (not merged) */
+ fragment->info.next=next;
+ /* now fragment has all the information about the rest of the list */
+
+
+ /* upate prev fragment (point or merge with fragment) */
+
+ if (prev==0) /* first item in free list */
+ block->free_list=fragment_ofs;
+ else if ( prev+FRAG_PTR(block,prev)->size == fragment_ofs ) {
+ /* merge fragment with previous fragment */
+ FRAG_PTR(block,prev)->size+= fragment->size;
+ FRAG_PTR(block,prev)->info.next=fragment->info.next;
+ } else
+ /* insert fragment after previous fragment */
+ FRAG_PTR(block,prev)->info.next=fragment_ofs;
+}
+
+/* use "first fit" algorithm,
+ * return: offset to data in fragment.
+ */
+int shm_FragmentAlloc(struct shm_block *block, int size)
+{
+ int prev;
+ int candidate;
+ struct shm_fragment *fragment;
+ struct shm_fragment *ret_fragment;
+
+ if (size <= 0)
+ return NIL;
+ /* add size of "fragment->size" */
+ size+= (char *)&fragment->info.data - (char *)fragment ;
+
+ /* round "size" to nearest 16 byte value */
+ size= (size+15) & ~15;
+ if (size > block->free)
+ return NIL;
+ /* scan free list to find candidates for allocation */
+ for (prev=0, candidate=block->free_list;
+ candidate!=0 ;
+ prev=candidate, candidate= fragment->info.next )
+ {
+ fragment=FRAG_PTR(block,candidate);
+ if (fragment->size >= size)
+ break;
+ }
+
+ if (candidate == 0)
+ return NIL;
+
+ block->free-=size;
+ if (fragment->size == size) {
+ if (prev == 0)
+ block->free_list= fragment->info.next;
+ else
+ FRAG_PTR(block,prev)->info.next= fragment->info.next;
+ return PTR2REL(block, &fragment->info.data);
+ }
+
+ /* fragment->size > size */
+
+ /* Split fragment in two, return one part, put the other in free list. */
+ /* The part that starts at the old location - will stay in the free list. */
+ fragment->size -= size;
+
+ ret_fragment=FRAG_PTR(block, candidate + fragment->size);
+ ret_fragment->size= size;
+ return PTR2REL(block, ret_fragment->info.data);
+}
+
+/* like shm_FragmentAlloc, returns pointer instead of offset */
+char *shm_FragPtrAlloc(struct shm_block *block, int size)
+{
+ int ofs;
+ ofs= shm_FragmentAlloc(block,size);
+ if (ofs == NIL)
+ return NULL;
+ else
+ return (char *) REL2PTR(block, ofs);
+}
+/* This is used for debugging only */
+void shm_print_free_list(struct shm_block *block)
+{
+ struct shm_fragment *fragment;
+ int item;
+
+ item=block->free_list;
+ if (item==0) {
+ fprintf(stddeb,"no free fragments");
+ } else {
+ for (; item ; item=fragment->info.next) {
+ fragment=FRAG_PTR(block,item);
+ fprintf(stddeb,"{0x%04x,0x%04x} ",item,fragment->size);
+ }
+ }
+ fprintf(stddeb," [total free=%04x]\n",block->free);
+ fflush(stddeb);
+}
diff --git a/ipc/shm_fragment_test.c b/ipc/shm_fragment_test.c
new file mode 100644
index 0000000..8964715
--- /dev/null
+++ b/ipc/shm_fragment_test.c
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: shm_fragment_test.c
+ * Purpose: Test data fragments and free list items. Allocate and free blocks.
+ ***************************************************************************
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stddebug.h>
+#define DEBUG_DEFINE_VARIABLES /* just avoid dumb errors */
+#include <debug.h> /* for "stddeb" */
+#include <stdlib.h>
+#include <string.h>
+#include "shm_block.h"
+#include "shm_fragment.h"
+
+#define DO_FREE(id) (-id)
+#define LIST_LENGTH 20
+
+int main()
+{
+ struct shm_block *block;
+ char *ret;
+ int size;
+ int i;
+
+ /* important: The test will work only for the current implementation of */
+ /* allocation, if the implementation will change, the list should also */
+ /* cahnge. */
+ static int sizes[LIST_LENGTH]={
+ SHM_MINBLOCK, /* 0: should fail */
+ 0x3fe0-4, /* 1: */
+ 0x4000-4, /* 2: */
+ 0x4000-4, /* 3: */
+ 0x4000-4+1, /* 4: should fail */
+ 0x4000-4, /* 5: */
+ /* allocated(5,3,2,1) free() */
+ -5, /* 6: */
+ 0x1c00-4, /* 7: */
+ 0x1400-4, /* 8: */
+ 0x1000-4, /* 9: */
+ /* allocated(9,8,7,3,2,1) free() */
+ -9, /* 10: */
+ -3, /* 11: */
+ -1, /* 12: */
+ /* allocated(8,7,2) free(9,3,1) */
+ 0x1000-4, /* 13: */
+ -13, /* 14: */
+ 0x1000+1-4, /* 15: */
+ /* allocated(8,7,15,2) free(9,[3-15],1) */
+ -2, /* 16: */
+ /* allocated(8,7,15) free(9,[3-15],1+2) */
+ -8, /* 17: */
+ -7, /* 18: */
+ -15 /* 19: */
+ };
+
+ static char *ptr[LIST_LENGTH];
+
+ block=malloc(SHM_MINBLOCK);
+ assert(block);
+
+ /* setup first item in the free list */
+ shm_FragmentInit(block, sizeof(*block), SHM_MINBLOCK);
+
+ fprintf(stddeb,"After shm_FragmentInit\n");
+ shm_print_free_list(block);
+
+ for(i=0 ; i < LIST_LENGTH; i++) {
+ size=sizes[i];
+ if (size>0) { /* allocate */
+ ret=shm_FragPtrAlloc(block, size);
+ ptr[i]=ret;
+ fprintf(stddeb,
+ "%d: After shm_FragmentAlloc(block, 0x%06x) == ",
+ i, size);
+ if (ret==NULL)
+ fprintf(stddeb, "NULL\n");
+ else {
+ fprintf(stddeb, "0x%06x\n", (int)ret-(int)block);
+ bzero (ret,size); /* test boundaries */
+ }
+ } else { /* free */
+ /* free shm fragment */
+ ret=ptr[-sizes[i]];
+ fprintf(stddeb, "%d: Doing shm_FragmentFree(block, ", i);
+ if (ret==NULL)
+ fprintf(stddeb, "NULL)\n");
+ else
+ fprintf(stddeb, "0x%06x)\n", (int)ret-(int)block);
+ fflush(stddeb);
+ shm_FragPtrFree(block, ret);
+ }
+ shm_print_free_list(block);
+ }
+ return 0;
+}
diff --git a/ipc/shm_main_blk.c b/ipc/shm_main_blk.c
new file mode 100644
index 0000000..3fb798e
--- /dev/null
+++ b/ipc/shm_main_blk.c
@@ -0,0 +1,264 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: shm_main_blk.c
+ * Purpose: Main Wine's shared memory block
+ ***************************************************************************
+ */
+#define inline __inline__
+#include <sys/sem.h>
+#include <stdio.h>
+#include <time.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <stddebug.h>
+#include <debug.h>
+#include "shm_fragment.h"
+#include "shm_block.h"
+#include "shm_main_blk.h"
+#include "shm_semaph.h"
+
+#define WineKey ( 'W'+((int)'i'<<8)+((int)'n'<<16)+((int)'e'<<24) )
+#define SHM_KEY_RANGE 8
+
+/* main block (set during initialization) */
+struct shm_main_block *main_block=NULL;
+static char *shm_header="Wine - Windows emulator DDE mechanism";
+static int main_shm_id;
+
+static void shm_main_refresh();
+
+/* for debugging only */
+static void print_perm(struct ipc_perm *perm)
+{
+ printf("Permission:\n");
+ printf("\tKey=%d, mode=%03o, sequence #=%d\n",
+ (int)perm->key,perm->mode, perm->seq);
+ printf("\towner: uid=%d, gid=%d ;" ,perm->uid, perm->gid);
+ printf(" creator: uid=%d, gid=%d\n",perm->cuid,perm->cgid);
+}
+
+/* for debugging only */
+/* print_shm_info: print shared memory descriptor info */
+static void print_shm_info(int shm_id)
+{
+ struct shmid_ds ds;
+ shmctl(shm_id, IPC_STAT, &ds );
+
+ printf("shm_id=%d, Size=0x%08x , Number of attaches=%d\n",
+ shm_id, ds.shm_segsz, (int)ds.shm_nattch);
+ if (ds.shm_atime)
+ printf("Last attach=%s",ctime(&ds.shm_atime));
+ if (ds.shm_dtime)
+ printf("Last detach=%s",ctime(&ds.shm_dtime));
+ printf("Last change=%s",ctime(&ds.shm_ctime));
+ printf("pid: creator=%d, last operator=%d\n",
+ (int)ds.shm_cpid,(int)ds.shm_lpid);
+ print_perm( &ds.shm_perm);
+
+}
+
+int proc_exist(__pid_t pid)
+{
+ if ( kill(pid,0) == 0) /* dummy signal to test existence */
+ return 1;
+ else if (errno==ESRCH) /* "no such process" */
+ return 0;
+ else
+ return 1;
+}
+
+/* setup a new main shm block (only construct a shm block object). */
+static void shm_setup_main_block()
+{
+ dprintf_shm(stddeb,"creating data structure\n");
+ main_block->build_lock=1;
+ strcpy(main_block->magic, shm_header);
+
+ shm_setup_block(&main_block->block,sizeof(*main_block),SHM_MINBLOCK);
+
+ dde_proc_init(main_block->proc);
+ ATOM_GlobalInit();
+ shm_sem_init(&main_block->sem);
+
+ /* main block set and data structure is stable now */
+ main_block->build_lock=0;
+}
+
+/* Delete everything related to main_block */
+void shm_delete_all(int shmid)
+{
+ int proc_idx;
+
+ if (shmid == -1)
+ shmid= main_shm_id;
+
+ shmctl( shmid, IPC_RMID, NULL);
+
+ for (proc_idx= 0 ; proc_idx < DDE_PROCS ; proc_idx++)
+ dde_proc_done( &main_block->proc[proc_idx] );
+
+ shm_sem_done(&main_block->sem);
+ shmdt( (void *) main_block);
+ main_block= NULL;
+}
+
+int DDE_no_of_attached()
+{
+ struct shmid_ds shm_info;
+
+ if (shmctl(main_shm_id, IPC_STAT, &shm_info) == -1)
+ return -1;
+
+ return shm_info.shm_nattch;
+}
+/*
+** Test if shm_id is MainBlock and attach it (if it is),
+** Return 1 if ok, 0 otherwise.
+*/
+static int attach_MainBlock(int shm_id)
+{
+ struct shmid_ds shm_info;
+
+ if (shmctl(shm_id, IPC_STAT, &shm_info) == -1)
+ return 0;
+
+ /* Make sure we don't work on somebody else's block */
+ if (shm_info.shm_perm.cuid != getuid()) { /* creator is not me */
+ dprintf_shm(stddeb,"Creator is not me!\n");
+ return 0;
+ }
+
+ dprintf_shm(stddeb,"shared memory exist, attaching anywhere\n");
+ main_block=(struct shm_main_block *)shmat(shm_id, 0, 0);
+ if ( (int)main_block==-1) {
+ dprintf_shm(stddeb,"Attach failed\n");
+ return 0;
+ }
+
+ if (strcmp(main_block->magic, shm_header) != 0) {
+ dprintf_shm(stddeb,"Detaching, wrong magic\n");
+ shmdt((void *)main_block);
+ return 0;
+ }
+
+ if (debugging_shm)
+ print_shm_info(shm_id);
+
+ /* Is it an old unused block ? */
+ if (shm_info.shm_nattch == 0) {
+ dprintf_shm(stddeb,"No attaches, deleting old data\n");
+ shm_delete_all(shm_id);
+ return 0;
+ }
+
+ /* Wait for data structure to stabilize */
+ while (main_block->build_lock)
+ usleep(10000);
+
+ main_shm_id= shm_id;
+
+ shm_main_refresh();
+ return 1;
+}
+
+/* (Function used by the constructor)
+ * Try to get existing shared memory with key="Wine", size=SHM_MINBLOCK
+ * complete user permission.
+ * If such block is found - return true (1), else return false (0)
+ */
+static int shm_locate_MainBlock(key_t shm_key)
+{
+ int shm_id; /* Descriptor to this shared memory */
+ int i;
+
+ dprintf_shm(stddeb,"shm_locate_MainBlock: trying to attach, key=0x%x\n",
+ shm_key);
+ for (i=0 ; i < SHM_KEY_RANGE ; i++) {
+ dprintf_shm(stddeb,"iteration=%d\n", i);
+
+ shm_id= shmget ( shm_key+i, SHM_MINBLOCK ,0700);
+
+ if (shm_id != -1) {
+ if ( attach_MainBlock(shm_id) ) {
+ return 1; /* success! */
+ }
+ } else {
+ switch(errno) {
+ case EIDRM: /* segment destroyed */
+ case EACCES: /* no user permision */
+ break;
+
+ case ENOMEM: /* no free memory */
+ case ENOENT: /* this key does not exist */
+ default :
+ dprintf_shm(stddeb,"shmget failed, errno=%d, %s\n",
+ errno, strerror(errno) );
+ return 0; /* Failed */
+ }
+ } /* if .. else */
+ } /* for */
+ return 0;
+}
+
+/* (Function used by the constructor)
+ * Try to allocate new shared memory with key="Wine", size=SHM_MINBLOCK
+ * with complete user permission.
+ * If allocation succeeds - return true (1), else return false (0)
+ */
+static int shm_create_MainBlock(key_t MainShmKey)
+{
+ int shm_id;
+ int flags= 0700 | IPC_CREAT | IPC_EXCL;
+ int i;
+
+ dprintf_shm(stddeb,"creating shared memory\n");
+
+ /* try to allocate shared memory with key="Wine", size=SHM_MINBLOCK, */
+ /* complete user permission */
+ for (i=0 ; i < SHM_KEY_RANGE ; i++) {
+ shm_id= shmget ( (key_t) MainShmKey, SHM_MINBLOCK, flags);
+ if (shm_id != -1)
+ break;
+ }
+ if (shm_id == -1) {
+ dprintf_shm(stddeb,"failed to create shared memory\n");
+ return 0;
+ }
+ dprintf_shm(stddeb,"shared memory created, attaching\n");
+ main_block=(struct shm_main_block*) shmat(shm_id, 0,0);
+ if (debugging_shm)
+ print_shm_info(shm_id);
+ main_shm_id= shm_id;
+ shm_setup_main_block();
+ dde_wnd_setup();
+ return 1;
+
+}
+
+/* link to the dde shared memory block */
+/* RETURN: 0 on success, non zero on failure */
+int shm_init(void)
+{
+ if ( !shm_locate_MainBlock(WineKey)
+ && !shm_create_MainBlock(WineKey)) {
+ fflush(stdout);
+ fprintf(stderr,"shm_init: failed to init main shm block\n");
+ exit(1);
+ }
+
+ dde_proc_add(main_block->proc);
+ return 0;
+}
+
+static void shm_main_refresh()
+{
+ int proc_idx;
+
+ for (proc_idx= 0 ; proc_idx < DDE_PROCS ; proc_idx++)
+ dde_proc_refresh( &main_block->proc[proc_idx] );
+}
diff --git a/ipc/shm_semaph.c b/ipc/shm_semaph.c
new file mode 100644
index 0000000..c226808
--- /dev/null
+++ b/ipc/shm_semaph.c
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: shm_semaph.c
+ * Purpose: Handle semaphores for shared memory operations.
+ ***************************************************************************
+ */
+#define inline __inline__
+#include <assert.h>
+#include <unistd.h>
+#include <sys/sem.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stddebug.h>
+#include <debug.h>
+#include "shm_semaph.h"
+#define SEM_READ 0
+#define SEM_WRITE 1
+
+/* IMPORTANT: Make sure that killed process will not lock everything.
+ * If possible, restrict usage of these functions.
+ */
+void shm_read_wait(shm_sem semid)
+{
+ struct sembuf sop[2];
+ int ret;
+
+ dprintf_sem(stddeb,"shm_read_wait(%d)\n",semid);
+ sop[0].sem_num=SEM_READ;
+ sop[0].sem_op=1; /* add this read instance */
+ sop[0].sem_flg=SEM_UNDO; /* undo in case process dies */
+
+ sop[1].sem_num=SEM_WRITE;
+ sop[1].sem_op=0; /* wait until no writing instance exists */
+ sop[1].sem_flg=SEM_UNDO;
+
+ do {
+ ret=semop (semid,sop , 2);
+ } while (ret<0 && errno==EINTR); /* interrupted system call? */
+
+ if (ret<0)
+ fprintf(stderr,"failed semaphore lock for read. semid=%d,errno=%d\n",
+ semid, errno);
+}
+void shm_write_wait(shm_sem semid)
+{
+ struct sembuf sop[3];
+ int ret;
+
+ dprintf_sem(stddeb,"shm_write_wait(%d)\n",semid);
+ sop[0].sem_num=SEM_READ;
+ sop[0].sem_op=0; /* wait until no reading instance exist */
+ sop[0].sem_flg=SEM_UNDO;
+
+ sop[1].sem_num=SEM_WRITE;
+ sop[1].sem_op=1; /* writing is in progress - disable read */
+ sop[1].sem_flg=SEM_UNDO; /* undo if process dies */
+
+ sop[2].sem_num=SEM_READ;
+ sop[2].sem_op=1; /* disable new writes */
+ sop[2].sem_flg=SEM_UNDO;
+
+ do {
+ ret=semop (semid,sop , 3);
+ } while (ret<0 && errno==EINTR); /* interrupted system call? */
+
+ if (ret<0) /* test for the error */
+ fprintf(stderr,"failed semaphore lock for write. semid=%d,errno=%d\n",
+ semid, errno);
+}
+void shm_write_signal(shm_sem semid)
+{
+ struct sembuf sop[2];
+ int ret;
+
+ dprintf_sem(stddeb,"shm_write_signal(%d)\n",semid);
+ sop[0].sem_num=SEM_READ;
+ sop[0].sem_op=-1;
+ sop[0].sem_flg=IPC_NOWAIT | SEM_UNDO; /* no reason to wait */
+
+ sop[1].sem_num=SEM_WRITE;
+ sop[1].sem_op=-1;
+ sop[1].sem_flg=IPC_NOWAIT | SEM_UNDO; /* no reason to wait */
+
+ do {
+ ret=semop (semid,sop , 2);
+ } while (ret<0 && errno==EINTR); /* interrupted system call? */
+
+ if (ret<0) /* test for the error */
+ fprintf(stderr,"failed semaphore unlock for write. semid=%d,errno=%d\n",
+ semid, errno);
+}
+
+void shm_read_signal(shm_sem semid)
+{
+ struct sembuf sop[2];
+ int ret;
+
+ dprintf_sem(stddeb,"shm_read_signal(%d)\n",semid);
+ sop[0].sem_num=SEM_READ;
+ sop[0].sem_op=-1;
+ sop[0].sem_flg=IPC_NOWAIT | SEM_UNDO; /* no reason to wait */
+
+ do {
+ ret=semop (semid,sop , 1);
+ } while (ret<0 && errno==EINTR); /* interrupted system call? */
+
+ if (ret<0) /* test for the error */
+ fprintf(stderr,"failed semaphore unlock for read. semid=%d,errno=%d\n",
+ semid, errno);
+}
+
+void shm_sem_init(shm_sem *sptr)
+{
+ shm_sem semid;
+ union semun arg;
+
+ semid=semget (IPC_PRIVATE, 2, 0700 | IPC_CREAT);
+
+ arg.val=0;
+ semctl (semid, 0, SETVAL, arg);
+ semctl (semid, 1, SETVAL, arg);
+ *sptr=semid;
+}
+
+void shm_sem_done(shm_sem *semptr)
+{
+ union semun arg;
+
+ semctl (*semptr, 0, IPC_RMID , arg);
+ semctl (*semptr, 1, IPC_RMID , arg);
+
+ *semptr= -1;
+}
diff --git a/ipc/shm_semaph_test.c b/ipc/shm_semaph_test.c
new file mode 100644
index 0000000..fdceeb5
--- /dev/null
+++ b/ipc/shm_semaph_test.c
@@ -0,0 +1,128 @@
+/***************************************************************************
+ * Copyright 1995, Technion, Israel Institute of Technology
+ * Electrical Eng, Software Lab.
+ * Author: Michael Veksler.
+ ***************************************************************************
+ * File: shm_semaph_test.c
+ * Purpose: Test semaphores handleingr shared memory operations.
+ ***************************************************************************
+ */
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include "shm_semaph.h"
+#include <sys/shm.h>
+#define DEBUG_DEFINE_VARIABLES
+#include <stddebug.h>
+#include <debug.h>
+
+static volatile int * volatile data;
+static int isparent=0;
+#define DELAY (rand()%10)
+shm_sem sem;
+
+static void read_write(int num)
+{
+ int i,j ;
+ volatile float dummy=0;
+ int val;
+
+ srand(num+time(NULL));
+ for (i=0x3fff;i>=0;i--) {
+ if((i&0x7ff)==0 && isparent)
+ fprintf(stderr,"0x%06x\r",i);
+ shm_write_wait(sem);
+ *data= num;
+ for (j=DELAY ; j>=0;j--)
+ dummy*=2;
+ if (*data!=num) {
+ fprintf(stderr,"\nbad shm_write_wait(), num=%d\n",num);
+ shm_write_signal(sem);
+ return;
+ }
+ shm_write_signal(sem);
+ for (j=DELAY ; j>=0 ;j--)
+ dummy*=2;
+ shm_read_wait(sem);
+ val=*data;
+ for (j=DELAY; j>=0 ;j--)
+ dummy*=0.5;
+ if (*data!=val) {
+ fprintf(stderr,"\nbad shm_read_wait(), num=%d,val=%d,*data=%d\n",
+ num,val,*data);
+ shm_read_signal(sem);
+ return;
+ }
+ shm_read_signal(sem);
+ }
+ if (isparent)
+ fputc('\n',stderr);
+}
+static void child1()
+{
+ read_write(2);
+}
+static void child2()
+{
+ read_write(10);
+}
+static void parent()
+{
+ isparent=1;
+ read_write(60);
+}
+
+int main()
+{
+ int shmid;
+ int ret1, ret2;
+ int pid1, pid2;
+ int stat=0;
+
+ shm_sem_init(&sem);
+ shmid=shmget(IPC_PRIVATE, 0x100, IPC_CREAT | 0700);
+ data= (int *)shmat ( shmid, NULL, 0);
+ *data=0;
+
+ switch (pid1=fork()) {
+ case -1:
+ perror("fork 1");
+ return 1;
+ case 0:
+ fprintf(stderr,"child1\n");
+ child1();
+ fprintf(stderr,"child1 done\n");
+ return 0;
+ default :
+ }
+ switch (pid2=fork()) {
+ case -1:
+ perror("fork 2");
+ stat|=1;
+ break;
+ case 0:
+ fprintf(stderr,"child2\n");
+ child2();
+ fprintf(stderr,"child2 done\n");
+ return 0;
+ default :
+ }
+ fprintf(stderr,"parent\n");
+ if (pid2>0) { /* if second fork did not fail */
+ parent();
+ fprintf(stderr,"parent done, waiting for child2\n");
+ waitpid(pid2,&ret2,WUNTRACED);
+ stat|=ret2;
+ }
+ fprintf(stderr,"parent done, waiting for child1\n");
+ waitpid(pid1,&ret1,WUNTRACED);
+ stat|=ret1;
+ fprintf(stderr,"all done\n");
+
+ shmctl(shmid, IPC_RMID,NULL);
+ shm_sem_done(&sem);
+ return stat;
+}
diff --git a/ipc/wine_test_stub.c b/ipc/wine_test_stub.c
new file mode 100644
index 0000000..fb1747e
--- /dev/null
+++ b/ipc/wine_test_stub.c
@@ -0,0 +1,117 @@
+#include <stdlib.h>
+#include "dde.h"
+#include <wintypes.h>
+#include "global.h"
+#include <win.h>
+#define DEBUG_DEFINE_VARIABLES
+#define DEBUG_ALL
+#include <stddebug.h>
+#include <debug.h>
+
+#define DDE_PROC2WIN(proc_idx) ( (HWND) ~( (proc_idx)+1) )
+#define DDE_WIN2PROC(win) ( (int) ~(short) ((win)+1) )
+#define DDE_IsRemoteWindow(win) ( (win)<0xffff && (win)>=(0xffff-DDE_PROCS))
+
+
+char *MessageTypeNames[0x400]={NULL};
+char *dummy_store_for_debug_msg_name;
+
+ldt_copy_entry ldt_copy[LDT_SIZE];
+
+int LDT_GetEntry( int entry, ldt_entry *content )
+{
+ return 0;
+}
+
+int LDT_SetEntry( int entry, ldt_entry *content )
+{
+ return 0;
+}
+
+void dummy_usage_of_debug_msg_name()
+{
+ dummy_store_for_debug_msg_name=debug_msg_name[0];
+}
+
+/* stub */
+HWND GetDesktopWindow()
+{
+ printf("GetDesktopWindow\n");
+ return 0;
+}
+/* stub */
+/* smart stub */
+LONG SendMessage(HWND a,WORD b,WORD c,LONG d)
+{
+ MSG msg;
+ printf("SendMessage(%04x,%04x,%04x,%04lx)\n",a,b,c,d);
+ if (DDE_IsRemoteWindow(a) || a==(HWND)-1)
+ return 0;
+ if (b!=WM_DDE_INITIATE)
+ return 0;
+ msg.hwnd=c;
+ msg.message= WM_DDE_ACK;
+ msg.lParam= 0;
+ msg.wParam= 0;
+ return DDE_SendMessage(&msg);
+}
+/* stub */
+BOOL PostMessage(HWND a,WORD b,WORD c,LONG d)
+{
+ printf("PostMessage(%04x,%04x,%04x,%04lx)\n",a,b,c,d);
+ return 0;
+}
+/* stub */
+HWND GetTopWindow(HWND a)
+{
+ printf("GetTopWindow(%04x)\n",a);
+ return 1;
+}
+/* stub */
+WORD FreeSelector(WORD a)
+{
+ printf("FreeSelector(%04x)\n",a);
+ return 0;
+}
+
+/* stub that partially emulates the true GLOBAL_CreateBlock function */
+HGLOBAL GLOBAL_CreateBlock( WORD flags, void *ptr, DWORD size,
+ HGLOBAL hOwner, BOOL isCode,
+ BOOL is32Bit, BOOL isReadOnly,
+ SHMDATA *shmdata )
+{
+
+ printf("GLOBAL_CreateBlock(flags=0x%x,ptr=0x%08lx, size=0x%x,hOwner=0x%x\n",
+ (int)flags, (long)ptr, (int)size, (int)hOwner);
+ printf("isCode=%d, is32Bit=%d, isReadOnly=%d, \n", isCode, is32Bit,
+ isReadOnly);
+ printf("*shmdata={handle=0x%x,sel=0x%x, shmid=%d})\n",
+ shmdata->handle, shmdata->sel, shmdata->shmid);
+ return 1;
+}
+
+/* stub */
+WND *WIN_FindWndPtr(HWND hwnd)
+{
+ static WND win;
+ printf("WIN_FindWndPtr(%d)\n",hwnd);
+ if (hwnd==0)
+ return NULL;
+ win.hwndNext=0;
+ win.dwStyle=WS_POPUP;
+
+ return &win;
+}
+
+/* stub */
+WORD GetCurrentPDB(void)
+{
+ printf("GetCurrentPDB()\n");
+
+ return 0;
+}
+
+/* stub */
+void Yield(void)
+{
+}