Release 970720

Sat Jul 19 13:03:01 1997  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [tools/build.c] [include/stackframe.h]
	Save the complete %ebp register in CallFrom16; fixes a crash with
	LabView reported by Andreas Mohr.

	* [loader/main.c]
	Avoid executing a built-in DLL.

	* [controls/static.c]
	Converted static window procedure to Win32.

	* [windows/message.c] [windows/queue.c] [include/queue.h]
	Hacked SendMessage functions to support inter-task messages with
	SendMessage32A/W.

Sun Jul 13 16:55:35 1997  Bernhard Rosenkraenzer <bero@bero-online.ml.org>

	* [ipc/bit_array.c]
	Don't use bitops.h in Linux 2.1.x (these versions do not return
	the previous state for clear_bit and set_bit)

	* [ipc/shm_main_blk.c]
	Adapt to GLIBC's ipc_perm structure.

	* [memory/ldt.c]
	Include <asm/unistd.h> on Linux/GLIBC systems (required for
	_syscall3).

Wed Jul 9 23:53:19 1997  David A. Cuthbert  <dacut@henry.ece.cmu.edu>

	* [include/options.h] [files/profile.c]
	Added PROFILE_GetWineIniBool and PROFILE_EnumerateWineIniSection.

	* [include/sysmetrics.h] [include/windows.h] [windows/sysmetrics.c]
	All sysmetrics moved to array (no more constant macros).  Added
	MOUSEWHEELPRESENT metric.

	* [include/bitmap.h] [objects/oembitmap.c]
	Added OBM_Init() (see also loader/main.c) and more support for Win95
	bitmaps; added size info to OEM bitmaps.

	* [include/graphics.h] [windows/graphics.h]
	Added GRAPH_DrawGenericReliefRect.

	* [loader/main.c]
	Added TWEAK_Init() and TWEAK_CheckConfiguration() calls (the
	latter checks for invalid entries in wine.conf).

	* [include/debug.h] [include/stddebug.h] [include/nonclient.h]
	  [include/tweak.h] [controls/menu.c] [misc/tweak.c]
	  [objects/gdiobj.c] [windows/syscolor.c] [windows/nonclient.c]
	  [BUGS] [documentation/win95look]
	Added tweaks for Windows 95 interface support.  See
 	documentation/win95look for more information.

	* [controls/edit.c]
	Fixed EDIT_MoveHome bug.

	* [misc/ver.c]
	Changed name of dprintf_ver_string to ver_dstring to fix
	problem with tools/make_debug utility.

Wed Jul 9 21:31:54 1997  Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>

	* [objects/dib.c]
	Don't use palettes with dibs with biBitCount > 8.

	* [misc/ole2nls.c][misc/ver.c]
	IsValidLocale, EnumSystemLocales fixed (winhlp32.exe works)
	Some VerLanguage coded moved to ole2nls.c, some cleanups.

	* [multimedia/mcistring.c]
	Fixed "capabilities <dev> device type" crash (cool.exe).

	* [misc/main.c]
	SystemParametersInfo*: added stub option 41
	(GETNONCLIENTMETRICS), duplicated some stuff away from SPI16
	that writes 32bit vars.(one COMCTL32.DLL crash, freecell.exe)

Tue Jul  8 22:40:53 1997  Morten Welinder  <terra@diku.dk>

	* [if1632/shell32.spec]
	Use Windows 95's ordinals. Help wanted, inquire within.

Mon Jul  7 11:20:36 1997  Philippe De Muyter  <phdm@info.ucl.ac.be>

	* [if1632/relay.c] [if1632/user.spec] [if1632/kernel.spec]
	  [tools/build-spec.txt] [tools/build.c]
	Added type 'segstr' (segmented pointer to null-terminated string)
	to .spec files.

	* [windows/user.c] [if1632/user.spec]
	ExitWindowsExec stub function added.

