Release 971221

Fri Dec 19 10:50:46 1997  Douglas Ridgway  <ridgway@winehq.com>

	* [Make.rules.in] [Makefile.in] [documentation/Makefile.in]
 	  [documentation/README.documentation]
	First cut at Wine API documentation. No longer install reference
	manual by default.

Wed Dec 17 21:32:23 1997  Andreas Mohr <100.30936@germany.net>

	* [files/file.c]
	Fixed GetTempFileName16() to use current path of requested drive
	as needed.

	* [if1632/Makefile.in] [if1632/builtin.c] [if1632/dciman32.spec]
	  [if1632/msvfw32.spec] [if1632/tapi32.spec] [if1632/wow32.spec]
	Added misc DLLs needed by various apps.

Wed Dec 17 12:01:50 1997  Morten Eriksen <mortene@sim.no>

	* [if1632/gdi32.spec] [include/windows.h] [objects/palette.c]
	Inserted empty stub for CreateHalftonePalette.

Tue Dec 16 22:08:06 1997  Huw D M Davies <h.davies1@physics.oxford.ac.uk>

	* [windows/mdi.c]
	Use VK_TAB instead of VK_SEPARATOR in TranslateMDISysAccel().

	* [graphics/metafiledrv/init.c]
	DeleteDC() on a MetaDC doesn't do anything - it shouldn't. Therefore
	fix cleanup of MetaDCs in CloseMetaFile(); they now actually get
	removed from the GDI heap!

	* [graphics/x11drv/xfont.c]
	Preserve FO_MATCH_XYINDEP flag in XFONT_MatchFIList(). Should reduce
	the number of bold-italic matches.

Tue Dec 16 20:11:43 1997  Bertho Stultiens <bertho@panter.soci.aau.dk>

	* [graphics/painting.c]
	Included an implementation of DrawState

	* [if1632/thunk.c]
	Changed many fprintfs into dprintf_thunk

	* [include/cache.h] [graphics/cache.c]
	New files to hold cached handles to regulary used GDI object.

	* [include/windows.h]
	Added DRAWSTATExx typedefs
	Added DSS_DEFAULT define for DrawState

	* [objects/text.c]
	New implementation of GrayString()

	* [controls/uitools.c]
	Implemented DrawFrameControl() functions
	Changed DrawEdge() behaviour to win95 implementation

