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)
+{
+}