fix copyright information in these files
[catagits/fcgi2.git] / libfcgi / fcgio.cpp
index 3ca1f69..5a54c11 100644 (file)
@@ -1,17 +1,12 @@
 //
-// $Id: fcgio.cpp,v 1.3 2000/07/27 12:21:54 robs Exp $
+// $Id: fcgio.cpp,v 1.14 2003/06/22 00:51:27 robs Exp $
 //
-// Allows you communicate with FastCGI streams using C++ iostream
-// objects
+// Allows you communicate with FastCGI streams using C++ iostreams
 //
 // ORIGINAL AUTHOR:     George Feinberg
 // REWRITTEN BY:        Michael Richards  06/20/1999
 // REWRITTEN AGAIN BY:  Michael Shell     02/23/2000
-//
-// Special Thanks to Dietmar Kuehl for his help and the numerous custom
-// streambuf examples on his web site.
-//
-// see the header file fcgio2.h for lotsa docs and a code example.
+// REWRITTEN AGAIN BY:  Rob Saccoccio     11 Nov 2001
 //
 // Copyright (c) 2000 Tux the Linux Penguin
 //
 // implied; without even the implied warranty of MERCHANTABILITY or
 // FITNESS FOR A PARTICULAR PURPOSE.
 
-/*------------------------------------------------------------------*/
-
+#ifdef _WIN32
+#define DLLAPI  __declspec(dllexport)
+#endif
 
+#include <limits.h>
 #include "fcgio.h"
 
+using std::streambuf;
+using std::istream;
+using std::ostream;
+using std::streamsize;
+
+fcgi_streambuf::fcgi_streambuf(FCGX_Stream * fs, char * b, int bs)
+{
+    init(fs, b, bs);
+}
+    
+fcgi_streambuf::fcgi_streambuf(char_type * b, streamsize bs)
+{
+    init(0, b, bs);
+}
+    
+fcgi_streambuf::fcgi_streambuf(FCGX_Stream * fs) 
+{ 
+    init(fs, 0, 0);
+}
+
+fcgi_streambuf::~fcgi_streambuf(void)
+{
+    overflow(EOF);
+    // FCGX_Finish()/FCGX_Accept() will flush and close
+}
+
+void fcgi_streambuf::init(FCGX_Stream * fs, char_type * b, streamsize bs)
+{
+    this->fcgx = fs;
+    this->buf = 0;
+    this->bufsize = 0;
+    setbuf(b, bs);    
+}
 
-// **** fcgio_streambuf
-
-// default constructor
-fcgi_streambuf::fcgi_streambuf(void)
-   {
-   // we setup the buffer
-   setb(buffer,buffer + (buffersize * sizeof(char)),0);
-   // but we do not know yet if we are a reader or
-   // a writer
-   setp(0,0);
-   setg(0,0,0);
-
-   // good idea to declare buffer status
-   if (buffersize < 1) unbuffered(1);
-   else unbuffered(0);
-
-   // disarm till initialized
-   fcgx_strm = NULL;
-   defined = 0;
-   }
-
-
-// destructor
-fcgi_streambuf::~fcgi_streambuf(void) {}
-
-
-// do nothing
-int fcgi_streambuf::doallocate()
-   {
-   return (0);
-   }
-
-
-// drain the get area
-// this method provides an advanced feature and is
-// considered experimental, see the header file for docs
-// it is provided as it may save somebody someday
-int fcgi_streambuf::drain_strm(char *s, int n)
-   {
-   int numinget;     // number of chars in get buffer
-   int numtoextract; // number of chars to pull out of the
-                     // get area
-
-   int i;            // counter
-
-   // no meaning if not initialized
-   if (!defined) return (EOF);
-   // is already drained if no buffer
-   if (unbuffered()) return (0);
-
-   numinget = egptr() - gptr(); // calculate how much stuff is in
-                                // the get buffer
-
-   // bogus requests deserve bogus answers
-   if (n<1) return (0);
-
-   // an empty get area is an already drained get area
-   if (numinget<1) return(0);
-
-   // the number we extract is the lesser of the number of chars
-   // in the get area and the number of chars the user's array
-   // can hold. Maybe should use a ? operator here.
-   if (numinget<n) numtoextract = numinget;
-   else numtoextract = n;
-
-   // copy over the data
-   // adjust the get pointer
-   // probably could use memcpy() to speed things up,
-   // however this may not be portable and if you are
-   // using drain(), performance is gonna take a back seat
-   for (i=0;i<numtoextract;i++)
-       {
-       s[i] = *gptr();
-       gbump(1);
-       }
-   // all done
-   return (numtoextract);
-   }
-
-
-// little routine so that you can tell if the streambuf
-// was ever initialized
-int fcgi_streambuf::isstrmdefined(void)
-   {
-   return defined;
-   }
-
-
-// overflow is called for flush and every time the put buffer
-// is full. Returns EOF on error
-// This is the only method that actually writes data to
-// the FCGI interface.
 int fcgi_streambuf::overflow(int c)
