Merge "Remove workarounds for old versions of clang and GCC."
diff --git a/libc/Android.bp b/libc/Android.bp
index c6991d8..212a1b1 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -333,7 +333,6 @@
     srcs: [
         "upstream-netbsd/common/lib/libc/stdlib/random.c",
         "upstream-netbsd/lib/libc/gen/nice.c",
-        "upstream-netbsd/lib/libc/gen/popen.c",
         "upstream-netbsd/lib/libc/gen/psignal.c",
         "upstream-netbsd/lib/libc/gen/utime.c",
         "upstream-netbsd/lib/libc/gen/utmp.c",
@@ -450,7 +449,6 @@
         "upstream-openbsd/lib/libc/stdio/fputwc.c",
         "upstream-openbsd/lib/libc/stdio/fputws.c",
         "upstream-openbsd/lib/libc/stdio/fvwrite.c",
-        "upstream-openbsd/lib/libc/stdio/fwalk.c",
         "upstream-openbsd/lib/libc/stdio/fwide.c",
         "upstream-openbsd/lib/libc/stdio/getdelim.c",
         "upstream-openbsd/lib/libc/stdio/gets.c",
diff --git a/libc/NOTICE b/libc/NOTICE
index ae7da18..744f42b 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -1813,38 +1813,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 1988, 1993
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software written by Ken Arnold and
-published in UNIX Review, Vol. 6, No. 8.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
 Copyright (c) 1989 The Regents of the University of California.
 All rights reserved.
 
diff --git a/libc/bionic/thread_private.cpp b/libc/bionic/thread_private.cpp
index 1c04019..94fb8bb 100644
--- a/libc/bionic/thread_private.cpp
+++ b/libc/bionic/thread_private.cpp
@@ -31,16 +31,6 @@
 
 // Some simple glue used to make BSD code thread-safe.
 
-static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
-
-void _thread_atexit_lock() {
-  pthread_mutex_lock(&g_atexit_lock);
-}
-
-void _thread_atexit_unlock() {
-  pthread_mutex_unlock(&g_atexit_lock);
-}
-
 static pthread_mutex_t g_arc4_lock = PTHREAD_MUTEX_INITIALIZER;
 
 void _thread_arc4_lock() {
diff --git a/libc/private/thread_private.h b/libc/private/thread_private.h
index 0081ad0..1f9eeb6 100644
--- a/libc/private/thread_private.h
+++ b/libc/private/thread_private.h
@@ -2,8 +2,7 @@
 
 /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */
 
-#ifndef _THREAD_PRIVATE_H_
-#define _THREAD_PRIVATE_H_
+#pragma once
 
 #include <pthread.h>
 
@@ -16,33 +15,15 @@
  * described functions for operation in a non-threaded environment.
  */
 
-/*
- * helper macro to make unique names in the thread namespace
- */
-#define __THREAD_NAME(name)	__CONCAT(_thread_tagname_,name)
-
-struct __thread_private_tag_t {
-    pthread_mutex_t    _private_lock;
-    pthread_key_t      _private_key;
-};
-
-#define _THREAD_PRIVATE_MUTEX(name)  \
-	static struct __thread_private_tag_t  __THREAD_NAME(name) = { PTHREAD_MUTEX_INITIALIZER, -1 }
-#define _THREAD_PRIVATE_MUTEX_LOCK(name)  \
-	pthread_mutex_lock( &__THREAD_NAME(name)._private_lock )
-#define _THREAD_PRIVATE_MUTEX_UNLOCK(name) \
-	pthread_mutex_unlock( &__THREAD_NAME(name)._private_lock )
+#define __MUTEX_NAME(name) __CONCAT(__libc_mutex_,name)
+#define _THREAD_PRIVATE_MUTEX(name) static pthread_mutex_t __MUTEX_NAME(name) = PTHREAD_MUTEX_INITIALIZER
+#define _THREAD_PRIVATE_MUTEX_LOCK(name) pthread_mutex_lock(&__MUTEX_NAME(name))
+#define _THREAD_PRIVATE_MUTEX_UNLOCK(name) pthread_mutex_unlock(&__MUTEX_NAME(name))
 
 /* Note that these aren't compatible with the usual OpenBSD ones which lazy-initialize! */
 #define _MUTEX_LOCK(l) pthread_mutex_lock((pthread_mutex_t*) l)
 #define _MUTEX_UNLOCK(l) pthread_mutex_unlock((pthread_mutex_t*) l)
 
-__LIBC_HIDDEN__ void  _thread_atexit_lock(void);
-__LIBC_HIDDEN__ void  _thread_atexit_unlock(void);
-
-#define _ATEXIT_LOCK() _thread_atexit_lock()
-#define _ATEXIT_UNLOCK() _thread_atexit_unlock()
-
 __LIBC_HIDDEN__ void    _thread_arc4_lock(void);
 __LIBC_HIDDEN__ void    _thread_arc4_unlock(void);
 
@@ -53,5 +34,3 @@
 extern volatile sig_atomic_t _rs_forked;
 
 __END_DECLS
-
-#endif /* _THREAD_PRIVATE_H_ */
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 441fcfe..d306a21 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -137,6 +137,9 @@
   // Equivalent to `_seek` but for _FILE_OFFSET_BITS=64.
   // Callers should use this but fall back to `__sFILE::_seek`.
   off64_t (*_seek64)(void*, off64_t, int);
+
+  // The pid of the child if this FILE* is from popen(3).
+  pid_t _popen_pid;
 };
 
 // Values for `__sFILE::_flags`.
@@ -201,7 +204,6 @@
 __LIBC32_LEGACY_PUBLIC__ int _fwalk(int (*)(FILE*));
 
 off64_t __sseek64(void*, off64_t, int);
-int __sflush_locked(FILE*);
 int __swhatbuf(FILE*, size_t*, int*);
 wint_t __fgetwc_unlock(FILE*);
 wint_t __ungetwc(wint_t, FILE*);
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index 1df4191..cfa2bfd 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -40,7 +40,7 @@
 lflush(FILE *fp)
 {
 	if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
-		return (__sflush_locked(fp));	/* ignored... */
+		return (__sflush(fp));	/* ignored... */
 	return (0);
 }
 
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index e066e5b..1f08ea1 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -41,7 +41,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/param.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include <async_safe/log.h>
@@ -64,33 +66,42 @@
     va_end(ap); \
     return result;
 
-#define std(flags, file) \
-    {0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,nullptr,__swrite, \
-    {(unsigned char *)(__sFext+file), 0},nullptr,0,{0},{0},{0,0},0,0}
-
-_THREAD_PRIVATE_MUTEX(__sfp_mutex);
-
-#define SBUF_INIT {}
-#define WCHAR_IO_DATA_INIT {}
+#define MAKE_STD_STREAM(flags, fd)                                          \
+  {                                                                         \
+    ._flags = flags, ._file = fd, ._cookie = __sF + fd, ._close = __sclose, \
+    ._read = __sread, ._write = __swrite, ._ext = {                         \
+      ._base = reinterpret_cast<uint8_t*>(__sFext + fd)                     \
+    }                                                                       \
+  }
 
 static struct __sfileext __sFext[3] = {
-  { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
-  { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
-  { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
+    {._lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+     ._caller_handles_locking = false,
+     ._seek64 = __sseek64,
+     ._popen_pid = 0},
+    {._lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+     ._caller_handles_locking = false,
+     ._seek64 = __sseek64,
+     ._popen_pid = 0},
+    {._lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+     ._caller_handles_locking = false,
+     ._seek64 = __sseek64,
+     ._popen_pid = 0},
 };
 
 // __sF is exported for backwards compatibility. Until M, we didn't have symbols
 // for stdin/stdout/stderr; they were macros accessing __sF.
 FILE __sF[3] = {
-  std(__SRD, STDIN_FILENO),
-  std(__SWR, STDOUT_FILENO),
-  std(__SWR|__SNBF, STDERR_FILENO),
+    MAKE_STD_STREAM(__SRD, STDIN_FILENO),
+    MAKE_STD_STREAM(__SWR, STDOUT_FILENO),
+    MAKE_STD_STREAM(__SWR|__SNBF, STDERR_FILENO),
 };
 
 FILE* stdin = &__sF[0];
 FILE* stdout = &__sF[1];
 FILE* stderr = &__sF[2];
 
+static pthread_mutex_t __stdio_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 struct glue __sglue = { nullptr, 3, __sF };
 static struct glue* lastglue = &__sglue;
 
@@ -108,8 +119,6 @@
 };
 
 static glue* moreglue(int n) {
-  static FILE empty;
-
   char* data = new char[sizeof(glue) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(__sfileext)];
   if (data == nullptr) return nullptr;
 
@@ -120,7 +129,7 @@
   g->niobs = n;
   g->iobs = p;
   while (--n >= 0) {
-    *p = empty;
+    *p = {};
     _FILEEXT_SETUP(p, pext);
     p++;
     pext++;
@@ -143,7 +152,7 @@
 	int n;
 	struct glue *g;
 
-	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
+	pthread_mutex_lock(&__stdio_mutex);
 	for (g = &__sglue; g != nullptr; g = g->next) {
 		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
 			if (fp->_flags == 0)
@@ -151,15 +160,15 @@
 	}
 
 	/* release lock while mallocing */
-	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
+	pthread_mutex_unlock(&__stdio_mutex);
 	if ((g = moreglue(NDYNAMIC)) == nullptr) return nullptr;
-	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
+	pthread_mutex_lock(&__stdio_mutex);
 	lastglue->next = g;
 	lastglue = g;
 	fp = g->iobs;
 found:
 	fp->_flags = 1;		/* reserve this slot; caller sets real flags */
-	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
+	pthread_mutex_unlock(&__stdio_mutex);
 	fp->_p = nullptr;		/* no current pointer */
 	fp->_w = 0;		/* nothing to read or write */
 	fp->_r = 0;
@@ -183,9 +192,20 @@
 	return fp;
 }
 
-extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
-  // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway.
-  _fwalk(__sflush);
+int _fwalk(int (*callback)(FILE*)) {
+  pthread_mutex_lock(&__stdio_mutex);
+  int result = 0;
+  for (glue* g = &__sglue; g != nullptr; g = g->next) {
+    FILE* fp = g->iobs;
+    for (int n = g->niobs; --n >= 0; ++fp) {
+      ScopedFileLock sfl(fp);
+      if (fp->_flags != 0 && (fp->_flags & __SIGN) == 0) {
+        result |= (*callback)(fp);
+      }
+    }
+  }
+  pthread_mutex_unlock(&__stdio_mutex);
+  return result;
 }
 
 static FILE* __fopen(int fd, int flags) {
@@ -383,6 +403,16 @@
   if (HASUB(fp)) FREEUB(fp);
   free_fgetln_buffer(fp);
 
+  // If we were created by popen(3), wait for the child.
+  pid_t pid = _EXT(fp)->_popen_pid;
+  if (pid > 0) {
+    int status;
+    if (TEMP_FAILURE_RETRY(wait4(pid, &status, 0, nullptr)) != -1) {
+      r = status;
+    }
+  }
+  _EXT(fp)->_popen_pid = 0;
+
   // Poison this FILE so accesses after fclose will be obvious.
   fp->_file = -1;
   fp->_r = fp->_w = 0;
@@ -391,6 +421,7 @@
   fp->_flags = 0;
   return r;
 }
+__strong_alias(pclose, fclose);
 
 int fileno_unlocked(FILE* fp) {
   CHECK_FP(fp);
@@ -465,11 +496,6 @@
   return 0;
 }
 
-int __sflush_locked(FILE* fp) {
-  ScopedFileLock sfl(fp);
-  return __sflush(fp);
-}
-
 int __sread(void* cookie, char* buf, int n) {
   FILE* fp = reinterpret_cast<FILE*>(cookie);
   return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
@@ -707,18 +733,16 @@
   return getc_unlocked(fp);
 }
 
-/*
- * Read at most n-1 characters from the given file.
- * Stop when a newline has been read, or the count runs out.
- * Return first argument, or NULL if no characters were read.
- * Do not return NULL if n == 1.
- */
 char* fgets(char* buf, int n, FILE* fp) {
   CHECK_FP(fp);
   ScopedFileLock sfl(fp);
   return fgets_unlocked(buf, n, fp);
 }
 
+// Reads at most n-1 characters from the given file.
+// Stops when a newline has been read, or the count runs out.
+// Returns first argument, or nullptr if no characters were read.
+// Does not return nullptr if n == 1.
 char* fgets_unlocked(char* buf, int n, FILE* fp) {
   if (n <= 0) {
     errno = EINVAL;
@@ -1013,7 +1037,7 @@
 }
 
 static int fflush_all() {
-  return _fwalk(__sflush_locked);
+  return _fwalk(__sflush);
 }
 
 int fflush(FILE* fp) {
@@ -1122,6 +1146,80 @@
   return (__sfvwrite(fp, &uio) == 0) ? count : ((n - uio.uio_resid) / size);
 }
 
+static int __close_if_popened(FILE* fp) {
+  if (_EXT(fp)->_popen_pid > 0) close(fileno(fp));
+  return 0;
+}
+
+static FILE* __popen_fail(int fds[2]) {
+  ErrnoRestorer errno_restorer;
+  close(fds[0]);
+  close(fds[1]);
+  return nullptr;
+}
+
+FILE* popen(const char* cmd, const char* mode) {
+  bool close_on_exec = (strchr(mode, 'e') != nullptr);
+
+  // Was the request for a socketpair or just a pipe?
+  int fds[2];
+  bool bidirectional = false;
+  if (strchr(mode, '+') != nullptr) {
+    if (socketpair(AF_LOCAL, SOCK_CLOEXEC | SOCK_STREAM, 0, fds) == -1) return nullptr;
+    bidirectional = true;
+    mode = "r+";
+  } else {
+    if (pipe2(fds, O_CLOEXEC) == -1) return nullptr;
+    mode = strrchr(mode, 'r') ? "r" : "w";
+  }
+
+  // If the parent wants to read, the child's fd needs to be stdout.
+  int parent, child, desired_child_fd;
+  if (*mode == 'r') {
+    parent = 0;
+    child = 1;
+    desired_child_fd = STDOUT_FILENO;
+  } else {
+    parent = 1;
+    child = 0;
+    desired_child_fd = STDIN_FILENO;
+  }
+
+  // Ensure that the child fd isn't the desired child fd.
+  if (fds[child] == desired_child_fd) {
+    int new_fd = fcntl(fds[child], F_DUPFD_CLOEXEC, 0);
+    if (new_fd == -1) return __popen_fail(fds);
+    close(fds[child]);
+    fds[child] = new_fd;
+  }
+
+  pid_t pid = vfork();
+  if (pid == -1) return __popen_fail(fds);
+
+  if (pid == 0) {
+    close(fds[parent]);
+    // POSIX says "The popen() function shall ensure that any streams from previous popen() calls
+    // that remain open in the parent process are closed in the new child process."
+    _fwalk(__close_if_popened);
+    // dup2 so that the child fd isn't closed on exec.
+    if (dup2(fds[child], desired_child_fd) == -1) _exit(127);
+    close(fds[child]);
+    if (bidirectional) dup2(STDOUT_FILENO, STDIN_FILENO);
+    execl(_PATH_BSHELL, "sh", "-c", cmd, nullptr);
+    _exit(127);
+  }
+
+  FILE* fp = fdopen(fds[parent], mode);
+  if (fp == nullptr) return __popen_fail(fds);
+
+  // The caller didn't ask for their pipe to be O_CLOEXEC, so flip it back now the child has forked.
+  if (!close_on_exec) fcntl(fds[parent], F_SETFD, 0);
+  close(fds[child]);
+
+  _EXT(fp)->_popen_pid = pid;
+  return fp;
+}
+
 namespace {
 
 namespace phony {
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index c817b63..a26bee4 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -29,18 +29,23 @@
  *
  */
 
-#include <sys/types.h>
-#include <sys/mman.h>
+#include "atexit.h"
+
+#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include "atexit.h"
-#include "private/thread_private.h"
+#include <sys/mman.h>
+#include <sys/types.h>
 
 /* BEGIN android-changed */
 #include "private/bionic_prctl.h"
 /* END android-changed */
 
+static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
+#define _ATEXIT_LOCK() pthread_mutex_lock(&g_atexit_lock)
+#define _ATEXIT_UNLOCK() pthread_mutex_unlock(&g_atexit_lock)
+
 struct atexit {
 	struct atexit *next;		/* next in list */
 	int ind;			/* next index in this table */
@@ -79,15 +84,14 @@
 int
 __cxa_atexit(void (*func)(void *), void *arg, void *dso)
 {
-	struct atexit *p = __atexit;
 	struct atexit_fn *fnp;
 	size_t pgsize = getpagesize();
 	int ret = -1;
 
-	if (pgsize < sizeof(*p))
+	if (pgsize < sizeof(struct atexit))
 		return (-1);
 	_ATEXIT_LOCK();
-	p = __atexit;
+	struct atexit *p = __atexit;
 	if (p != NULL) {
 		if (p->ind + 1 >= p->max)
 			p = NULL;
@@ -185,8 +189,7 @@
 	}
 	_ATEXIT_UNLOCK();
 
-  extern void __libc_stdio_cleanup(void);
-  __libc_stdio_cleanup();
+  fflush(NULL);
 
   /* BEGIN android-changed: call __unregister_atfork if dso is not null */
   if (dso != NULL) {
diff --git a/libc/upstream-netbsd/lib/libc/gen/popen.c b/libc/upstream-netbsd/lib/libc/gen/popen.c
deleted file mode 100644
index 593e346..0000000
--- a/libc/upstream-netbsd/lib/libc/gen/popen.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*	$NetBSD: popen.c,v 1.32 2012/06/25 22:32:43 abs Exp $	*/
-
-/*
- * Copyright (c) 1988, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software written by Ken Arnold and
- * published in UNIX Review, Vol. 6, No. 8.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
-#else
-__RCSID("$NetBSD: popen.c,v 1.32 2012/06/25 22:32:43 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include "namespace.h"
-#include <sys/param.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <paths.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "env.h"
-#include "reentrant.h"
-
-#ifdef __weak_alias
-__weak_alias(popen,_popen)
-__weak_alias(pclose,_pclose)
-#endif
-
-static struct pid {
-	struct pid *next;
-	FILE *fp;
-#ifdef _REENTRANT
-	int fd;
-#endif
-	pid_t pid;
-} *pidlist; 
-	
-#ifdef _REENTRANT
-static rwlock_t pidlist_lock = RWLOCK_INITIALIZER;
-#endif
-
-FILE *
-popen(const char *command, const char *type)
-{
-	struct pid *cur, *old;
-	FILE *iop;
-	const char * volatile xtype = type;
-	int pdes[2], pid, serrno;
-	volatile int twoway;
-	int flags;
-
-	_DIAGASSERT(command != NULL);
-	_DIAGASSERT(xtype != NULL);
-
-	flags = strchr(xtype, 'e') ? O_CLOEXEC : 0;
-	if (strchr(xtype, '+')) {
-		int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM;
-		twoway = 1;
-		xtype = "r+";
-		if (socketpair(AF_LOCAL, stype, 0, pdes) < 0)
-			return NULL;
-	} else  {
-		twoway = 0;
-		xtype = strrchr(xtype, 'r') ? "r" : "w";
-		if (pipe2(pdes, flags) == -1)
-			return NULL;
-	}
-
-	if ((cur = malloc(sizeof(struct pid))) == NULL) {
-		(void)close(pdes[0]);
-		(void)close(pdes[1]);
-		errno = ENOMEM;
-		return (NULL);
-	}
-
-	(void)rwlock_rdlock(&pidlist_lock);
-	(void)__readlockenv();
-	switch (pid = vfork()) {
-	case -1:			/* Error. */
-		serrno = errno;
-		(void)__unlockenv();
-		(void)rwlock_unlock(&pidlist_lock);
-		free(cur);
-		(void)close(pdes[0]);
-		(void)close(pdes[1]);
-		errno = serrno;
-		return (NULL);
-		/* NOTREACHED */
-	case 0:				/* Child. */
-		/* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
-		   from previous popen() calls that remain open in the 
-		   parent process are closed in the new child process. */
-		for (old = pidlist; old; old = old->next)
-#ifdef _REENTRANT
-			close(old->fd); /* don't allow a flush */
-#else
-			close(fileno(old->fp)); /* don't allow a flush */
-#endif
-
-		if (*xtype == 'r') {
-			(void)close(pdes[0]);
-			if (pdes[1] != STDOUT_FILENO) {
-				(void)dup2(pdes[1], STDOUT_FILENO);
-				(void)close(pdes[1]);
-			}
-			if (twoway)
-				(void)dup2(STDOUT_FILENO, STDIN_FILENO);
-		} else {
-			(void)close(pdes[1]);
-			if (pdes[0] != STDIN_FILENO) {
-				(void)dup2(pdes[0], STDIN_FILENO);
-				(void)close(pdes[0]);
-			}
-		}
-
-		execl(_PATH_BSHELL, "sh", "-c", command, NULL);
-		_exit(127);
-		/* NOTREACHED */
-	}
-	(void)__unlockenv();
-
-	/* Parent; assume fdopen can't fail. */
-	if (*xtype == 'r') {
-		iop = fdopen(pdes[0], xtype);
-#ifdef _REENTRANT
-		cur->fd = pdes[0];
-#endif
-		(void)close(pdes[1]);
-	} else {
-		iop = fdopen(pdes[1], xtype);
-#ifdef _REENTRANT
-		cur->fd = pdes[1];
-#endif
-		(void)close(pdes[0]);
-	}
-
-	/* Link into list of file descriptors. */
-	cur->fp = iop;
-	cur->pid =  pid;
-	cur->next = pidlist;
-	pidlist = cur;
-	(void)rwlock_unlock(&pidlist_lock);
-
-	return (iop);
-}
-
-/*
- * pclose --
- *	Pclose returns -1 if stream is not associated with a `popened' command,
- *	if already `pclosed', or waitpid returns an error.
- */
-int
-pclose(FILE *iop)
-{
-	struct pid *cur, *last;
-	int pstat;
-	pid_t pid;
-
-	_DIAGASSERT(iop != NULL);
-
-	rwlock_wrlock(&pidlist_lock);
-
-	/* Find the appropriate file pointer. */
-	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
-		if (cur->fp == iop)
-			break;
-	if (cur == NULL) {
-		(void)rwlock_unlock(&pidlist_lock);
-		return (-1);
-	}
-
-	(void)fclose(iop);
-
-	/* Remove the entry from the linked list. */
-	if (last == NULL)
-		pidlist = cur->next;
-	else
-		last->next = cur->next;
-
-	(void)rwlock_unlock(&pidlist_lock);
-
-	do {
-		pid = waitpid(cur->pid, &pstat, 0);
-	} while (pid == -1 && errno == EINTR);
-
-	free(cur);
-
-	return (pid == -1 ? -1 : pstat);
-}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fwalk.c b/libc/upstream-openbsd/lib/libc/stdio/fwalk.c
deleted file mode 100644
index 4b1aa43..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/fwalk.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*	$OpenBSD: fwalk.c,v 1.12 2016/05/23 00:21:48 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include "local.h"
-#include "glue.h"
-
-int
-_fwalk(int (*function)(FILE *))
-{
-	FILE *fp;
-	int n, ret;
-	struct glue *g;
-
-	ret = 0;
-	for (g = &__sglue; g != NULL; g = g->next)
-		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) {
-			if ((fp->_flags != 0) && ((fp->_flags & __SIGN) == 0))
-				ret |= (*function)(fp);
-		}
-	return (ret);
-}
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 6f8c403..1ee8b53 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -73,27 +73,21 @@
   return g_envp;
 }
 
-namespace testing {
-namespace internal {
+static constexpr const char* COLOR_RESET  = "\033[m";
+static constexpr const char* COLOR_RED    = "\033[0;31m";
+static constexpr const char* COLOR_GREEN  = "\033[0;32m";
+static constexpr const char* COLOR_YELLOW = "\033[0;33m";
 
-// Reuse of testing::internal::ColoredPrintf in gtest.
-enum GTestColor {
-  COLOR_DEFAULT,
-  COLOR_RED,
-  COLOR_GREEN,
-  COLOR_YELLOW
-};
+static void ColoredPrintf(const char* const color, const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
 
-void ColoredPrintf(GTestColor color, const char* fmt, ...);
+    printf("%s", color);
+    vprintf(fmt, args);
+    printf("%s", COLOR_RESET);
 
-}  // namespace internal
-}  // namespace testing
-
-using testing::internal::GTestColor;
-using testing::internal::COLOR_RED;
-using testing::internal::COLOR_GREEN;
-using testing::internal::COLOR_YELLOW;
-using testing::internal::ColoredPrintf;
+    va_end(args);
+}
 
 constexpr int DEFAULT_GLOBAL_TEST_RUN_DEADLINE_MS = 90000;
 constexpr int DEFAULT_GLOBAL_TEST_RUN_SLOW_THRESHOLD_MS = 2000;
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 33514d4..6282ec3 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -22,6 +22,7 @@
 #include <math.h>
 #include <stdio.h>
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <wchar.h>
@@ -888,7 +889,7 @@
   ASSERT_EQ(-1, fclose(fp));
 }
 
-TEST(STDIO_TEST, popen) {
+TEST(STDIO_TEST, popen_r) {
   FILE* fp = popen("cat /proc/version", "r");
   ASSERT_TRUE(fp != NULL);
 
@@ -900,6 +901,63 @@
   ASSERT_EQ(0, pclose(fp));
 }
 
+TEST(STDIO_TEST, popen_socketpair) {
+  FILE* fp = popen("cat", "r+");
+  ASSERT_TRUE(fp != NULL);
+
+  fputs("hello\nworld\n", fp);
+  fflush(fp);
+
+  char buf[16];
+  ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+  EXPECT_STREQ("hello\n", buf);
+  ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+  EXPECT_STREQ("world\n", buf);
+
+  ASSERT_EQ(0, pclose(fp));
+}
+
+TEST(STDIO_TEST, popen_socketpair_shutdown) {
+  FILE* fp = popen("uniq -c", "r+");
+  ASSERT_TRUE(fp != NULL);
+
+  fputs("a\na\na\na\nb\n", fp);
+  fflush(fp);
+  ASSERT_EQ(0, shutdown(fileno(fp), SHUT_WR));
+
+  char buf[16];
+  ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+  EXPECT_STREQ("      4 a\n", buf);
+  ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+  EXPECT_STREQ("      1 b\n", buf);
+
+  ASSERT_EQ(0, pclose(fp));
+}
+
+TEST(STDIO_TEST, popen_return_value_0) {
+  FILE* fp = popen("true", "r");
+  ASSERT_TRUE(fp != NULL);
+  int status = pclose(fp);
+  EXPECT_TRUE(WIFEXITED(status));
+  EXPECT_EQ(0, WEXITSTATUS(status));
+}
+
+TEST(STDIO_TEST, popen_return_value_1) {
+  FILE* fp = popen("false", "r");
+  ASSERT_TRUE(fp != NULL);
+  int status = pclose(fp);
+  EXPECT_TRUE(WIFEXITED(status));
+  EXPECT_EQ(1, WEXITSTATUS(status));
+}
+
+TEST(STDIO_TEST, popen_return_value_signal) {
+  FILE* fp = popen("kill -7 $$", "r");
+  ASSERT_TRUE(fp != NULL);
+  int status = pclose(fp);
+  EXPECT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(7, WTERMSIG(status));
+}
+
 TEST(STDIO_TEST, getc) {
   FILE* fp = fopen("/proc/version", "r");
   ASSERT_TRUE(fp != NULL);