Mon Jul  7 01:18:25 1997  U. Bonnes <bon@elektron.ikp.physik.th-darmstadt.de>

	* [files/file.c] [include/winbase.h] [if1632/kernel32.spec]
	Implement MoveFileEx32, some enhancement for Movefile32.

Sat Jul  5 18:13:48 1997  Bruce Milner <Bruce.Milner@genetics.utah.edu.

	* [files/file.c] [if1632/kernel32.spec] [include/winerror.h]
          [msdos/int21.c] [win32/file.c]
	Add LockFile/UnlockFile implementation.
	Add back in int21 func(0x5c) Record locking functions.

	* [files/file.c]
	Fixed bug with OF_REOPEN in FILE_DoOpenFile.

Fri Jul 4 12:00:00 1997  Henrik Olsen <Henrik.Olsen@iaeste.dk>

	* [misc/ole2nls.c] [programs/progman/Da.rc] [programs/winhelp/Da.rc]
	  [resources/sysres_Da.rc]
	Added/updated Danish language support.

Thu Jul  3 13:04:20 1997  Claus Fischer  <fischer@iue.tuwien.ac.at>

	* [files/dos_fs.c]
	Properly implemented DOSFS_UnixTimeToFileTime and
	DOSFS_FileTimeToUnixTime.

	* [documentation/wine.texinfo]
	First version of texinfo documentation.
diff --git a/files/dos_fs.c b/files/dos_fs.c
index 5bb8b35..014b41e 100644
--- a/files/dos_fs.c
+++ b/files/dos_fs.c
@@ -1202,16 +1202,121 @@
  *
  * Convert a Unix time to FILETIME format.
  * The FILETIME structure is a 64-bit value representing the number of
- * 100-nanosecond intervals since January 1, 1601.
- * 'remainder' is the fraction of 100-ns intervals smaller than 1 second
- * that couldn't be stored in the time_t value.
+ * 100-nanosecond intervals since January 1, 1601, 0:00.
+ * 'remainder' is the nonnegative number of 100-ns intervals
+ * corresponding to the time fraction smaller than 1 second that
+ * couldn't be stored in the time_t value.
  */
 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
                                DWORD remainder )
 {
-    /* FIXME :-) */
-    filetime->dwLowDateTime  = unix_time;
-    filetime->dwHighDateTime = 0;
+    /* NOTES:
+
+       CONSTANTS: 
+       The time difference between 1 January 1601, 00:00:00 and
+       1 January 1970, 00:00:00 is 369 years, plus the leap years
+       from 1604 to 1968, excluding 1700, 1800, 1900.
+       This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
+       of 134774 days.
+
+       Any day in that period had 24 * 60 * 60 = 86400 seconds.
+
+       The time difference is 134774 * 86400 * 10000000, which can be written
+       116444736000000000
+       27111902 * 2^32 + 3577643008
+       413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
+
+       If you find that these constants are buggy, please change them in all
+       instances in both conversion functions.
+
+       VERSIONS:
+       There are two versions, one of them uses long long variables and
+       is presumably faster but not ISO C. The other one uses standard C
+       data types and operations but relies on the assumption that negative
+       numbers are stored as 2's complement (-1 is 0xffff....). If this
+       assumption is violated, dates before 1970 will not convert correctly.
+       This should however work on any reasonable architecture where WINE
+       will run.
+
+       DETAILS:
+       
+       Take care not to remove the casts. I have tested these functions
+       (in both versions) for a lot of numbers. I would be interested in
+       results on other compilers than GCC.
+
+       The operations have been designed to account for the possibility
+       of 64-bit time_t in future UNICES. Even the versions without
+       internal long long numbers will work if time_t only is 64 bit.
+       A 32-bit shift, which was necessary for that operation, turned out
+       not to work correctly in GCC, besides giving the warning. So I
+       used a double 16-bit shift instead. Numbers are in the ISO version
+       represented by three limbs, the most significant with 32 bit, the
+       other two with 16 bit each.
+
+       As the modulo-operator % is not well-defined for negative numbers,
+       negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
+
+       There might be quicker ways to do this in C. Certainly so in
+       assembler.
+
+       Claus Fischer, fischer@iue.tuwien.ac.at
+       */
+
+#if __GNUC__
+#  define USE_LONG_LONG 1
+#else
+#  define USE_LONG_LONG 0
+#endif
+
+#if USE_LONG_LONG		/* gcc supports long long type */
+
+    long long int t = unix_time;
+    t *= 10000000;
+    t += 116444736000000000LL;
+    t += remainder;
+    filetime->dwLowDateTime  = (UINT32)t;
+    filetime->dwHighDateTime = (UINT32)(t >> 32);
+
+#else  /* ISO version */
+
+    UINT32 a0;			/* 16 bit, low    bits */
+    UINT32 a1;			/* 16 bit, medium bits */
+    UINT32 a2;			/* 32 bit, high   bits */
+
+    /* Copy the unix time to a2/a1/a0 */
+    a0 =  unix_time & 0xffff;
+    a1 = (unix_time >> 16) & 0xffff;
+    /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
+       Do not replace this by >> 32, it gives a compiler warning and it does
+       not work. */
+    a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
+	  ~((~unix_time >> 16) >> 16));
+
+    /* Multiply a by 10000000 (a = a2/a1/a0)
+       Split the factor into 10000 * 1000 which are both less than 0xffff. */
+    a0 *= 10000;
+    a1 = a1 * 10000 + (a0 >> 16);
+    a2 = a2 * 10000 + (a1 >> 16);
+    a0 &= 0xffff;
+    a1 &= 0xffff;
+
+    a0 *= 1000;
+    a1 = a1 * 1000 + (a0 >> 16);
+    a2 = a2 * 1000 + (a1 >> 16);
+    a0 &= 0xffff;
+    a1 &= 0xffff;
+
+    /* Add the time difference and the remainder */
+    a0 += 32768 + (remainder & 0xffff);
+    a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
+    a2 += 27111902                     + (a1 >> 16);
+    a0 &= 0xffff;
+    a1 &= 0xffff;
+
+    /* Set filetime */
+    filetime->dwLowDateTime  = (a1 << 16) + a0;
+    filetime->dwHighDateTime = a2;
+#endif
 }
 
 
