Release 980726

Sat Jul 25 19:45:45 1998  Juergen Schmied <juergen.schmied@metronet.de>

	* [include/shlobj.h][misc/shell.c][misc/shellord.c][ole/folders.c]
	[shell32.spec]
	Added SHFILEOPSTRUCT32[A|W] and constants, prototypes.
	Implemented SHGetSpecialFolderLocation, SHGetPathFromIDList32[A].
	Many IShellFolder, pidl, shell -related changes.

	SHChangeNotifyRegister, SHChangeNotifyDeregister,
	SHShellFolderView_Message, SHMapPIDLToSystemImageListIndex,
	SHAddToRecentDocs32, SHFileOperation, SHChangeNotify, 
	SHCreateShellFolderViewEx stubs.

Sat Jul 25 17:16:25 1998  Huw D M Davies <daviesh@abacus.physics.ox.ac.uk>

	* [files/profile.c]
	Fix return value of PROFILE_GetSection().

Fri Jul 24 22:45:19 1998  Ove Kaaven <ovek@isflak.arcticnet.no>

	* [controls/edit.c]
	Killed the modified flag on WM_SETTEXT. Eudora should no longer
	bother asking whether you want to save an unchanged message.

Fri Jul 24 21:21:35 1998  Andreas Mohr <100.30936@germany.net>

	* [controls/menu.c]
	Fixed bug in GetMenuState32.
	Doesn't fix Free Agent 32 :((

	* [documentation/debugging]
	Hints added.

	* [files/dos_fs.c] [include/msdos.h] [msdos/int21.c]
	Enhanced DOS device support.

	* [if1632/Makefile.in] [if1632/builtin.c] [if1632/rasapi16.spec]
	  [relay32/Makefile.in] [relay32/builtin32.c] [relay32/rasapi32.spec]
	Added RASAPI16/32.DLL.

	* [misc/aspi.c] [relay32/wnaspi32.spec]
	Implemented GetASPI32SupportInfo.

	* [multimedia/mmsystem.c]
	Implemented mmTaskCreate.

Fri Jul 24 20:55:31 1998  Eric Kohl <ekohl@abo.rhein-zeitung.de>

	* [controls/toolbar.c]
	Fixed some bugs and added new features.

	* [controls/tooltips.c][include/tooltips.h]
	Added more messages and started display code.

	* [misc/shell.c][misc/shellord.c][relay32/shell.spec]
	Fixed StrToOleStrN (SHELL32_79) and added OleStrToStrN (SHELL32_78).
	Added some new stubs.

	* [objects/cursoricon.c][misc/imagelist.c][include/windows.h]
	Fixed GetIconInfo and removed the GetIconInfo hack from the
	image list code.

	* [controls/pager.c][include/pager.h][controls/treeview.c]
	  [include/treeview.h]
	Added some messages.

	* [misc/tweak.c][winows/nonclient.c][documentation/win95look]
	Removed unused tweak variables.

	* [documentation/common_controls]
	Updated.

Fri Jul 24 18:36:32 1998  James Moody <013263m@dragon.acadiau.ca>

	* [objects/font.c]
	Fixed a bug in GetTextFace.

Fri Jul 24 17:09:33 1998  Marcus Meissner <marcus@jet.franken.de>

	* [misc/commdlg.c]
	Fixed stacksmashing bug due to invalid specified function
	pointers.

	* [files/dos_fs.c]
	Small change in case handling... be able to create files with
	uppercase in them (like Program Files/).

	* [graphics/ddraw.c]
	XF86DGA support made threadsafe, added more Xlib dependent stuff
	(create Window using CreateWindow(), draw into it). xlib support
	is not satisfying.

	* [scheduler/critsection.c]
	Don't recurse on HeapLock with semaphore id 0.

	* [win32/user32.c][windows/message.c][windows/event.c]
	Moved win32 *Message functions where they belong.
	Removed some potential races between XPending and XNextEvent by
	a bit more locking.

Fri Jul 24 13:58:19 1998  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [loader/pe_image.c] [loader/ne/segment.c]
	Use bogus pointer value instead of NULL for unresolved externals.

	* [memory/selector.c]
	Clear saved_fs on selector free.

	* [msdos/cdrom.c] [configure.in]
	Added check for linux/ucdrom.h.

	* [scheduler/client.c] [server/socket.c]
	Fix for missing struct cmsghdr.
	Attempt to support msg_accrights fd passing (completely untested).

	* [windows/event.c]
	Do not grab the pointer in SetCapture (Win32 behavior).

