On Mon, Nov 05, 2001 at 07:57:47PM +0100, Lars Gullik Bjønnes wrote:
> | So either we provide two pipestream implementations (one for "old g++"
> | and one for "Standard C++") or we have to leave it. Forcing efverybody to
> | upgrade is certainly not an option...
> 
> You have seen the DebugStream right?

Yes.

> works with old and new. (et even worked with gcc 2.7.2 once upon a
> time)

The problem is, I don't have a working g++ 3.0 (just the sources), so
figuring out what is needed is a bit... difficult.

I'll attach my latest attempt which removes some of the differences, but
there is certainly still some way to go. Maybe someone with 3.0 could try
to get it working _somehow_ and we'll figure out what it takes to combine
it afterwards.

Andre'

PS: Btw, I am now using yesterday's pipestream in my own (2.95 based)
project and it is really cute... I got rid of most of the "system
interface" stuff ;-)

-- 
André Pönitz .............................................. [EMAIL PROTECTED]
/* This file is part of
 * ======================================================
 *
 *           LyX, The Document Processor
 *
 *           Copyright 2001 The LyX Team.
 *
 * ====================================================== */


#ifndef PIPESTREAM_H
#define PIPESTREAM_H

#include <iostream>

class pipestream : public std::iostream {
public:
        /// constructor taking the external command as argument
        explicit pipestream(const char * cmd);

private:
        /// unimplemented
        pipestream(pipestream const &);
        /// unimplemented
        void operator=(pipestream const &);
};

#endif
// pipestream.C -*- C++ -*- socket library
// Copyright(C) 1992,1993,1994 Gnanasekaran Swaminathan <[EMAIL PROTECTED]>
//
// Permission is granted to use at your own risk and distribute this software
// in source and binary forms provided the above copyright
// notice and this paragraph are preserved on all copies.
// This software is provided "as is" with no express or implied warranty.
//
// Version: 17Oct95 1.10


// You can simultaneously read and write into a pipebuf just like you
// can listen and talk through a telephone. Hence, the read and the write
// buffers are different. That is, they do not share the same memory.
//
// Read: gptr() points to the start of the get area.  The unread chars are
// gptr() - egptr().  base() points to the read buffer
//
// eback() is set to base() so that pbackfail() is called only when there
// is no place to putback a char. And pbackfail() always returns EOF.
//
// Write: pptr() points to the start of the put area The unflushed chars
// are pbase() - pptr() pbase() points to the write buffer.  epptr()
// points to the end of the write buffer.
//
// Output is flushed whenever one of the following conditions holds: (1)
// pptr() == epptr() (2) EOF is written (3) linebuffered and '\n' is
// written
//
// Unbuffered: Input buffer size is assumed to be of size 1 and output
// buffer is of size 0. That is, egptr() <= base()+1 and epptr() ==
// pbase().

#include <sys/types.h>
#include <sys/socket.h>

#include <cstddef>
#include <cstdio>
#include <unistd.h>
#include <errno.h>

#include "pipestream.h"

using std::streambuf;
using std::streamsize;

namespace {

void error(const char * msg)
{
        if (errno)
                perror(msg);
        std::cerr << msg << std::endl;
        errno = 0;
}

}



class pipebuf : public streambuf {

public:
#ifndef MODERN_STL_STREAMS
        typedef char char_type;
        typedef int  int_type;
#endif

public:
        /// The only constructor we need
        explicit pipebuf(int sock);
        /// no copying to save us ref counting sockets
        pipebuf(const pipebuf &);
        /// no assignment
        pipebuf & operator=(const pipebuf &);
        /// destructor
        ~pipebuf();
        ///
        streamsize xsputn(char_type const * s, streamsize n);

private:
        /// try to reduce the number of #ifdef in "client code"
        static int_type end_of_file();
        ///
        int_type overflow(int_type c = end_of_file());
        ///
        int_type underflow();
        ///
        int_type doallocate();
        ///
        int_type flush();
        ///

        ///
        static int const bufsize_ = 1024;
        /// our socket.
        int const sock_;
};



pipebuf::pipebuf(int sock)
        : streambuf(_IO_LINE_BUF), sock_(sock)
{}


pipebuf::~pipebuf()
{
        overflow();
        if (sock_ >= 0)
                ::close(sock_);
        delete [] base();
}


pipebuf::int_type pipebuf::end_of_file()
{
#ifdef MODERN_STL_STREAMS
        return traits_type::eof();
#else
        return EOF;
#endif
}