@@ -1219,13 +1324,95 @@
  *           DOSFS_FileTimeToUnixTime
  *
  * Convert a FILETIME format to Unix time.
- * If not NULL, 'remainder' contains the fractional part of the filetime.
+ * If not NULL, 'remainder' contains the fractional part of the filetime,
+ * in the range of [0..9999999] (even if time_t is negative).
  */
 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
 {
-    /* FIXME :-) */
-    if (remainder) *remainder = 0;
-    return filetime->dwLowDateTime;
+    /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
+#if USE_LONG_LONG
+
+    long long int t = filetime->dwHighDateTime;
+    t <<= 32;
+    t += (UINT32)filetime->dwLowDateTime;
+    t -= 116444736000000000LL;
+    if (t < 0)
+    {
+	if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
+	return -1 - ((-t - 1) / 10000000);
+    }
+    else
+    {
+	if (remainder) *remainder = t % 10000000;
+	return t / 10000000;
+    }
+
+#else  /* ISO version */
+
+    UINT32 a0;			/* 16 bit, low    bits */
+    UINT32 a1;			/* 16 bit, medium bits */
+    UINT32 a2;			/* 32 bit, high   bits */
+    UINT32 r;			/* remainder of division */
+    unsigned int carry;		/* carry bit for subtraction */
+    int negative;		/* whether a represents a negative value */
+
+    /* Copy the time values to a2/a1/a0 */
+    a2 =  (UINT32)filetime->dwHighDateTime;
+    a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
+    a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
+
+    /* Subtract the time difference */
+    if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
+    else                        a0 += (1 << 16) - 32768        , carry = 1;
+
+    if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
+    else                        a1 += (1 << 16) - 54590 - carry, carry = 1;
+
+    a2 -= 27111902 + carry;
+    
+    /* If a is negative, replace a by (-1-a) */
+    negative = (a2 >= ((UINT32)1) << 31);
+    if (negative)
+    {
+	/* Set a to -a - 1 (a is a2/a1/a0) */
+	a0 = 0xffff - a0;
+	a1 = 0xffff - a1;
+	a2 = ~a2;
+    }
+
+    /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
+       Split the divisor into 10000 * 1000 which are both less than 0xffff. */
+    a1 += (a2 % 10000) << 16;
+    a2 /=       10000;
+    a0 += (a1 % 10000) << 16;
+    a1 /=       10000;
+    r   =  a0 % 10000;
+    a0 /=       10000;
+
+    a1 += (a2 % 1000) << 16;
+    a2 /=       1000;
+    a0 += (a1 % 1000) << 16;
+    a1 /=       1000;
+    r  += (a0 % 1000) * 10000;
+    a0 /=       1000;
+
+    /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
+    if (negative)
+    {
+	/* Set a to -a - 1 (a is a2/a1/a0) */
+	a0 = 0xffff - a0;
+	a1 = 0xffff - a1;
+	a2 = ~a2;
+
+        r  = 9999999 - r;
+    }
+
+    if (remainder) *remainder = r;
+
+    /* Do not replace this by << 32, it gives a compiler warning and it does
+       not work. */
+    return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
+#endif
 }
 
 