-  {
-  int numinbuf; // number of chars in put buffer
-
-  // we will not allow a bogus fcgx_strm (because the user
-  // forgot to attach()) to do even more harm.
-  if (!defined) return (EOF);
-
-  // get the number of chars in the put buffer
-  numinbuf = pptr() - pbase();
-
-  // send out the entire put buffer
-  if (numinbuf > 0 && !unbuffered())
-     if (FCGX_PutStr(pbase(), numinbuf, fcgx_strm) < 0) return (EOF);
-
-  // reset the put buffer to empty
-  setp(pbase(),epptr());
-
-  // if we are given an overflow char, send it out too.
-  if (c >= 0)
-     if (FCGX_PutChar(c, fcgx_strm) < 0) return (EOF);
-
-  // all done
-  return (0);
-  }
-
-
-// we have our own methods to setup buffering
-// in case somebody calls this, ignore it
-streambuf * fcgi_streambuf::setbuf(char *s, int n)
-   {
-   // tell them what they want to hear to make them
-   // go away
-   return this;
-   }
-
-
-// just sets up the pointer and declares things defined.
-// used by fcgi_iostream attach()
-void fcgi_streambuf::stream_initialize(FCGX_Stream *str, int dir)
-   {
-   // setup the main buffer
-   setb(buffer,buffer + (buffersize * sizeof(char)),0);
-   setp(0,0);
-   setg(0,0,0);
-
-   if (buffersize < 1) unbuffered(1);
-   else // setup the get or put buffers
-      {
-      unbuffered(0);
-      if (dir) // if writer
-         {
-         setg(0,0,0); // no get buffer
-         setp(base(),ebuf()); // buffer is all put
-         }
-      else // reader
-         {
-         setg(base(),ebuf(),ebuf()); // buffer is all get
-         setp(0,0); // no put buffer
-         }
-      }
-
-   // set the FCGI interface
-   fcgx_strm = str;
-   // we are ready for action
-   defined = 1;
-   }
-
-
-// flush all the output
+{
+    if (this->bufsize)
+    {
+        int plen = pptr() - pbase();
+
+        if (plen) 
+        {
+            if (FCGX_PutStr(pbase(), plen, this->fcgx) != plen) return EOF;
+            pbump(-plen);
+        }
+    }
+
+    if (c != EOF) 
+    {
+        if (FCGX_PutChar(c, this->fcgx) != c) return EOF;
+    }
+
+    return 0;
+}
+
+// default base class behaviour seems to be inconsistent
 int fcgi_streambuf::sync()