Mon Dec 15 23:43:01 1997  Martin Boehme <boehme@informatik.mu-luebeck.de>

	* [graphics/path.c] [include/path.h] [graphics/painting.c]
	  [if1632/gdi32.spec] [include/gdi.h] [include/windows.h]
	  [objects/dc.c]
        Added preliminary support for GDI paths.

	* [objects/dc.c]
	Added DC_Init_DC_INFO function for initializing WIN_DC_INFO
	structure.

	* [include/windows.h] [include/gdi.h] [objects/gdiobj.c]
	Added DEFAULT_GUI_FONT.

	* [include/winerror.h]
	Added a few error codes.

	* [memory/heap.c]
	Changed HeapAlloc to make the correct calls to SetLastError
	(now conforms to NT's behaviour).

	* [windows/win.c]
	Changed WIN_CreateWindowEx to allow child windows with zero
	width / height.

Sun Dec 14 12:01:07 1997  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [if1632/*] [relay32/*]
	Moved all 32-bit relay stuff to relay32/

	* [fi1632/thunk.c] [win32/kernel32.c]
	Moved all KERNEL32 ordinal functions to kernel32.c

	* [memory/selector.c]
	Initialize selectors in AllocSelectorArray.

	* [tools/build.c]
	Generate C instead of assembly for Win32 relays.
	Fixed stack corruption in CallTo16 functions, found by Bertho
	Stultiens.
	
Sun Dec 14 10:55:00 1997  Andreas Mohr <100.30936@germany.net>

	* [if1632/Makefile.in] [if1632/builtin.c] [if1632/ole2thk.spec]
	Added built-in OLE2THK.DLL. 

	* [if1632/toolhelp.spec] [include/toolhelp.h] [memory/selector.c]
	  [misc/toolhelp.c]
	Added stubs for StackTraceFirst(), StackTraceCSIPFirst(),
	StackTraceNext(), UTSelectorOffsetToLinear()
	and UTLinearToSelectorOffset().

Sat Dec 13 17:26:41 1997  Alex Korobka  <alex@trantor.pharm.sunysb.edu>

	* [misc/winsock.c]
	32-bit API fixes for reported problems (thanks to Marcus
	and David).

	* [graphics/x11drv/xfont.c]
	Little tweak in point size calculation.

	* [windows/defwnd.c] [windows/dce.c] [windows/winhelp.c]
	  [windows/winproc.c] [windows/win.c]
	Bug fixes.

Sat Dec 13 16:35:14 1997  Kai Morich <kai.morich@rhein-neckar.netsurf.de>

	* [files/dos_fs.c]
	OpenFile with empty filename and OF_PARSE returns current dir.

	* [misc/commdlg.c]
	Ignore initial dir if bogus.

	* [files/file.c]
	Locking an identic region in a file must not be an error.

	* [misc/lstr.c]
	Use wide char ctype functions.

Fri Dec 12 23:46:22 1997  Uwe Bonnes  <bon@elektron.ikp.physik.tu-darmstadt.de>

	* [file/drive.c]
	First attempt for GetDiskFreeSpaceEx.

Fri Dec 12 23:18:41 1997  Marcus Meissner  <msmeissn@cip.informatik.uni-erlangen.de>

	* [loader/pe_resource.c]	
	Fixed wrongly appearing menus problem (only use default lookups in
	last resource subtree).

	* [multimedia/*.c]
	Added win32 support for time* and joy* lowlevel drivers,
	(not excessively tested), some misc fixes and cleanups.

	* [misc/shellord.c][misc/shell.c][ole/folders.c][ole/ifs.c]
	  [include/interfaces.h][include/shlobj.h]
	Added some more undocumented SHELL32 functions, some shell folder
 	interface stubs added, SHGetMalloc, SHGetDesktopFolder,
	SHGetSpecialFolderLocation, SHGetPathFromIDList stubs added,
	IMalloc, IUnknown implemented.

	* [windows/msgbox.c]
	Implemented MessageBoxIndirect*, some internal changes.

	* [if1632/thunk.c]
	KERNEL_431 implemented.

	* [objects/gdiobj.c]
	GetCurrentObject implemented.

Wed Dec  3 01:09:17 1997  Gordon Chaffee  <chaffee@apu.cs.berkeley.edu>

	* [objects/dib.c]
	Fix a couple small DIB problems.

	* [controls/edit.c]
	Fix a typo.

	* [files/dos_fs.c]
	Try normal readdir in case fs is specified as vfat but isn't.

	* [files/profile.c]
	Implementation of WritePrivateProfileSection32A from Uwe Bonnes.

	* [misc/printdrv.c]
	OpenPrinter32A stub, helps Word97 start.

	* [objects/text.c]
	Fixup implementation of GetTextCharsetInfo.

	* [scheduler/process.c]
	Fix environment variable expansion.

	* [win32/code_page.c]
	Make MultiByteToWideChar and WideCharToMultiByte conform in return
	values and error conditions to those in Windows NT 4.0.

	* [windows/message.c]
	Fix broadcasting problems in Win32.  The Win32 docs say to use
	HWND_TOPMOST to broadcast to all Win32 Windows.

	* [memory/virtual.c] [loader/pe_image.c]
	Do not map in VirtualAlloc if address is specified and space is
	not available.  This is required by Win32.

	* [include/pen.h] [include/x11drv.h] [objects/dc.c]
	  [objects/pen.c] [graphics/x11drv/pen.c]
	Support for ExtCreatePen.

Tue Dec  2 20:22:06 1997  Morten Welinder  <terra@diku.dk>

	* [*/*.c] [*/*.h]
	Add lots of prototypes.

	* [if1632/kernel32.spec][include/windows.h][include/winnt.h]
	  [misc/cpu.c]
	Define IsProcessorFeaturePresent.

	* [misc/crtdll.c]
 	(CRTDLL__getcwd): Allocate enough memory for the terminating zero.

	* [misc/ver.c]
	Improve check for null component in _find_data[AW].  Plug leaks
	in VerQueryValue*.

	* [win32/console.c][if1632/kernel32.spec]
	Add stubs for GetConsoleCursorInfo32, SetConsoleCursorInfo32.

	* [windows/message.c][if1632/user32.spec][include/windows.h]
	Define SendMessageTimeout*.

	* [graphics/x11drv/xfont.c]
	Change algorithm of __genericCheckSum to be alignment safe.

	* [misc/main.c] [misc/winsock.c] [misc/winsock_dns.c]
	Include winsock.h early to avoid Solaris problem.

	* [include/windows.h]
	Undef FSHIFT before we define it.

	* [rc/winerc.c]
	Include <fcntl.h> instead of <sys/fcntl.h>.

	* [files/file.c]
	Use strerror in FILE_SetDosError if available.

	* [include/config.h.in] [configure.in]
	Check for strerror.

	* [objects/gdiobj.c]
	Make static font structures aligned.

Mon Dec  1 10:10:21 1997  Karl Garrison  <karlos@eznet.net>

	* [win32/console.c] [if1632/kernel32.spec] [include/windows.h]
	Added stub for GetNumberOfConsoleMouseButtons.
	Added stub for PeekConsoleInput(A,W).
	Fixed parameter list for WriteConsole(A,W).
	GetNumberOfConsoleInputEvents now returns 0 events instead of 1
	(since low-level console functions are not yet supported).
	GetConsoleMode no longer returns ENABLE_WINDOW_INPUT and
	ENABLE_MOUSE_INPUT since these are not currently implemented.
diff --git a/graphics/path.c b/graphics/path.c
new file mode 100644
index 0000000..e4e4013
--- /dev/null
+++ b/graphics/path.c
@@ -0,0 +1,704 @@
+/*
+ * Graphics paths (BeginPath, EndPath etc.)
+ *
+ * Copyright 1997 Martin Boehme
+ */
+
+#include <assert.h>
+#include <malloc.h>
+
+#include "windows.h"
+#include "winerror.h"
+
+#include "dc.h"
+#include "debug.h"
+#include "path.h"
+
+/* Notes on the implementation
+ *
+ * The implementation is based on dynamically resizable arrays of points and
+ * flags. I dithered for a bit before deciding on this implementation, and
+ * I had even done a bit of work on a linked list version before switching
+ * to arrays. It's a bit of a tradeoff. When you use linked lists, the
+ * implementation of FlattenPath is easier, because you can rip the
+ * PT_BEZIERTO entries out of the middle of the list and link the
+ * corresponding PT_LINETO entries in. However, when you use arrays,
+ * PathToRegion becomes easier, since you can essentially just pass your array
+ * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
+ * have had the extra effort of creating a chunk-based allocation scheme
+ * in order to use memory effectively. That's why I finally decided to use
+ * arrays. Note by the way that the array based implementation has the same
+ * linear time complexity that linked lists would have since the arrays grow
+ * exponentially.
+ *
+ * The points are stored in the path in device coordinates. This is
+ * consistent with the way Windows does things (for instance, see the Win32
+ * SDK documentation for GetPath).
+ *
+ * The word "stroke" appears in several places (e.g. in the flag
+ * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
+ * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
+ * PT_MOVETO. Note that this is not the same as the definition of a figure;
+ * a figure can contain several strokes.
+ *
+ * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
+ * the path is open and to call the corresponding function in path.c if this
+ * is the case. A more elegant approach would be to modify the function
+ * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
+ * complex. Also, the performance degradation caused by my approach in the
+ * case where no path is open is so small that it cannot be measured.
+ *
+ * Martin Boehme
+ */
+
+/* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
+
+#define NUM_ENTRIES_INITIAL 16  /* Initial size of points / flags arrays  */
+#define GROW_FACTOR_NUMER    2  /* Numerator of grow factor for the array */
+#define GROW_FACTOR_DENOM    1  /* Denominator of grow factor             */
+
+
+static BOOL32 PATH_PathToRegion(const GdiPath *pPath, INT32 nPolyFillMode,
+   HRGN32 *pHrgn);
+static void   PATH_EmptyPath(GdiPath *pPath);
+static BOOL32 PATH_AddEntry(GdiPath *pPath, POINT32 point, BYTE flags);
+static BOOL32 PATH_ReserveEntries(GdiPath *pPath, INT32 numEntries);
+static BOOL32 PATH_GetPathFromHDC(HDC32 hdc, GdiPath **ppPath);
+
+/***********************************************************************
+ *           BeginPath32    (GDI32.9)
+ */
+BOOL32 WINAPI BeginPath32(HDC32 hdc)
+{
+   GdiPath *pPath;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_HANDLE);
+      return FALSE;
+   }
+   
+   /* If path is already open, do nothing */
+   if(pPath->state==PATH_Open)
+      return TRUE;
+
+   /* Make sure that path is empty */
+   PATH_EmptyPath(pPath);
+
+   /* Initialize variables for new path */
+   pPath->newStroke=TRUE;
+   pPath->state=PATH_Open;
+   
+   return TRUE;
+}
+
+
+/***********************************************************************
+ *           EndPath32    (GDI32.78)
+ */
+BOOL32 WINAPI EndPath32(HDC32 hdc)
+{
+   GdiPath *pPath;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_HANDLE);
+      return FALSE;
+   }
+   
+   /* Check that path is currently being constructed */
+   if(pPath->state!=PATH_Open)
+   {
+      SetLastError(ERROR_CAN_NOT_COMPLETE);
+      return FALSE;
+   }
+   
+   /* Set flag to indicate that path is finished */
+   pPath->state=PATH_Closed;
+   
+   return TRUE;
+}
+
+
+/***********************************************************************
+ *           AbortPath32    (GDI32.1)
+ */
+BOOL32 WINAPI AbortPath32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+   GdiPath *pPath;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return FALSE;
+   }
+   
+   /* Remove all entries from the path */
+   PATH_EmptyPath(pPath);
+
+   return TRUE;
+}
+
+
+/***********************************************************************
+ *           CloseFigure32    (GDI32.16)
+ */
+BOOL32 WINAPI CloseFigure32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+   GdiPath *pPath;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return FALSE;
+   }
+   
+   /* Check that path is open */
+   if(pPath->state!=PATH_Open)
+   {
+      SetLastError(ERROR_CAN_NOT_COMPLETE);
+      return FALSE;
+   }
+   
+   /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
+   if(pPath->numEntriesUsed)
+   {
+      pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
+      pPath->newStroke=TRUE;
+   }
+
+   return TRUE;
+}
+
+
+/***********************************************************************
+ *           GetPath32    (GDI32.210)
+ */
+INT32 WINAPI GetPath32(HDC32 hdc, LPPOINT32 pPoints, LPBYTE pTypes,
+   INT32 nSize)
+{
+   GdiPath *pPath;
+   BOOL32  temp_flag;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return -1;
+   }
+   
+   /* Check that path is closed */
+   if(pPath->state!=PATH_Closed)
+   {
+      SetLastError(ERROR_CAN_NOT_COMPLETE);
+      return -1;
+   }
+   
+   if(nSize==0)
+      return pPath->numEntriesUsed;
+   else if(nSize<pPath->numEntriesUsed)
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return -1;
+   }
+   else
+   {
+      memcpy(pPoints, pPath->pPoints, sizeof(POINT32)*pPath->numEntriesUsed);
+      memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
+
+      /* Convert the points to logical coordinates */
+      temp_flag=DPtoLP32(hdc, pPoints, pPath->numEntriesUsed);
+
+      /* Since hdc is valid, conversion should never fail */
+      assert(temp_flag);
+      
+      return pPath->numEntriesUsed;
+   }
+}
+
+
+/***********************************************************************
+ *           PathToRegion32    (GDI32.261)
+ */
+HRGN32 WINAPI PathToRegion32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+/* The documentation does not state this explicitly, but a test under Windows
+ * shows that the region which is returned should be in device coordinates.
+ */
+{
+   GdiPath *pPath;
+   HRGN32  hrgnRval;
+
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return 0;
+   }
+   
+   /* Check that path is closed */
+   if(pPath->state!=PATH_Closed)
+   {
+      SetLastError(ERROR_CAN_NOT_COMPLETE);
+      return 0;
+   }
+   
+   /* FIXME: Should we empty the path even if conversion failed? */
+   if(PATH_PathToRegion(pPath, GetPolyFillMode32(hdc), &hrgnRval))
+      PATH_EmptyPath(pPath);
+   else
+      hrgnRval=0;
+
+   return hrgnRval;
+}
+
+
+/***********************************************************************
+ *           FillPath32    (GDI32.100)
+ */
+BOOL32 WINAPI FillPath32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+   GdiPath *pPath;
+   INT32   mapMode;
+   POINT32 ptViewportExt, ptViewportOrg, ptWindowExt, ptWindowOrg;
+   HRGN32  hrgn;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return FALSE;
+   }
+   
+   /* Check that path is closed */
+   if(pPath->state!=PATH_Closed)
+   {
+      SetLastError(ERROR_CAN_NOT_COMPLETE);
+      return FALSE;
+   }
+   
+   /* Construct a region from the path and fill it */
+   if(PATH_PathToRegion(pPath, GetPolyFillMode32(hdc), &hrgn))
+   {
+      /* Since PaintRgn interprets the region as being in logical coordinates
+       * but the points we store for the path are already in device
+       * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
+       */
+       
+      /* Save the information about the old mapping mode */
+      mapMode=GetMapMode32(hdc);
+      GetViewportExtEx32(hdc, &ptViewportExt);
+      GetViewportOrgEx32(hdc, &ptViewportOrg);
+      GetWindowExtEx32(hdc, &ptWindowExt);
+      GetWindowOrgEx32(hdc, &ptWindowOrg);
+      
+      /* FIXME: Once world transforms become available, we will have to do
+       * a GetWorldTransform, too (along with a SetWorldTransform later on).
+       * Moral: Perhaps I should have used SaveDC right away. The reason why
+       * I didn't is that I wanted to avoid the overhead of a full SaveDC
+       * (especially since SaveDC now saves the current path as well).
+       */
+
+      /* Set MM_TEXT */
+      SetMapMode32(hdc, MM_TEXT);
+      
+      /* Paint the region */
+      PaintRgn32(hdc, hrgn);
+
+      /* Restore the old mapping mode */
+      SetMapMode32(hdc, mapMode);
+      SetViewportExtEx32(hdc, ptViewportExt.x, ptViewportExt.y, NULL);
+      SetViewportOrgEx32(hdc, ptViewportOrg.x, ptViewportOrg.y, NULL);
+      SetWindowExtEx32(hdc, ptWindowExt.x, ptWindowExt.y, NULL);
+      SetWindowOrgEx32(hdc, ptWindowOrg.x, ptWindowOrg.y, NULL);
+
+      /* Empty the path */
+      PATH_EmptyPath(pPath);
+      return TRUE;
+   }
+   else
+   {
+      /* FIXME: Should the path be emptied even if conversion failed? */
+      /* PATH_EmptyPath(pPath); */
+      return FALSE;
+   }
+}
+
+
+/***********************************************************************
+ *           SelectClipPath32    (GDI32.296)
+ */
+BOOL32 WINAPI SelectClipPath32(HDC32 hdc, int iMode)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+   GdiPath *pPath;
+   HRGN32  hrgnPath, hrgnClip;
+   BOOL32  success = FALSE;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return FALSE;
+   }
+   
+   /* Check that path is closed */
+   if(pPath->state!=PATH_Closed)
+   {
+      SetLastError(ERROR_CAN_NOT_COMPLETE);
+      return FALSE;
+   }
+   
+   /* Construct a region from the path */
+   if(PATH_PathToRegion(pPath, GetPolyFillMode32(hdc), &hrgnPath))
+   {
+      hrgnClip=CreateRectRgn32(0, 0, 0, 0);
+      if(hrgnClip!=NULL)
+      {
+         success=(GetClipRgn32(hdc, hrgnClip)!=-1) &&
+	    (CombineRgn32(hrgnClip, hrgnClip, hrgnPath, iMode)!=ERROR) &&
+	    (SelectClipRgn32(hdc, hrgnClip)!=ERROR);
+	 DeleteObject32(hrgnClip);
+      }
+
+      DeleteObject32(hrgnPath);
+
+      /* Empty the path */
+      if(success)
+         PATH_EmptyPath(pPath);
+      /* FIXME: Should this function delete the path even if it failed? */
+
+      return success;
+   }
+   else
+      return FALSE;
+}
+
+
+/***********************************************************************
+ * Exported functions
+ */
+
+/* PATH_InitGdiPath
+ *
+ * Initializes the GdiPath structure.
+ */
+void PATH_InitGdiPath(GdiPath *pPath)
+{
+   assert(pPath!=NULL);
+
+   pPath->state=PATH_Null;
+   pPath->pPoints=NULL;
+   pPath->pFlags=NULL;
+   pPath->numEntriesUsed=0;
+   pPath->numEntriesAllocated=0;
+}
+
+/* PATH_DestroyGdiPath
+ *
+ * Destroys a GdiPath structure (frees the memory in the arrays).
+ */
+void PATH_DestroyGdiPath(GdiPath *pPath)
+{
+   assert(pPath!=NULL);
+
+   free(pPath->pPoints);
+   free(pPath->pFlags);
+}
+
+/* PATH_AssignGdiPath
+ *
+ * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
+ * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
+ * not just the pointers. Since this means that the arrays in pPathDest may
+ * need to be resized, pPathDest should have been initialized using
+ * PATH_InitGdiPath (in C++, this function would be an assignment operator,
+ * not a copy constructor).
+ * Returns TRUE if successful, else FALSE.
+ */
+BOOL32 PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
+{
+   assert(pPathDest!=NULL && pPathSrc!=NULL);
+
+   /* Make sure destination arrays are big enough */
+   if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
+      return FALSE;
+
+   /* Perform the copy operation */
+   memcpy(pPathDest->pPoints, pPathSrc->pPoints,
+      sizeof(POINT32)*pPathSrc->numEntriesUsed);
+   memcpy(pPathDest->pFlags, pPathSrc->pFlags,
+      sizeof(INT32)*pPathSrc->numEntriesUsed);
+   pPathDest->state=pPathSrc->state;
+   pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
+   pPathDest->newStroke=pPathSrc->newStroke;
+
+   return TRUE;
+}
+
+/* PATH_MoveTo
+ *
+ * Should be called when a MoveTo is performed on a DC that has an
+ * open path. This starts a new stroke. Returns TRUE if successful, else
+ * FALSE.
+ */
+BOOL32 PATH_MoveTo(HDC32 hdc)
+{
+   GdiPath *pPath;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+      return FALSE;
+   
+   /* Check that path is open */
+   if(pPath->state!=PATH_Open)
+      /* FIXME: Do we have to call SetLastError? */
+      return FALSE;
+
+   /* Start a new stroke */
+   pPath->newStroke=TRUE;
+
+   return TRUE;
+}
+
+/* PATH_LineTo
+ *
+ * Should be called when a LineTo is performed on a DC that has an
+ * open path. This adds a PT_LINETO entry to the path (and possibly
+ * a PT_MOVETO entry, if this is the first LineTo in a stroke).
+ * Returns TRUE if successful, else FALSE.
+ */
+BOOL32 PATH_LineTo(HDC32 hdc, INT32 x, INT32 y)
+{
+   GdiPath *pPath;
+   POINT32 point, pointCurPos;
+   
+   /* Get pointer to path */
+   if(!PATH_GetPathFromHDC(hdc, &pPath))
+      return FALSE;
+   
+   /* Check that path is open */
+   if(pPath->state!=PATH_Open)
+      /* FIXME: Do we have to call SetLastError? */
+      return FALSE;
+
+   /* Convert point to device coordinates */
+   point.x=x;
+   point.y=y;
+   if(!LPtoDP32(hdc, &point, 1))
+      return FALSE;
+   
+   /* Add a PT_MOVETO if necessary */
+   if(pPath->newStroke)
+   {
+      pPath->newStroke=FALSE;
+      if(!GetCurrentPositionEx32(hdc, &pointCurPos) ||
+         !LPtoDP32(hdc, &pointCurPos, 1))
+         return FALSE;
+      if(!PATH_AddEntry(pPath, pointCurPos, PT_MOVETO))
+         return FALSE;
+   }
+   
+   /* Add a PT_LINETO entry */
+   return PATH_AddEntry(pPath, point, PT_LINETO);
+}
+
+
+/***********************************************************************
+ * Internal functions
+ */
+
+/* PATH_PathToRegion
+ *
+ * Creates a region from the specified path using the specified polygon
+ * filling mode. The path is left unchanged. A handle to the region that
+ * was created is stored in *pHrgn. If successful, TRUE is returned; if an
+ * error occurs, SetLastError is called with the appropriate value and
+ * FALSE is returned.
+ */
+static BOOL32 PATH_PathToRegion(const GdiPath *pPath, INT32 nPolyFillMode,
+   HRGN32 *pHrgn)
+{
+   int    numStrokes, iStroke, i;
+   INT32  *pNumPointsInStroke;
+   HRGN32 hrgn;
+
+   assert(pPath!=NULL);
+   assert(pHrgn!=NULL);
+   
+   /* FIXME: What happens when number of points is zero? */
+   
+   /* First pass: Find out how many strokes there are in the path */
+   /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
+   numStrokes=0;
+   for(i=0; i<pPath->numEntriesUsed; i++)
+      if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
+         numStrokes++;
+
+   /* Allocate memory for number-of-points-in-stroke array */
+   pNumPointsInStroke=(int *)malloc(sizeof(int)*numStrokes);
+   if(!pNumPointsInStroke)
+   {
+      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+      return FALSE;
+   }
+   
+   /* Second pass: remember number of points in each polygon */
+   iStroke=-1;  /* Will get incremented to 0 at beginning of first stroke */
+   for(i=0; i<pPath->numEntriesUsed; i++)
+   {
+      /* Is this the beginning of a new stroke? */
+      if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
+      {
+         iStroke++;
+	 pNumPointsInStroke[iStroke]=0;
+      }
+
+      pNumPointsInStroke[iStroke]++;
+   }
+
+   /* Create a region from the strokes */
+   hrgn=CreatePolyPolygonRgn32(pPath->pPoints, pNumPointsInStroke,
+      numStrokes, nPolyFillMode);
+   if(hrgn==NULL)
+   {
+      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+      return FALSE;
+   }
+
+   /* Free memory for number-of-points-in-stroke array */
+   free(pNumPointsInStroke);
+
+   /* Success! */
+   *pHrgn=hrgn;
+   return TRUE;
+}
+
+/* PATH_EmptyPath
+ *
+ * Removes all entries from the path and sets the path state to PATH_Null.
+ */
+static void PATH_EmptyPath(GdiPath *pPath)
+{
+   assert(pPath!=NULL);
+
+   pPath->state=PATH_Null;
+   pPath->numEntriesUsed=0;
+}
+
+/* PATH_AddEntry
+ *
+ * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
+ * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
+ * successful, FALSE otherwise (e.g. if not enough memory was available).
+ */
+BOOL32 PATH_AddEntry(GdiPath *pPath, POINT32 point, BYTE flags)
+{
+   assert(pPath!=NULL);
+   
+   /* Check that path is open */
+   if(pPath->state!=PATH_Open)
+      return FALSE;
+   
+   /* Reserve enough memory for an extra path entry */
+   if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
+      return FALSE;
+
+   /* Store information in path entry */
+   pPath->pPoints[pPath->numEntriesUsed]=point;
+   pPath->pFlags[pPath->numEntriesUsed]=flags;
+
+   /* Increment entry count */
+   pPath->numEntriesUsed++;
+
+   return TRUE;
+}
+
+/* PATH_ReserveEntries
+ *
+ * Ensures that at least "numEntries" entries (for points and flags) have
+ * been allocated; allocates larger arrays and copies the existing entries
+ * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
+ */
+static BOOL32 PATH_ReserveEntries(GdiPath *pPath, INT32 numEntries)
+{
+   INT32   numEntriesToAllocate;
+   POINT32 *pPointsNew;
+   BYTE    *pFlagsNew;
+   
+   assert(pPath!=NULL);
+   assert(numEntries>=0);
+
+   /* Do we have to allocate more memory? */
+   if(numEntries > pPath->numEntriesAllocated)
+   {
+      /* Find number of entries to allocate. We let the size of the array
+       * grow exponentially, since that will guarantee linear time
+       * complexity. */
+      if(pPath->numEntriesAllocated)
+      {
+	 numEntriesToAllocate=pPath->numEntriesAllocated;
+	 while(numEntriesToAllocate<numEntries)
+	    numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
+	       GROW_FACTOR_DENOM;
+      }
+      else
+         numEntriesToAllocate=NUM_ENTRIES_INITIAL;
+
+      /* Allocate new arrays */
+      pPointsNew=(POINT32 *)malloc(numEntriesToAllocate * sizeof(POINT32));
+      if(!pPointsNew)
+         return FALSE;
+      pFlagsNew=(BYTE *)malloc(numEntriesToAllocate * sizeof(BYTE));
+      if(!pFlagsNew)
+      {
+         free(pPointsNew);
+	 return FALSE;
+      }
+
+      /* Copy old arrays to new arrays and discard old arrays */
+      if(pPath->pPoints)
+      {
+         assert(pPath->pFlags);
+
+	 memcpy(pPointsNew, pPath->pPoints,
+	     sizeof(POINT32)*pPath->numEntriesUsed);
+	 memcpy(pFlagsNew, pPath->pFlags,
+	     sizeof(BYTE)*pPath->numEntriesUsed);
+
+	 free(pPath->pPoints);
+	 free(pPath->pFlags);
+      }
+      pPath->pPoints=pPointsNew;
+      pPath->pFlags=pFlagsNew;
+      pPath->numEntriesAllocated=numEntriesToAllocate;
+   }
+
+   return TRUE;
+}
+
+/* PATH_GetPathFromHDC
+ *
+ * Retrieves a pointer to the GdiPath structure contained in an HDC and
+ * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
+ */
+static BOOL32 PATH_GetPathFromHDC(HDC32 hdc, GdiPath **ppPath)
+{
+   DC *pDC;
+
+   pDC=DC_GetDCPtr(hdc);
+   if(pDC)
+   {
+      *ppPath=&pDC->w.path;
+      return TRUE;
+   }
+   else
+      return FALSE;
+}