Update Gnulib to v0.0-5158-g7d06b32; remove `strcase' and `version-etc-fsf'.
[bpt/guile.git] / lib / write.c
index 7d02daa..3093df2 100644 (file)
@@ -20,8 +20,9 @@
 /* Specification.  */
 #include <unistd.h>
 
-/* Replace this function only if module 'sigpipe' is requested.  */
-#if GNULIB_SIGPIPE
+/* Replace this function only if module 'nonblocking' or module 'sigpipe' is
+   requested.  */
+#if GNULIB_NONBLOCKING || GNULIB_SIGPIPE
 
 /* On native Windows platforms, SIGPIPE does not exist.  When write() is
    called on a pipe with no readers, WriteFile() fails with error
@@ -41,21 +42,81 @@ ssize_t
 rpl_write (int fd, const void *buf, size_t count)
 #undef write
 {
-  ssize_t ret = write (fd, buf, count);
-
-  if (ret < 0)
+  for (;;)
     {
-      if (GetLastError () == ERROR_NO_DATA
-          && GetFileType ((HANDLE) _get_osfhandle (fd)) == FILE_TYPE_PIPE)
+      ssize_t ret = write (fd, buf, count);
+
+      if (ret < 0)
         {
-          /* Try to raise signal SIGPIPE.  */
-          raise (SIGPIPE);
-          /* If it is currently blocked or ignored, change errno from EINVAL
-             to EPIPE.  */
-          errno = EPIPE;
+#  if GNULIB_NONBLOCKING
+          if (errno == ENOSPC)
+            {
+              HANDLE h = (HANDLE) _get_osfhandle (fd);
+              if (GetFileType (h) == FILE_TYPE_PIPE)
+                {
+                  /* h is a pipe or socket.  */
+                  DWORD state;
+                  if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL,
+                                               NULL, 0)
+                      && (state & PIPE_NOWAIT) != 0)
+                    {
+                      /* h is a pipe in non-blocking mode.
+                         We can get here in four situations:
+                           1. When the pipe buffer is full.
+                           2. When count <= pipe_buf_size and the number of
+                              free bytes in the pipe buffer is < count.
+                           3. When count > pipe_buf_size and the number of free
+                              bytes in the pipe buffer is > 0, < pipe_buf_size.
+                           4. When count > pipe_buf_size and the pipe buffer is
+                              entirely empty.
+                         The cases 1 and 2 are POSIX compliant.  In cases 3 and
+                         4 POSIX specifies that write() must split the request
+                         and succeed with a partial write.  We fix case 4.
+                         We don't fix case 3 because it is not essential for
+                         programs.  */
+                      DWORD out_size; /* size of the buffer for outgoing data */
+                      DWORD in_size;  /* size of the buffer for incoming data */
+                      if (GetNamedPipeInfo (h, NULL, &out_size, &in_size, NULL))
+                        {
+                          size_t reduced_count = count;
+                          /* In theory we need only one of out_size, in_size.
+                             But I don't know which of the two.  The description
+                             is ambiguous.  */
+                          if (out_size != 0 && out_size < reduced_count)
+                            reduced_count = out_size;
+                          if (in_size != 0 && in_size < reduced_count)
+                            reduced_count = in_size;
+                          if (reduced_count < count)
+                            {
+                              /* Attempt to write only the first part.  */
+                              count = reduced_count;
+                              continue;
+                            }
+                        }
+                      /* Change errno from ENOSPC to EAGAIN.  */
+                      errno = EAGAIN;
+                    }
+                }
+            }
+          else
+#  endif
+            {
+#  if GNULIB_SIGPIPE
+              if (GetLastError () == ERROR_NO_DATA
+                  && GetFileType ((HANDLE) _get_osfhandle (fd))
+                     == FILE_TYPE_PIPE)
+                {
+                  /* Try to raise signal SIGPIPE.  */
+                  raise (SIGPIPE);
+                  /* If it is currently blocked or ignored, change errno from
+                     EINVAL to EPIPE.  */
+                  errno = EPIPE;
+                }
+#  endif
+            }
         }
+      return ret;
     }
-  return ret;
 }
 
 # endif