diff --git a/files/drive.c b/files/drive.c
index 3fb43c1..c4a88de 100644
--- a/files/drive.c
+++ b/files/drive.c
@@ -14,7 +14,7 @@
 #if defined(__linux__) || defined(sun) || defined(hpux)
 #include <sys/vfs.h>
 #endif
-#if defined(__NetBSD__) || defined(__FreeBSD__)
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
 #include <sys/param.h>
 #include <sys/mount.h>
 #include <sys/errno.h>
diff --git a/files/file.c b/files/file.c
index 1625f39..8b1c31e 100644
--- a/files/file.c
+++ b/files/file.c
@@ -38,6 +38,19 @@
 #define MAP_ANON MAP_ANONYMOUS
 #endif
 
+struct DOS_FILE_LOCK {
+  struct DOS_FILE_LOCK *	next;
+  DWORD				base;
+  DWORD				len;
+  DWORD				processId;
+  FILE_OBJECT *			dos_file;
+  char *			unix_name;
+};
+
+typedef struct DOS_FILE_LOCK DOS_FILE_LOCK;
+
+static DOS_FILE_LOCK *locks = NULL;
+static void DOS_RemoveFileLocks(FILE_OBJECT *file);
 
 /***********************************************************************
  *           FILE_Alloc
@@ -75,6 +88,8 @@
     FILE_OBJECT *file = (FILE_OBJECT *)ptr;
     assert( ptr->type == K32OBJ_FILE );
 
+    DOS_RemoveFileLocks(file);
+
     if (file->unix_handle != -1) close( file->unix_handle );
     if (file->unix_name) HeapFree( SystemHeap, 0, file->unix_name );
     ptr->type = K32OBJ_UNKNOWN;
@@ -621,16 +636,19 @@
     char *p;
     int unixMode;
 
-    if (!name || !ofs) return HFILE_ERROR32;
+    if (!ofs) return HFILE_ERROR32;
+
+
+    ofs->cBytes = sizeof(OFSTRUCT);
+    ofs->nErrCode = 0;
+    if (mode & OF_REOPEN) name = ofs->szPathName;
 
     if (!name) {
 	fprintf(stderr, "ERROR: FILE_DoOpenFile() called with `name' set to NULL ! Please debug.\n");
  
 	return HFILE_ERROR32;
     }
-    ofs->cBytes = sizeof(OFSTRUCT);
-    ofs->nErrCode = 0;
-    if (mode & OF_REOPEN) name = ofs->szPathName;
+
     dprintf_file( stddeb, "OpenFile: %s %04x\n", name, mode );
 
     /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
@@ -1201,8 +1219,12 @@
     if (!file)
     {
 	/* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
+#ifdef MAP_SHARED
 	flags &= ~MAP_SHARED;
+#endif
+#ifdef MAP_PRIVATE
 	flags |= MAP_PRIVATE;
+#endif
 #ifdef MAP_ANON
         flags |= MAP_ANON;
 #else
@@ -1238,24 +1260,148 @@
 
 
 /**************************************************************************
+ *           MoveFileEx32A   (KERNEL32.???)
+ *
+ * 
+ */
+BOOL32 MoveFileEx32A( LPCSTR fn1, LPCSTR fn2, DWORD flag )
+{
+    DOS_FULL_NAME full_name1, full_name2;
+    int mode=0; /* mode == 1: use copy */
+
+    dprintf_file( stddeb, "MoveFileEx32A(%s,%s,%04lx)\n", fn1, fn2, flag);
+
+    if (!DOSFS_GetFullName( fn1, TRUE, &full_name1 )) return FALSE;
+    if (fn2) { /* !fn2 means delete fn1 */
+      if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 )) return FALSE;
+      /* Source name and target path are valid */
+      if ( full_name1.drive != full_name2.drive)
+	/* use copy, if allowed */
+	if (!(flag & MOVEFILE_COPY_ALLOWED)) {
+	  /* FIXME: Use right error code */
+	  DOS_ERROR( ER_FileExists, EC_Exists, SA_Abort, EL_Disk );
+	  return FALSE;
+	}
+	else mode =1;
+      if (DOSFS_GetFullName( fn2, TRUE, &full_name2 )) 
+	/* target exists, check if we may overwrite */
+	if (!(flag & MOVEFILE_REPLACE_EXISTING)) {
+	  /* FIXME: Use right error code */
+	  DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
+	  return FALSE;
+	}
+    }
+    else /* fn2 == NULL means delete source */
+      if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) {
+	if (flag & MOVEFILE_COPY_ALLOWED) {  
+	  fprintf( stderr,
+		   "MoveFileEx32A: Illegal flag\n");
+	  DOS_ERROR( ER_GeneralFailure, EC_SystemFailure, SA_Abort,
+		     EL_Unknown );
+	  return FALSE;
+	}
+	/* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
+	   Perhaps we should queue these command and execute it 
+	   when exiting... What about using on_exit(2)
+	   */
+	fprintf( stderr,"MoveFileEx32A: Please delete file %s\n",
+		 full_name1.long_name);
+	fprintf( stderr,"               when Wine has finished\n");
+	fprintf( stderr,"               like \"rm %s\"\n",
+		 full_name1.long_name);
+	return TRUE;
+      }
+      else if (unlink( full_name1.long_name ) == -1)
+      {
+        FILE_SetDosError();
+        return FALSE;
+      }
+      else  return TRUE; /* successfully deleted */
+
+    if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) {
+	/* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
+	   Perhaps we should queue these command and execute it 
+	   when exiting... What about using on_exit(2)
+	   */
+	fprintf( stderr,"MoveFileEx32A: Please move existing file %s\n"
+		 ,full_name1.long_name);
+	fprintf( stderr,"               to file %s\n"
+		 ,full_name2.long_name);
+	fprintf( stderr,"               when Wine has finished\n");
+	fprintf( stderr,"               like \" mv %s %s\"\n",
+		 full_name1.long_name,full_name2.long_name);
+	return TRUE;
+    }
+
+    if (!mode) /* move the file */
+      if (rename( full_name1.long_name, full_name2.long_name ) == -1)
+	{
+	  FILE_SetDosError();
+	  return FALSE;
+	}
+      else return TRUE;
+    else /* copy File */
+      return CopyFile32A(fn1, fn2, (!(flag & MOVEFILE_REPLACE_EXISTING))); 
+    
+}
+
+/**************************************************************************
+ *           MoveFileEx32W   (KERNEL32.???)
+ */
+BOOL32 MoveFileEx32W( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
+{
+    LPSTR afn1 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn1 );
+    LPSTR afn2 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn2 );
+    BOOL32 res = MoveFileEx32A( afn1, afn2, flag );
+    HeapFree( GetProcessHeap(), 0, afn1 );
+    HeapFree( GetProcessHeap(), 0, afn2 );
+    return res;
+}
+
+
+/**************************************************************************
  *           MoveFile32A   (KERNEL32.387)
+ *
+ *  Move file or directory
  */
 BOOL32 MoveFile32A( LPCSTR fn1, LPCSTR fn2 )
 {
     DOS_FULL_NAME full_name1, full_name2;
+    struct stat fstat;
 
     dprintf_file( stddeb, "MoveFile32A(%s,%s)\n", fn1, fn2 );
 
     if (!DOSFS_GetFullName( fn1, TRUE, &full_name1 )) return FALSE;
+    if (DOSFS_GetFullName( fn2, TRUE, &full_name2 )) 
+      /* The new name must not already exist */ 
+      return FALSE;
     if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 )) return FALSE;
