diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 0d957168..a89ec591 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -27,6 +27,7 @@ typedef struct int mode; // The buffering mode. } _buf; int _flags; // The file access mode with which the file was opened. + pid_t _pid; // For popen(3) files, the pid of the child process. } FILE; #define EOF -1 @@ -52,6 +53,7 @@ extern "C" { #endif + /* Flush a stream's buffers. */ int fflush(FILE*); /* Open a file and bind a stream to it. */ @@ -201,6 +203,9 @@ extern "C" /* Move a file's location across a file system. */ int rename(const char* oldpath, const char* newpath); + /* Pipe a stream to or from a process. */ + FILE* popen(const char* command, const char* type); + #ifdef __cplusplus } #endif diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index 77fce1db..a4308457 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include FILE* stdin = nullptr; @@ -761,4 +762,87 @@ extern "C" unlink(oldpath); return 0; } + + FILE* popen(const char* command, const char* type) + { + int pfds[2]; + if (pipe(pfds) < 0) return nullptr; + + if (*type != 'r' && *type != 'w') + { + errno = EINVAL; + return nullptr; + } + + pid_t child = fork(); + if (child < 0) + { + close(pfds[0]); + close(pfds[1]); + return nullptr; + } + if (child == 0) + { + if (*type == 'r') + { + close(pfds[0]); + dup2(pfds[1], STDOUT_FILENO); + } + else + { + close(pfds[1]); + dup2(pfds[0], STDIN_FILENO); + } + + execl("/bin/sh", "sh", "-c", command, nullptr); + _exit(127); + } + + int fd; + if (*type == 'r') + { + close(pfds[1]); + fd = pfds[0]; + } + else + { + close(pfds[0]); + fd = pfds[1]; + } + + int err = errno; + FILE* f = (FILE*)malloc(sizeof(FILE)); + if (!f) + { + errno = err; + close(fd); + return nullptr; + } + + f->_fd = fd; + f->_pid = child; + clearerr(f); + + f->_flags = *type == 'r' ? O_RDONLY : O_WRONLY; + f->_buf.status = 0; + f->_buf.mode = _IOFBF; + f->_buf.size = f->_buf.index = 0; + f->_buf.buffer = nullptr; + setvbuf(f, NULL, f->_buf.mode, 0); + + s_open_files[fd] = f; + + return f; + } + + int pclose(FILE* stream) + { + pid_t pid = stream->_pid; + fclose(stream); + + int status; + if (waitpid(pid, &status, 0) < 0) return -1; + + return status; + } }