fixup debian/copyright to the point it passes lintian
[gnulib.git] / lib / popen.c
index a1f1d45..001354c 100644 (file)
@@ -1,5 +1,5 @@
 /* Open a stream to a sub-process.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009-2014 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <config.h>
 
-/* Get the original definition of popen.  It might be defined as a macro.  */
-#define __need_FILE
-# include <stdio.h>
-#undef __need_FILE
+/* Specification.  */
+#include <stdio.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Native Windows API.  */
 
-static inline FILE *
-orig_popen (const char *filename, const char *mode)
+# include <string.h>
+
+FILE *
+popen (const char *filename, const char *mode)
 {
-  return popen (filename, mode);
+  /* Use binary mode by default.  */
+  if (strcmp (mode, "r") == 0)
+    mode = "rb";
+  else if (strcmp (mode, "w") == 0)
+    mode = "wb";
+
+  return _popen (filename, mode);
 }
 
-/* Specification.  */
-#include <stdio.h>
+#else
 
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
+# include <errno.h>
+# include <fcntl.h>
+# include <stdlib.h>
+# include <unistd.h>
+
+# undef popen
 
 FILE *
 rpl_popen (const char *filename, const char *mode)
 {
-  /* The mingw popen works fine, and all other platforms have fcntl.
+  /* All other platforms have popen and fcntl.
      The bug of the child clobbering its own file descriptors if stdin
      or stdout was closed in the parent can be worked around by
      opening those two fds as close-on-exec to begin with.  */
@@ -49,28 +59,38 @@ rpl_popen (const char *filename, const char *mode)
      the fd leaks into subsequent popen calls.  We could work around
      this by maintaining a list of all fd's opened by popen, and
      temporarily marking them cloexec around the real popen call, but
-     we would also have to override pclose, and the bookkeepping seems
+     we would also have to override pclose, and the bookkeeping seems
      extreme given that cygwin 1.7 no longer has the bug.  */
   FILE *result;
   int cloexec0 = fcntl (STDIN_FILENO, F_GETFD);
   int cloexec1 = fcntl (STDOUT_FILENO, F_GETFD);
   int saved_errno;
 
+  /* If either stdin or stdout was closed (that is, fcntl failed),
+     then we open a dummy close-on-exec fd to occupy that slot.  That
+     way, popen's internal use of pipe() will not contain either fd 0
+     or 1, overcoming the fact that the child process blindly calls
+     close() on the parent's end of the pipe without first checking
+     whether it is clobbering the fd just placed there via dup2(); the
+     exec will get rid of the dummy fd's in the child.  Fortunately,
+     closed stderr in the parent does not cause problems in the
+     child.  */
   if (cloexec0 < 0)
     {
       if (open ("/dev/null", O_RDONLY) != STDIN_FILENO
-         || fcntl (STDIN_FILENO, F_SETFD,
-                   fcntl (STDIN_FILENO, F_GETFD) | FD_CLOEXEC) == -1)
-       abort ();
+          || fcntl (STDIN_FILENO, F_SETFD,
+                    fcntl (STDIN_FILENO, F_GETFD) | FD_CLOEXEC) == -1)
+        abort ();
     }
   if (cloexec1 < 0)
     {
       if (open ("/dev/null", O_RDONLY) != STDOUT_FILENO
-         || fcntl (STDOUT_FILENO, F_SETFD,
-                   fcntl (STDOUT_FILENO, F_GETFD) | FD_CLOEXEC) == -1)
-       abort ();
+          || fcntl (STDOUT_FILENO, F_SETFD,
+                    fcntl (STDOUT_FILENO, F_GETFD) | FD_CLOEXEC) == -1)
+        abort ();
     }
-  result = orig_popen (filename, mode);
+  result = popen (filename, mode);
+  /* Now, close any dummy fd's created in the parent.  */
   saved_errno = errno;
   if (cloexec0 < 0)
     close (STDIN_FILENO);
@@ -79,3 +99,5 @@ rpl_popen (const char *filename, const char *mode)
   errno = saved_errno;
   return result;
 }
+
+#endif