Tue Jul 21 22:28:13 1998  James Juran  <jrj120@psu.edu>

	* [Make.rules.in]
	Changed $(MKDIR) macro to use -p option (make parent directories
	if they don't already exist.  This fixes an error in 'make install'
	if /usr/local/include doesn't already exist.

Tue Jul 21 13:37:04 Rein Klazes <rklazes@casema.net>

	* [include/heap.h]
	Replaced macro SEGPTR_GET by inline function to avoid *lots*
	of wrong use of this macro.

	* [relay32/comdlg32.spec]
	Corrected GetSaveFileNameW entry.

	* [relay32/advapi32.spec] [win32/advapi.c]
	  [relay32/ole32.spec] [ ole/moniker.c]
	Added stubs for SetFileSecurity[AW] and CreateFileMoniker32

	* [graphics/x11drv/graphics.c]
	Finished implementation of bezier drawing code.

Tue Jul 21 11:00:51 1998  Claus Fischer <cfischer@td2cad.intel.com>

	* [files/drive.c]
	Remove label trailing blanks in GetVolumeInformation32A.

	* [documentation/cdrom-labels]
	Added documentation on how to find out a CD-ROM label.

Sun Jul 19 23:16:41 1998  Pascal Cuoq <pcuoq@ens-lyon.fr>

	* [include/windows.h]
	Added some DM_* and DISP_CHANGE_* flags.

	* [relay32/user32.spec] [windows/user.c]
	Added stub for ChangeDisplaySettingA.

	* [ole/ole2nls.c]
	is_punctuation: reuse information from another table.

Sun Jul 19 22:04:46 1998  Douglas Ridgway  <ridgway@winehq.com>

	* [Make.rules.in]
	Updated automatic documentation rules.

	* [graphics/path.c] [misc/aspi.c] [misc/ntdll.c] [misc/winsock_dns.c]
	[ole/ole2dsp.c] [relay32/user32.spec]
	Comment format futzing to keep c2man happy.

	* [documentation/README.documentation]
	Updated description of automatic documentation.

Wed Jul 15 19:10:09 1998   Andrew M. Bishop <amb@gedanken.demon.co.uk>

	* [files/profile.c]
	Cache the 10 most recently used .ini files.

Tue May 20 19:20:23 1997  Pablo Saratxaga <srtxg@chanae.alphanet.ch>

	* [misc/commdlg.c]
	Makes PrintDlg32A() return TRUE even if it is an empty
	stub, so most programs are happy and run anyway instead of
	aborting at startup.

	* [graphics/x11drv/xfont.c]
	Increased the maximum font families as (X11) font aliases
	eated up a lot of families causing wine to stop reading fonts.
diff --git a/files/profile.c b/files/profile.c
index 4110e71..c1f0daf 100644
--- a/files/profile.c
+++ b/files/profile.c
@@ -9,6 +9,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <sys/stat.h>
+
 #include "windows.h"
 #include "file.h"
 #include "heap.h"
@@ -36,11 +38,16 @@
     char            *dos_name;
     char            *unix_name;
     char            *filename;
+    time_t           mtime;
 } PROFILE;
 
 
-/* Cached profile file */
-static PROFILE CurProfile = { FALSE, NULL, NULL };
+#define N_CACHED_PROFILES 10
+
+/* Cached profile files */
+static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
+
+#define CurProfile (MRUProfile[0])
 
 /* wine.ini profile content */
 static PROFILESECTION *WineProfile;
@@ -205,17 +212,22 @@
                 next_section  = &section->next;
                 next_key      = &section->key;
                 prev_key      = NULL;
+
+                TRACE(profile, "New section: '%s'\n",section->name);
+
                 continue;
             }
         }
+
+        p2=p+strlen(p) - 1;
+        while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
+
         if ((p2 = strchr( p, '=' )) != NULL)
         {
             char *p3 = p2 - 1;
             while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
             *p2++ = '\0';
             while (*p2 && PROFILE_isspace(*p2)) p2++;
-            p3 = p2 + strlen(p2) - 1;
-            while ((p3 > p2) && ((*p3 == '\n') || PROFILE_isspace(*p3))) *p3--='\0';
         }
 
         if(*p || !prev_key || *prev_key->name)
@@ -341,9 +353,16 @@
     char *p, buffer[MAX_PATHNAME_LEN];
     const char *unix_name;
     FILE *file = NULL;
+    struct stat buf;
 