// return 0 when there is nothing to flush or when the flush is a success
// return EOF when it could not flush
int pipebuf::flush()
{
        if (pptr() <= pbase())
                return 0;

        streamsize len  = pptr() - pbase();
        streamsize rlen = len;
        streamsize wlen = 0;
        while (rlen > 0) {
                streamsize wval = ::write(sock_, pbase(), rlen);
                if (wval == -1) {
                        error("pipebuf::write");
                        break;
                }
                rlen -= wval;
                wlen += wval;
        }
        int status = (len == wlen) ? 0 : EOF;
        if (unbuffered())
                setp(pbase(), pbase());
        else
                setp(pbase(), pbase() + bufsize_);
        return status;
}


// return 1 on allocation and 0 if there is no need
int pipebuf::doallocate()
{
        if (base())
                return 0;

        char_type * buf = new char_type[2 * bufsize_];
        setb(buf, buf + bufsize_, 0);
        setg(buf, buf, buf);

        buf += bufsize_;
        setp(buf, buf + bufsize_);
        return 1;
}


int pipebuf::underflow()
{
        if (xflags() & _IO_NO_READS)
                return end_of_file();

        if (gptr() < egptr())
                return gptr() != 0;

        if (base() == 0 && doallocate() == 0)
                return end_of_file();

        streamsize len = unbuffered() ? 1 : bufsize_;
        int rval = ::read(sock_, base(), len);
        if (rval == -1)
                error("pipebuf::read");
        if (rval == 0)
                rval = end_of_file();

        if (rval == end_of_file()) {
                xsetflags(_IO_EOF_SEEN);
                return end_of_file();
        }

        if (rval == 0)
                return end_of_file();

        setg(eback(), base(), base() + rval);
        return gptr() != 0;
}


// if c == EOF, return flush();
// if c == '\n' and linebuffered, insert c and
// return (flush() == EOF) ? EOF : c;
// otherwise insert c into the buffer and return c
pipebuf::int_type pipebuf::overflow(int c)
{
        if (c == end_of_file())
                return flush();

        if (pbase() == 0 && doallocate() == 0)
                return end_of_file();

        if (pptr() >= epptr() && flush() == end_of_file())
                return end_of_file();

        xput_char(c);

        if ((unbuffered() || (linebuffered() && c == '\n') || pptr() >= epptr())
                        && flush() == end_of_file())
                return end_of_file();

        return c;
}


streamsize pipebuf::xsputn(char_type const * p, streamsize n)
{
        if (n <= 0)
                return 0;

        for (streamsize i = 0; i < n; i++, p++) {
                if (*p == '\n') {
                        if (overflow(*p) == end_of_file())
                                return i;
                } else
                        if (sputc(*p) == end_of_file())
                                return i;
        }
        return n;
}



/////////////////////////////////////////////////////////////////

extern char** environ;

namespace {

pipebuf * create(const char * cmd)
{
        // child closes sockets[1] and uses sockets[0]
        // parent closes sockets[0] and uses sockets[1]
        int sockets[2];
        if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
                error("pipestream: socketpair");
                return 0;
        }

        pid_t pid = vfork();
        //pid_t pid = fork();
        if (pid == -1) {
                error("pipestream: fork");
                return 0;
        }

        if (pid == 0) {
                // child process
                if (::close(sockets[1]) == -1)
                        error("pipestream: child close 1");
                if (::dup2(sockets[0], 1) == -1) {
                        error("pipestream: child dup2 1");
                        _exit(0x70);
                }
                if (::dup2(sockets[0], 0) == -1) {
                        error("pipestream: child dup2 0");
                        _exit(0x71);
                }
                if (::close(sockets[0]) == -1)
                        error("pipestream: child close 0");
                const char * argv[4];
                argv[0] = "/bin/sh";
                argv[1] = "-c";
                argv[2] = cmd;
                argv[3] = 0;
                execve("/bin/sh", const_cast<char**>(argv), environ);
                error("pipestream: execve");
                _exit(0x7f);
        }
        // parent process
        pipebuf * s = new pipebuf(sockets[1]);
        if (::close(sockets[0]) == -1)
                error("pipestream: parent close 0");
        return s;
}

} // end anon namespace


pipestream::pipestream(const char * cmd)
        : ios(create(cmd))
{}



#ifdef PIPESTREAM_MAIN

#include <string>

int main()
{
        if (1) {
                pipestream ps("maple -q");
                ps << "1+2;\n";
                std::string result;
                ps >> result;
                std::cout << "res: " << result << "\n";
        }

        if (1) {
                pipestream ps("ls -la");
                while (ps) {
                        std::string line;
                        getline(ps, line);
                        std::cout << "res: " << line << "\n";
                }
        }

        return 0;
}
        

#endif // PIPESTREAM_MAIN

Reply via email to