diff --git a/libc/Makefile b/libc/Makefile
index cfc355ff..42c62ebe 100644
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -200,6 +200,7 @@ openat.o \
open.o \
pipe.o \
poll.o \
+popen.o \
ppoll.o \
print.o \
putc.o \
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 82a0eb6d..a9439820 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -1,6 +1,6 @@
/*******************************************************************************
- Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
+ Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of the Sortix C Library.
@@ -97,7 +97,9 @@ extern int getc(FILE* stream);
extern int getchar(void);
extern ssize_t getdelim(char** restrict lineptr, size_t* restrict n, int delimiter, FILE* restrict stream);
extern ssize_t getline(char** restrict lineptr, size_t* restrict n, FILE* restrict stream);
+extern int pclose(FILE* steam);
extern void perror(const char* s);
+extern FILE* popen(const char* command, const char* mode);
extern int printf(const char* restrict format, ...);
extern int putc(int c, FILE* stream);
extern int putchar(int c);
@@ -128,7 +130,6 @@ extern int vsscanf(const char* restrict s, const char* restrict format, __gnuc_v
extern char* ctermid(char* s);
extern FILE *fmemopen(void* restrict buf, size_t size, const char* restrict mode);
extern FILE* open_memstream(char** bufp, size_t* sizep);
-extern FILE* popen(const char* command, const char* mode);
extern FILE* tmpfile(void);
extern int dprintf(int fildes, const char* restrict format, ...);
extern int fgetpos(FILE* restrict stream, fpos_t* restrict pos);
@@ -136,7 +137,6 @@ extern int fsetpos(FILE* stream, const fpos_t* pos);
extern int ftrylockfile(FILE* file);
extern int getchar_unlocked(void);
extern int getc_unlocked(FILE* stream);
-extern int pclose(FILE* steam);
extern int putchar_unlocked(int c);
extern int putc_unlocked(int c, FILE* steam);
extern int setvbuf(FILE* restrict stream, char* restrict buf, int type, size_t size);
diff --git a/libc/popen.cpp b/libc/popen.cpp
new file mode 100644
index 00000000..04f7b6ce
--- /dev/null
+++ b/libc/popen.cpp
@@ -0,0 +1,231 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2013.
+
+ This file is part of the Sortix C Library.
+
+ The Sortix C Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ The Sortix C Library is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with the Sortix C Library. If not, see .
+
+ popen.cpp
+ Pipe stream to or from a process.
+
+*******************************************************************************/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+const int POPEN_WRITING = 1 << 0;
+const int POPEN_READING = 1 << 1;
+const int POPEN_CLOEXEC = 1 << 2;
+const int POPEN_ERROR = 1 << 3;
+const int POPEN_EOF = 1 << 4;
+
+typedef struct popen_struct
+{
+ int flags;
+ int fd;
+ pid_t pid;
+} popen_t;
+
+static size_t popen_read(void* ptr, size_t size, size_t nmemb, void* user)
+{
+ uint8_t* buf = (uint8_t*) ptr;
+ popen_t* info = (popen_t*) user;
+ if ( !(info->flags & POPEN_READING) )
+ return errno = EBADF, 0;
+ size_t sofar = 0;
+ size_t total = size * nmemb;
+ while ( sofar < total )
+ {
+ ssize_t numbytes = read(info->fd, buf + sofar, total - sofar);
+ if ( numbytes < 0 ) { info->flags |= POPEN_ERROR; break; }
+ if ( numbytes == 0 ) { info->flags |= POPEN_EOF; break; }
+ return numbytes / size;
+ sofar += numbytes;
+ }
+ return sofar / size;
+}
+
+static size_t popen_write(const void* ptr, size_t size, size_t nmemb, void* user)
+{
+ const uint8_t* buf = (const uint8_t*) ptr;
+ popen_t* info = (popen_t*) user;
+ if ( !(info->flags & POPEN_WRITING) )
+ return errno = EBADF, 0;
+ size_t sofar = 0;
+ size_t total = size * nmemb;
+ while ( sofar < total )
+ {
+ ssize_t numbytes = write(info->fd, buf + sofar, total - sofar);
+ if ( numbytes < 0 ) { info->flags |= POPEN_ERROR; break; }
+ if ( numbytes == 0 ) { info->flags |= POPEN_EOF; break; }
+ sofar += numbytes;
+ }
+ return sofar / size;
+}
+
+static int popen_seek(void* /*user*/, off_t /*offset*/, int /*whence*/)
+{
+ return errno = ESPIPE, -1;
+}
+
+static off_t popen_tell(void* /*user*/)
+{
+ return errno = ESPIPE, -1;
+}
+
+static void popen_seterr(void* user)
+{
+ popen_t* info = (popen_t*) user;
+ info->flags |= POPEN_ERROR;
+}
+
+static void popen_clearerr(void* user)
+{
+ popen_t* info = (popen_t*) user;
+ info->flags &= ~POPEN_ERROR;
+}
+
+static int popen_eof(void* user)
+{
+ popen_t* info = (popen_t*) user;
+ return info->flags & POPEN_EOF;
+}
+
+static int popen_error(void* user)
+{
+ popen_t* info = (popen_t*) user;
+ return info->flags & POPEN_ERROR;
+}
+
+static int popen_fileno(void* user)
+{
+ popen_t* info = (popen_t*) user;
+ return info->fd;
+}
+
+static int popen_close(void* user)
+{
+ popen_t* info = (popen_t*) user;
+ if ( close(info->fd) )
+ {
+ /* TODO: Should this return value be discarded? */;
+ }
+ int status;
+ if ( waitpid(info->pid, &status, 0) < 0 )
+ status = -1;
+ free(info);
+ return status;
+}
+
+static int parse_popen_type(const char* type)
+{
+ int ret = 0;
+ switch ( *type++ )
+ {
+ case 'r': ret = POPEN_READING; break;
+ case 'w': ret = POPEN_WRITING; break;
+ default: return errno = -EINVAL, -1;
+ }
+ while ( char c = *type++ )
+ switch ( c )
+ {
+ case 'e': ret |= POPEN_CLOEXEC; break;
+ default: return errno = -EINVAL, -1;
+ }
+ return ret;
+}
+
+void popen_install(FILE* fp, popen_t* info)
+{
+ fp->user = info;
+ fp->read_func = popen_read;
+ fp->write_func = popen_write;
+ fp->seek_func = popen_seek;
+ fp->tell_func = popen_tell;
+ fp->seterr_func = popen_seterr;
+ fp->clearerr_func = popen_clearerr;
+ fp->eof_func = popen_eof;
+ fp->error_func = popen_error;
+ fp->fileno_func = popen_fileno;
+ fp->close_func = popen_close;
+ fp->flags |= _FILE_STREAM;
+}
+
+extern "C" FILE* popen(const char* command, const char* type)
+{
+ int flags, used_end, unused_end, redirect_what;
+ int pipefds[2];
+ popen_t* info;
+ FILE* fp;
+ pid_t childpid;
+
+ if ( (flags = parse_popen_type(type)) < 0 )
+ goto cleanup_out;
+ if ( !(info = (popen_t*) calloc(1, sizeof(popen_t))) )
+ goto cleanup_out;
+ if ( !(fp = fnewfile()) )
+ goto cleanup_info;
+ if ( pipe(pipefds) )
+ goto cleanup_fp;
+ if ( (childpid = fork()) < 0 )
+ goto cleanup_pipes;
+
+ if ( childpid )
+ {
+ used_end = flags & POPEN_WRITING ? 1 /*writing*/ : 0 /*reading*/;
+ unused_end = flags & POPEN_WRITING ? 0 /*reading*/ : 1 /*writing*/;
+ close(pipefds[unused_end]);
+ if ( flags & POPEN_CLOEXEC )
+ fcntl(pipefds[used_end], F_SETFL, FD_CLOEXEC);
+ info->fd = pipefds[used_end];
+ info->flags = flags;
+ info->pid = childpid;
+ popen_install(fp, info);
+ return fp;
+ }
+
+ redirect_what = flags & POPEN_WRITING ? 0 /*stdin*/ : 1 /*stdout*/;
+ used_end = flags & POPEN_WRITING ? 0 /*reading*/ : 1 /*writing*/;
+ unused_end = flags & POPEN_WRITING ? 1 /*writing*/ : 0 /*reading*/;
+ if ( dup2(pipefds[used_end], redirect_what) < 0 ||
+ close(pipefds[used_end]) ||
+ close(pipefds[unused_end]) )
+ _exit(127);
+
+ execlp("sh", "sh", "-c", command, NULL);
+ _exit(127);
+
+cleanup_pipes:
+ close(pipefds[0]);
+ close(pipefds[1]);
+cleanup_fp:
+ fclose(fp);
+cleanup_info:
+ free(info);
+cleanup_out:
+ return NULL;
+}
+
+extern "C" int pclose(FILE* fp)
+{
+ return fclose(fp);
+}