diff --git a/files/profile.c b/files/profile.c
index c20fbb57276e4dab9141a9baa8253541c94c0ced..7761acf0113bee76a9e807a8067aa2b97ac643fe 100644
--- a/files/profile.c
+++ b/files/profile.c
@@ -755,28 +755,42 @@ static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
     return 0;
 }
 
-
+/* See GetPrivateProfileSectionNamesA for documentation */
 static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
 {
-    LPSTR buf = buffer;
-    WORD l, cursize = 0;
+    LPSTR buf;
+    UINT f,l;
     PROFILESECTION *section;
 
-    if(!buffer) return 0;
-
-    for (section = CurProfile->section; section; section = section->next)
-	if (section->name[0]) {
-	    l = strlen(section->name);
-	    cursize += l+1;
-	    if (cursize > len+1)
-		return len-2;
-
-	    strcpy(buf, section->name);
-	    buf += l+1;
-	}
+    if (!buffer || !len)
+        return 0;
+    if (len==1) {
+        *buffer='\0';
+        return 0;
+    }
 
-    *buf=0;
-    buf++;
+    f=len-1;
+    buf=buffer;
+    section = CurProfile->section;
+    while ((section!=NULL)) {
+        if (section->name[0]) {
+            l = strlen(section->name)+1;
+            if (l > f) {
+                if (f>0) {
+                    strncpy(buf, section->name, f-1);
+                    buf += f-1;
+                    *buf++='\0';
+                }
+                *buf='\0';
+                return len-2;
+            }
+            strcpy(buf, section->name);
+            buf += l;
+            f -= l;
+        }
+        section = section->next;
+    }
+    *buf='\0';
     return buf-buffer;
 }
 
@@ -1588,37 +1602,71 @@ BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
                                              LPCSTR filename )
 {
-    WORD ret = 0;
-
-    EnterCriticalSection( &PROFILE_CritSect );
-
-    if (PROFILE_Open( filename ))
-	ret = PROFILE_GetSectionNames(buffer, size);
-
-    LeaveCriticalSection( &PROFILE_CritSect );
-
-    return ret;
+    return GetPrivateProfileSectionNamesA(buffer,size,filename);
 }
 
 
 /***********************************************************************
  *           GetProfileSectionNames   (KERNEL.142)
  */
-WORD WINAPI GetProfileSectionNames16( LPSTR buffer, WORD size)
+WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
 
 {
-    return (GetPrivateProfileSectionNames16 (buffer,size,"win.ini"));
+    return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
 }
 
 
 /***********************************************************************
  *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
+ *
+ * Returns the section names contained in the specified file.
+ * FIXME: Where do we find this file when the path is relative?
+ * The section names are returned as a list of strings with an extra 
+ * '\0' to mark the end of the list. Except for that the behavior 
+ * depends on the Windows version.
+ *
+ * Win95:
+ * - if the buffer is 0 or 1 character long then it is as if it was of 
+ *   infinite length.
+ * - otherwise, if the buffer is to small only the section names that fit 
+ *   are returned.
+ * - note that this means if the buffer was to small to return even just 
+ *   the first section name then a single '\0' will be returned.
+ * - the return value is the number of characters written in the buffer, 
+ *   except if the buffer was too smal in which case len-2 is returned
+ *
+ * Win2000:
+ * - if the buffer is 0, 1 or 2 characters long then it is filled with 
+ *   '\0' and the return value is 0
+ * - otherwise if the buffer is too small then the first section name that 
+ *   does not fit is truncated so that the string list can be terminated 
+ *   correctly (double '\0')
+ * - the return value is the number of characters written in the buffer 
+ *   except for the trailing '\0'. If the buffer is too small, then the 
+ *   return value is len-2
+ * - Win2000 has a bug that triggers when the section names and the 
+ *   trailing '\0' fit exactly in the buffer. In that case the trailing 
+ *   '\0' is missing.
+ *
+ * Wine implements the observed Win2000 behavior (except for the bug).
+ *
+ * Note that when the buffer is big enough then the return value may be any 
+ * value between 1 and len-1 (or len in Win95), including len-2.
  */
 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
 					     LPCSTR filename)
 
 {
- return (GetPrivateProfileSectionNames16 (buffer,size,filename));
+    DWORD ret = 0;
+
+    EnterCriticalSection( &PROFILE_CritSect );
+
+    if (PROFILE_Open( filename ))
+        ret = PROFILE_GetSectionNames(buffer, size);
+
+    LeaveCriticalSection( &PROFILE_CritSect );
+
+    return ret;
 }
 
 
@@ -1629,16 +1677,16 @@ DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
 					     LPCWSTR filename)
 
 {
-   LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
-   LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, size);
+    LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
+    LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, size);
 
-   INT ret = GetPrivateProfileSectionNames16 (bufferA, size, filenameA);
-   if (size > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, size ))
+    INT ret = GetPrivateProfileSectionNamesA(bufferA, size, filenameA);
+    if (size > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, size ))
         buffer[size-1] = 0;
-   HeapFree( GetProcessHeap(), 0, bufferA);
-   HeapFree( GetProcessHeap(), 0, filenameA );
+    HeapFree( GetProcessHeap(), 0, bufferA);
+    HeapFree( GetProcessHeap(), 0, filenameA );
 
-   return ret;
+    return ret;
 }
 
 /***********************************************************************