-   {
-   // flush the put area
-   if (overflow(-1) < 0) return (EOF);
-
-   // now flush FCGX
-   return FCGX_FFlush(fcgx_strm);
-   }
-
-
-// Underflow is called to get characters to fill up the get buffer
-// so something that uses the stream can get the data. If there
-// is nothing else to read, this returns EOF. This is the only method
-// which reads from the FCGI interface.
+{
+    if (overflow(EOF)) return EOF;
+    if (FCGX_FFlush(this->fcgx)) return EOF;
+    return 0;
+}
+
+// uflow() removes the char, underflow() doesn't
+int fcgi_streambuf::uflow() 
+{
+    if (this->bufsize)
+    {
+        int c = underflow();        
+        gbump(1);
+        return c;
+    }
+    else
+    {
+        return FCGX_GetChar(this->fcgx);
+    }
+}
+                               
 int fcgi_streambuf::underflow()
-   {
-   int numread;
-
-   // we will not allow a bogus fcgx_strm (because the user
-   // forgot to attach()) to do even more harm.
-   if (!defined) return (EOF);
-
-   // if it is unbuffered, then get & return a char
-   if (unbuffered()) return FCGX_GetChar(fcgx_strm);
-
-   // read as much data as we can, then adjust the get area so it
-   // reflects the data we just read
-   numread=FCGX_GetStr(base(), blen(), fcgx_strm);
-   if (numread<=0) return EOF;
-   setg(base(),base(),base()+numread);
-
-   // We avoid a common bug. You NEED the unsigned char cast or
-   // else this routine will return a false EOF when reading
-   // control chars outside the normal ascii range!
-   // i.e. "negative" chars map to positive ints and we
-   // reserve negative return values for EOF and error
-   // conditions. Now, even binary data will come through
-   // perfectly.
-   // The reason the get buffer uses chars rather
-   // than unsigned chars is a mystery from AT&T history.
-   // It probably has to due with the old 7bit text limits.
-   return ((unsigned char)(*eback()));
-   }
-
-
-
-// here comes the higher level iostream stuff
-
-// **** Istream
-
-// parameterless constructor allows us to create first, then
-// initialize later via attach()
-fcgi_istream::fcgi_istream() {}
-
-
-// or we can create and initialize in one step
-// constructor calls ios::init to assign our streambuf to
-// the istream derived class. Also sets up buffering.
-fcgi_istream::fcgi_istream(FCGX_Stream *str)
-   {
-   // initialize as a reader, use all buffering for the
-   // get area
-   fcgi_strmbuf.stream_initialize(str,0);
-   // init is a protected member of ios
-   init(&fcgi_strmbuf);
-   }
-
-
-// destructor
-fcgi_istream::~fcgi_istream() {}
-
-
-// does everything the constructor with parameter does at the
-// request of the programmer
-void fcgi_istream::attach(FCGX_Stream *str)
-   {
-   // initialize as a reader, use all buffering for the
-   // get area
-   fcgi_strmbuf.stream_initialize(str,0);
-   // init is a protected member of ios
-   init(&fcgi_strmbuf);
-   }
-
-
-// experimental method to drain the get buffer. Allows you
-// to sync reads with FCGI library (non-istream reads).
-// reads upto n chars into the s array from the get buffer, does
-// not call underflow(). Returned is the number of chars extracted
-// or EOF on error. If n>0 (normal use) and returns a nonnegative
-// value less than n, the get buffer is empty and it is safe to
-// use an FCGI library read.
-// see the header file for more info
-int fcgi_istream::drain(char *s, int n)
-   {
-   return (fcgi_strmbuf.drain_strm(s,n));
-   }
-
-
-// little routine so that you can tell if the streambuf
-// was ever defined
-int fcgi_istream::isdefined(void)
-   {
-   return (fcgi_strmbuf.isstrmdefined());
-   }
-
-
-
-// **** Ostream
-
-// parameterless constructor allows us to create first, then
-// initialize later via attach()
-fcgi_ostream::fcgi_ostream() {}
-
-
-// or we can create and initialize in one step
-// constructor calls ios::init to assign our streambuf to
-// the ostream derived class. Also sets up buffering
-fcgi_ostream::fcgi_ostream(FCGX_Stream *str)
-  {
-  // initialize as a writer, use all buffering for the
-  // put area
-  fcgi_strmbuf.stream_initialize(str,1);
-  // init is a protected member of ios
-  init(&fcgi_strmbuf);
-  }
-
-
-// destructor
-fcgi_ostream::~fcgi_ostream()
-   {
-   // don't blowup if the user never defined the
-   // stream
-   if (fcgi_strmbuf.isstrmdefined())
-      {
-      // this may protect a few poor souls who forgot to flush
-      // before deleting. Always flush when you are done.
-      fcgi_strmbuf.sync();
-      }
-   }
-
-
-// does everything the constructor with parameter does at the
-// request of the programmer
-void fcgi_ostream::attach(FCGX_Stream *str)
-   {
-   // initialize as a writer, use all buffering for the
-   // put area
-   fcgi_strmbuf.stream_initialize(str,1);
-   // init is a protected member of ios
-   init(&fcgi_strmbuf);
-   }
-
-
-// helper function to find out if this
-// stream was ever initialized.
-int fcgi_ostream::isdefined(void)
-   {
-   return (fcgi_strmbuf.isstrmdefined());
-   }
-
+{
+    if (this->bufsize)
+    {
+        if (in_avail() == 0)
+        {
+            int glen = FCGX_GetStr(eback(), this->bufsize, this->fcgx);
+            if (glen <= 0) return EOF;
+
+            setg(eback(), eback(), eback() + glen);
+        }
+
+        return (unsigned char) *gptr();       
+    }
+    else
+    {
+        return FCGX_UnGetChar(FCGX_GetChar(this->fcgx), this->fcgx);
+    } 
+}
+
+void fcgi_streambuf::reset(void)
+{
+    // it should be ok to set up both the get and put areas
+    setg(this->buf, this->buf, this->buf);
+    setp(this->buf, this->buf + this->bufsize);
+}
+
+std::streambuf * fcgi_streambuf::setbuf(char_type * b, streamsize bs)
+{
+    // XXX support moving data from an old buffer
+    if (this->bufsize) return 0;
+
+    this->buf = b;
+    this->bufsize = bs;
+
+    // the base setbuf() *has* to be called
+    streambuf::setbuf(b, bs);
+
+    reset();
+
+    return this;
+}
+
+int fcgi_streambuf::attach(FCGX_Stream * fs)
+{ 
+    this->fcgx = fs;
+
+    if (this->bufsize)
+    {
+        reset();
+    }
+
+    return 0;
+}
+
+streamsize fcgi_streambuf::xsgetn(char_type * s, streamsize n) 
+{
+    if (n > INT_MAX) return 0;
+    return (this->bufsize) 
+        ? streambuf::xsgetn(s, n) 
+        : (streamsize) FCGX_GetStr((char *) s, (int) n, this->fcgx);
+}
+   
+streamsize fcgi_streambuf::xsputn(const char_type * s, streamsize n) 
+{
+    if (n > INT_MAX) return 0;
+    return (this->bufsize) 
+        ? streambuf::xsputn(s, n) 
+        : (streamsize) FCGX_PutStr((char *) s, (int) n, this->fcgx);
+}
+
+// deprecated
+fcgi_istream::fcgi_istream(FCGX_Stream * fs) :
+    istream(&fcgi_strmbuf)
+{
+    fcgi_strmbuf.attach(fs);
+}
+
+// deprecated
+void fcgi_istream::attach(FCGX_Stream * fs)
+{
+    fcgi_strmbuf.attach(fs);
+}
+
+// deprecated
+fcgi_ostream::fcgi_ostream(FCGX_Stream * fs) :
+    ostream(&fcgi_strmbuf)
+{
+    fcgi_strmbuf.attach(fs);
+}
+
+// deprecated
+void fcgi_ostream::attach(FCGX_Stream * fs)
+{
+    fcgi_strmbuf.attach(fs);
+}