-    if (!CurProfile.changed || !CurProfile.dos_name) return TRUE;
-    if (!(unix_name = CurProfile.unix_name) || !(file = fopen(unix_name, "w")))
+    if(!CurProfile)
+    {
+        WARN(profile, "No current profile!\n");
+        return FALSE;
+    }
+
+    if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
+    if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
     {
         /* Try to create it in $HOME/.wine */
         /* FIXME: this will need a more general solution */
@@ -352,7 +371,7 @@
             strcpy( buffer, p );
             strcat( buffer, "/.wine/" );
             p = buffer + strlen(buffer);
-            strcpy( p, strrchr( CurProfile.dos_name, '\\' ) + 1 );
+            strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
             CharLower32A( p );
             file = fopen( buffer, "w" );
             unix_name = buffer;
@@ -361,14 +380,16 @@
     
     if (!file)
     {
-        WARN(profile, "could not save profile file %s\n", CurProfile.dos_name);
+        WARN(profile, "could not save profile file %s\n", CurProfile->dos_name);
         return FALSE;
     }
 
-    TRACE(profile, "Saving '%s' into '%s'\n", CurProfile.dos_name, unix_name );
-    PROFILE_Save( file, CurProfile.section );
+    TRACE(profile, "Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name );
+    PROFILE_Save( file, CurProfile->section );
     fclose( file );
-    CurProfile.changed = FALSE;
+    CurProfile->changed = FALSE;
+    if(!stat(unix_name,&buf))
+       CurProfile->mtime=buf.st_mtime;
     return TRUE;
 }
 
@@ -384,13 +405,25 @@
     char buffer[MAX_PATHNAME_LEN];
     char *newdos_name, *p;
     FILE *file = NULL;
+    int i,j;
+    struct stat buf;
+    PROFILE *tempProfile;
 
-    if (CurProfile.filename && !strcmp( filename, CurProfile.filename ))
-    {
-        TRACE(profile, "(%s): already opened\n",
-                         filename );
-        return TRUE;
-    }
+    /* First time around */
+
+    if(!CurProfile)
+       for(i=0;i<N_CACHED_PROFILES;i++)
+         {
+          MRUProfile[i]=HEAP_xalloc( SystemHeap, 0, sizeof(PROFILE) );
+          MRUProfile[i]->changed=FALSE;
+          MRUProfile[i]->section=NULL;
+          MRUProfile[i]->dos_name=NULL;
+          MRUProfile[i]->unix_name=NULL;
+          MRUProfile[i]->filename=NULL;
+          MRUProfile[i]->mtime=0;
+         }
+
+    /* Check for a match */
 
     if (strchr( filename, '/' ) || strchr( filename, '\\' ) || 
         strchr( filename, ':' ))
@@ -404,25 +437,61 @@
         strcat( buffer, filename );
         if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
     }
-    if (CurProfile.dos_name &&
-        !strcmp( full_name.short_name, CurProfile.dos_name ))
-    {
-        TRACE(profile, "(%s): already opened\n",
-                         filename );
-        return TRUE;
-    }
 
-    /* Flush the previous profile */
+    for(i=0;i<N_CACHED_PROFILES;i++)
+      {
+       if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) ||
+           (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name )))
+         {
+          if(i)
+            {
+             PROFILE_FlushFile();
+             tempProfile=MRUProfile[i];
+             for(j=i;j>0;j--)
+                MRUProfile[j]=MRUProfile[j-1];
+             CurProfile=tempProfile;
+            }
+          if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
+            {
+             TRACE(profile, "(%s): already opened (mru=%d)\n",
+                              filename, i );
+             return TRUE;
+            }
+          TRACE(profile, "(%s): already opened, needs refreshing (mru=%d)\n",
+                           filename, i );
+         }
+      }
+
+    /* Rotate the oldest to the top to be replaced */
+
+    if(i==N_CACHED_PROFILES)
+      {
+       tempProfile=MRUProfile[N_CACHED_PROFILES-1];
+       for(i=N_CACHED_PROFILES-1;i>0;i--)
+          MRUProfile[i]=MRUProfile[i-1];
+       CurProfile=tempProfile;
+      }
+
+    /* Flush the profile */
+
+    if(CurProfile->filename)
+      {
+       PROFILE_FlushFile();
+       PROFILE_Free( CurProfile->section );
+       if (CurProfile->dos_name) HeapFree( SystemHeap, 0, CurProfile->dos_name );
+       if (CurProfile->unix_name) HeapFree( SystemHeap, 0, CurProfile->unix_name );
+       if (CurProfile->filename) HeapFree( SystemHeap, 0, CurProfile->filename );
+       CurProfile->changed=FALSE;
+       CurProfile->section=NULL;
+       CurProfile->dos_name=NULL;
+       CurProfile->unix_name=NULL;
+       CurProfile->filename=NULL;
+       CurProfile->mtime=0;
+      }
 
     newdos_name = HEAP_strdupA( SystemHeap, 0, full_name.short_name );
-    PROFILE_FlushFile();
-    PROFILE_Free( CurProfile.section );
-    if (CurProfile.dos_name) HeapFree( SystemHeap, 0, CurProfile.dos_name );
-    if (CurProfile.unix_name) HeapFree( SystemHeap, 0, CurProfile.unix_name );
-    if (CurProfile.filename) HeapFree( SystemHeap, 0, CurProfile.filename );
-    CurProfile.section   = NULL;
-    CurProfile.dos_name  = newdos_name;
-    CurProfile.filename  = HEAP_strdupA( SystemHeap, 0, filename );
+    CurProfile->dos_name  = newdos_name;
+    CurProfile->filename  = HEAP_strdupA( SystemHeap, 0, filename );
 
     /* Try to open the profile file, first in $HOME/.wine */
 
