# Description: When overwriting history prevent race conditions from losing
#              history by writing tempfile then renaming it, as opposed to
#              truncating existing file and then writing it.
#
# Author:      Russell Stuart <russell-debian@stuart.id.au>

--- a/lib/readline/histfile.c
+++ b/lib/readline/histfile.c
@@ -142,17 +142,24 @@
 }
 
 static char *
-history_backupfile (filename)
+history_tempfile (filename)
      const char *filename;
 {
   char *ret;
   size_t len;
+  int pid;
 
   len = strlen (filename);
-  ret = xmalloc (len + 2);
+  ret = xmalloc (len + 11);
   strcpy (ret, filename);
-  ret[len] = '-';
-  ret[len+1] = '\0';
+  pid = getpid();
+  ret[len+0] = '-';
+  ret[len+1] = '0' + pid / 10000 % 10;
+  ret[len+2] = '0' + pid / 1000 % 10;
+  ret[len+3] = '0' + pid / 100 % 10;
+  ret[len+4] = '0' + pid / 10 % 10;
+  ret[len+5] = '0' + pid / 1 % 10;
+  strcpy(&ret[len+6], ".tmp");
   return ret;
 }
   
@@ -320,11 +327,13 @@
      int lines;
 {
   char *buffer, *filename, *bp, *bp1;		/* bp1 == bp+1 */
+  char *temp_filename;
   int file, chars_read, rv;
   struct stat finfo;
   size_t file_size;
 
   buffer = (char *)NULL;
+  temp_filename = (char *)NULL;
   filename = history_filename (fname);
   file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
   rv = 0;
@@ -409,19 +418,26 @@
 
   /* Write only if there are more lines in the file than we want to
      truncate to. */
-  if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
+  temp_filename = history_tempfile (filename);
+  if (bp > buffer && ((file = open (temp_filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1))
     {
       if (write (file, bp, chars_read - (bp - buffer)) < 0)
 	rv = errno;
 
+#if 0
 #if defined (__BEOS__)
       /* BeOS ignores O_TRUNC. */
       ftruncate (file, chars_read - (bp - buffer));
 #endif
+#endif
 
       if (close (file) < 0 && rv == 0)
 	rv = errno;
+      if (rv == 0)
+	rename(temp_filename, filename);
+      unlink(temp_filename);
     }
+  FREE(temp_filename);
 
  truncate_exit:
 
@@ -440,7 +456,7 @@
      int nelements, overwrite;
 {
   register int i;
-  char *output, *bakname;
+  char *hist_filename, *temp_filename, *output;
   int file, mode, rv;
 #ifdef HISTORY_USE_MMAP
   size_t cursize;
@@ -449,11 +465,17 @@
 #else
   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
 #endif
-  output = history_filename (filename);
-  bakname = (overwrite && output) ? history_backupfile (output) : 0;
-
-  if (output && bakname)
-    rename (output, bakname);
+  hist_filename = history_filename (filename);
+  if (overwrite && hist_filename)
+    {
+      temp_filename = history_tempfile (hist_filename);
+      output = temp_filename;
+    }
+  else
+    {
+      temp_filename = 0;
+      output = hist_filename;
+    }
 
   file = output ? open (output, mode, 0600) : -1;
   rv = 0;
@@ -461,10 +483,8 @@
   if (file == -1)
     {
       rv = errno;
-      if (output && bakname)
-        rename (bakname, output);
-      FREE (output);
-      FREE (bakname);
+      FREE (hist_filename);
+      FREE (temp_filename);
       return (rv);
     }
 
@@ -506,10 +526,10 @@
 mmap_error:
 	rv = errno;
 	close (file);
-	if (output && bakname)
-	  rename (bakname, output);
-	FREE (output);
-	FREE (bakname);
+	if (temp_filename)
+	  unlink (temp_filename);
+	FREE (hist_filename);
+	FREE (temp_filename);
 	return rv;
       }
 #else    
@@ -518,10 +538,10 @@
       {
       	rv = errno;
 	close (file);
-	if (output && bakname)
-	  rename (bakname, output);
-	FREE (output);
-	FREE (bakname);
+	if (temp_filename)
+	  unlink(temp_filename);
+	FREE (hist_filename);
+	FREE (temp_filename);
 	return rv;
       }
 #endif
@@ -552,13 +572,15 @@
   if (close (file) < 0 && rv == 0)
     rv = errno;
 
-  if (rv != 0 && output && bakname)
-    rename (bakname, output);
-  else if (rv == 0 && bakname)
-    unlink (bakname);
+  if (temp_filename)
+    {
+      if (temp_filename)
+	rename (temp_filename, hist_filename);
+      unlink (temp_filename);
+    }
 
-  FREE (output);
-  FREE (bakname);
+  FREE (hist_filename);
+  FREE (temp_filename);
 
   return (rv);
 }