-    /* FIXME: should not replace an existing file */
-    /* FIXME: should handle renaming across devices */
+
+    if (full_name1.drive == full_name2.drive) /* move */
     if (rename( full_name1.long_name, full_name2.long_name ) == -1)
     {
         FILE_SetDosError();
         return FALSE;
     }
-    return TRUE;
+      else return TRUE;
+    else /*copy */ {
+      if (stat(  full_name1.long_name, &fstat ))
+	{
+	  dprintf_file( stddeb, "Invalid source file %s\n",
+			full_name1.long_name);
+	  FILE_SetDosError();
+	  return FALSE;
+	}
+      if (S_ISDIR(fstat.st_mode)) {
+	/* No Move for directories across file systems */
+	/* FIXME: Use right error code */
+	DOS_ERROR( ER_GeneralFailure, EC_SystemFailure, SA_Abort,
+		   EL_Unknown );
+	return FALSE;
+      }
+      else
+	return CopyFile32A(fn1, fn2, TRUE); /*fail, if exist */ 
+    }
 }
 
 
@@ -1365,3 +1511,175 @@
     FILE_ReleaseFile( file );
     return TRUE;
 }
+
+/* Locks need to be mirrored because unix file locking is based
+ * on the pid. Inside of wine there can be multiple WINE processes
+ * that share the same unix pid.
+ * Read's and writes should check these locks also - not sure
+ * how critical that is at this point (FIXME).
+ */
+
+static BOOL32 DOS_AddLock(FILE_OBJECT *file, struct flock *f)
+{
+  DOS_FILE_LOCK *curr;
+  DWORD		processId;
+
+  processId = GetCurrentProcessId();
+
+  /* check if lock overlaps a current lock for the same file */
+  for (curr = locks; curr; curr = curr->next) {
+    if (strcmp(curr->unix_name, file->unix_name) == 0) {
+      if ((f->l_start < (curr->base + curr->len)) &&
+	  ((f->l_start + f->l_len) > curr->base)) {
+	/* region overlaps */
+	return FALSE;
+      }
+    }
+  }
+
+  curr = HeapAlloc( SystemHeap, 0, sizeof(DOS_FILE_LOCK) );
+  curr->processId = GetCurrentProcessId();
+  curr->base = f->l_start;
+  curr->len = f->l_len;
+  curr->unix_name = HEAP_strdupA( SystemHeap, 0, file->unix_name);
+  curr->next = locks;
+  curr->dos_file = file;
+  locks = curr;
+  return TRUE;
+}
+
+static void DOS_RemoveFileLocks(FILE_OBJECT *file)
+{
+  DWORD		processId;
+  DOS_FILE_LOCK **curr;
+  DOS_FILE_LOCK *rem;
+
+  processId = GetCurrentProcessId();
+  curr = &locks;
+  while (*curr) {
+    if ((*curr)->dos_file == file) {
+      rem = *curr;
+      *curr = (*curr)->next;
+      HeapFree( SystemHeap, 0, rem->unix_name );
+      HeapFree( SystemHeap, 0, rem );
+    }
+    else
+      curr = &(*curr)->next;
+  }
+}
+
+static BOOL32 DOS_RemoveLock(FILE_OBJECT *file, struct flock *f)
+{
+  DWORD		processId;
+  DOS_FILE_LOCK **curr;
+  DOS_FILE_LOCK *rem;
+
+  processId = GetCurrentProcessId();
+  for (curr = &locks; *curr; curr = &(*curr)->next) {
+    if ((*curr)->processId == processId &&
+	(*curr)->dos_file == file &&
+	(*curr)->base == f->l_start &&
+	(*curr)->len == f->l_len) {
+      /* this is the same lock */
+      rem = *curr;
+      *curr = (*curr)->next;
+      HeapFree( SystemHeap, 0, rem->unix_name );
+      HeapFree( SystemHeap, 0, rem );
+      return TRUE;
+    }
+  }
+  /* no matching lock found */
+  return FALSE;
+}
+
+
+/**************************************************************************
+ *           LockFile   (KERNEL32.511)
+ */
+BOOL32 LockFile(
+	HFILE32 hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
+	DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh )
+{
+  struct flock f;
+  FILE_OBJECT *file;
+
+  dprintf_file(stddeb, "LockFile32: handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
+	       hFile, dwFileOffsetLow, dwFileOffsetHigh,
+	       nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
+
+  if (dwFileOffsetHigh || nNumberOfBytesToLockHigh) {
+    dprintf_file(stddeb, "LockFile32: Unimplemented bytes > 32bits\n");
+    return FALSE;
+  }
+
+  f.l_start = dwFileOffsetLow;
+  f.l_len = nNumberOfBytesToLockLow;
+  f.l_whence = SEEK_SET;
+  f.l_pid = 0;
+  f.l_type = F_WRLCK;
+
+  if (!(file = FILE_GetFile(hFile))) return FALSE;
+
+  /* shadow locks internally */
+  if (!DOS_AddLock(file, &f)) {
+    DOS_ERROR( ER_LockViolation, EC_AccessDenied, SA_Ignore, EL_Disk );
+    return FALSE;
+  }
+
+  /* FIXME: Unix locking commented out for now, doesn't work with Excel */
+#ifdef USE_UNIX_LOCKS
+  if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
+    if (errno == EACCES || errno == EAGAIN) {
+      DOS_ERROR( ER_LockViolation, EC_AccessDenied, SA_Ignore, EL_Disk );
+    }
+    else {
+      FILE_SetDosError();
+    }
+    /* remove our internal copy of the lock */
+    DOS_RemoveLock(file, &f);
+    return FALSE;
+  }
+#endif
+  return TRUE;
+}
+
+
+/**************************************************************************
+ *           UnlockFile   (KERNEL32.703)
+ */
+BOOL32 UnlockFile(
+	HFILE32 hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
+	DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh )
+{
+  FILE_OBJECT *file;
+  struct flock f;
+
+  dprintf_file(stddeb, "UnlockFile32: handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
+	       hFile, dwFileOffsetLow, dwFileOffsetHigh,
+	       nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
+
+  if (dwFileOffsetHigh || nNumberOfBytesToUnlockHigh) {
+    dprintf_file(stddeb, "UnlockFile32: Unimplemented bytes > 32bits\n");
+    return FALSE;
+  }
+
+  f.l_start = dwFileOffsetLow;
+  f.l_len = nNumberOfBytesToUnlockLow;
+  f.l_whence = SEEK_SET;
+  f.l_pid = 0;
+  f.l_type = F_UNLCK;
+
+  if (!(file = FILE_GetFile(hFile))) return FALSE;
+
+  DOS_RemoveLock(file, &f);	/* ok if fails - may be another wine */
+
+  /* FIXME: Unix locking commented out for now, doesn't work with Excel */
+#ifdef USE_UNIX_LOCKS
+  if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
+    FILE_SetDosError();
+    return FALSE;
+  }
+#endif
+  return TRUE;
+}
+
diff --git a/files/profile.c b/files/profile.c
index d537a8e..6fac8eb 100644
--- a/files/profile.c
+++ b/files/profile.c
@@ -604,6 +604,118 @@
 }
 
 
