/* * popen_streambuf.hh * * Created on: Nov 27, 2014 * Author: Hovanes Egiyan * * Class to open an insput stream from a file descriptor * obtained from popen. I could not find a standard way * of doing this in C++, so I lifted this code from * http://stackoverflow.com/questions/1683051/file-and-istream-connect-the-two */ #ifndef POPEN_STREAMBUF_HH_ #define POPEN_STREAMBUF_HH_ #include #include #define BUFFER_SIZE 65536 class popen_streambuf : public std::streambuf { public: popen_streambuf() : fp(NULL) { } ~popen_streambuf() { close(); } popen_streambuf *open(const char *command, const char *mode) { fp = popen(command, mode); if (fp == NULL) return NULL; buffer = new char_type[BUFFER_SIZE]; // It's good to check because exceptions can be disabled if (buffer == NULL) { close(); return NULL; } setg(buffer, buffer, buffer); return this; } void close() { if (fp != NULL) { pclose(fp); fp = NULL; } } std::streamsize xsgetn(char_type *ptr, std::streamsize n) { std::streamsize got = showmanyc(); if (n <= got) { memcpy(ptr, gptr(), n * sizeof(char_type)); gbump(n); return n; } memcpy(ptr, gptr(), got * sizeof(char_type)); gbump(got); if (traits_type::eof() == underflow()) { return got; } return (got + xsgetn(ptr + got, n - got)); } int_type underflow() { if (gptr() == 0) { return traits_type::eof(); } if (gptr() < egptr()) { return traits_type::to_int_type(*gptr()); } size_t len = fread(eback(), sizeof(char_type), BUFFER_SIZE, fp); setg(eback(), eback(), eback() + (sizeof(char_type) * len)); if (0 == len) { return traits_type::eof(); } return traits_type::to_int_type(*gptr()); } std::streamsize showmanyc() { if (gptr() == 0) { return 0; } if (gptr() < egptr()) { return egptr() - gptr(); } return 0; } private: FILE *fp; char_type *buffer; }; #endif /* POPEN_STREAMBUF_HH_ */