@@ -438,13 +507,13 @@
         {
             TRACE(profile, "(%s): found it in %s\n",
                              filename, buffer );
-            CurProfile.unix_name = HEAP_strdupA( SystemHeap, 0, buffer );
+            CurProfile->unix_name = HEAP_strdupA( SystemHeap, 0, buffer );
         }
     }
 
     if (!file)
     {
-        CurProfile.unix_name = HEAP_strdupA( SystemHeap, 0,
+        CurProfile->unix_name = HEAP_strdupA( SystemHeap, 0,
                                              full_name.long_name );
         if ((file = fopen( full_name.long_name, "r" )))
             TRACE(profile, "(%s): found it in %s\n",
@@ -453,8 +522,10 @@
 
     if (file)
     {
-        CurProfile.section = PROFILE_Load( file );
+        CurProfile->section = PROFILE_Load( file );
         fclose( file );
+        if(!stat(CurProfile->unix_name,&buf))
+           CurProfile->mtime=buf.st_mtime;
     }
     else
     {
@@ -481,7 +552,7 @@
         if (section->name && !strcasecmp( section->name, section_name ))
         {
             UINT32 oldlen = len;
-            for (key = section->key; key; key = key->next)
+            for (key = section->key; key && *(key->name); key = key->next)
             {
                 if (len <= 2) break;
                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
@@ -507,7 +578,7 @@
 		buffer[-1] = '\0';
                 return oldlen - 2;
             }
-            return oldlen - len + 1;
+            return oldlen - len;
         }
         section = section->next;
     }
@@ -529,14 +600,14 @@
     if (!def_val) def_val = "";
     if (key_name && key_name[0])
     {
-        key = PROFILE_Find( &CurProfile.section, section, key_name, FALSE );
+        key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE );
         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
                            len, FALSE );
         TRACE(profile, "('%s','%s','%s'): returning '%s'\n",
                          section, key_name, def_val, buffer );
         return strlen( buffer );
     }
-    return PROFILE_GetSection(CurProfile.section, section, buffer, len,
+    return PROFILE_GetSection(CurProfile->section, section, buffer, len,
 				FALSE, FALSE);
 }
 
@@ -552,8 +623,8 @@
     if (!key_name)  /* Delete a whole section */
     {
         TRACE(profile, "('%s')\n", section_name);
-        CurProfile.changed |= PROFILE_DeleteSection( &CurProfile.section,
-                                                     section_name );
+        CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
+                                                      section_name );
         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
                                 this is not an error on application's level.*/
     }
@@ -561,13 +632,13 @@
     {
         TRACE(profile, "('%s','%s')\n",
                          section_name, key_name );
-        CurProfile.changed |= PROFILE_DeleteKey( &CurProfile.section,
-                                                 section_name, key_name );
+        CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
+                                                  section_name, key_name );
         return TRUE;          /* same error handling as above */
     }
     else  /* Set the key value */
     {
-        PROFILEKEY *key = PROFILE_Find( &CurProfile.section, section_name,
+        PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name,
                                         key_name, TRUE );
         TRACE(profile, "('%s','%s','%s'): \n",
                          section_name, key_name, value );
@@ -584,7 +655,7 @@
         }
         else TRACE(profile, "  creating key\n" );
         key->value = HEAP_strdupA( SystemHeap, 0, value );
-        CurProfile.changed = TRUE;
+        CurProfile->changed = TRUE;
     }
     return TRUE;
 }
@@ -1013,7 +1084,7 @@
                                           UINT32 len, LPCSTR filename )
 {
     if (PROFILE_Open( filename ))
-        return PROFILE_GetSection(CurProfile.section, section, buffer, len,
+        return PROFILE_GetSection(CurProfile->section, section, buffer, len,
                                 FALSE, TRUE);
 
     return 0;
@@ -1092,7 +1163,7 @@
     if (PROFILE_Open( filename )) {
 	buf=buffer;
 	cursize=0;
-	section=CurProfile.section;
+	section=CurProfile->section;
 	for ( ; section; section = section->next) 
             if (section->name) {
 		l=strlen (section->name);
@@ -1122,7 +1193,7 @@
     PROFILEKEY *k;
 
     if (PROFILE_Open( filename )) {
-        k=PROFILE_Find ( &CurProfile.section, section, key, FALSE);
+        k=PROFILE_Find ( &CurProfile->section, section, key, FALSE);
 	if (!k) return FALSE;
     	lstrcpyn32A( buf, k->value, strlen(k->value));
         return TRUE;