+/******************************************************************************
+ *
+ *   int  PROFILE_EnumerateWineIniSection(
+ *     char const  *section,  // Name of the section to enumerate
+ *     void  (*cbfn)(char const *key, char const *value, void *user),
+                              // Address of the callback function
+ *     void  *user )          // User-specified pointer.
+ *
+ *   For each entry in a section in the wine.conf file, this function will
+ *   call the specified callback function, informing it of each key and
+ *   value.  An optional user pointer may be passed to it (if this is not
+ *   needed, pass NULL through it and ignore the value in the callback
+ *   function).
+ *
+ *   The callback function must accept three parameters:
+ *      The name of the key (char const *)
+ *      The value of the key (char const *)
+ *      A user-specified parameter (void *)
+ *   Note that the first two are char CONST *'s, not char *'s!  The callback
+ *   MUST not modify these strings!
+ *
+ *   The return value indicates the number of times the callback function
+ *   was called.
+ */
+int  PROFILE_EnumerateWineIniSection(
+    char const  *section,
+    void  (*cbfn)(char const *, char const *, void *),
+    void  *userptr )
+{
+    PROFILESECTION  *scansect;
+    PROFILEKEY  *scankey;
+    int  calls = 0;
+
+    /* Search for the correct section */
+    for(scansect = WineProfile; scansect; scansect = scansect->next) {
+	if(scansect->name && !lstrcmpi32A(scansect->name, section)) {
+
+	    /* Enumerate each key with the callback */
+	    for(scankey = scansect->key; scankey; scankey = scankey->next) {
+
+		/* Ignore blank entries -- these shouldn't exist, but let's
+		   be extra careful */
+		if(scankey->name[0]) {
+		    cbfn(scankey->name, scankey->value, userptr);
+		    ++calls;
+		}
+	    }
+	
+	    break;
+	}
+    }
+
+    return calls;
+}
+
+
+/******************************************************************************
+ *
+ *   int  PROFILE_GetWineIniBool(
+ *      char const  *section,
+ *      char const  *key_name,
+ *      int  def )
+ *
+ *   Reads a boolean value from the wine.ini file.  This function attempts to
+ *   be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
+ *   (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
+ *   true.  Anything else results in the return of the default value.
+ *
+ *   This function uses 1 to indicate true, and 0 for false.  You can check
+ *   for existence by setting def to something other than 0 or 1 and
+ *   examining the return value.
+ */
+int  PROFILE_GetWineIniBool(
+    char const  *section,
+    char const  *key_name,
+    int  def )
+{
+    char  key_value[2];
+    int  retval;
+
+    PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
+
+    switch(key_value[0]) {
+    case 'n':
+    case 'N':
+    case 'f':
+    case 'F':
+    case '0':
+	retval = 0;
+	break;
+
+    case 'y':
+    case 'Y':
+    case 't':
+    case 'T':
+    case '1':
+	retval = 1;
+	break;
+
+    default:
+	retval = def;
+    }
+
+    dprintf_profile(stddeb, "PROFILE_GetWineIniBool(\"%s\", \"%s\", %s), "
+		    "[%c], ret %s.\n", section, key_name,
+		    def ? "TRUE" : "FALSE", key_value[0],
+		    retval ? "TRUE" : "FALSE");
+
+    return retval;
+}
+
+
 /***********************************************************************
  *           PROFILE_LoadWineIni
  *