-// FILE NAME: fcgio.cpp
//
-// $Id: fcgio.cpp,v 1.1 1999/08/15 03:37:59 roberts Exp $
+// $Id: fcgio.cpp,v 1.5 2001/06/22 14:27:06 robs Exp $
//
-// REWRITTEN BY: Michael Richards 06/20/1999
-// -added cin support
-// -added ability to replace cin/cout so existing code only
-// needs to include the header file and use the normal cin/cout
-// -added buffered read support which is required by getline
+// Allows you communicate with FastCGI streams using C++ iostream
+// objects
//
-// ORIGINAL AUTHOR: George Feinberg
+// ORIGINAL AUTHOR: George Feinberg
+// REWRITTEN BY: Michael Richards 06/20/1999
+// REWRITTEN AGAIN BY: Michael Shell 02/23/2000
//
-// NOTES: encapsulates FCGI function in an ostream and replaces cin,
-// cout and cerr
+// Special Thanks to Dietmar Kuehl for his help and the numerous custom
+// streambuf examples on his web site.
//
-// This work is based on routines written by George Feinberg. They
-// have been mostly re-written and extensively changed by
-// Michael Richards.
-//
-// Copyright (c) 1999 by Apollo Software. All rights reserved.
+// see the header file fcgio2.h for lotsa docs and a code example.
+//
+// Copyright (c) 2000 Tux the Linux Penguin
//
// You are free to use this software without charge or royalty
// as long as this notice is not removed or altered, and recognition
-// is given to the author(s)
-//
+// is given to the author(s)
+//
+// This code is offered as-is without any warranty either expressed or
+// implied; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.
+
+/*------------------------------------------------------------------*/
+
+#ifdef _WIN32
+#define DLLAPI __declspec(dllexport)
+#endif
-#include <fcgiapp.h>
#include "fcgio.h"
-// the definition that replaces cout and cin
-#ifndef USE_CIN_COUT_CERR
- fcgio_ostream *FCGIX_cout;
- fcgio_ostream *FCGIX_cerr;
- fcgio_istream *FCGIX_cin;
-#endif
-
-/*---------------------------------------------------------------------*/
-
-
-// gets a number of characters at once for a more efficient implementation
-int fcgio_buf::xsgetn(char *s, int n) {
- return FCGX_GetStr(s, n, fcgi_str);
-}
-
-
-// writes a gob of data out, This is called for things like:
-// output << "hello world"
-// output.write(string, n);
-int fcgio_buf::xsputn(const char* s, int n) {
- return FCGX_PutStr(s, n, fcgi_str);
-}
-
-
-// overflow is called for "endl" and every time the put buffer
-// is full. Since we are using a single char put buffer, we just
-// pump the waiting character out and return the number of chars
-// we just got rid of (1). Returns EOF on error
-int fcgio_buf::overflow(int c) {
- if (c)
- return FCGX_PutChar(c, fcgi_str);
- else
- return EOF;
-}
+// **** 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)
+ {
+ // Touch the args to avoid compiler warnings
+ s = NULL; n = 0;
+ 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
+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
-int fcgio_buf::underflow() {
- // if it is unbuffered, then get & return a char
- if (unbuffered())
- return FCGX_GetChar(fcgi_str);
+// is nothing else to read, this returns EOF. This is the only method
+// which reads from the FCGI interface.
+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
- int numread=FCGX_GetStr(eback(), blen(), fcgi_str);
- if (numread==0) return EOF;
- setg(eback(),eback(),eback()+numread);
+ // 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);
- return *eback();
-}
+ // 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()));
+ }
-// sets up any buffering used for input or output
-streambuf* fcgio_buf::setbuf(char *p, int n) {
- // if setbuf is offered a buffer, then use it, otherwise, set the stream to unbuffered
- setb(p,p+n,0);
- // output is not buffered
- setp(0,0);
- setg(p,p+n,p+n);
- unbuffered(p==NULL);
- return this;
-}
+// here comes the higher level iostream stuff
+// **** Istream
-// flush all the output using FCGX_FFlush
-int fcgio_buf::sync() {
- return FCGX_FFlush(fcgi_str);
-}
+// 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 ostream derived class. Also sets up buffering
-fcgio_ostream::fcgio_ostream(FCGX_Stream *str) : buf(str) {
- // init is a protected member of ios
- init(&buf);
- buf.setbuf(NULL,0);
-}
+// 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
-fcgio_istream::fcgio_istream(FCGX_Stream *str) : buf(str) {
+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(&buf);
- // set up the buffers
- buf.setbuf(buffer,buffersize);
-}
+ 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());
+ }